Merged branch 'jetty-9.3.x' into 'jetty-9.4.x'.

This commit is contained in:
Simone Bordet 2016-07-15 18:12:02 +02:00
commit 6adb5eb031
6 changed files with 271 additions and 184 deletions

View File

@ -271,7 +271,9 @@ public class MultiPartContentProvider extends AbstractTypedContentProvider imple
continue;
buffer.write(field.getName().getBytes(StandardCharsets.US_ASCII));
buffer.write(COLON_SPACE_BYTES);
buffer.write(field.getValue().getBytes(StandardCharsets.UTF_8));
String value = field.getValue();
if (value != null)
buffer.write(value.getBytes(StandardCharsets.UTF_8));
buffer.write(CR_LF_BYTES);
}
buffer.write(CR_LF_BYTES);

View File

@ -83,6 +83,9 @@ public class HttpField
public String[] getValues()
{
if (_value == null)
return null;
ArrayList<String> list = new ArrayList<>();
int state = 0;
int start=0;
@ -191,8 +194,8 @@ public class HttpField
return list.toArray(new String[list.size()]);
}
/* ------------------------------------------------------------ */
/** Look for a value in a possible multi valued field
/**
* Look for a value in a possible multi valued field
* @param search Values to search for (case insensitive)
* @return True iff the value is contained in the field value entirely or
* as an element of a quoted comma separated list. List element parameters (eg qualities) are ignored,
@ -412,7 +415,7 @@ public class HttpField
@Override
public int hashCode()
{
int vhc = _value==null?0:_value.hashCode();
int vhc = Objects.hashCode(_value);
if (_header==null)
return vhc ^ nameHashCode();
return vhc ^ _header.hashCode();

View File

@ -35,9 +35,8 @@ import org.eclipse.jetty.util.Trie;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
/** HPACK - Header Compression for HTTP/2
/**
* HPACK - Header Compression for HTTP/2
* <p>This class maintains the compression context for a single HTTP/2
* connection. Specifically it holds the static and dynamic Header Field Tables
* and the associated sizes and limits.
@ -343,24 +342,13 @@ public class HpackContext
return String.format("HpackContext@%x{entries=%d,size=%d,max=%d}",hashCode(),_dynamicTable.size(),_dynamicTableSizeInBytes,_maxDynamicTableSizeInBytes);
}
/* ------------------------------------------------------------ */
/**
*/
private class DynamicTable extends ArrayQueue<HpackContext.Entry>
{
/* ------------------------------------------------------------ */
/**
* @param initCapacity
* @param growBy
*/
private DynamicTable(int initCapacity, int growBy)
{
super(initCapacity,growBy);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.ArrayQueue#growUnsafe()
*/
@ -373,7 +361,6 @@ public class HpackContext
((Entry)_elements[s])._slot=s;
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.ArrayQueue#enqueue(java.lang.Object)
*/
@ -383,7 +370,6 @@ public class HpackContext
return super.enqueue(e);
}
/* ------------------------------------------------------------ */
/**
* @see org.eclipse.jetty.util.ArrayQueue#dequeue()
*/
@ -393,23 +379,12 @@ public class HpackContext
return super.dequeue();
}
/* ------------------------------------------------------------ */
/**
* @param entry
* @return
*/
private int index(Entry entry)
{
return entry._slot>=_nextE?_size-entry._slot+_nextE:_nextSlot-entry._slot;
}
}
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
/* ------------------------------------------------------------ */
public static class Entry
{
final HttpField _field;
@ -435,7 +410,8 @@ public class HpackContext
public int getSize()
{
return 32+_field.getName().length()+_field.getValue().length();
String value = _field.getValue();
return 32 + _field.getName().length() + (value == null ? 0 : value.length());
}
public HttpField getHttpField()
@ -510,6 +486,4 @@ public class HpackContext
return _encodedField;
}
}
}

View File

