From d7f2c42e2d1a0af6e7786537fb13a0afed6f9403 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 5 Aug 2014 12:55:12 +1000 Subject: [PATCH 1/7] misc optimisation of http2 field creation --- .../org/eclipse/jetty/http/HttpField.java | 61 +++++++++++++++++-- .../jetty/http2/hpack/HpackContext.java | 20 ++++-- .../jetty/http2/hpack/HpackDecoder.java | 30 +++------ .../jetty/http2/hpack/HpackEncoder.java | 36 +++++++---- .../jetty/http2/hpack/MetaDataBuilder.java | 7 ++- ...tpField.java => StaticTableHttpField.java} | 14 +---- 6 files changed, 107 insertions(+), 61 deletions(-) rename jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/{StaticValueHttpField.java => StaticTableHttpField.java} (78%) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java index 74db3037482..cf3147d0409 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java @@ -69,9 +69,14 @@ public class HttpField return _value; } + public int getIntValue() + { + return Integer.valueOf(_value); + } + public long getLongValue() { - return StringUtil.toLong(_value); + return Long.valueOf(_value); } public String[] getValues() @@ -416,21 +421,65 @@ public class HttpField return true; } + + public static class IntValueHttpField extends HttpField + { + final int _int; + + public IntValueHttpField(HttpHeader header, String value, int intValue) + { + super(header,value); + _int=intValue; + } + + public IntValueHttpField(HttpHeader header, String value) + { + this(header,value,Integer.valueOf(value)); + } + + public IntValueHttpField(HttpHeader header, int value) + { + this(header,Integer.toString(value),value); + } + + @Override + public int getIntValue() + { + return _int; + } + + @Override + public long getLongValue() + { + return _int; + } + } public static class LongValueHttpField extends HttpField { final long _long; - public LongValueHttpField(HttpHeader header, long value) + public LongValueHttpField(HttpHeader header, String value, long longValue) { - super(header,Long.toString(value)); - _long=value; + super(header,value); + _long=longValue; } public LongValueHttpField(HttpHeader header, String value) { - super(header,value); - _long=StringUtil.toLong(value); + this(header,value,StringUtil.toLong(value)); + } + + public LongValueHttpField(HttpHeader header,long value) + { + this(header,Long.toString(value),value); + } + + + @Override + public int getIntValue() + { + return (int)_long; } @Override diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java index f058885a3b6..6c929e3fa01 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java @@ -119,20 +119,20 @@ public class HpackContext switch(i) { case 2: - entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpMethod.GET)); + entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpMethod.GET)); break; case 3: - entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpMethod.POST)); + entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpMethod.POST)); break; case 6: - entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpScheme.HTTP)); + entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpScheme.HTTP)); break; case 7: - entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpScheme.HTTPS)); + entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],HttpScheme.HTTPS)); break; case 8: case 11: - entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],Integer.valueOf(STATIC_TABLE[i][1]))); + entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],Integer.valueOf(STATIC_TABLE[i][1]))); break; case 9: @@ -140,7 +140,7 @@ public class HpackContext case 12: case 13: case 14: - entry=new StaticEntry(i,new StaticValueHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],Integer.valueOf(STATIC_TABLE[i][1]))); + entry=new StaticEntry(i,new StaticTableHttpField(STATIC_TABLE[i][0],STATIC_TABLE[i][1],Integer.valueOf(STATIC_TABLE[i][1]))); break; default: @@ -429,6 +429,7 @@ public class HpackContext public static class StaticEntry extends Entry { private final byte[] _huffmanValue; + private final byte _encodedField; StaticEntry(int index,HttpField field) { @@ -450,6 +451,8 @@ public class HpackContext } else _huffmanValue=null; + + _encodedField=(byte)(0x80|index); } @Override @@ -463,6 +466,11 @@ public class HpackContext { return _huffmanValue; } + + public byte getEncodedField() + { + return _encodedField; + } } diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java index cb32ccff76a..131746908a5 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java @@ -41,7 +41,8 @@ import org.eclipse.jetty.util.log.Logger; public class HpackDecoder { public static final Logger LOG = Log.getLogger(HpackDecoder.class); - public final static HttpField.LongValueHttpField CONTENT_LENGTH_0 = new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH,0L); + public final static HttpField.LongValueHttpField CONTENT_LENGTH_0 = + new HttpField.LongValueHttpField(HttpHeader.CONTENT_LENGTH,0L); private final HpackContext _context; private final MetaDataBuilder _builder; @@ -191,41 +192,26 @@ public class HpackDecoder HttpField field; if (header==null) { + // just make a normal field and bypass header name lookup field = new HttpField(null,name,value); } else { + // might be worthwhile to create a value HttpField if it is indexed + // and/or of a type that may be looked up multiple times. switch(header) { - case C_METHOD: - HttpMethod method=HttpMethod.CACHE.get(value); - if (method!=null) - field = new StaticValueHttpField(HttpHeader.C_METHOD,method.asString(),method); - else - field = new AuthorityHttpField(value); - break; - case C_STATUS: - Integer code = Integer.valueOf(value); - field = new StaticValueHttpField(HttpHeader.C_STATUS,value,code); - break; - - case C_SCHEME: - HttpScheme scheme=HttpScheme.CACHE.get(value); - if (scheme!=null) - field = new StaticValueHttpField(HttpHeader.C_SCHEME,scheme.asString(),scheme); + if (indexed) + field = new HttpField.IntValueHttpField(header,value); else - field = new AuthorityHttpField(value); + field = new HttpField(header,name,value); break; case C_AUTHORITY: field = new AuthorityHttpField(value); break; - case C_PATH: - field = new HttpField(HttpHeader.C_PATH,value); - break; - case CONTENT_LENGTH: if ("0".equals(value)) field = CONTENT_LENGTH_0; diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index c08d500c88d..ee297cc74c7 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -28,6 +28,7 @@ import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.hpack.HpackContext.Entry; +import org.eclipse.jetty.http2.hpack.HpackContext.StaticEntry; import org.eclipse.jetty.io.ByteBufferPool.Lease; import org.eclipse.jetty.util.BufferUtil; import org.eclipse.jetty.util.TypeUtil; @@ -78,6 +79,7 @@ public class HpackEncoder } private final HpackContext _context; + private final boolean _debug; private int _remoteMaxHeaderTableSize; private int _localMaxHeaderTableSize; @@ -96,6 +98,7 @@ public class HpackEncoder _context=new HpackContext(remoteMaxHeaderTableSize); _remoteMaxHeaderTableSize=remoteMaxHeaderTableSize; _localMaxHeaderTableSize=localMaxHeaderTableSize; + _debug=LOG.isDebugEnabled(); } public HpackContext getContext() @@ -154,15 +157,13 @@ public class HpackEncoder int code=response.getStatus(); HttpField status = code<__status.length?__status[code]:null; if (status==null) - status=new HttpField(HttpHeader.C_STATUS,Integer.toString(code)); + status=new HttpField.IntValueHttpField(HttpHeader.C_STATUS,code); encode(buffer,status); } // Add all the other fields for (HttpField field : metadata) - { encode(buffer,field); - } if (LOG.isDebugEnabled()) LOG.debug(String.format("CtxTbl[%x] encoded %d octets",_context.hashCode(), buffer.position() - pos)); @@ -179,7 +180,7 @@ public class HpackEncoder private void encode(ByteBuffer buffer, HttpField field) { - final int p=LOG.isDebugEnabled()?buffer.position():-1; + final int p=_debug?buffer.position():-1; String encoding=null; @@ -190,16 +191,24 @@ public class HpackEncoder if (entry!=null) { // Known field entry, so encode it as indexed - int index=_context.index(entry); - if (p>=0) - encoding="IdxField"+(entry.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(7,index)); - buffer.put((byte)0x80); - NBitInteger.encode(buffer,7,index); + if (entry.isStatic()) + { + buffer.put(((StaticEntry)entry).getEncodedField()); + if (_debug) + encoding="IdxFieldS1"; + } + else + { + int index=_context.index(entry); + buffer.put((byte)0x80); + NBitInteger.encode(buffer,7,index); + if (_debug) + encoding="IdxField"+(entry.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(7,index)); + } } else { // Unknown field entry, so we will have to send literally. - final Entry name; final boolean indexed; final boolean never_index; @@ -211,6 +220,7 @@ public class HpackEncoder if (header==null) { name = _context.get(field.getName()); + // has the custom header name been seen before? if (name==null) { @@ -226,7 +236,7 @@ public class HpackEncoder else { // known custom name, but unknown value. - // This is probably a custom field with changing value, so don't index now. + // This is probably a custom field with changing value, so don't index. indexed=false; never_index=false; huffman=true; @@ -267,7 +277,7 @@ public class HpackEncoder } } - if (p>=0) + if (_debug) { encoding="Lit"+ ((name==null)?"HuffN":("IdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(bits,_context.index(name)))))+ @@ -316,7 +326,7 @@ public class HpackEncoder _context.add(field); } - if (p>=0) + if (_debug) { int e=buffer.position(); if (LOG.isDebugEnabled()) diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java index 2f12502695d..5f1df1b8390 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java @@ -72,9 +72,9 @@ public class MetaDataBuilder if (_size>_maxSize) throw new BadMessageException(HttpStatus.REQUEST_ENTITY_TOO_LARGE_413,"Header size "+_size+">"+_maxSize); - if (field instanceof StaticValueHttpField) + if (field instanceof StaticTableHttpField) { - StaticValueHttpField value = (StaticValueHttpField)field; + StaticTableHttpField value = (StaticTableHttpField)field; switch(field.getHeader()) { case C_STATUS: @@ -98,7 +98,7 @@ public class MetaDataBuilder switch(field.getHeader()) { case C_STATUS: - _status=Integer.parseInt(field.getValue()); + _status=field.getIntValue(); break; case C_METHOD: @@ -114,6 +114,7 @@ public class MetaDataBuilder 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()); _fields.add(field); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticValueHttpField.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticTableHttpField.java similarity index 78% rename from jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticValueHttpField.java rename to jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticTableHttpField.java index 39553264753..cd1da1c910f 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticValueHttpField.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/StaticTableHttpField.java @@ -23,19 +23,11 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; /* ------------------------------------------------------------ */ -public class StaticValueHttpField extends HttpField +public class StaticTableHttpField extends HttpField { private final Object _value; - public StaticValueHttpField(HttpHeader header,String name, String valueString, Object value) - { - super(header,name,valueString); - if (value==null) - throw new IllegalArgumentException(); - _value=value; - } - - public StaticValueHttpField(HttpHeader header,String valueString, Object value) + public StaticTableHttpField(HttpHeader header,String valueString, Object value) { super(header,header.asString(),valueString); if (value==null) @@ -43,7 +35,7 @@ public class StaticValueHttpField extends HttpField _value=value; } - public StaticValueHttpField(String name, String valueString, Object value) + public StaticTableHttpField(String name, String valueString, Object value) { super(name,valueString); if (value==null) From 6b3c8d06a9693b4924b4f9f369e523ee7a01486b Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 5 Aug 2014 16:14:53 +1000 Subject: [PATCH 2/7] partial 100 continues support in http2 --- .../server/HTTP2ServerConnectionFactory.java | 2 +- .../http2/server/HttpChannelOverHTTP2.java | 55 ++++++++++++++++--- .../jetty/server/HttpChannelOverHttp.java | 1 - 3 files changed, 47 insertions(+), 11 deletions(-) diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java index 22a44885926..8b080691518 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HTTP2ServerConnectionFactory.java @@ -100,7 +100,7 @@ public class HTTP2ServerConnectionFactory extends AbstractHTTP2ServerConnectionF HttpChannelOverHTTP2 channel = new HttpChannelOverHTTP2(connector, httpConfiguration, endPoint, transport, input, stream); stream.setAttribute(CHANNEL_ATTRIBUTE, channel); - channel.requestHeaders(frame); + channel.onHeadersFrame(frame); return frame.isEndStream() ? null : this; } diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java index 2e0f6122c01..3ee7c939a7e 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java @@ -18,11 +18,14 @@ package org.eclipse.jetty.http2.server; +import java.io.IOException; import java.nio.ByteBuffer; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpHeaderValue; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.api.Stream; import org.eclipse.jetty.http2.frames.DataFrame; @@ -46,6 +49,7 @@ public class HttpChannelOverHTTP2 extends HttpChannel private static final HttpField SERVER_VERSION=new HttpField(HttpHeader.SERVER,HttpConfiguration.SERVER_VERSION); private static final HttpField POWERED_BY=new HttpField(HttpHeader.X_POWERED_BY,HttpConfiguration.SERVER_VERSION); private final Stream stream; // TODO recycle channel for new Stream? + private boolean _expect100Continue = false; public HttpChannelOverHTTP2(Connector connector, HttpConfiguration configuration, EndPoint endPoint, HttpTransport transport, HttpInput input, Stream stream) { @@ -53,7 +57,13 @@ public class HttpChannelOverHTTP2 extends HttpChannel this.stream = stream; } - public void requestHeaders(HeadersFrame frame) + @Override + public boolean isExpecting100Continue() + { + return _expect100Continue; + } + + public void onHeadersFrame(HeadersFrame frame) { MetaData metaData = frame.getMetaData(); if (!metaData.isRequest()) @@ -63,22 +73,20 @@ public class HttpChannelOverHTTP2 extends HttpChannel } MetaData.Request request = (MetaData.Request)metaData; - - // The specification says user agents MUST support gzip encoding. - // Based on that, some browser does not send the header, but it's - // important that applications can find it (e.g. GzipFilter). HttpFields fields = request.getFields(); - if (!fields.contains(HttpHeader.ACCEPT_ENCODING, "gzip")) - fields.add(ACCEPT_ENCODING_GZIP); + + _expect100Continue = fields.contains(HttpHeader.EXPECT,HttpHeaderValue.CONTINUE.asString()); // TODO make this a better field for h2 hpack generation + HttpFields response=getResponse().getHttpFields(); if (getHttpConfiguration().getSendServerVersion()) - getResponse().getHttpFields().add(SERVER_VERSION); + response.add(SERVER_VERSION); if (getHttpConfiguration().getSendXPoweredBy()) - getResponse().getHttpFields().add(POWERED_BY); + response.add(POWERED_BY); onRequest(request); + if (frame.isEndStream()) { onRequestComplete(); @@ -127,4 +135,33 @@ public class HttpChannelOverHTTP2 extends HttpChannel onRequestComplete(); } } + + /** + * If the associated response has the Expect header set to 100 Continue, + * then accessing the input stream indicates that the handler/servlet + * is ready for the request body and thus a 100 Continue response is sent. + * + * @throws IOException if the InputStream cannot be created + */ + @Override + public void continue100(int available) throws IOException + { + // If the client is expecting 100 CONTINUE, then send it now. + // TODO: consider using an AtomicBoolean ? + if (isExpecting100Continue()) + { + _expect100Continue = false; + + // is content missing? + if (available == 0) + { + if (getResponse().isCommitted()) + throw new IOException("Committed before 100 Continues"); + + boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false); + if (!committed) + throw new IOException("Concurrent commit while trying to send 100-Continue"); + } + } + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java index ae8c6143441..625213050eb 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java @@ -199,7 +199,6 @@ class HttpChannelOverHttp extends HttpChannel implements HttpParser.RequestHandl if (getResponse().isCommitted()) throw new IOException("Committed before 100 Continues"); - // TODO: break this dependency with HttpGenerator boolean committed = sendResponse(HttpGenerator.CONTINUE_100_INFO, null, false); if (!committed) throw new IOException("Concurrent commit while trying to send 100-Continue"); From bec34b460fc370d10b3bc92bee5a09a1787c6f8f Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 5 Aug 2014 17:26:27 +1000 Subject: [PATCH 3/7] optimised HttpFields --- .../org/eclipse/jetty/http/HttpFields.java | 158 +++++++++++------- .../eclipse/jetty/http/HttpFieldsTest.java | 154 ++++++++++++----- .../server/ForwardedRequestCustomizer.java | 6 +- .../org/eclipse/jetty/server/Request.java | 4 +- .../org/eclipse/jetty/server/Response.java | 2 +- .../jetty/server/PartialRFC2616Test.java | 2 +- .../eclipse/jetty/server/ResponseTest.java | 26 +-- .../jetty/test/rfcs/RFC2616BaseTest.java | 2 +- .../jetty/server/session/IdleSessionTest.java | 2 +- .../server/session/DirtyAttributeTest.java | 2 +- .../session/MaxInactiveMigrationTest.java | 2 +- .../ModifyMaxInactiveIntervalTest.java | 2 +- .../ReloadedSessionMissingClassTest.java | 2 +- .../server/session/SaveIntervalTest.java | 2 +- .../server/session/SessionExpiryTest.java | 2 +- .../mongodb/PurgeInvalidSessionTest.java | 2 +- .../nosql/mongodb/PurgeValidSessionTest.java | 2 +- .../nosql/mongodb/SessionSavingValueTest.java | 4 +- .../StopSessionManagerDeleteSessionTest.java | 2 +- ...AbstractClientCrossContextSessionTest.java | 2 +- .../session/AbstractImmortalSessionTest.java | 2 +- .../AbstractInvalidationSessionTest.java | 2 +- .../session/AbstractLastAccessTimeTest.java | 4 +- .../server/session/AbstractLightLoadTest.java | 2 +- .../AbstractLocalSessionScavengingTest.java | 2 +- .../session/AbstractNewSessionTest.java | 2 +- .../session/AbstractOrphanedSessionTest.java | 2 +- .../AbstractProxySerializationTest.java | 2 +- .../session/AbstractRemoveSessionTest.java | 2 +- .../session/AbstractSessionCookieTest.java | 2 +- .../session/AbstractSessionExpiryTest.java | 4 +- ...bstractSessionInvalidateAndCreateTest.java | 2 +- .../session/AbstractSessionMigrationTest.java | 2 +- .../session/AbstractSessionRenewTest.java | 4 +- .../AbstractSessionValueSavingTest.java | 4 +- ...StopSessionManagerPreserveSessionTest.java | 2 +- .../AbstractWebAppObjectInSessionTest.java | 2 +- 37 files changed, 265 insertions(+), 157 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 278be78cd83..0779a5ae6b1 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -19,8 +19,10 @@ package org.eclipse.jetty.http; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collection; import java.util.Collections; +import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -34,7 +36,6 @@ import java.util.StringTokenizer; import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.LazyList; import org.eclipse.jetty.util.QuotedStringTokenizer; -import org.eclipse.jetty.util.StringUtil; import org.eclipse.jetty.util.Trie; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; @@ -55,11 +56,12 @@ public class HttpFields implements Iterable private static final Logger LOG = Log.getLogger(HttpFields.class); - private final List _fields; + private HttpField[] _fields; + private int _size; public HttpFields() { - _fields=new ArrayList<>(); + _fields=new HttpField[20]; } /** @@ -67,18 +69,18 @@ public class HttpFields implements Iterable */ public HttpFields(int capacity) { - _fields=new ArrayList<>(capacity); + _fields=new HttpField[capacity]; } public int size() { - return _fields.size(); + return _size; } @Override public Iterator iterator() { - return _fields.iterator(); + return new Itr(); } /** @@ -86,7 +88,7 @@ public class HttpFields implements Iterable */ public Collection getFieldNamesCollection() { - final Set list = new HashSet<>(_fields.size()); + final Set list = new HashSet<>(_size); for (HttpField f : this) { if (f!=null) @@ -111,14 +113,16 @@ public class HttpFields implements Iterable */ public HttpField getField(int i) { - return _fields.get(i); + if (i>=_size) + throw new NoSuchElementException(); + return _fields[i]; } public HttpField getField(HttpHeader header) { - for (int i=0;i<_fields.size();i++) + for (int i=0;i<_size;i++) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.getHeader()==header) return f; } @@ -127,9 +131,9 @@ public class HttpFields implements Iterable public HttpField getField(String name) { - for (int i=0;i<_fields.size();i++) + for (int i=0;i<_size;i++) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.getName().equalsIgnoreCase(name)) return f; } @@ -138,9 +142,9 @@ public class HttpFields implements Iterable public boolean contains(HttpField field) { - for (int i=_fields.size();i-->0;) + for (int i=_size;i-->0;) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.isSameName(field) && f.contains(field.getValue())) return true; } @@ -149,9 +153,9 @@ public class HttpFields implements Iterable public boolean contains(HttpHeader header, String value) { - for (int i=_fields.size();i-->0;) + for (int i=_size;i-->0;) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.getHeader()==header && f.contains(value)) return true; } @@ -160,9 +164,9 @@ public class HttpFields implements Iterable public boolean contains(String name, String value) { - for (int i=_fields.size();i-->0;) + for (int i=_size;i-->0;) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.getName().equalsIgnoreCase(name) && f.contains(value)) return true; } @@ -172,39 +176,35 @@ public class HttpFields implements Iterable public boolean containsKey(String name) { - for (int i=_fields.size();i-->0;) + for (int i=_size;i-->0;) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.getName().equalsIgnoreCase(name)) return true; } return false; } - public String getStringField(HttpHeader header) - { - return getStringField(header.asString()); - } - public String get(HttpHeader header) { - return getStringField(header.asString()); + for (int i=0;i<_size;i++) + { + HttpField f=_fields[i]; + if (f.getHeader()==header) + return f.getValue(); + } + return null; } public String get(String header) { - return getStringField(header); - } - - /** - * @return the value of a field, or null if not found. For multiple fields of the same name, - * only the first is returned. - * @param name the case-insensitive field name - */ - public String getStringField(String name) - { - HttpField field = getField(name); - return field==null?null:field.getValue(); + for (int i=0;i<_size;i++) + { + HttpField f=_fields[i]; + if (f.getName().equalsIgnoreCase(header)) + return f.getValue(); + } + return null; } /** @@ -230,9 +230,9 @@ public class HttpFields implements Iterable */ public Enumeration getValues(final String name) { - for (int i=0;i<_fields.size();i++) + for (int i=0;i<_size;i++) { - final HttpField f = _fields.get(i); + final HttpField f = _fields[i]; if (f.getName().equalsIgnoreCase(name) && f.getValue()!=null) { @@ -247,9 +247,9 @@ public class HttpFields implements Iterable { if (field==null) { - while (i<_fields.size()) + while (i<_size) { - field=_fields.get(i++); + field=_fields[i++]; if (field.getName().equalsIgnoreCase(name) && field.getValue()!=null) return true; } @@ -270,7 +270,6 @@ public class HttpFields implements Iterable } throw new NoSuchElementException(); } - }; } } @@ -328,22 +327,24 @@ public class HttpFields implements Iterable public void put(HttpField field) { boolean put=false; - for (int i=_fields.size();i-->0;) + for (int i=_size;i-->0;) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.isSameName(field)) { if (put) - _fields.remove(i); + { + System.arraycopy(_fields,i+1,_fields,i,--_size-i); + } else { - _fields.set(i,field); + _fields[i]=field; put=true; } } } if (!put) - _fields.add(field); + add(field); } /** @@ -408,7 +409,7 @@ public class HttpFields implements Iterable return; HttpField field = new HttpField(name, value); - _fields.add(field); + add(field); } public void add(HttpHeader header, HttpHeaderValue value) throws IllegalArgumentException @@ -429,7 +430,7 @@ public class HttpFields implements Iterable if (value == null) throw new IllegalArgumentException("null value"); HttpField field = new HttpField(header, value); - _fields.add(field); + add(field); } /** @@ -439,13 +440,17 @@ public class HttpFields implements Iterable */ public HttpField remove(HttpHeader name) { - for (int i=_fields.size();i-->0;) + HttpField removed=null; + for (int i=_size;i-->0;) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.getHeader()==name) - return _fields.remove(i); + { + removed=f; + System.arraycopy(_fields,i+1,_fields,i,--_size-i); + } } - return null; + return removed; } /** @@ -455,13 +460,17 @@ public class HttpFields implements Iterable */ public HttpField remove(String name) { - for (int i=_fields.size();i-->0;) + HttpField removed=null; + for (int i=_size;i-->0;) { - HttpField f=_fields.get(i); + HttpField f=_fields[i]; if (f.getName().equalsIgnoreCase(name)) - return _fields.remove(i); + { + removed=f; + System.arraycopy(_fields,i+1,_fields,i,--_size-i); + } } - return null; + return removed; } /** @@ -622,17 +631,20 @@ public class HttpFields implements Iterable public void clear() { - _fields.clear(); + _size=0; } public void add(HttpField field) { - _fields.add(field); + if (_size==_fields.length) + _fields=Arrays.copyOf(_fields,_size*2); + _fields[_size++]=field; } public void addAll(HttpFields fields) { - _fields.addAll(fields._fields); + for (int i=0;i } + private class Itr implements Iterator + { + int _cursor; // index of next element to return + + public boolean hasNext() + { + return _cursor != _size; + } + + @SuppressWarnings("unchecked") + public HttpField next() + { + int i = _cursor; + if (i >= _size) + throw new NoSuchElementException(); + _cursor = i + 1; + return _fields[i]; + } + + public void remove() + { + throw new UnsupportedOperationException(); + } + } } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java index ed2bef169ea..150bfa75687 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java @@ -26,10 +26,13 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.nio.ByteBuffer; +import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; import java.util.Locale; +import java.util.NoSuchElementException; import java.util.Set; + import org.eclipse.jetty.util.BufferUtil; import org.hamcrest.Matchers; import org.junit.Assert; @@ -48,9 +51,10 @@ public class HttpFieldsTest header.put("name0", "value:0"); header.put("name1", "value1"); - assertEquals("value:0",header.getStringField("name0")); - assertEquals("value1",header.getStringField("name1")); - assertNull(header.getStringField("name2")); + assertEquals(2,header.size()); + assertEquals("value:0",header.get("name0")); + assertEquals("value1",header.get("name1")); + assertNull(header.get("name2")); int matches=0; Enumeration e = header.getFieldNames(); @@ -68,6 +72,7 @@ public class HttpFieldsTest assertEquals(true, e.hasMoreElements()); assertEquals(e.nextElement(), "value:0"); assertEquals(false, e.hasMoreElements()); + } @Test @@ -99,10 +104,45 @@ public class HttpFieldsTest header.put("name0", "value0"); header.put("name1", "value1"); - assertEquals("value0",header.getStringField("name0")); - assertEquals("value0",header.getStringField("Name0")); - assertEquals("value1",header.getStringField("name1")); - assertEquals("value1",header.getStringField("Name1")); + assertEquals("value0",header.get("name0")); + assertEquals("value0",header.get("Name0")); + assertEquals("value1",header.get("name1")); + assertEquals("value1",header.get("Name1")); + assertEquals(null,header.get("Name2")); + + assertEquals("value0",header.getField("name0").getValue()); + assertEquals("value0",header.getField("Name0").getValue()); + assertEquals("value1",header.getField("name1").getValue()); + assertEquals("value1",header.getField("Name1").getValue()); + assertEquals(null,header.getField("Name2")); + + assertEquals("value0",header.getField(0).getValue()); + assertEquals("value1",header.getField(1).getValue()); + try + { + header.getField(2); + Assert.fail(); + } + catch(NoSuchElementException e) + {} + } + + @Test + public void testGetKnown() throws Exception + { + HttpFields header = new HttpFields(); + + header.put("Connection", "value0"); + header.put(HttpHeader.ACCEPT, "value1"); + + assertEquals("value0",header.get(HttpHeader.CONNECTION)); + assertEquals("value1",header.get(HttpHeader.ACCEPT)); + + assertEquals("value0",header.getField(HttpHeader.CONNECTION).getValue()); + assertEquals("value1",header.getField(HttpHeader.ACCEPT).getValue()); + + assertEquals(null,header.getField(HttpHeader.AGE)); + assertEquals(null,header.get(HttpHeader.AGE)); } @Test @@ -153,16 +193,16 @@ public class HttpFieldsTest header.put("name1", "xxxxxx"); header.put("name2", "value2"); - assertEquals("value0",header.getStringField("name0")); - assertEquals("xxxxxx",header.getStringField("name1")); - assertEquals("value2",header.getStringField("name2")); + assertEquals("value0",header.get("name0")); + assertEquals("xxxxxx",header.get("name1")); + assertEquals("value2",header.get("name2")); header.put("name1", "value1"); - assertEquals("value0",header.getStringField("name0")); - assertEquals("value1",header.getStringField("name1")); - assertEquals("value2",header.getStringField("name2")); - assertNull(header.getStringField("name3")); + assertEquals("value0",header.get("name0")); + assertEquals("value1",header.get("name1")); + assertEquals("value2",header.get("name2")); + assertNull(header.get("name3")); int matches=0; Enumeration e = header.getFieldNames(); @@ -188,22 +228,22 @@ public class HttpFieldsTest @Test public void testRemovePut() throws Exception { - HttpFields header = new HttpFields(); + HttpFields header = new HttpFields(1); header.put("name0", "value0"); header.put("name1", "value1"); header.put("name2", "value2"); - assertEquals("value0",header.getStringField("name0")); - assertEquals("value1",header.getStringField("name1")); - assertEquals("value2",header.getStringField("name2")); + assertEquals("value0",header.get("name0")); + assertEquals("value1",header.get("name1")); + assertEquals("value2",header.get("name2")); header.remove("name1"); - assertEquals("value0",header.getStringField("name0")); - assertNull(header.getStringField("name1")); - assertEquals("value2",header.getStringField("name2")); - assertNull(header.getStringField("name3")); + assertEquals("value0",header.get("name0")); + assertNull(header.get("name1")); + assertEquals("value2",header.get("name2")); + assertNull(header.get("name3")); int matches=0; Enumeration e = header.getFieldNames(); @@ -232,16 +272,16 @@ public class HttpFieldsTest fields.add("name1", "valueA"); fields.add("name2", "value2"); - assertEquals("value0",fields.getStringField("name0")); - assertEquals("valueA",fields.getStringField("name1")); - assertEquals("value2",fields.getStringField("name2")); + assertEquals("value0",fields.get("name0")); + assertEquals("valueA",fields.get("name1")); + assertEquals("value2",fields.get("name2")); fields.add("name1", "valueB"); - assertEquals("value0",fields.getStringField("name0")); - assertEquals("valueA",fields.getStringField("name1")); - assertEquals("value2",fields.getStringField("name2")); - assertNull(fields.getStringField("name3")); + assertEquals("value0",fields.get("name0")); + assertEquals("valueA",fields.get("name1")); + assertEquals("value2",fields.get("name2")); + assertNull(fields.get("name3")); int matches=0; Enumeration e = fields.getFieldNames(); @@ -346,7 +386,7 @@ public class HttpFieldsTest assertEquals(951825600000L,d5); fields.putDateField("D2",d1); - assertEquals("Fri, 31 Dec 1999 23:59:59 GMT",fields.getStringField("D2")); + assertEquals("Fri, 31 Dec 1999 23:59:59 GMT",fields.get("D2")); } @Test @@ -355,16 +395,16 @@ public class HttpFieldsTest HttpFields fields = new HttpFields(); fields.putDateField("Dzero",0); - assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Dzero")); + assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.get("Dzero")); fields.putDateField("Dminus",-1); - assertEquals("Wed, 31 Dec 1969 23:59:59 GMT",fields.getStringField("Dminus")); + assertEquals("Wed, 31 Dec 1969 23:59:59 GMT",fields.get("Dminus")); fields.putDateField("Dminus",-1000); - assertEquals("Wed, 31 Dec 1969 23:59:59 GMT",fields.getStringField("Dminus")); + assertEquals("Wed, 31 Dec 1969 23:59:59 GMT",fields.get("Dminus")); fields.putDateField("Dancient",Long.MIN_VALUE); - assertEquals("Sun, 02 Dec 55 16:47:04 GMT",fields.getStringField("Dancient")); + assertEquals("Sun, 02 Dec 55 16:47:04 GMT",fields.get("Dancient")); } @Test @@ -374,15 +414,33 @@ public class HttpFieldsTest header.put("I1", "42"); header.put("I2", " 43 99"); - header.put("I3", "-44;"); + header.put("I3", "-44"); header.put("I4", " - 45abc"); header.put("N1", " - "); header.put("N2", "xx"); long i1=header.getLongField("I1"); - long i2=header.getLongField("I2"); + try + { + header.getLongField("I2"); + assertTrue(false); + } + catch(NumberFormatException e) + { + assertTrue(true); + } + long i3=header.getLongField("I3"); - long i4=header.getLongField("I4"); + + try + { + header.getLongField("I4"); + assertTrue(false); + } + catch(NumberFormatException e) + { + assertTrue(true); + } try{ header.getLongField("N1"); @@ -403,14 +461,12 @@ public class HttpFieldsTest } assertEquals(42,i1); - assertEquals(43,i2); assertEquals(-44,i3); - assertEquals(-45,i4); header.putLongField("I5", 46); header.putLongField("I6",-47); - assertEquals("46",header.getStringField("I5")); - assertEquals("-47",header.getStringField("I6")); + assertEquals("46",header.get("I5")); + assertEquals("-47",header.get("I6")); } @@ -429,12 +485,28 @@ public class HttpFieldsTest header.add("n6", "def"); header.add("N6", "hig"); header.add("n7", "abc , def;q=0.9 , hig"); + header.add("n8", "abc , def;q=0 , hig"); + header.add(HttpHeader.ACCEPT, "abc , def;q=0 , hig"); for (int i=0;i<8;i++) { + assertTrue(header.containsKey("n"+i)); + assertTrue(header.containsKey("N"+i)); assertFalse(""+i,header.contains("n"+i,"xyz")); assertEquals(""+i,i>=4,header.contains("n"+i,"def")); } + + + assertTrue(header.contains(new HttpField("N5","def"))); + assertTrue(header.contains(new HttpField("accept","abc"))); + assertTrue(header.contains(HttpHeader.ACCEPT,"abc")); + assertFalse(header.contains(new HttpField("N5","xyz"))); + assertFalse(header.contains(new HttpField("N8","def"))); + assertFalse(header.contains(HttpHeader.ACCEPT,"def")); + assertFalse(header.contains(HttpHeader.AGE,"abc")); + + assertFalse(header.containsKey("n11")); + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java index f2943cfa5e6..0ddb9634196 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ForwardedRequestCustomizer.java @@ -202,13 +202,13 @@ public class ForwardedRequestCustomizer implements Customizer // Do SSL first if (getForwardedCipherSuiteHeader()!=null) { - String cipher_suite=httpFields.getStringField(getForwardedCipherSuiteHeader()); + String cipher_suite=httpFields.get(getForwardedCipherSuiteHeader()); if (cipher_suite!=null) request.setAttribute("javax.servlet.request.cipher_suite",cipher_suite); } if (getForwardedSslSessionIdHeader()!=null) { - String ssl_session_id=httpFields.getStringField(getForwardedSslSessionIdHeader()); + String ssl_session_id=httpFields.get(getForwardedSslSessionIdHeader()); if(ssl_session_id!=null) { request.setAttribute("javax.servlet.request.ssl_session_id", ssl_session_id); @@ -260,7 +260,7 @@ public class ForwardedRequestCustomizer implements Customizer if (header == null) return null; - String headerValue = fields.getStringField(header); + String headerValue = fields.get(header); if (headerValue == null) return null; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index 4b1c23d4929..bcc992e703c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -510,7 +510,7 @@ public class Request implements HttpServletRequest @Override public String getContentType() { - String content_type = _metadata.getFields().getStringField(HttpHeader.CONTENT_TYPE); + String content_type = _metadata.getFields().get(HttpHeader.CONTENT_TYPE); if (_characterEncoding==null && content_type!=null) { MimeTypes.Type mime = MimeTypes.CACHE.get(content_type); @@ -603,7 +603,7 @@ public class Request implements HttpServletRequest @Override public String getHeader(String name) { - return _metadata==null?null:_metadata.getFields().getStringField(name); + return _metadata==null?null:_metadata.getFields().get(name); } /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java index 366805a6f27..d8b30d246e3 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Response.java @@ -830,7 +830,7 @@ public class Response implements HttpServletResponse @Override public String getHeader(String name) { - return _fields.getStringField(name); + return _fields.get(name); } @Override diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java b/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java index bfb52afab07..62a29c20c75 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/PartialRFC2616Test.java @@ -102,7 +102,7 @@ public class PartialRFC2616Test assertEquals("3.3.1 RFC 850 ANSI C",d3,d2); fields.putDateField("Date",d1.getTime()); - assertEquals("3.3.1 RFC 822 preferred","Sun, 06 Nov 1994 08:49:37 GMT",fields.getStringField("Date")); + assertEquals("3.3.1 RFC 822 preferred","Sun, 06 Nov 1994 08:49:37 GMT",fields.get("Date")); } catch (Exception e) { diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index e68b6a0a4cc..d5191f9863c 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -607,7 +607,7 @@ public class ResponseTest response.addCookie(cookie); - String set = response.getHttpFields().getStringField("Set-Cookie"); + String set = response.getHttpFields().get("Set-Cookie"); assertEquals("name=value;Version=1;Path=/path;Domain=domain;Secure;HttpOnly;Comment=comment", set); } @@ -669,23 +669,23 @@ public class ResponseTest HttpFields fields = response.getHttpFields(); response.addSetCookie("null",null,null,null,-1,null,false,false,-1); - assertEquals("null=",fields.getStringField("Set-Cookie")); + assertEquals("null=",fields.get("Set-Cookie")); fields.clear(); response.addSetCookie("minimal","value",null,null,-1,null,false,false,-1); - assertEquals("minimal=value",fields.getStringField("Set-Cookie")); + assertEquals("minimal=value",fields.get("Set-Cookie")); fields.clear(); //test cookies with same name, domain and path, only 1 allowed response.addSetCookie("everything","wrong","domain","path",0,"to be replaced",true,true,0); response.addSetCookie("everything","value","domain","path",0,"comment",true,true,0); - assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.getStringField("Set-Cookie")); + assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",fields.get("Set-Cookie")); Enumeration e =fields.getValues("Set-Cookie"); assertTrue(e.hasMoreElements()); assertEquals("everything=value;Version=1;Path=path;Domain=domain;Expires=Thu, 01-Jan-1970 00:00:00 GMT;Max-Age=0;Secure;HttpOnly;Comment=comment",e.nextElement()); assertFalse(e.hasMoreElements()); - assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.getStringField("Expires")); + assertEquals("Thu, 01 Jan 1970 00:00:00 GMT",fields.get("Expires")); assertFalse(e.hasMoreElements()); //test cookies with same name, different domain @@ -744,31 +744,31 @@ public class ResponseTest fields.clear(); response.addSetCookie("ev erything","va lue","do main","pa th",1,"co mment",true,true,1); - String setCookie=fields.getStringField("Set-Cookie"); + String setCookie=fields.get("Set-Cookie"); assertThat(setCookie,Matchers.startsWith("\"ev erything\"=\"va lue\";Version=1;Path=\"pa th\";Domain=\"do main\";Expires=")); assertThat(setCookie,Matchers.endsWith(" GMT;Max-Age=1;Secure;HttpOnly;Comment=\"co mment\"")); fields.clear(); response.addSetCookie("name","value",null,null,-1,null,false,false,0); - setCookie=fields.getStringField("Set-Cookie"); + setCookie=fields.get("Set-Cookie"); assertEquals(-1,setCookie.indexOf("Version=")); fields.clear(); response.addSetCookie("name","v a l u e",null,null,-1,null,false,false,0); - setCookie=fields.getStringField("Set-Cookie"); + setCookie=fields.get("Set-Cookie"); fields.clear(); response.addSetCookie("json","{\"services\":[\"cwa\", \"aa\"]}",null,null,-1,null,false,false,-1); - assertEquals("json=\"{\\\"services\\\":[\\\"cwa\\\", \\\"aa\\\"]}\"",fields.getStringField("Set-Cookie")); + assertEquals("json=\"{\\\"services\\\":[\\\"cwa\\\", \\\"aa\\\"]}\"",fields.get("Set-Cookie")); fields.clear(); response.addSetCookie("name","value","domain",null,-1,null,false,false,-1); response.addSetCookie("name","other","domain",null,-1,null,false,false,-1); - assertEquals("name=other;Domain=domain",fields.getStringField("Set-Cookie")); + assertEquals("name=other;Domain=domain",fields.get("Set-Cookie")); response.addSetCookie("name","more","domain",null,-1,null,false,false,-1); - assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie")); + assertEquals("name=more;Domain=domain",fields.get("Set-Cookie")); response.addSetCookie("foo","bar","domain",null,-1,null,false,false,-1); response.addSetCookie("foo","bob","domain",null,-1,null,false,false,-1); - assertEquals("name=more;Domain=domain",fields.getStringField("Set-Cookie")); + assertEquals("name=more;Domain=domain",fields.get("Set-Cookie")); e=fields.getValues("Set-Cookie"); assertEquals("name=more;Domain=domain",e.nextElement()); @@ -776,7 +776,7 @@ public class ResponseTest fields.clear(); response.addSetCookie("name","value%=",null,null,-1,null,false,false,0); - setCookie=fields.getStringField("Set-Cookie"); + setCookie=fields.get("Set-Cookie"); assertEquals("name=value%=",setCookie); } diff --git a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java index ae891279c7b..1543084fda8 100644 --- a/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java +++ b/tests/test-integration/src/test/java/org/eclipse/jetty/test/rfcs/RFC2616BaseTest.java @@ -146,7 +146,7 @@ public abstract class RFC2616BaseTest // Test formatting fields.putDateField("Date",expected.getTime().getTime()); - Assert.assertEquals("3.3.1 RFC 822 preferred","Sun, 06 Nov 1994 08:49:37 GMT",fields.getStringField("Date")); + Assert.assertEquals("3.3.1 RFC 822 preferred","Sun, 06 Nov 1994 08:49:37 GMT",fields.get("Date")); } /** diff --git a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java index 4bcfdd86bb7..8e2d444e8dd 100644 --- a/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java +++ b/tests/test-sessions/test-hash-sessions/src/test/java/org/eclipse/jetty/server/session/IdleSessionTest.java @@ -133,7 +133,7 @@ public class IdleSessionTest //make a request to set up a session on the server ContentResponse response = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java index 871ea43ee42..557a498cc5c 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/DirtyAttributeTest.java @@ -77,7 +77,7 @@ public class DirtyAttributeTest ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java index cd8db0795d4..fd6ac0aa707 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/MaxInactiveMigrationTest.java @@ -103,7 +103,7 @@ public class MaxInactiveMigrationTest ContentResponse response = request.send(); assertEquals(HttpServletResponse.SC_OK, response.getStatus()); - sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue( sessionCookie != null ); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java index 5f9427903ab..b63b8ae2687 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ModifyMaxInactiveIntervalTest.java @@ -70,7 +70,7 @@ public class ModifyMaxInactiveIntervalTest ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java index aaf62e212b7..34f4e2fd65c 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/ReloadedSessionMissingClassTest.java @@ -104,7 +104,7 @@ public class ReloadedSessionMissingClassTest ContentResponse response = client.GET("http://localhost:" + port1 + contextPath +"/bar?action=set"); assertEquals( HttpServletResponse.SC_OK, response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); String sessionId = (String)webApp.getServletContext().getAttribute("foo"); assertNotNull(sessionId); diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java index d965d687841..aa307ffbaaa 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SaveIntervalTest.java @@ -77,7 +77,7 @@ public class SaveIntervalTest // Perform a request to create a session ContentResponse response = client.GET("http://localhost:" + port + "/mod/test?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java index 37661e8892f..af5da2be1b4 100644 --- a/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java +++ b/tests/test-sessions/test-jdbc-sessions/src/test/java/org/eclipse/jetty/server/session/SessionExpiryTest.java @@ -102,7 +102,7 @@ public class SessionExpiryTest extends AbstractSessionExpiryTest //make a request to set up a session on the server ContentResponse response1 = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java index fce3a610d43..f1a4aecf78d 100644 --- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeInvalidSessionTest.java @@ -94,7 +94,7 @@ public class PurgeInvalidSessionTest //Create a session ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java index 77e04240c8a..43c7d42e47b 100644 --- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/PurgeValidSessionTest.java @@ -97,7 +97,7 @@ public class PurgeValidSessionTest //Create a session ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java index 946f4f66f89..f0f7d90eb06 100644 --- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/SessionSavingValueTest.java @@ -117,7 +117,7 @@ public class SessionSavingValueTest extends AbstractSessionValueSavingTest sessionTestValue = sessionTestResponse; - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path="); @@ -144,7 +144,7 @@ public class SessionSavingValueTest extends AbstractSessionValueSavingTest sessionTestValue = sessionTestResponse; - String setCookie = response2.getHeaders().getStringField("Set-Cookie"); + String setCookie = response2.getHeaders().get("Set-Cookie"); if (setCookie != null) sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=","$1\\$Path="); diff --git a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java index 03d959cbe07..478873ec5e2 100644 --- a/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java +++ b/tests/test-sessions/test-mongodb-sessions/src/test/java/org/eclipse/jetty/nosql/mongodb/StopSessionManagerDeleteSessionTest.java @@ -86,7 +86,7 @@ public class StopSessionManagerDeleteSessionTest //Create a session ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractClientCrossContextSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractClientCrossContextSessionTest.java index 011f2e256a3..fe1a4060af4 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractClientCrossContextSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractClientCrossContextSessionTest.java @@ -74,7 +74,7 @@ public abstract class AbstractClientCrossContextSessionTest ContentResponse response = client.GET("http://localhost:" + port + contextA + servletMapping); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java index 7c04a93eca8..26ec1f4afd3 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractImmortalSessionTest.java @@ -64,7 +64,7 @@ public abstract class AbstractImmortalSessionTest int value = 42; ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=set&value=" + value); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java index 001b3e029c9..f99df0c699d 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractInvalidationSessionTest.java @@ -79,7 +79,7 @@ public abstract class AbstractInvalidationSessionTest ContentResponse response1 = client.GET(urls[0] + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java index 67aa0f7aab1..1eb62b13c05 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLastAccessTimeTest.java @@ -87,7 +87,7 @@ public abstract class AbstractLastAccessTimeTest ContentResponse response1 = client.GET("http://localhost:" + port1 + contextPath + servletMapping + "?action=init"); assertEquals(HttpServletResponse.SC_OK, response1.getStatus()); assertEquals("test", response1.getContentAsString()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue( sessionCookie != null ); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); @@ -106,7 +106,7 @@ public abstract class AbstractLastAccessTimeTest assertEquals(HttpServletResponse.SC_OK , response2.getStatus()); assertEquals("test", response2.getContentAsString()); - String setCookie = response2.getHeaders().getStringField("Set-Cookie"); + String setCookie = response2.getHeaders().get("Set-Cookie"); if (setCookie!=null) sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLightLoadTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLightLoadTest.java index 56df9033dda..9e88651752b 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLightLoadTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLightLoadTest.java @@ -82,7 +82,7 @@ public abstract class AbstractLightLoadTest ContentResponse response1 = client.GET(urls[0] + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().getStringField( "Set-Cookie" ); + String sessionCookie = response1.getHeaders().get( "Set-Cookie" ); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java index 5d25542dae0..872ed3d4ab9 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractLocalSessionScavengingTest.java @@ -86,7 +86,7 @@ public abstract class AbstractLocalSessionScavengingTest // Create the session on node1 ContentResponse response1 = client.GET(urls[0] + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java index 8ec7ddcc8cb..dce05a730ab 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractNewSessionTest.java @@ -74,7 +74,7 @@ public abstract class AbstractNewSessionTest { ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractOrphanedSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractOrphanedSessionTest.java index 7bb9e7b57e2..1a48ccbb998 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractOrphanedSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractOrphanedSessionTest.java @@ -74,7 +74,7 @@ public abstract class AbstractOrphanedSessionTest // Connect to server1 to create a session and get its session cookie ContentResponse response1 = client.GET("http://localhost:" + port1 + contextPath + servletMapping + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractProxySerializationTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractProxySerializationTest.java index 03dab1b070d..2999faaec32 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractProxySerializationTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractProxySerializationTest.java @@ -101,7 +101,7 @@ public abstract class AbstractProxySerializationTest { ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java index 24f39687d79..9b684243a3a 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractRemoveSessionTest.java @@ -66,7 +66,7 @@ public abstract class AbstractRemoveSessionTest { ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java index 8cefb73f0af..3fb5585366f 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionCookieTest.java @@ -81,7 +81,7 @@ public abstract class AbstractSessionCookieTest ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. //sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java index dc4ce28be53..3d41393feb5 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionExpiryTest.java @@ -75,7 +75,7 @@ public abstract class AbstractSessionExpiryTest //make a request to set up a session on the server ContentResponse response = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); @@ -125,7 +125,7 @@ public abstract class AbstractSessionExpiryTest //make a request to set up a session on the server ContentResponse response1 = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java index f1c3c4c0369..0d558e5d3b3 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionInvalidateAndCreateTest.java @@ -119,7 +119,7 @@ public abstract class AbstractSessionInvalidateAndCreateTest // Create the session ContentResponse response1 = client.GET(url + "?action=init"); assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionMigrationTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionMigrationTest.java index d6532eb9fc0..6bdb1d01871 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionMigrationTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionMigrationTest.java @@ -72,7 +72,7 @@ public abstract class AbstractSessionMigrationTest Request request1 = client.POST("http://localhost:" + port1 + contextPath + servletMapping + "?action=set&value=" + value); ContentResponse response1 = request1.send(); assertEquals(HttpServletResponse.SC_OK,response1.getStatus()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java index e9326153da4..80e7856e41d 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionRenewTest.java @@ -70,7 +70,7 @@ public abstract class AbstractSessionRenewTest ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); assertFalse(testListener.isCalled()); @@ -79,7 +79,7 @@ public abstract class AbstractSessionRenewTest request.header("Cookie", sessionCookie); ContentResponse renewResponse = request.send(); assertEquals(HttpServletResponse.SC_OK,renewResponse.getStatus()); - String renewSessionCookie = renewResponse.getHeaders().getStringField("Set-Cookie"); + String renewSessionCookie = renewResponse.getHeaders().get("Set-Cookie"); assertNotNull(renewSessionCookie); assertNotSame(sessionCookie, renewSessionCookie); assertTrue(testListener.isCalled()); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java index 7cb0e9252bc..26e7bf6af3c 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractSessionValueSavingTest.java @@ -72,7 +72,7 @@ public abstract class AbstractSessionValueSavingTest sessionTestValue = Long.parseLong(response1.getContentAsString()); - String sessionCookie = response1.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response1.getHeaders().get("Set-Cookie"); assertTrue( sessionCookie != null ); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); @@ -95,7 +95,7 @@ public abstract class AbstractSessionValueSavingTest assertTrue(sessionTestValue < Long.parseLong(response2.getContentAsString())); sessionTestValue = Long.parseLong(response2.getContentAsString()); - String setCookie = response1.getHeaders().getStringField("Set-Cookie"); + String setCookie = response1.getHeaders().get("Set-Cookie"); if (setCookie!=null) sessionCookie = setCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractStopSessionManagerPreserveSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractStopSessionManagerPreserveSessionTest.java index 41247daf677..28987c53c3a 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractStopSessionManagerPreserveSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractStopSessionManagerPreserveSessionTest.java @@ -74,7 +74,7 @@ public abstract class AbstractStopSessionManagerPreserveSessionTest //Create a session ContentResponse response = client.GET("http://localhost:" + port + contextPath + servletMapping + "?action=create"); assertEquals(HttpServletResponse.SC_OK,response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); diff --git a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractWebAppObjectInSessionTest.java b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractWebAppObjectInSessionTest.java index c2c614428c6..02b912ea9fd 100644 --- a/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractWebAppObjectInSessionTest.java +++ b/tests/test-sessions/test-sessions-common/src/main/java/org/eclipse/jetty/server/session/AbstractWebAppObjectInSessionTest.java @@ -120,7 +120,7 @@ public abstract class AbstractWebAppObjectInSessionTest ContentResponse response = request.send(); assertEquals( HttpServletResponse.SC_OK, response.getStatus()); - String sessionCookie = response.getHeaders().getStringField("Set-Cookie"); + String sessionCookie = response.getHeaders().get("Set-Cookie"); assertTrue(sessionCookie != null); // Mangle the cookie, replacing Path with $Path, etc. sessionCookie = sessionCookie.replaceFirst("(\\W)(P|p)ath=", "$1\\$Path="); From a00e65cb1f14f048db93b594da7ed0de22a1d353 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Tue, 5 Aug 2014 17:43:31 +1000 Subject: [PATCH 4/7] optimised HttpFields fixes --- .../main/java/org/eclipse/jetty/http/HttpFields.java | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 0779a5ae6b1..393b6719f81 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -823,25 +823,30 @@ public class HttpFields implements Iterable private class Itr implements Iterator { int _cursor; // index of next element to return + int _last=-1; public boolean hasNext() { return _cursor != _size; } - @SuppressWarnings("unchecked") public HttpField next() { int i = _cursor; if (i >= _size) throw new NoSuchElementException(); _cursor = i + 1; - return _fields[i]; + return _fields[_last=i]; } public void remove() { - throw new UnsupportedOperationException(); + if (_last<0) + throw new IllegalStateException(); + + System.arraycopy(_fields,_last+1,_fields,_last,--_size-_last); + _cursor=_last; + _last=-1; } } From 8108a811f06683a17fc81556c85d6b8f5952671b Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 6 Aug 2014 10:15:55 +1000 Subject: [PATCH 5/7] extra HttpFields tests --- .../eclipse/jetty/http/HttpFieldsTest.java | 23 ++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java index 150bfa75687..695b7bb290f 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java @@ -29,6 +29,7 @@ import java.nio.ByteBuffer; import java.util.Collections; import java.util.Enumeration; import java.util.HashSet; +import java.util.List; import java.util.Locale; import java.util.NoSuchElementException; import java.util.Set; @@ -344,9 +345,29 @@ public class HttpFieldsTest assertEquals(true, e.hasMoreElements()); assertEquals(e.nextElement(), "value1D"); assertEquals(false, e.hasMoreElements()); - } + + @Test + public void testGetQualityValues() throws Exception + { + HttpFields fields = new HttpFields(); + + fields.put("some", "value"); + fields.add("name", "zero;q=0.9,four;q=0.1"); + fields.put("other", "value"); + fields.add("name", "nothing;q=0"); + fields.add("name", "one;q=0.4"); + fields.add("name", "three;x=y;q=0.2;a=b,two;q=0.3"); + + List list = HttpFields.qualityList(fields.getValues("name",",")); + assertEquals("zero",HttpFields.valueParameters(list.get(0),null)); + assertEquals("one",HttpFields.valueParameters(list.get(1),null)); + assertEquals("two",HttpFields.valueParameters(list.get(2),null)); + assertEquals("three",HttpFields.valueParameters(list.get(3),null)); + assertEquals("four",HttpFields.valueParameters(list.get(4),null)); + } + @Test public void testDateFields() throws Exception { From b5971484a57212a39af6f70267ea00a24fdbf100 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 6 Aug 2014 10:50:55 +1000 Subject: [PATCH 6/7] Basic push mechanism skeleton --- .../fcgi/server/HttpTransportOverFCGI.java | 10 +++++ .../org/eclipse/jetty/http/HttpFields.java | 9 ++++ .../http2/server/HttpTransportOverHTTP2.java | 10 +++++ .../jetty/rewrite/handler/RuleContainer.java | 2 +- .../authentication/FormAuthenticator.java | 4 +- .../org/eclipse/jetty/server/Dispatcher.java | 44 ++++++++++++++----- .../eclipse/jetty/server/HttpConnection.java | 10 +++++ .../eclipse/jetty/server/HttpTransport.java | 3 ++ .../org/eclipse/jetty/server/Request.java | 23 ++++++++++ .../eclipse/jetty/server/ResponseTest.java | 5 +++ .../org/eclipse/jetty/servlet/Invoker.java | 2 +- .../eclipse/jetty/servlet/ServletHandler.java | 4 +- .../jetty/servlets/CloseableDoSFilter.java | 2 +- .../server/http/HttpTransportOverSPDY.java | 12 +++++ 14 files changed, 123 insertions(+), 17 deletions(-) diff --git a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java index 54819e430ed..786f505c258 100644 --- a/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java +++ b/jetty-fcgi/fcgi-server/src/main/java/org/eclipse/jetty/fcgi/server/HttpTransportOverFCGI.java @@ -77,6 +77,16 @@ public class HttpTransportOverFCGI implements HttpTransport flusher.shutdown(); } } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.server.HttpTransport#push(org.eclipse.jetty.http.MetaData.Request) + */ + @Override + public void push(org.eclipse.jetty.http.MetaData.Request request) + { + // LOG.debug("ignore push in {}",this); + } private void commit(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback) { diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index 393b6719f81..a1d1a358bc2 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -71,6 +71,15 @@ public class HttpFields implements Iterable { _fields=new HttpField[capacity]; } + + /** + * Constructor. + */ + public HttpFields(HttpFields fields) + { + _fields=Arrays.copyOf(fields._fields,_fields.length+10); + _size=fields._size; + } public int size() { diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java index 0116485e333..86f37c83e0e 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpTransportOverHTTP2.java @@ -86,6 +86,16 @@ public class HttpTransportOverHTTP2 implements HttpTransport } } + /** + * @see org.eclipse.jetty.server.HttpTransport#push(org.eclipse.jetty.http.MetaData.Request) + */ + @Override + public void push(org.eclipse.jetty.http.MetaData.Request request) + { + // TODO implement push + LOG.warn("NOT YET IMPLEMENTED push in {}",this); + } + private void commit(HttpGenerator.ResponseInfo info, boolean endStream, Callback callback) { if (LOG.isDebugEnabled()) diff --git a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java index 20d78778d5f..1a4fabf4c8e 100644 --- a/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java +++ b/jetty-rewrite/src/main/java/org/eclipse/jetty/rewrite/handler/RuleContainer.java @@ -227,7 +227,7 @@ public class RuleContainer extends Rule if (rule.isHandling()) { LOG.debug("handling {}",rule); - (request instanceof Request?(Request)request:HttpChannel.getCurrentHttpChannel().getRequest()).setHandled(true); + Request.getBaseRequest(request).setHandled(true); } if (rule.isTerminating()) diff --git a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java index 790d544536a..57a1720ea18 100644 --- a/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java +++ b/jetty-security/src/main/java/org/eclipse/jetty/security/authentication/FormAuthenticator.java @@ -289,8 +289,8 @@ public class FormAuthenticator extends LoginAuthenticator LOG.debug("authenticated {}->{}",form_auth,nuri); response.setContentLength(0); - Response base_response = HttpChannel.getCurrentHttpChannel().getResponse(); - Request base_request = HttpChannel.getCurrentHttpChannel().getRequest(); + Request base_request = Request.getBaseRequest(req); + Response base_response = base_request.getResponse(); int redirectCode = (base_request.getHttpVersion().getVersion() < HttpVersion.HTTP_1_1.getVersion() ? HttpServletResponse.SC_MOVED_TEMPORARILY : HttpServletResponse.SC_SEE_OTHER); base_response.sendRedirect(redirectCode, response.encodeRedirectURL(nuri)); return form_auth; diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java index b296ff62aa7..e1b563b381d 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Dispatcher.java @@ -31,7 +31,11 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.HttpFields; +import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpURI; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.server.handler.ContextHandler; import org.eclipse.jetty.util.Attributes; import org.eclipse.jetty.util.MultiMap; @@ -46,14 +50,14 @@ public class Dispatcher implements RequestDispatcher private final ContextHandler _contextHandler; private final HttpURI _uri; - private final String _pathInfo; + private final String _pathInContext; private final String _named; - public Dispatcher(ContextHandler contextHandler, HttpURI uri, String pathInfo) + public Dispatcher(ContextHandler contextHandler, HttpURI uri, String pathInContext) { _contextHandler=contextHandler; _uri=uri; - _pathInfo=pathInfo; + _pathInContext=pathInContext; _named=null; } @@ -61,7 +65,7 @@ public class Dispatcher implements RequestDispatcher { _contextHandler=contextHandler; _uri=null; - _pathInfo=null; + _pathInContext=null; _named=name; } @@ -79,7 +83,7 @@ public class Dispatcher implements RequestDispatcher @Override public void include(ServletRequest request, ServletResponse response) throws ServletException, IOException { - Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(); + Request baseRequest=Request.getBaseRequest(request); if (!(request instanceof HttpServletRequest)) request = new ServletRequestHttpWrapper(request); @@ -104,14 +108,14 @@ public class Dispatcher implements RequestDispatcher attr._requestURI=_uri.getPath(); attr._contextPath=_contextHandler.getContextPath(); attr._servletPath=null; // set by ServletHandler - attr._pathInfo=_pathInfo; + attr._pathInfo=_pathInContext; attr._query=_uri.getQuery(); if (attr._query!=null) baseRequest.mergeQueryParameters(baseRequest.getQueryString(),attr._query, false); baseRequest.setAttributes(attr); - _contextHandler.handle(_pathInfo, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); + _contextHandler.handle(_pathInContext, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); } } finally @@ -126,7 +130,7 @@ public class Dispatcher implements RequestDispatcher protected void forward(ServletRequest request, ServletResponse response, DispatcherType dispatch) throws ServletException, IOException { - Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(); + Request baseRequest=Request.getBaseRequest(request); Response base_response=baseRequest.getResponse(); base_response.resetForForward(); @@ -187,13 +191,13 @@ public class Dispatcher implements RequestDispatcher baseRequest.setContextPath(_contextHandler.getContextPath()); baseRequest.setServletPath(null); - baseRequest.setPathInfo(_pathInfo); + baseRequest.setPathInfo(_pathInContext); if (_uri.getQuery()!=null || old_uri.getQuery()!=null) baseRequest.mergeQueryParameters(old_uri.getQuery(),_uri.getQuery(), true); baseRequest.setAttributes(attr); - _contextHandler.handle(_pathInfo, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); + _contextHandler.handle(_pathInContext, baseRequest, (HttpServletRequest)request, (HttpServletResponse)response); if (!baseRequest.getHttpChannelState().isAsync()) commitResponse(response,baseRequest); @@ -212,6 +216,26 @@ public class Dispatcher implements RequestDispatcher baseRequest.setDispatcherType(old_type); } } + + public void push(ServletRequest request) + { + Request baseRequest = Request.getBaseRequest(request); + HttpFields fields = new HttpFields(baseRequest.getHttpFields()); + + String query=baseRequest.getQueryString(); + if (_uri.hasQuery()) + { + if (query==null) + query=_uri.getQuery(); + else + query=query+"&"+_uri.getQuery(); // TODO is this correct semantic? + } + HttpURI uri = new HttpURI(request.getProtocol(),request.getServerName(),request.getServerPort(),_uri.getPath(),baseRequest.getHttpURI().getParam(),query,null); + + MetaData.Request push = new MetaData.Request(HttpMethod.GET.asString(),uri,baseRequest.getHttpVersion(),fields); + + baseRequest.getHttpChannel().getHttpTransport().push(push); + } private void commitResponse(ServletResponse response, Request baseRequest) throws IOException { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java index 8c5aa76bb05..61fb63d392c 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpConnection.java @@ -634,4 +634,14 @@ public class HttpConnection extends AbstractConnection implements Runnable, Http // response is bad either with RST or by abnormal completion of chunked response. getEndPoint().close(); } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.server.HttpTransport#push(org.eclipse.jetty.http.MetaData.Request) + */ + @Override + public void push(org.eclipse.jetty.http.MetaData.Request request) + { + LOG.debug("ignore push in {}",this); + } } diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java index d04a43e9bc3..975c80a67c5 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpTransport.java @@ -21,12 +21,15 @@ package org.eclipse.jetty.server; import java.nio.ByteBuffer; import org.eclipse.jetty.http.HttpGenerator; +import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.util.Callback; public interface HttpTransport { void send(HttpGenerator.ResponseInfo info, ByteBuffer content, boolean lastContent, Callback callback); + void push (MetaData.Request request); + void completed(); /* ------------------------------------------------------------ */ diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java index bcc992e703c..eb38f250bbc 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Request.java @@ -52,6 +52,7 @@ import javax.servlet.ServletInputStream; import javax.servlet.ServletRequest; import javax.servlet.ServletRequestAttributeEvent; import javax.servlet.ServletRequestAttributeListener; +import javax.servlet.ServletRequestWrapper; import javax.servlet.ServletResponse; import javax.servlet.http.Cookie; import javax.servlet.http.HttpServletRequest; @@ -132,6 +133,28 @@ public class Request implements HttpServletRequest private static final MultiMap NO_PARAMS = new MultiMap<>(); + /* ------------------------------------------------------------ */ + /** + * Obtain the base {@link Request} instance of a {@link ServletRequest}, by + * coercion, unwrapping or thread local. + * @param request The request + * @return the base {@link Request} instance of a {@link ServletRequest}. + */ + public static Request getBaseRequest(ServletRequest request) + { + if (request instanceof Request) + return (Request)request; + + while (request instanceof ServletRequestWrapper) + request=((ServletRequestWrapper)request).getRequest(); + + if (request instanceof Request) + return (Request)request; + + return HttpChannel.getCurrentHttpChannel().getRequest(); + } + + private final HttpChannel _channel; private final List _requestAttributeListeners=new ArrayList<>(); private final HttpInput _input; diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java index d5191f9863c..2bc866863f5 100644 --- a/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java +++ b/jetty-server/src/test/java/org/eclipse/jetty/server/ResponseTest.java @@ -91,6 +91,11 @@ public class ResponseTest callback.succeeded(); } + @Override + public void push(org.eclipse.jetty.http.MetaData.Request request) + { + } + @Override public void completed() { diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java index b7f0e748f74..f411078e48a 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/Invoker.java @@ -227,7 +227,7 @@ public class Invoker extends HttpServlet if (holder!=null) { - final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(); + final Request baseRequest=Request.getBaseRequest(request); holder.handle(baseRequest, new InvokedRequest(request,included,servlet,servlet_path,path_info), response); diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java index e650a709fc2..359ee67e451 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/ServletHandler.java @@ -1630,7 +1630,7 @@ public class ServletHandler extends ScopedHandler public void doFilter(ServletRequest request, ServletResponse response) throws IOException, ServletException { - final Request baseRequest=(request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(); + final Request baseRequest=Request.getBaseRequest(request); // pass to next filter if (_filterHolder!=null) @@ -1732,7 +1732,7 @@ public class ServletHandler extends ScopedHandler // Call servlet HttpServletRequest srequest = (HttpServletRequest)request; if (_servletHolder == null) - notFound((request instanceof Request)?((Request)request):HttpChannel.getCurrentHttpChannel().getRequest(), srequest, (HttpServletResponse)response); + notFound(Request.getBaseRequest(request), srequest, (HttpServletResponse)response); else { if (LOG.isDebugEnabled()) diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java index 5d011221333..157cd0f981a 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/CloseableDoSFilter.java @@ -35,7 +35,7 @@ public class CloseableDoSFilter extends DoSFilter @Override protected void closeConnection(HttpServletRequest request, HttpServletResponse response, Thread thread) { - Request base_request=(request instanceof Request)?(Request)request:HttpChannel.getCurrentHttpChannel().getRequest(); + Request base_request=Request.getBaseRequest(request); base_request.getHttpChannel().getEndPoint().close(); } } diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java index 88b690c0484..682d7541aed 100644 --- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java +++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/http/HttpTransportOverSPDY.java @@ -159,6 +159,18 @@ public class HttpTransportOverSPDY implements HttpTransport throw new IllegalStateException("not lastContent, no content and no responseInfo!"); } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.server.HttpTransport#push(org.eclipse.jetty.http.MetaData.Request) + */ + @Override + public void push(org.eclipse.jetty.http.MetaData.Request request) + { + LOG.warn("NOT YET IMPLEMENTED push in {}",this); + } + + private void sendReply(HttpGenerator.ResponseInfo info, Callback callback, boolean close) { Fields headers = new Fields(); From 17f46665df2e0b066b40a3889dbf3f56173e0554 Mon Sep 17 00:00:00 2001 From: Greg Wilkins Date: Wed, 6 Aug 2014 21:09:26 +1000 Subject: [PATCH 7/7] preencoded httpfield optimisation --- .../jetty/http/Http1FieldPreEncoder.java | 69 ++++++++ .../org/eclipse/jetty/http/HttpField.java | 36 +++- .../jetty/http/HttpFieldPreEncoder.java | 36 ++++ .../org/eclipse/jetty/http/HttpFields.java | 11 +- .../org/eclipse/jetty/http/HttpGenerator.java | 24 +-- .../org/eclipse/jetty/http/HttpParser.java | 10 +- .../org/eclipse/jetty/http/MimeTypes.java | 6 +- .../jetty/http/PreEncodedHttpField.java | 80 +++++++++ ...org.eclipse.jetty.http.HttpFieldPreEncoder | 1 + .../org/eclipse/jetty/http/HttpFieldTest.java | 16 ++ .../eclipse/jetty/http/HttpFieldsTest.java | 4 - .../jetty/http2/hpack/HpackContext.java | 11 +- .../jetty/http2/hpack/HpackDecoder.java | 6 +- .../jetty/http2/hpack/HpackEncoder.java | 161 ++++++++++-------- .../http2/hpack/HpackFieldPreEncoder.java | 74 ++++++++ ...org.eclipse.jetty.http.HttpFieldPreEncoder | 1 + .../jetty/http2/hpack/HpackContextTest.java | 4 - .../jetty/http2/hpack/HpackDecoderTest.java | 1 - .../jetty/http2/hpack/HpackEncoderTest.java | 2 - .../eclipse/jetty/http2/hpack/HpackTest.java | 15 +- .../http2/server/HttpChannelOverHTTP2.java | 9 +- .../java/org/eclipse/jetty/server/Server.java | 3 +- .../eclipse/jetty/servlet/DefaultServlet.java | 6 +- .../jetty/servlets/AsyncGzipFilter.java | 6 +- .../jetty/servlets/gzip/GzipHttpOutput.java | 4 +- 25 files changed, 449 insertions(+), 147 deletions(-) create mode 100644 jetty-http/src/main/java/org/eclipse/jetty/http/Http1FieldPreEncoder.java create mode 100644 jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldPreEncoder.java create mode 100644 jetty-http/src/main/java/org/eclipse/jetty/http/PreEncodedHttpField.java create mode 100644 jetty-http/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder create mode 100644 jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java create mode 100644 jetty-http2/http2-hpack/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/Http1FieldPreEncoder.java b/jetty-http/src/main/java/org/eclipse/jetty/http/Http1FieldPreEncoder.java new file mode 100644 index 00000000000..545611563c4 --- /dev/null +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/Http1FieldPreEncoder.java @@ -0,0 +1,69 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + + +package org.eclipse.jetty.http; + +import static java.nio.charset.StandardCharsets.ISO_8859_1; + +import java.util.Arrays; + + +/* ------------------------------------------------------------ */ +/** + */ +public class Http1FieldPreEncoder implements HttpFieldPreEncoder +{ + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.HttpFieldPreEncoder#getHttpVersion() + */ + @Override + public HttpVersion getHttpVersion() + { + return HttpVersion.HTTP_1_0; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.HttpFieldPreEncoder#getEncodedField(org.eclipse.jetty.http.HttpHeader, java.lang.String, java.lang.String) + */ + @Override + public byte[] getEncodedField(HttpHeader header, String headerString, String value) + { + if (header!=null) + { + int cbl=header.getBytesColonSpace().length; + byte[] bytes=Arrays.copyOf(header.getBytesColonSpace(), cbl+value.length()+2); + System.arraycopy(value.getBytes(ISO_8859_1),0,bytes,cbl,value.length()); + bytes[bytes.length-2]=(byte)'\r'; + bytes[bytes.length-1]=(byte)'\n'; + return bytes; + } + + byte[] n=headerString.getBytes(ISO_8859_1); + byte[] v=value.getBytes(ISO_8859_1); + byte[] bytes=Arrays.copyOf(n,n.length+2+v.length+2); + bytes[n.length]=(byte)':'; + bytes[n.length]=(byte)' '; + bytes[bytes.length-2]=(byte)'\r'; + bytes[bytes.length-1]=(byte)'\n'; + + return bytes; + } +} diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java index cf3147d0409..8d522379680 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java @@ -426,20 +426,30 @@ public class HttpField { final int _int; + public IntValueHttpField(HttpHeader header, String name, String value, int intValue) + { + super(header,name,value); + _int=intValue; + } + public IntValueHttpField(HttpHeader header, String value, int intValue) { - super(header,value); - _int=intValue; + this(header,header.asString(),value,Integer.valueOf(value)); + } + + public IntValueHttpField(HttpHeader header, String name, String value) + { + this(header,name,value,Integer.valueOf(value)); } public IntValueHttpField(HttpHeader header, String value) { - this(header,value,Integer.valueOf(value)); + this(header,header.asString(),value); } public IntValueHttpField(HttpHeader header, int value) { - this(header,Integer.toString(value),value); + this(header,header.asString(),value); } @Override @@ -459,20 +469,30 @@ public class HttpField { final long _long; - public LongValueHttpField(HttpHeader header, String value, long longValue) + public LongValueHttpField(HttpHeader header, String name, String value, long longValue) { - super(header,value); + super(header,name,value); _long=longValue; } + public LongValueHttpField(HttpHeader header, String name, String value) + { + this(header,name,value,Long.valueOf(value)); + } + + public LongValueHttpField(HttpHeader header, String name, long value) + { + this(header,name,Long.toString(value),value); + } + public LongValueHttpField(HttpHeader header, String value) { - this(header,value,StringUtil.toLong(value)); + this(header,header.asString(),value); } public LongValueHttpField(HttpHeader header,long value) { - this(header,Long.toString(value),value); + this(header,header.asString(),value); } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldPreEncoder.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldPreEncoder.java new file mode 100644 index 00000000000..f05d5e4bad0 --- /dev/null +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFieldPreEncoder.java @@ -0,0 +1,36 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + + +package org.eclipse.jetty.http; + + +/* ------------------------------------------------------------ */ +/** Interface to pre-encode HttpFields. Used by {@link PreEncodedHttpField} + */ +public interface HttpFieldPreEncoder +{ + /* ------------------------------------------------------------ */ + /** The major version this encoder is for. Both HTTP/1.0 and HTTP/1.1 + * use the same field encoding, so the {@link HttpVersion#HTTP_1_0} should + * be return for all HTTP/1.x encodings. + * @return The major version this encoder is for. + */ + HttpVersion getHttpVersion(); + byte[] getEncodedField(HttpHeader header, String headerString, String value); +} \ No newline at end of file diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java index a1d1a358bc2..54b2f98202f 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpFields.java @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.Collections; -import java.util.ConcurrentModificationException; import java.util.Enumeration; import java.util.HashMap; import java.util.HashSet; @@ -602,10 +601,14 @@ public class HttpFields implements Iterable if (size() != that.size()) return false; - for (HttpField field : this) + loop: for (HttpField fi : this) { - if (!that.contains(field)) - return false; + for (HttpField fa : that) + { + if (fi.equals(fa)) + continue loop; + } + return false; } return true; } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java index 42d0d9c9bee..b67deac5623 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpGenerator.java @@ -21,7 +21,6 @@ package org.eclipse.jetty.http; import java.io.IOException; import java.nio.BufferOverflowException; import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; import java.util.Arrays; import org.eclipse.jetty.http.HttpTokens.EndOfContent; @@ -1058,9 +1057,9 @@ public class HttpGenerator public static void putTo(HttpField field, ByteBuffer bufferInFillMode) { - if (field instanceof CachedHttpField) + if (field instanceof PreEncodedHttpField) { - ((CachedHttpField)field).putTo(bufferInFillMode); + ((PreEncodedHttpField)field).putTo(bufferInFillMode,HttpVersion.HTTP_1_0); } else { @@ -1090,23 +1089,4 @@ public class HttpGenerator } BufferUtil.putCRLF(bufferInFillMode); } - - public static class CachedHttpField extends HttpField - { - private final byte[] _bytes; - public CachedHttpField(HttpHeader header,String value) - { - super(header,value); - int cbl=header.getBytesColonSpace().length; - _bytes=Arrays.copyOf(header.getBytesColonSpace(), cbl+value.length()+2); - System.arraycopy(value.getBytes(StandardCharsets.ISO_8859_1),0,_bytes,cbl,value.length()); - _bytes[_bytes.length-2]=(byte)'\r'; - _bytes[_bytes.length-1]=(byte)'\n'; - } - - public void putTo(ByteBuffer bufferInFillMode) - { - bufferInFillMode.put(_bytes); - } - } } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java index a53ae22e3e4..0fbadad181c 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java @@ -183,15 +183,15 @@ public class HttpParser // Add common Content types as fields for (String type : new String[]{"text/plain","text/html","text/xml","text/json","application/json","application/x-www-form-urlencoded"}) { - HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type); + HttpField field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type); CACHE.put(field); for (String charset : new String[]{"utf-8","iso-8859-1"}) { - CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset)); - CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset)); - CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset.toUpperCase())); - CACHE.put(new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset.toUpperCase())); + CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset)); + CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset)); + CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+";charset="+charset.toUpperCase())); + CACHE.put(new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,type+"; charset="+charset.toUpperCase())); } } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java b/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java index dad35e248fc..5778529c874 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/MimeTypes.java @@ -90,7 +90,7 @@ public class MimeTypes _charset=null; _charsetString=null; _assumedCharset=false; - _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string); + _field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,_string); } /* ------------------------------------------------------------ */ @@ -103,7 +103,7 @@ public class MimeTypes _charset=Charset.forName(s.substring(i+9)); _charsetString=_charset==null?null:_charset.toString().toLowerCase(); _assumedCharset=false; - _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string); + _field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,_string); } /* ------------------------------------------------------------ */ @@ -115,7 +115,7 @@ public class MimeTypes _charset=cs; _charsetString=_charset==null?null:_charset.toString().toLowerCase(); _assumedCharset=true; - _field=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_TYPE,_string); + _field=new PreEncodedHttpField(HttpHeader.CONTENT_TYPE,_string); } /* ------------------------------------------------------------ */ diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/PreEncodedHttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/PreEncodedHttpField.java new file mode 100644 index 00000000000..9e47f50574e --- /dev/null +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/PreEncodedHttpField.java @@ -0,0 +1,80 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + + +package org.eclipse.jetty.http; + +import java.nio.ByteBuffer; +import java.util.ArrayList; +import java.util.List; +import java.util.ServiceLoader; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; + + +/* ------------------------------------------------------------ */ +/** Pre encoded HttpField. + *

A HttpField that will be cached and used many times can be created as + * a {@link PreEncodedHttpField}, which will use the {@link HttpFieldPreEncoder} + * instances discovered by the {@link ServiceLoader} to pre-encode the header + * for each version of HTTP in use. This will save garbage + * and CPU each time the field is encoded into a response. + *

+ */ +public class PreEncodedHttpField extends HttpField +{ + private final static Logger LOG = Log.getLogger(PreEncodedHttpField.class); + private final static HttpFieldPreEncoder[] __encoders; + + static + { + List encoders = new ArrayList<>(); + for (HttpFieldPreEncoder enc : ServiceLoader.load(HttpFieldPreEncoder.class,PreEncodedHttpField.class.getClassLoader())) + encoders.add(enc); + LOG.debug("HttpField encoders loaded: {}",encoders); + __encoders = encoders.toArray(new HttpFieldPreEncoder[encoders.size()]); + } + + private final byte[][] _encodedField=new byte[2][]; + + public PreEncodedHttpField(HttpHeader header,String name,String value) + { + super(header,name, value); + + for (HttpFieldPreEncoder e:__encoders) + { + _encodedField[e.getHttpVersion()==HttpVersion.HTTP_2?1:0]=e.getEncodedField(header,header.asString(),value); + } + } + + public PreEncodedHttpField(HttpHeader header,String value) + { + this(header,header.asString(),value); + } + + public PreEncodedHttpField(String name,String value) + { + this(null,name,value); + } + + public void putTo(ByteBuffer bufferInFillMode, HttpVersion version) + { + bufferInFillMode.put(_encodedField[version==HttpVersion.HTTP_2?1:0]); + } +} \ No newline at end of file diff --git a/jetty-http/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder b/jetty-http/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder new file mode 100644 index 00000000000..92640bd6b23 --- /dev/null +++ b/jetty-http/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder @@ -0,0 +1 @@ +org.eclipse.jetty.http.Http1FieldPreEncoder \ No newline at end of file diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldTest.java index 71ef0835768..06b8c100cd4 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldTest.java @@ -22,6 +22,9 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertTrue; +import java.nio.ByteBuffer; + +import org.eclipse.jetty.util.BufferUtil; import org.junit.Test; /** @@ -135,4 +138,17 @@ public class HttpFieldTest assertEquals("c",values[2]); } + + @Test + public void testCachedField() + { + PreEncodedHttpField field = new PreEncodedHttpField(HttpHeader.ACCEPT,"something"); + ByteBuffer buf = BufferUtil.allocate(256); + BufferUtil.clearToFill(buf); + field.putTo(buf,HttpVersion.HTTP_1_0); + BufferUtil.flipToFlush(buf,0); + String s=BufferUtil.toString(buf); + + assertEquals("Accept: something\r\n",s); + } } diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java index 695b7bb290f..ff2e53d7475 100644 --- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java +++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpFieldsTest.java @@ -26,13 +26,9 @@ import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; import java.nio.ByteBuffer; -import java.util.Collections; import java.util.Enumeration; -import java.util.HashSet; import java.util.List; -import java.util.Locale; import java.util.NoSuchElementException; -import java.util.Set; import org.eclipse.jetty.util.BufferUtil; import org.hamcrest.Matchers; diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java index 6c929e3fa01..d1cf6172e90 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java @@ -28,7 +28,6 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpScheme; -import org.eclipse.jetty.http2.hpack.HpackContext.Entry; import org.eclipse.jetty.util.ArrayQueue; import org.eclipse.jetty.util.ArrayTernaryTrie; import org.eclipse.jetty.util.StringUtil; @@ -231,6 +230,11 @@ public class HpackContext return get(header.asString()); return e; } + + public static Entry getStatic(HttpHeader header) + { + return __headerEntryTable[header.ordinal()]; + } public Entry add(HttpField field) { @@ -420,6 +424,11 @@ public class HpackContext return null; } + public int getSlot() + { + return _slot; + } + public String toString() { return String.format("{%s,%d,%s,%x}",isStatic()?"S":"D",_slot,_field,hashCode()); diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java index 131746908a5..7d24b2ed1da 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java @@ -24,8 +24,6 @@ import java.nio.ByteBuffer; import org.eclipse.jetty.http.BadMessageException; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; -import org.eclipse.jetty.http.HttpMethod; -import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http2.hpack.HpackContext.Entry; @@ -203,7 +201,7 @@ public class HpackDecoder { case C_STATUS: if (indexed) - field = new HttpField.IntValueHttpField(header,value); + field = new HttpField.IntValueHttpField(header,name,value); else field = new HttpField(header,name,value); break; @@ -216,7 +214,7 @@ public class HpackDecoder if ("0".equals(value)) field = CONTENT_LENGTH_0; else - field = new HttpField.LongValueHttpField(header,value); + field = new HttpField.LongValueHttpField(header,name,value); break; default: diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java index ee297cc74c7..33c524984e5 100644 --- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java @@ -26,7 +26,9 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpStatus; +import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; +import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http2.hpack.HpackContext.Entry; import org.eclipse.jetty.http2.hpack.HpackContext.StaticEntry; import org.eclipse.jetty.io.ByteBufferPool.Lease; @@ -184,8 +186,6 @@ public class HpackEncoder String encoding=null; - // TODO currently we do not check if there is enough space, so we may fail nastily. - // Is there an entry for the field? Entry entry = _context.get(field); if (entry!=null) @@ -209,18 +209,18 @@ public class HpackEncoder else { // Unknown field entry, so we will have to send literally. - final Entry name; final boolean indexed; - final boolean never_index; - final boolean huffman; - final int bits; + // But do we know it's name? HttpHeader header = field.getHeader(); + + // Select encoding strategy if (header==null) { - name = _context.get(field.getName()); - + // Select encoding strategy for unknown header names + Entry name = _context.get(field.getName()); + // has the custom header name been seen before? if (name==null) { @@ -228,95 +228,71 @@ public class HpackEncoder // the first time we have seen a custom name or a custom field. // unless the name is changing, this is worthwhile indexed=true; - never_index=false; - huffman=true; - bits = 6; - buffer.put((byte)0x40); + encodeName(buffer,(byte)0x40,6,field.getName(),null); + encodeValue(buffer,true,field.getValue()); + if (_debug) + encoding="LitHuffNHuffVIdx"; + } else { // known custom name, but unknown value. // This is probably a custom field with changing value, so don't index. indexed=false; - never_index=false; - huffman=true; - bits = 4; - buffer.put((byte)0x00); + encodeName(buffer,(byte)0x00,4,field.getName(),null); + encodeValue(buffer,true,field.getValue()); + if (_debug) + encoding="LitHuffNHuffV!Idx"; } } else { - name = _context.get(header); + // Select encoding strategy for known header names + Entry name = _context.get(header); if (__DO_NOT_INDEX.contains(header)) { // Non indexed field indexed=false; - never_index=__NEVER_INDEX.contains(header); - huffman=!__DO_NOT_HUFFMAN.contains(header); - bits = 4; - buffer.put(never_index?(byte)0x10:(byte)0x00); + boolean never_index=__NEVER_INDEX.contains(header); + boolean huffman=!__DO_NOT_HUFFMAN.contains(header); + encodeName(buffer,never_index?(byte)0x10:(byte)0x00,4,header.asString(),name); + encodeValue(buffer,huffman,field.getValue()); + + if (_debug) + encoding="Lit"+ + ((name==null)?"HuffN":("IdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(4,_context.index(name)))))+ + (huffman?"HuffV":"LitV")+ + (indexed?"Idx":(never_index?"!!Idx":"!Idx")); } else if (header==HttpHeader.CONTENT_LENGTH && field.getValue().length()>1) { // Non indexed content length for non zero value indexed=false; - never_index=false; - huffman=true; - bits = 4; - buffer.put((byte)0x00); + encodeName(buffer,(byte)0x00,4,header.asString(),name); + encodeValue(buffer,true,field.getValue()); + if (_debug) + encoding="LitIdxNS"+(1+NBitInteger.octectsNeeded(4,_context.index(name)))+"HuffV!Idx"; + } + else if (field instanceof PreEncodedHttpField) + { + // Preencoded field + indexed=true; + ((PreEncodedHttpField)field).putTo(buffer,HttpVersion.HTTP_2); + if (_debug) + encoding=((name==null)?"LitHuffN":("LitIdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(6,_context.index(name)))))+ + "HuffVIdx"; } else { // indexed indexed=true; - never_index=false; - huffman=!__DO_NOT_HUFFMAN.contains(header); - bits = 6; - buffer.put((byte)0x40); - } - } - - if (_debug) - { - encoding="Lit"+ - ((name==null)?"HuffN":("IdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(bits,_context.index(name)))))+ - (huffman?"HuffV":"LitV")+ - (indexed?"Idx":(never_index?"!!Idx":"!Idx")); - } - - if (name!=null) - NBitInteger.encode(buffer,bits,_context.index(name)); - else - { - // leave name index bits as 0 - // Encode the name always with lowercase huffman - buffer.put((byte)0x80); - NBitInteger.encode(buffer,7,Huffman.octetsNeededLC(field.getName())); - Huffman.encodeLC(buffer,field.getName()); - } - - // Add the literal value - String value=field.getValue(); - - if (huffman) - { - // huffman literal value - buffer.put((byte)0x80); - NBitInteger.encode(buffer,7,Huffman.octetsNeeded(value)); - Huffman.encode(buffer,field.getValue()); - } - else - { - // add literal assuming iso_8859_1 - buffer.put((byte)0x00); - NBitInteger.encode(buffer,7,value.length()); - for (int i=0;i127) - throw new IllegalArgumentException(); - buffer.put((byte)c); + boolean huffman=!__DO_NOT_HUFFMAN.contains(header); + encodeName(buffer,(byte)0x40,6,header.asString(),name); + encodeValue(buffer,huffman,field.getValue()); + if (_debug) + encoding=((name==null)?"LitHuffN":("LitIdxN"+(name.isStatic()?"S":"")+(1+NBitInteger.octectsNeeded(6,_context.index(name)))))+ + (huffman?"HuffVIdx":"LitVIdx"); } } @@ -333,4 +309,45 @@ public class HpackEncoder LOG.debug("encode {}:'{}' to '{}'",encoding,field,TypeUtil.toHexString(buffer.array(),buffer.arrayOffset()+p,e-p)); } } + + private void encodeName(ByteBuffer buffer, byte mask, int bits, String name, Entry entry) + { + buffer.put(mask); + if (entry==null) + { + // leave name index bits as 0 + // Encode the name always with lowercase huffman + buffer.put((byte)0x80); + NBitInteger.encode(buffer,7,Huffman.octetsNeededLC(name)); + Huffman.encodeLC(buffer,name); + } + else + { + NBitInteger.encode(buffer,bits,_context.index(entry)); + } + } + + private void encodeValue(ByteBuffer buffer, boolean huffman, String value) + { + if (huffman) + { + // huffman literal value + buffer.put((byte)0x80); + NBitInteger.encode(buffer,7,Huffman.octetsNeeded(value)); + Huffman.encode(buffer,value); + } + else + { + // add literal assuming iso_8859_1 + buffer.put((byte)0x00); + NBitInteger.encode(buffer,7,value.length()); + for (int i=0;i127) + throw new IllegalArgumentException(); + buffer.put((byte)c); + } + } + } } diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java new file mode 100644 index 00000000000..330cd2c932a --- /dev/null +++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackFieldPreEncoder.java @@ -0,0 +1,74 @@ +// +// ======================================================================== +// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd. +// ------------------------------------------------------------------------ +// All rights reserved. This program and the accompanying materials +// are made available under the terms of the Eclipse Public License v1.0 +// and Apache License v2.0 which accompanies this distribution. +// +// The Eclipse Public License is available at +// http://www.eclipse.org/legal/epl-v10.html +// +// The Apache License v2.0 is available at +// http://www.opensource.org/licenses/apache2.0.php +// +// You may elect to redistribute this code under either of these licenses. +// ======================================================================== +// + + +package org.eclipse.jetty.http2.hpack; + +import java.nio.ByteBuffer; + +import org.eclipse.jetty.http.HttpFieldPreEncoder; +import org.eclipse.jetty.http.HttpHeader; +import org.eclipse.jetty.http.HttpVersion; +import org.eclipse.jetty.http2.hpack.HpackContext.Entry; +import org.eclipse.jetty.util.BufferUtil; + + +/* ------------------------------------------------------------ */ +/** + */ +public class HpackFieldPreEncoder implements HttpFieldPreEncoder +{ + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.HttpFieldPreEncoder#getHttpVersion() + */ + @Override + public HttpVersion getHttpVersion() + { + return HttpVersion.HTTP_2; + } + + /* ------------------------------------------------------------ */ + /** + * @see org.eclipse.jetty.http.HttpFieldPreEncoder#getEncodedField(org.eclipse.jetty.http.HttpHeader, java.lang.String, java.lang.String) + */ + @Override + public byte[] getEncodedField(HttpHeader header, String name, String value) + { + ByteBuffer buffer = BufferUtil.allocate(name.length()+value.length()+10); + BufferUtil.clearToFill(buffer); + buffer.put((byte)0x40); + Entry entry = header==null?null:HpackContext.getStatic(header); + if (entry==null) + { + buffer.put((byte)0x80); + NBitInteger.encode(buffer,7,Huffman.octetsNeededLC(name)); + Huffman.encodeLC(buffer,name); + } + else + { + NBitInteger.encode(buffer,6,entry.getSlot()); + } + + buffer.put((byte)0x80); + NBitInteger.encode(buffer,7,Huffman.octetsNeeded(value)); + Huffman.encode(buffer,value); + BufferUtil.flipToFlush(buffer,0); + return BufferUtil.toArray(buffer); + } +} diff --git a/jetty-http2/http2-hpack/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder b/jetty-http2/http2-hpack/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder new file mode 100644 index 00000000000..4dca767af12 --- /dev/null +++ b/jetty-http2/http2-hpack/src/main/resources/META-INF/services/org.eclipse.jetty.http.HttpFieldPreEncoder @@ -0,0 +1 @@ +org.eclipse.jetty.http2.hpack.HpackFieldPreEncoder \ No newline at end of file diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackContextTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackContextTest.java index bdf8fcf3cfa..120fc199e6e 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackContextTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackContextTest.java @@ -24,12 +24,8 @@ import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; -import static org.junit.Assert.fail; import java.nio.ByteBuffer; -import java.util.HashSet; -import java.util.Iterator; -import java.util.NoSuchElementException; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http2.hpack.HpackContext.Entry; diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java index c0f81d58809..af16d2b5da6 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackDecoderTest.java @@ -30,7 +30,6 @@ import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.util.TypeUtil; -import org.junit.Ignore; import org.junit.Test; diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java index f1bd5a0e1eb..d3b4f883996 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackEncoderTest.java @@ -23,13 +23,11 @@ package org.eclipse.jetty.http2.hpack; import static org.junit.Assert.assertThat; import java.nio.ByteBuffer; -import java.util.HashSet; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; -import org.eclipse.jetty.http2.hpack.HpackContext.Entry; import org.eclipse.jetty.util.BufferUtil; import org.hamcrest.Matchers; import org.junit.Assert; diff --git a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java index ac6a9ac7568..8c256180766 100644 --- a/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java +++ b/jetty-http2/http2-hpack/src/test/java/org/eclipse/jetty/http2/hpack/HpackTest.java @@ -24,12 +24,15 @@ import static org.junit.Assert.assertEquals; import java.nio.ByteBuffer; import org.eclipse.jetty.http.BadMessageException; +import org.eclipse.jetty.http.DateGenerator; +import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.http.HttpVersion; import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.MetaData.Response; +import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.util.BufferUtil; import org.junit.Assert; import org.junit.Test; @@ -37,6 +40,10 @@ import org.junit.Test; public class HpackTest { + final static HttpField ServerJetty = new PreEncodedHttpField(HttpHeader.SERVER,"jetty"); + final static HttpField XPowerJetty = new PreEncodedHttpField(HttpHeader.X_POWERED_BY,"jetty"); + final static HttpField Date = new PreEncodedHttpField(HttpHeader.DATE,DateGenerator.formatDate(System.currentTimeMillis())); + @Test public void encodeDecodeResponseTest() { @@ -47,7 +54,9 @@ public class HpackTest HttpFields fields0 = new HttpFields(); fields0.add(HttpHeader.CONTENT_TYPE,"text/html"); fields0.add(HttpHeader.CONTENT_LENGTH,"1024"); - fields0.add(HttpHeader.SERVER,"jetty"); + fields0.add(ServerJetty); + fields0.add(XPowerJetty); + fields0.add(Date); fields0.add(HttpHeader.SET_COOKIE,"abcdefghijklmnopqrstuvwxyz"); fields0.add("custom-key","custom-value"); Response original0 = new MetaData.Response(HttpVersion.HTTP_2,200,fields0); @@ -70,7 +79,9 @@ public class HpackTest HttpFields fields1 = new HttpFields(); fields1.add(HttpHeader.CONTENT_TYPE,"text/plain"); fields1.add(HttpHeader.CONTENT_LENGTH,"1234"); - fields1.add(HttpHeader.SERVER,"jetty"); + fields1.add(ServerJetty); + fields0.add(XPowerJetty); + fields0.add(Date); fields1.add("Custom-Key","Other-Value"); Response original1 = new MetaData.Response(HttpVersion.HTTP_2,200,fields1); diff --git a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java index 3ee7c939a7e..6b4b114aadf 100644 --- a/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java +++ b/jetty-http2/http2-server/src/main/java/org/eclipse/jetty/http2/server/HttpChannelOverHTTP2.java @@ -21,6 +21,7 @@ package org.eclipse.jetty.http2.server; import java.io.IOException; import java.nio.ByteBuffer; +import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpGenerator; @@ -45,9 +46,8 @@ import org.eclipse.jetty.util.log.Logger; public class HttpChannelOverHTTP2 extends HttpChannel { private static final Logger LOG = Log.getLogger(HttpChannelOverHTTP2.class); - private static final HttpField ACCEPT_ENCODING_GZIP = new HttpField(HttpHeader.ACCEPT_ENCODING,"gzip"); - private static final HttpField SERVER_VERSION=new HttpField(HttpHeader.SERVER,HttpConfiguration.SERVER_VERSION); - private static final HttpField POWERED_BY=new HttpField(HttpHeader.X_POWERED_BY,HttpConfiguration.SERVER_VERSION); + private static final HttpField SERVER_VERSION=new PreEncodedHttpField(HttpHeader.SERVER,HttpConfiguration.SERVER_VERSION); + private static final HttpField POWERED_BY=new PreEncodedHttpField(HttpHeader.X_POWERED_BY,HttpConfiguration.SERVER_VERSION); private final Stream stream; // TODO recycle channel for new Stream? private boolean _expect100Continue = false; @@ -86,11 +86,8 @@ public class HttpChannelOverHTTP2 extends HttpChannel onRequest(request); - if (frame.isEndStream()) - { onRequestComplete(); - } if (LOG.isDebugEnabled()) { diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java index e207466cd4b..370259f6552 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/Server.java @@ -38,6 +38,7 @@ import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.DateGenerator; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpGenerator; @@ -299,7 +300,7 @@ public class Server extends HandlerWrapper implements Attributes df = _dateField; if (df==null || df._seconds!=seconds) { - HttpField field=new HttpGenerator.CachedHttpField(HttpHeader.DATE,DateGenerator.formatDate(now)); + HttpField field=new PreEncodedHttpField(HttpHeader.DATE,DateGenerator.formatDate(now)); _dateField=new DateField(seconds,field); return field; } diff --git a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java index 29bcb57f117..d6c2a341f0f 100644 --- a/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java +++ b/jetty-servlet/src/main/java/org/eclipse/jetty/servlet/DefaultServlet.java @@ -39,10 +39,10 @@ import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.HttpContent; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.http.HttpGenerator.CachedHttpField; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.MimeTypes; @@ -144,7 +144,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory private static final long serialVersionUID = 4930458713846881193L; - private static final CachedHttpField ACCEPT_RANGES = new CachedHttpField(HttpHeader.ACCEPT_RANGES, "bytes"); + private static final PreEncodedHttpField ACCEPT_RANGES = new PreEncodedHttpField(HttpHeader.ACCEPT_RANGES, "bytes"); private ServletContext _servletContext; private ContextHandler _contextHandler; @@ -241,7 +241,7 @@ public class DefaultServlet extends HttpServlet implements ResourceFactory String cc=getInitParameter("cacheControl"); if (cc!=null) - _cacheControl=new CachedHttpField(HttpHeader.CACHE_CONTROL, cc); + _cacheControl=new PreEncodedHttpField(HttpHeader.CACHE_CONTROL, cc); String resourceCache = getInitParameter("resourceCache"); int max_cache_size=getInitInt("maxCacheSize", -2); diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java index febeae01166..9097aa47b50 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/AsyncGzipFilter.java @@ -37,9 +37,9 @@ import javax.servlet.ServletResponse; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; +import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpMethod; import org.eclipse.jetty.http.HttpVersion; @@ -152,7 +152,7 @@ public class AsyncGzipFilter extends UserAgentFilter implements GzipFactory protected Set _excludedAgentPatterns; protected Set _excludedPaths; protected Set _excludedPathPatterns; - protected HttpField _vary=new HttpGenerator.CachedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT); + protected HttpField _vary=new PreEncodedHttpField(HttpHeader.VARY,HttpHeader.ACCEPT_ENCODING+", "+HttpHeader.USER_AGENT); /* ------------------------------------------------------------ */ /** @@ -278,7 +278,7 @@ public class AsyncGzipFilter extends UserAgentFilter implements GzipFactory tmp=filterConfig.getInitParameter("vary"); if (tmp!=null) - _vary=new HttpGenerator.CachedHttpField(HttpHeader.VARY,tmp); + _vary=new PreEncodedHttpField(HttpHeader.VARY,tmp); LOG.debug("{} vary={}",this,_vary); } diff --git a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java index 37a6bc111a2..63cd39860e1 100644 --- a/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java +++ b/jetty-servlets/src/main/java/org/eclipse/jetty/servlets/gzip/GzipHttpOutput.java @@ -24,8 +24,8 @@ import java.util.concurrent.atomic.AtomicReference; import java.util.zip.CRC32; import java.util.zip.Deflater; +import org.eclipse.jetty.http.PreEncodedHttpField; import org.eclipse.jetty.http.HttpFields; -import org.eclipse.jetty.http.HttpGenerator; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.MimeTypes; import org.eclipse.jetty.server.HttpChannel; @@ -41,7 +41,7 @@ import org.eclipse.jetty.util.log.Logger; public class GzipHttpOutput extends HttpOutput { public static Logger LOG = Log.getLogger(GzipHttpOutput.class); - private final static HttpGenerator.CachedHttpField CONTENT_ENCODING_GZIP=new HttpGenerator.CachedHttpField(HttpHeader.CONTENT_ENCODING,"gzip"); + private final static PreEncodedHttpField CONTENT_ENCODING_GZIP=new PreEncodedHttpField(HttpHeader.CONTENT_ENCODING,"gzip"); private final static byte[] GZIP_HEADER = new byte[] { (byte)0x1f, (byte)0x8b, Deflater.DEFLATED, 0, 0, 0, 0, 0, 0, 0 }; private enum GZState { NOT_COMPRESSING, MIGHT_COMPRESS, COMMITTING, COMPRESSING, FINISHED};