@ -24,6 +24,7 @@ import org.eclipse.jetty.http.BadMessageException;
import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpVersion;
@ -39,10 +40,8 @@ public class MetaDataBuilder
private HostPortHttpField _authority;
private String _path;
private long _contentLength=Long.MIN_VALUE;
private HttpFields _fields = new HttpFields(10);
/* ------------------------------------------------------------ */
/**
* @param maxHeadersSize The maximum size of the headers, expressed as total name and value characters.
*/
@ -69,61 +68,73 @@ public class MetaDataBuilder
public void emit(HttpField field)
{
int field_size = field.getName().length()+field.getValue().length();
HttpHeader header = field.getHeader();
String name = field.getName();
String value = field.getValue();
int field_size = name.length() + (value == null ? 0 : value.length());
_size+=field_size;
if (_size>_maxSize)
throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,"Header size "+_size+">"+_maxSize);
if (field instanceof StaticTableHttpField)
{
StaticTableHttpField value = (StaticTableHttpField)field;
switch(field.getHeader())
StaticTableHttpField staticField = (StaticTableHttpField)field;
switch(header)
{
case C_STATUS:
_status=(Integer)value.getStaticValue();
_status=(Integer)staticField.getStaticValue();
break;
case C_METHOD:
_method=field.getValue();
_method=value;
break;
case C_SCHEME:
_scheme = (HttpScheme)value.getStaticValue();
_scheme = (HttpScheme)staticField.getStaticValue();
break;
default:
throw new IllegalArgumentException(field.getName());
throw new IllegalArgumentException(name);
}
}
else if (field.getHeader()!=null)
else if (header!=null)
{
switch(field.getHeader())
switch(header)
{
case C_STATUS:
_status=field.getIntValue();
break;
case C_METHOD:
_method=field.getValue();
_method=value;
break;
case C_SCHEME:
_scheme = HttpScheme.CACHE.get(field.getValue());
if (value != null)
_scheme = HttpScheme.CACHE.get(value);
break;
case C_AUTHORITY:
_authority=(field instanceof HostPortHttpField)?((HostPortHttpField)field):new AuthorityHttpField(field.getValue());
if (field instanceof HostPortHttpField)
_authority = (HostPortHttpField)field;
else if (value != null)
_authority = new AuthorityHttpField(value);
break;
case HOST:
// :authority fields must come first. If we have one, ignore the host header as far as authority goes.
if (_authority==null)
_authority=(field instanceof HostPortHttpField)?((HostPortHttpField)field):new AuthorityHttpField(field.getValue());
{
if (field instanceof HostPortHttpField)
_authority = (HostPortHttpField)field;
else if (value != null)
_authority = new AuthorityHttpField(value);
}
_fields.add(field);
break;
case C_PATH:
_path = field.getValue();
_path = value;
break;
case CONTENT_LENGTH:
@ -132,13 +143,14 @@ public class MetaDataBuilder
break;
default:
if (field.getName().charAt(0)!=':')
if (name.charAt(0)!=':')
_fields.add(field);
break;
}
}
else
{
if (field.getName().charAt(0)!=':')
if (name.charAt(0)!=':')
_fields.add(field);
}
}
@ -168,8 +180,8 @@ public class MetaDataBuilder
}
}
/* ------------------------------------------------------------ */
/** Check that the max size will not be exceeded.
/**
* Check that the max size will not be exceeded.
* @param length the length
* @param huffman the huffman name
*/

View File

@ -943,14 +943,14 @@ public class HttpOutput extends ServletOutputStream implements Runnable
switch(_state.get())
{
case CLOSED:
// Even though a write is not possible, because a close has
// occurred, we need to call onWritePossible to tell async
// producer that the last write completed.
// So fall through
case ASYNC:
case READY:
case PENDING:
case UNREADY:
case READY:
// Even though a write is not possible, because a close has
// occurred, we need to call onWritePossible to tell async
// producer that the last write completed, so fall through.
case CLOSED:
try
{
_writeListener.onWritePossible();

View File

@ -18,19 +18,14 @@
package org.eclipse.jetty.servlet;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.Socket;
import java.nio.charset.StandardCharsets;
import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
@ -47,6 +42,8 @@ import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.eclipse.jetty.http.HttpStatus;
import org.eclipse.jetty.http.HttpTester;
import org.eclipse.jetty.server.HttpChannel;
import org.eclipse.jetty.server.HttpConnectionFactory;
import org.eclipse.jetty.server.Request;
@ -59,11 +56,20 @@ import org.eclipse.jetty.toolchain.test.http.SimpleHttpParser;
import org.eclipse.jetty.toolchain.test.http.SimpleHttpResponse;
import org.eclipse.jetty.util.IO;
import org.eclipse.jetty.util.log.StacklessLogging;
import org.hamcrest.Matchers;
import org.junit.After;
import org.junit.Assert;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.instanceOf;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.Matchers.not;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
@RunWith (AdvancedRunner.class)
public class AsyncIOServletTest
{
@ -102,7 +108,6 @@ public class AsyncIOServletTest
throw new IllegalStateException();
}
scope.set(new Throwable());
}
@Override
public void exitScope(Context context, Request request)
@ -116,8 +121,6 @@ public class AsyncIOServletTest
server.start();
}
private static void assertScope()
{
if (scope.get()==null)
@ -216,7 +219,7 @@ public class AsyncIOServletTest
}
line=in.readLine();
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
assertTrue(latch.await(5, TimeUnit.SECONDS));
}
}
@ -414,7 +417,7 @@ public class AsyncIOServletTest
SimpleHttpParser parser = new SimpleHttpParser();
SimpleHttpResponse response = parser.readResponse(new BufferedReader(new InputStreamReader(client.getInputStream(), "UTF-8")));
Assert.assertTrue(latch.await(5, TimeUnit.SECONDS));
assertTrue(latch.await(5, TimeUnit.SECONDS));
Assert.assertEquals("500", response.getCode());
}
}
@ -925,4 +928,97 @@ public class AsyncIOServletTest
Assert.assertFalse(oda.get());
}
@Test
public void testWriteFromOnDataAvailable() throws Exception
{
Queue<Throwable> errors = new ConcurrentLinkedQueue<>();
CountDownLatch writeLatch = new CountDownLatch(1);
startServer(new HttpServlet()
{
@Override
protected void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException
{
AsyncContext asyncContext = request.startAsync();
request.getInputStream().setReadListener(new ReadListener()
{
@Override
public void onDataAvailable() throws IOException
{
ServletInputStream input = request.getInputStream();
ServletOutputStream output = response.getOutputStream();
while (input.isReady())
{
byte[] buffer = new byte[512];
int read = input.read(buffer);
if (read < 0)
{
asyncContext.complete();
break;
}
if (output.isReady())
output.write(buffer, 0, read);
else
Assert.fail();
}
}
@Override
public void onAllDataRead() throws IOException
{
}
@Override
public void onError(Throwable t)
{
errors.offer(t);
}
});
response.getOutputStream().setWriteListener(new WriteListener()
{
@Override
public void onWritePossible() throws IOException
{
writeLatch.countDown();
}
@Override
public void onError(Throwable t)
{
errors.offer(t);
}
});
}
});
String content = "0123456789ABCDEF";
try (Socket client = new Socket("localhost", connector.getLocalPort()))
{
OutputStream output = client.getOutputStream();
String request = "POST " + path + " HTTP/1.1\r\n" +
"Host: localhost:" + connector.getLocalPort() + "\r\n" +
"Transfer-Encoding: chunked\r\n" +
"\r\n" +
"10\r\n" +
content + "\r\n";
output.write(request.getBytes("UTF-8"));
output.flush();
assertTrue(writeLatch.await(5, TimeUnit.SECONDS));
request = "" +
"0\r\n" +
"\r\n";
output.write(request.getBytes("UTF-8"));
output.flush();
HttpTester.Input input = HttpTester.from(client.getInputStream());
HttpTester.Response response = HttpTester.parseResponse(input);
assertThat(response.getStatus(), Matchers.equalTo(HttpStatus.OK_200));
assertThat(response.getContent(), Matchers.equalTo(content));
assertThat(errors, Matchers.hasSize(0));
}
}
}