Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGreg Wilkins2014-06-11 13:16:40 +0000
committerGreg Wilkins2014-06-11 13:16:40 +0000
commitbbd61f8e192b0af9cf7c4aae51a65e5cd07c863c (patch)
treee8ab3e1ef0481b203b43d969c4cfade54b6e5b73
parent70223cbda9cb8a5575641050819c080c4b7d5a57 (diff)
downloadorg.eclipse.jetty.project-bbd61f8e192b0af9cf7c4aae51a65e5cd07c863c.tar.gz
org.eclipse.jetty.project-bbd61f8e192b0af9cf7c4aae51a65e5cd07c863c.tar.xz
org.eclipse.jetty.project-bbd61f8e192b0af9cf7c4aae51a65e5cd07c863c.zip
Multiple mixed in changes and improvements
Simplified HttpParser as per rfc7230 implemented local/remote hpack max table sizes
-rw-r--r--jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java9
-rw-r--r--jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java3
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/BadMessage.java67
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java102
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpField.java13
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpParser.java528
-rw-r--r--jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java6
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java3
-rw-r--r--jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java147
-rw-r--r--jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/AuthorityHttpField.java64
-rw-r--r--jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackContext.java30
-rw-r--r--jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackDecoder.java14
-rw-r--r--jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/HpackEncoder.java57
-rw-r--r--jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/MetaDataBuilder.java3
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java6
-rw-r--r--jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannelOverHttp.java6
-rw-r--r--jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java107
-rw-r--r--jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java6
18 files changed, 601 insertions, 570 deletions
diff --git a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
index f98f482894..34e586f5ee 100644
--- a/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
+++ b/jetty-client/src/main/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTP.java
@@ -197,14 +197,11 @@ public class HttpReceiverOverHTTP extends HttpReceiver implements HttpParser.Res
}
@Override
- public boolean parsedHeader(HttpField field)
+ public void parsedHeader(HttpField field)
{
HttpExchange exchange = getHttpExchange();
- if (exchange == null)
- return false;
-
- responseHeader(exchange, field);
- return false;
+ if (exchange != null)
+ responseHeader(exchange, field);
}
@Override
diff --git a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
index 83a9a79ddb..ef82e32fdf 100644
--- a/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
+++ b/jetty-fcgi/fcgi-client/src/main/java/org/eclipse/jetty/fcgi/parser/ResponseContentParser.java
@@ -153,7 +153,7 @@ public class ResponseContentParser extends StreamContentParser
}
@Override
- public boolean parsedHeader(HttpField httpField)
+ public void parsedHeader(HttpField httpField)
{
try
{
@@ -188,7 +188,6 @@ public class ResponseContentParser extends StreamContentParser
{
logger.debug("Exception while invoking listener " + listener, x);
}
- return false;
}
private void notifyBegin(int code, String reason)
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/BadMessage.java b/jetty-http/src/main/java/org/eclipse/jetty/http/BadMessage.java
new file mode 100644
index 0000000000..c21beff3ce
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/BadMessage.java
@@ -0,0 +1,67 @@
+//
+// ========================================================================
+// 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;
+
+/* ------------------------------------------------------------------------------- */
+class BadMessage extends Error
+{
+ final int _code;
+ final String _reason;
+
+ BadMessage()
+ {
+ this(400,null);
+ }
+
+ BadMessage(int code)
+ {
+ this(code,null);
+ }
+
+ BadMessage(String reason)
+ {
+ this(400,reason);
+ }
+
+ BadMessage(int code,String reason)
+ {
+ _code=code;
+ _reason=reason;
+ }
+
+ BadMessage(int code,String reason,Throwable cause)
+ {
+ super(cause);
+ _code=code;
+ _reason=reason;
+ }
+
+ public int getCode()
+ {
+ return _code;
+ }
+
+ public String getReason()
+ {
+ return _reason;
+ }
+
+
+} \ No newline at end of file
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java
new file mode 100644
index 0000000000..11c233cd4f
--- /dev/null
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HostPortHttpField.java
@@ -0,0 +1,102 @@
+//
+// ========================================================================
+// 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 org.eclipse.jetty.util.StringUtil;
+
+
+
+/* ------------------------------------------------------------ */
+/**
+ */
+public class HostPortHttpField extends HttpField
+{
+ public final String _host;
+ public final int _port;
+
+ public HostPortHttpField(HttpHeader header, String name, String authority)
+ {
+ super(header,name,authority);
+
+ try
+ {
+ if (authority.charAt(0)=='[')
+ {
+ // ipv6reference
+ int close=authority.lastIndexOf(']');
+ if (close<0)
+ throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad ipv6");
+ _host=authority.substring(1,close);
+
+ if (authority.length()>close+1)
+ {
+ if (authority.charAt(close+1)!=':')
+ throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad ipv6 port");
+ _port=StringUtil.toInt(authority,close+2);
+ }
+ else
+ _port=0;
+ }
+ else
+ {
+ // ipv4address or hostname
+ int c = authority.lastIndexOf(':');
+ if (c>=0)
+ {
+ _host=authority.substring(0,c);
+ _port=StringUtil.toInt(authority,c+1);
+ }
+ else
+ {
+ _host=authority;
+ _port=0;
+ }
+ }
+ }
+ catch (BadMessage bm)
+ {
+ throw bm;
+ }
+ catch(Exception e)
+ {
+ throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad HostPort",e);
+ }
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Get the host.
+ * @return the host
+ */
+ public String getHost()
+ {
+ return _host;
+ }
+
+ /* ------------------------------------------------------------ */
+ /** Get the port.
+ * @return the port
+ */
+ public int getPort()
+ {
+ return _port;
+ }
+
+
+}
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 f270482f3a..7cece45c30 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
@@ -18,6 +18,8 @@
package org.eclipse.jetty.http;
+import org.eclipse.jetty.util.QuotedStringTokenizer;
+
/* ------------------------------------------------------------ */
/** A HTTP Field
@@ -65,6 +67,17 @@ public class HttpField
return _value;
}
+ public String[] getValues()
+ {
+ QuotedStringTokenizer tok = new QuotedStringTokenizer(_value, ",", false, false);
+ tok.setSingle(false);
+ String[] v = new String[tok.countTokens()];
+ int t=0;
+ while(tok.hasMoreTokens())
+ v[t++]=tok.nextToken();
+ return v;
+ }
+
@Override
public String toString()
{
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 ac50f342de..e0a7e9552c 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
@@ -33,7 +33,7 @@ import org.eclipse.jetty.util.log.Logger;
/* ------------------------------------------------------------ */
-/** A Parser for 1.0 and 1.1
+/** A Parser for 1.0 and 1.1 as defined by RFC7230
* <p>
* The is parser parses HTTP client and server messages from buffers
* passed in the {@link #parseNext(ByteBuffer)} method. The parsed
@@ -71,6 +71,8 @@ import org.eclipse.jetty.util.log.Logger;
* fields. Otherwise a fast case insensitive string lookup is used that may alter the
* case of the method and/or headers
* </p>
+ * <p>
+ * @see http://tools.ietf.org/html/rfc7230
*/
public class HttpParser
{
@@ -330,36 +332,6 @@ public class HttpParser
}
/* ------------------------------------------------------------------------------- */
- private static class BadMessage extends Error
- {
- private static final long serialVersionUID = 1L;
- private final int _code;
- private final String _message;
-
- BadMessage()
- {
- this(400,null);
- }
-
- BadMessage(int code)
- {
- this(code,null);
- }
-
- BadMessage(String message)
- {
- this(400,message);
- }
-
- BadMessage(int code,String message)
- {
- _code=code;
- _message=message;
- }
-
- }
-
- /* ------------------------------------------------------------------------------- */
private byte next(ByteBuffer buffer)
{
byte ch = buffer.get();
@@ -781,124 +753,105 @@ public class HttpParser
return handle;
}
- private boolean handleKnownHeaders(ByteBuffer buffer)
+ private void parsedHeader()
{
- boolean add_to_connection_trie=false;
- switch (_header)
+ // handler last header if any. Delayed to here just in case there was a continuation line (above)
+ if (_headerString!=null || _valueString!=null)
{
- case CONTENT_LENGTH:
- if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
- {
- try
- {
- _contentLength=Long.parseLong(_valueString);
- }
- catch(NumberFormatException e)
- {
- LOG.ignore(e);
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
- }
- if (_contentLength <= 0)
- _endOfContent=EndOfContent.NO_CONTENT;
- else
- _endOfContent=EndOfContent.CONTENT_LENGTH;
- }
- break;
-
- case TRANSFER_ENCODING:
- if (_value==HttpHeaderValue.CHUNKED)
- _endOfContent=EndOfContent.CHUNKED_CONTENT;
- else
- {
- if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
- _endOfContent=EndOfContent.CHUNKED_CONTENT;
- else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
- {
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad chunking");
- }
- }
- break;
-
- case HOST:
- add_to_connection_trie=_connectionFields!=null && _field==null;
- _host=true;
- String host=_valueString;
- int port=0;
- if (host==null || host.length()==0)
- {
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
- }
-
- int len=host.length();
- loop: for (int i = len; i-- > 0;)
+ // Handle known headers
+ if (_header!=null)
+ {
+ boolean add_to_connection_trie=false;
+ switch (_header)
{
- char c2 = (char)(0xff & host.charAt(i));
- switch (c2)
- {
- case ']':
- break loop;
-
- case ':':
+ case CONTENT_LENGTH:
+ if (_endOfContent != EndOfContent.CHUNKED_CONTENT)
+ {
try
{
- len=i;
- port = StringUtil.toInt(host,i+1);
+ _contentLength=Long.parseLong(_valueString);
}
- catch (NumberFormatException e)
+ catch(NumberFormatException e)
{
- if (DEBUG)
- LOG.debug(e);
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Host header");
+ LOG.ignore(e);
+ throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Content-Length");
}
- break loop;
- }
- }
- if (host.charAt(0)=='[')
- {
- if (host.charAt(len-1)!=']')
- {
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad IPv6 Host header");
- }
- host = host.substring(1,len-1);
+ if (_contentLength <= 0)
+ _endOfContent=EndOfContent.NO_CONTENT;
+ else
+ _endOfContent=EndOfContent.CONTENT_LENGTH;
+ }
+ break;
+
+ case TRANSFER_ENCODING:
+ if (_value==HttpHeaderValue.CHUNKED)
+ _endOfContent=EndOfContent.CHUNKED_CONTENT;
+ else
+ {
+ if (_valueString.endsWith(HttpHeaderValue.CHUNKED.toString()))
+ _endOfContent=EndOfContent.CHUNKED_CONTENT;
+ else if (_valueString.contains(HttpHeaderValue.CHUNKED.toString()))
+ {
+ throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad chunking");
+ }
+ }
+ break;
+
+ case HOST:
+ _host=true;
+ HostPortHttpField hpfield;
+ if (_field!=null)
+ {
+ hpfield = (HostPortHttpField)_field;
+ }
+ else
+ {
+ _field=hpfield=new HostPortHttpField(_header,_strict?_headerString:_header.asString(),_valueString);
+ add_to_connection_trie=_connectionFields!=null;
+ }
+
+ if (_requestHandler!=null)
+ _requestHandler.parsedHostHeader(hpfield.getHost(),hpfield.getPort());
+
+ break;
+
+ case CONNECTION:
+ // Don't cache if not persistent
+ if (_valueString!=null && _valueString.contains("close"))
+ {
+ _closed=true;
+ _connectionFields=null;
+ }
+ break;
+
+ case AUTHORIZATION:
+ case ACCEPT:
+ case ACCEPT_CHARSET:
+ case ACCEPT_ENCODING:
+ case ACCEPT_LANGUAGE:
+ case COOKIE:
+ case CACHE_CONTROL:
+ case USER_AGENT:
+ add_to_connection_trie=_connectionFields!=null && _field==null;
+ break;
+
+ default: break;
}
- else if (len!=host.length())
- host = host.substring(0,len);
-
- if (_requestHandler!=null)
- _requestHandler.parsedHostHeader(host,port);
-
- break;
-
- case CONNECTION:
- // Don't cache if not persistent
- if (_valueString!=null && _valueString.contains("close"))
+
+ if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
{
- _closed=true;
- _connectionFields=null;
+ if (_field==null)
+ _field=new HttpField(_header,_strict?_headerString:_header.asString(),_valueString);
+ _connectionFields.put(_field);
}
- break;
-
- case AUTHORIZATION:
- case ACCEPT:
- case ACCEPT_CHARSET:
- case ACCEPT_ENCODING:
- case ACCEPT_LANGUAGE:
- case COOKIE:
- case CACHE_CONTROL:
- case USER_AGENT:
- add_to_connection_trie=_connectionFields!=null && _field==null;
- break;
-
- default: break;
- }
-
- if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
- {
- _field=new HttpField(_header,_valueString);
- _connectionFields.put(_field);
+ }
+ _handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString));
}
- return false;
+ _headerString=_valueString=null;
+ _header=null;
+ _value=null;
+ _field=null;
}
@@ -932,194 +885,160 @@ public class HttpParser
case HttpTokens.COLON:
case HttpTokens.SPACE:
case HttpTokens.TAB:
+ throw new BadMessage(HttpStatus.BAD_REQUEST_400,"Bad Continuation");
+
+ case HttpTokens.LINE_FEED:
{
- // header value without name - continuation?
- if (_valueString==null)
+ _contentPosition=0;
+
+ // End of headers!
+
+ // Was there a required host header?
+ if (!_host && _version!=HttpVersion.HTTP_1_0 && _requestHandler!=null)
{
- _string.setLength(0);
- _length=0;
+ throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Host");
}
- else
+
+ // is it a response that cannot have a body?
+ if (_responseHandler !=null && // response
+ (_responseStatus == 304 || // not-modified response
+ _responseStatus == 204 || // no-content response
+ _responseStatus < 200)) // 1xx response
+ _endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
+
+ // else if we don't know framing
+ else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
{
- setString(_valueString);
- _string.append(' ');
- _length++;
- _valueString=null;
+ if (_responseStatus == 0 // request
+ || _responseStatus == 304 // not-modified response
+ || _responseStatus == 204 // no-content response
+ || _responseStatus < 200) // 1xx response
+ _endOfContent=EndOfContent.NO_CONTENT;
+ else
+ _endOfContent=EndOfContent.EOF_CONTENT;
+ }
+
+ // How is the message ended?
+ switch (_endOfContent)
+ {
+ case EOF_CONTENT:
+ setState(State.EOF_CONTENT);
+ handle=_handler.headerComplete()||handle;
+ break;
+
+ case CHUNKED_CONTENT:
+ setState(State.CHUNKED_CONTENT);
+ handle=_handler.headerComplete()||handle;
+ break;
+
+ case NO_CONTENT:
+ handle=_handler.headerComplete()||handle;
+ setState(State.END);
+ handle=_handler.messageComplete()||handle;
+ break;
+
+ default:
+ setState(State.CONTENT);
+ handle=_handler.headerComplete()||handle;
+ break;
}
- setState(State.HEADER_VALUE);
break;
}
default:
{
- // handler last header if any. Delayed to here just in case there was a continuation line (above)
- if (_headerString!=null || _valueString!=null)
- {
- // Handle known headers
- if (_header!=null && handleKnownHeaders(buffer))
- {
- _headerString=_valueString=null;
- _header=null;
- _value=null;
- _field=null;
- return true;
- }
- handle=_handler.parsedHeader(_field!=null?_field:new HttpField(_header,_headerString,_valueString))||handle;
- }
- _headerString=_valueString=null;
- _header=null;
- _value=null;
- _field=null;
-
// now handle the ch
- if (ch == HttpTokens.LINE_FEED)
- {
- _contentPosition=0;
+ if (ch<=HttpTokens.SPACE)
+ throw new BadMessage();
- // End of headers!
+ if (buffer.hasRemaining())
+ {
+ // Try a look ahead for the known header name and value.
+ HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
+ if (field==null)
+ field=CACHE.getBest(buffer,-1,buffer.remaining());
- // Was there a required host header?
- if (!_host && _version!=HttpVersion.HTTP_1_0 && _requestHandler!=null)
+ if (field!=null)
{
- throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Host");
- }
+ final String n;
+ final String v;
- // is it a response that cannot have a body?
- if (_responseHandler !=null && // response
- (_responseStatus == 304 || // not-modified response
- _responseStatus == 204 || // no-content response
- _responseStatus < 200)) // 1xx response
- _endOfContent=EndOfContent.NO_CONTENT; // ignore any other headers set
-
- // else if we don't know framing
- else if (_endOfContent == EndOfContent.UNKNOWN_CONTENT)
- {
- if (_responseStatus == 0 // request
- || _responseStatus == 304 // not-modified response
- || _responseStatus == 204 // no-content response
- || _responseStatus < 200) // 1xx response
- _endOfContent=EndOfContent.NO_CONTENT;
+ if (_strict)
+ {
+ // Have to get the fields exactly from the buffer to match case
+ String fn=field.getName();
+ String fv=field.getValue();
+ n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII);
+ if (fv==null)
+ v=null;
+ else
+ {
+ v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1);
+ field=new HttpField(field.getHeader(),n,v);
+ }
+ }
else
- _endOfContent=EndOfContent.EOF_CONTENT;
- }
-
- // How is the message ended?
- switch (_endOfContent)
- {
- case EOF_CONTENT:
- setState(State.EOF_CONTENT);
- handle=_handler.headerComplete()||handle;
- break;
-
- case CHUNKED_CONTENT:
- setState(State.CHUNKED_CONTENT);
- handle=_handler.headerComplete()||handle;
- break;
+ {
+ n=field.getName();
+ v=field.getValue();
+ }
- case NO_CONTENT:
- handle=_handler.headerComplete()||handle;
- setState(State.END);
- handle=_handler.messageComplete()||handle;
- break;
+ _header=field.getHeader();
+ _headerString=n;
- default:
- setState(State.CONTENT);
- handle=_handler.headerComplete()||handle;
+ if (v==null)
+ {
+ // Header only
+ setState(State.HEADER_VALUE);
+ _string.setLength(0);
+ _length=0;
+ buffer.position(buffer.position()+n.length()+1);
break;
- }
- }
- else if (ch<=HttpTokens.SPACE)
- throw new BadMessage();
- else
- {
- if (buffer.hasRemaining())
- {
- // Try a look ahead for the known header name and value.
- HttpField field=_connectionFields==null?null:_connectionFields.getBest(buffer,-1,buffer.remaining());
- if (field==null)
- field=CACHE.getBest(buffer,-1,buffer.remaining());
-
- if (field!=null)
+ }
+ else
{
- final String n;
- final String v;
+ // Header and value
+ int pos=buffer.position()+n.length()+v.length()+1;
+ byte b=buffer.get(pos);
- if (_strict)
- {
- // Have to get the fields exactly from the buffer to match case
- String fn=field.getName();
- String fv=field.getValue();
- n=BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII);
- if (fv==null)
- v=null;
- else
+ if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
+ {
+ _field=field;
+ _valueString=v;
+ setState(State.HEADER_IN_VALUE);
+
+ if (b==HttpTokens.CARRIAGE_RETURN)
{
- v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1);
- field=new HttpField(field.getHeader(),n,v);
+ _cr=true;
+ buffer.position(pos+1);
}
- }
- else
- {
- n=field.getName();
- v=field.getValue();
- }
-
- _header=field.getHeader();
- _headerString=n;
-
- if (v==null)
- {
- // Header only
- setState(State.HEADER_VALUE);
- _string.setLength(0);
- _length=0;
- buffer.position(buffer.position()+n.length()+1);
+ else
+ buffer.position(pos);
break;
}
else
{
- // Header and value
- int pos=buffer.position()+n.length()+v.length()+1;
- byte b=buffer.get(pos);
-
- if (b==HttpTokens.CARRIAGE_RETURN || b==HttpTokens.LINE_FEED)
- {
- _field=field;
- _valueString=v;
- setState(State.HEADER_IN_VALUE);
-
- if (b==HttpTokens.CARRIAGE_RETURN)
- {
- _cr=true;
- buffer.position(pos+1);
- }
- else
- buffer.position(pos);
- break;
- }
- else
- {
- setState(State.HEADER_IN_VALUE);
- setString(v);
- buffer.position(pos);
- break;
- }
+ setState(State.HEADER_IN_VALUE);
+ setString(v);
+ buffer.position(pos);
+ break;
}
}
}
-
- // New header
- setState(State.HEADER_IN_NAME);
- _string.setLength(0);
- _string.append((char)ch);
- _length=1;
}
+
+ // New header
+ setState(State.HEADER_IN_NAME);
+ _string.setLength(0);
+ _string.append((char)ch);
+ _length=1;
+
}
}
break;
case HEADER_IN_NAME:
- if (ch==HttpTokens.COLON || ch==HttpTokens.LINE_FEED)
+ if (ch==HttpTokens.COLON)
{
if (_headerString==null)
{
@@ -1128,11 +1047,11 @@ public class HttpParser
}
_length=-1;
- setState(ch==HttpTokens.LINE_FEED?State.HEADER:State.HEADER_VALUE);
+ setState(State.HEADER_VALUE);
break;
}
- if (ch>=HttpTokens.SPACE || ch==HttpTokens.TAB)
+ if (ch>HttpTokens.SPACE)
{
if (_header!=null)
{
@@ -1160,19 +1079,8 @@ public class HttpParser
if (ch==HttpTokens.SPACE || ch==HttpTokens.TAB)
break;
-
- if (ch==HttpTokens.LINE_FEED)
- {
- if (_length > 0)
- {
- _value=null;
- _valueString=(_valueString==null)?takeString():(_valueString+" "+takeString());
- }
- setState(State.HEADER);
- break;
- }
- throw new BadMessage("Illegal character");
+ throw new BadMessage();
case HEADER_IN_VALUE:
if (ch>=HttpTokens.SPACE || ch<0 || ch==HttpTokens.TAB)
@@ -1197,6 +1105,7 @@ public class HttpParser
_valueString=takeString();
_length=-1;
}
+ parsedHeader();
setState(State.HEADER);
break;
}
@@ -1332,11 +1241,11 @@ public class HttpParser
{
BufferUtil.clear(buffer);
- LOG.warn("badMessage: "+e._code+(e._message!=null?" "+e._message:"")+" for "+_handler);
+ LOG.warn("badMessage: "+e._code+(e.getReason()!=null?" "+e.getReason():"")+" for "+_handler);
if (DEBUG)
LOG.debug(e);
setState(State.CLOSED);
- _handler.badMessage(e._code, e._message);
+ _handler.badMessage(e.getCode(), e.getReason());
return false;
}
catch(Exception e)
@@ -1609,10 +1518,9 @@ public class HttpParser
/**
* This is the method called by parser when a HTTP Header name and value is found
* @param field The field parsed
- * @return True if the parser should return to its caller
*/
- public boolean parsedHeader(HttpField field);
-
+ public void parsedHeader(HttpField field);
+
/* ------------------------------------------------------------ */
/** Called to signal that an EOF was received unexpectedly
* during the parsing of a HTTP message
@@ -1648,14 +1556,14 @@ public class HttpParser
* @param version
* @return true if handling parsing should return.
*/
- public abstract boolean startRequest(String method, HttpURI uri, HttpVersion version);
+ public boolean startRequest(String method, HttpURI uri, HttpVersion version);
/**
* This is the method called by the parser after it has parsed the host header (and checked it's format). This is
* called after the {@link HttpHandler#parsedHeader(HttpField)} methods and before
* HttpHandler#headerComplete();
*/
- public abstract boolean parsedHostHeader(String host,int port);
+ public void parsedHostHeader(String host,int port);
}
public interface ResponseHandler<T> extends HttpHandler<T>
@@ -1663,7 +1571,7 @@ public class HttpParser
/**
* This is the method called by parser when the HTTP request line is parsed
*/
- public abstract boolean startResponse(HttpVersion version, int status, String reason);
+ public boolean startResponse(HttpVersion version, int status, String reason);
}
public Trie<HttpField> getFieldCache()
diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java
index 0385ffcb7e..a7f081e76a 100644
--- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java
+++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpTester.java
@@ -132,10 +132,9 @@ public class HttpTester
}
}
@Override
- public boolean parsedHeader(HttpField field)
+ public void parsedHeader(HttpField field)
{
put(field.getName(),field.getValue());
- return false;
}
@Override
@@ -302,9 +301,8 @@ public class HttpTester
}
@Override
- public boolean parsedHostHeader(String host,int port)
+ public void parsedHostHeader(String host,int port)
{
- return false;
}
}
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java
index 2925f3e41f..5fc8436bfb 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpGeneratorServerHTTPTest.java
@@ -247,9 +247,8 @@ public class HttpGeneratorServerHTTPTest
}
@Override
- public boolean parsedHeader(HttpField field)
+ public void parsedHeader(HttpField field)
{
- return false;
}
@Override
diff --git a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
index 12b2b38908..8b25973d8c 100644
--- a/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
+++ b/jetty-http/src/test/java/org/eclipse/jetty/http/HttpParserTest.java
@@ -181,6 +181,73 @@ public class HttpParserTest
assertEquals("close", _val[1]);
assertEquals(1, _headers);
}
+
+ @Test
+ public void test7230NoContinuations() throws Exception
+ {
+ ByteBuffer buffer= BufferUtil.toBuffer(
+ "GET / HTTP/1.0\015\012" +
+ "Host: localhost\015\012" +
+ "Name: value\015\012" +
+ " extra\015\012" +
+ "\015\012");
+
+ HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
+ HttpParser parser= new HttpParser(handler);
+ parseAll(parser,buffer);
+
+ Assert.assertThat(_bad,Matchers.notNullValue());
+ Assert.assertThat(_bad,Matchers.containsString("Bad Continuation"));
+ }
+
+
+ @Test
+ public void test7230NoWhiteSpaceInName() throws Exception
+ {
+ ByteBuffer buffer= BufferUtil.toBuffer(
+ "GET / HTTP/1.0\015\012" +
+ "Host: localhost\015\012" +
+ " Name: value\015\012" +
+ "\015\012");
+
+ HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
+ HttpParser parser= new HttpParser(handler);
+ parseAll(parser,buffer);
+
+ Assert.assertThat(_bad,Matchers.notNullValue());
+ Assert.assertThat(_bad,Matchers.containsString("Bad"));
+
+ init();
+ buffer= BufferUtil.toBuffer(
+ "GET / HTTP/1.0\015\012" +
+ "Host: localhost\015\012" +
+ "N ame: value\015\012" +
+ "\015\012");
+
+ handler = new Handler();
+ parser= new HttpParser(handler);
+ parseAll(parser,buffer);
+
+ Assert.assertThat(_bad,Matchers.containsString("Illegal character"));
+
+
+ init();
+ buffer= BufferUtil.toBuffer(
+ "GET / HTTP/1.0\015\012" +
+ "Host: localhost\015\012" +
+ "Name : value\015\012" +
+ "\015\012");
+
+ handler = new Handler();
+ parser= new HttpParser(handler);
+ parseAll(parser,buffer);
+
+ Assert.assertThat(_bad,Matchers.containsString("Illegal character"));
+
+ }
+
+
+
@Test
public void testHeaderParseDirect() throws Exception
@@ -189,13 +256,11 @@ public class HttpParserTest
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Header1: value1\015\012" +
- "Header 2 : value 2a \015\012" +
- " value 2b \015\012" +
- "Header3: \015\012" +
- "Header4 \015\012" +
- " value4\015\012" +
- "Server5 : notServer\015\012" +
- "Host Header: notHost\015\012" +
+ "Header2: value 2a \015\012" +
+ "Header3: 3\015\012" +
+ "Header4:value4\015\012" +
+ "Server5: notServer\015\012" +
+ "HostHeader: notHost\015\012" +
"Connection: close\015\012" +
"Accept-Encoding: gzip, deflated\015\012" +
"Accept: unknown\015\012" +
@@ -216,15 +281,15 @@ public class HttpParserTest
assertEquals("localhost", _val[0]);
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
- assertEquals("Header 2", _hdr[2]);
- assertEquals("value 2a value 2b", _val[2]);
+ assertEquals("Header2", _hdr[2]);
+ assertEquals("value 2a", _val[2]);
assertEquals("Header3", _hdr[3]);
- assertEquals(null, _val[3]);
+ assertEquals("3", _val[3]);
assertEquals("Header4", _hdr[4]);
assertEquals("value4", _val[4]);
assertEquals("Server5", _hdr[5]);
assertEquals("notServer", _val[5]);
- assertEquals("Host Header", _hdr[6]);
+ assertEquals("HostHeader", _hdr[6]);
assertEquals("notHost", _val[6]);
assertEquals("Connection", _hdr[7]);
assertEquals("close", _val[7]);
@@ -242,13 +307,11 @@ public class HttpParserTest
"GET / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Header1: value1\015\012" +
- "Header 2 : value 2a \015\012" +
- " value 2b \015\012" +
- "Header3: \015\012" +
- "Header4 \015\012" +
- " value4\015\012" +
- "Server5 : notServer\015\012" +
- "Host Header: notHost\015\012" +
+ "Header2: value 2a \015\012" +
+ "Header3: 3\015\012" +
+ "Header4:value4\015\012" +
+ "Server5: notServer\015\012" +
+ "HostHeader: notHost\015\012" +
"Connection: close\015\012" +
"Accept-Encoding: gzip, deflated\015\012" +
"Accept: unknown\015\012" +
@@ -264,15 +327,15 @@ public class HttpParserTest
assertEquals("localhost", _val[0]);
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
- assertEquals("Header 2", _hdr[2]);
- assertEquals("value 2a value 2b", _val[2]);
+ assertEquals("Header2", _hdr[2]);
+ assertEquals("value 2a", _val[2]);
assertEquals("Header3", _hdr[3]);
- assertEquals(null, _val[3]);
+ assertEquals("3", _val[3]);
assertEquals("Header4", _hdr[4]);
assertEquals("value4", _val[4]);
assertEquals("Server5", _hdr[5]);
assertEquals("notServer", _val[5]);
- assertEquals("Host Header", _hdr[6]);
+ assertEquals("HostHeader", _hdr[6]);
assertEquals("notHost", _val[6]);
assertEquals("Connection", _hdr[7]);
assertEquals("close", _val[7]);
@@ -292,13 +355,11 @@ public class HttpParserTest
"GET / HTTP/1.0\n" +
"Host: localhost\n" +
"Header1: value1\n" +
- "Header 2 : value 2a \n" +
- " value 2b \n" +
- "Header3: \n" +
- "Header4 \n" +
- " value4\n" +
- "Server5 : notServer\n" +
- "Host Header: notHost\n" +
+ "Header2: value 2a value 2b \n" +
+ "Header3: 3\n" +
+ "Header4:value4\n" +
+ "Server5: notServer\n" +
+ "HostHeader: notHost\n" +
"Connection: close\n" +
"Accept-Encoding: gzip, deflated\n" +
"Accept: unknown\n" +
@@ -314,15 +375,15 @@ public class HttpParserTest
assertEquals("localhost", _val[0]);
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
- assertEquals("Header 2", _hdr[2]);
+ assertEquals("Header2", _hdr[2]);
assertEquals("value 2a value 2b", _val[2]);
assertEquals("Header3", _hdr[3]);
- assertEquals(null, _val[3]);
+ assertEquals("3", _val[3]);
assertEquals("Header4", _hdr[4]);
assertEquals("value4", _val[4]);
assertEquals("Server5", _hdr[5]);
assertEquals("notServer", _val[5]);
- assertEquals("Host Header", _hdr[6]);
+ assertEquals("HostHeader", _hdr[6]);
assertEquals("notHost", _val[6]);
assertEquals("Connection", _hdr[7]);
assertEquals("close", _val[7]);
@@ -496,11 +557,9 @@ public class HttpParserTest
"XXXXSPLIT / HTTP/1.0\015\012" +
"Host: localhost\015\012" +
"Header1: value1\015\012" +
- "Header2 : value 2a \015\012" +
- " value 2b \015\012" +
- "Header3: \015\012" +
- "Header4 \015\012" +
- " value4\015\012" +
+ "Header2: value 2a \015\012" +
+ "Header3: 3\015\012" +
+ "Header4:value4\015\012" +
"Server5: notServer\015\012" +
"\015\012ZZZZ");
buffer.position(2);
@@ -534,9 +593,9 @@ public class HttpParserTest
assertEquals("Header1", _hdr[1]);
assertEquals("value1", _val[1]);
assertEquals("Header2", _hdr[2]);
- assertEquals("value 2a value 2b", _val[2]);
+ assertEquals("value 2a", _val[2]);
assertEquals("Header3", _hdr[3]);
- assertEquals(null, _val[3]);
+ assertEquals("3", _val[3]);
assertEquals("Header4", _hdr[4]);
assertEquals("value4", _val[4]);
assertEquals("Server5", _hdr[5]);
@@ -1305,7 +1364,7 @@ public class HttpParserTest
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
- assertEquals("Bad IPv6 Host header",_bad);
+ Assert.assertThat(_bad,Matchers.containsString("Bad"));
}
@Test
@@ -1336,7 +1395,7 @@ public class HttpParserTest
HttpParser.RequestHandler<ByteBuffer> handler = new Handler();
HttpParser parser= new HttpParser(handler);
parser.parseNext(buffer);
- assertEquals("Bad Host header",_bad);
+ Assert.assertThat(_bad,Matchers.containsString("Bad Host"));
}
@Test
@@ -1517,21 +1576,19 @@ public class HttpParserTest
}
@Override
- public boolean parsedHeader(HttpField field)
+ public void parsedHeader(HttpField field)
{
_fields.add(field);
//System.err.println("header "+name+": "+value);
_hdr[++_headers]= field.getName();
_val[_headers]= field.getValue();
- return false;
}
@Override
- public boolean parsedHostHeader(String host,int port)
+ public void parsedHostHeader(String host,int port)
{
_host=host;
_port=port;
- return false;
}
@Override
diff --git a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/AuthorityHttpField.java b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/AuthorityHttpField.java
index e845f1426f..121788b66c 100644
--- a/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/AuthorityHttpField.java
+++ b/jetty-http2/http2-hpack/src/main/java/org/eclipse/jetty/http2/hpack/AuthorityHttpField.java
@@ -19,76 +19,20 @@
package org.eclipse.jetty.http2.hpack;
-import org.eclipse.jetty.http.HttpField;
+import org.eclipse.jetty.http.HostPortHttpField;
+import org.eclipse.jetty.http2.hpack.HpackContext;
import org.eclipse.jetty.util.StringUtil;
/* ------------------------------------------------------------ */
/**
*/
-public class AuthorityHttpField extends HttpField
+public class AuthorityHttpField extends HostPortHttpField
{
public final static String AUTHORITY = HpackContext.STATIC_TABLE[1][0];
- public final String _host;
- public final int _port;
-
public AuthorityHttpField(String authority)
{
- super(AUTHORITY,authority);
-
- if (authority.charAt(0)=='[')
- {
- // ipv6reference
- int close=authority.indexOf(']');
- if (close<0)
- throw new IllegalArgumentException("Bad ipv6");
- _host=authority.substring(1,close-1);
-
- if (authority.length()>close+1)
- {
- if (authority.charAt(close+1)!=':')
- throw new IllegalArgumentException("Bad ipv6 port");
- _port=StringUtil.toInt(authority,close+2);
-
- }
- else
- _port=0;
- }
- else
- {
- // ipv4address or hostname
- int c = authority.lastIndexOf(':');
- if (c>=0)
- {
- _host=authority.substring(0,c);
- _port=StringUtil.toInt(authority,c+1);
- }
- else
- {
- _host=authority;
- _port=0;
- }
- }
+ super(null,AUTHORITY,authority);
}
-
- /* ------------------------------------------------------------ */
- /** Get the host.
- * @return the host
- */
- public String getHost()
- {
- return _host;
- }
-
- /* ------------------------------------------------------------ */
- /** Get the port.
- * @return the port
- */
- public int getPort()
- {
- return _port;
- }
-
-
}
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 c1afa1a676..4b2eb4a8e2 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
@@ -187,11 +187,11 @@ public class HpackContext
_headerTable=new HeaderTable(guesstimateEntries,guesstimateEntries+10);
}
- public void resize(int maxHeaderTableSize)
+ public void resize(int newMaxHeaderTableSize)
{
- LOG.debug("HdrTbl resized {}",maxHeaderTableSize);
- _maxHeaderTableSizeInBytes=maxHeaderTableSize;
- int guesstimateEntries = 10+maxHeaderTableSize/(32+10+10);
+ LOG.debug("HdrTbl resized {}",newMaxHeaderTableSize);
+ _maxHeaderTableSizeInBytes=newMaxHeaderTableSize;
+ int guesstimateEntries = 10+newMaxHeaderTableSize/(32+10+10);
evict();
_headerTable.resizeUnsafe(guesstimateEntries);
}
@@ -245,11 +245,30 @@ public class HpackContext
return entry;
}
- public Object size()
+ /**
+ * @return Current Header table size in entries
+ */
+ public int size()
{
return _headerTable.size();
}
+ /**
+ * @return Current Header table size in Octets
+ */
+ public int getHeaderTableSize()
+ {
+ return _headerTableSizeInBytes;
+ }
+
+ /**
+ * @return Max Header table size in Octets
+ */
+ public int getMaxHeaderTableSize()
+ {
+ return _maxHeaderTableSizeInBytes;
+ }
+
public int index(Entry entry)
{
if (entry._index<0)
@@ -575,4 +594,5 @@ public class HpackContext
}
}
+
}
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 2ecb070ff0..a31df90c15 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
@@ -21,6 +21,7 @@ package org.eclipse.jetty.http2.hpack;
import java.nio.ByteBuffer;
+import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpMethod;
@@ -41,15 +42,22 @@ public class HpackDecoder
private final HpackContext _context;
private final MetaDataBuilder _builder = new MetaDataBuilder();
+ private int _localMaxHeaderTableSize;
public HpackDecoder()
{
this(4096);
}
- public HpackDecoder(int maxHeaderTableSize)
+ public HpackDecoder(int localMaxHeaderTableSize)
{
- _context=new HpackContext(maxHeaderTableSize);
+ _context=new HpackContext(localMaxHeaderTableSize);
+ _localMaxHeaderTableSize=localMaxHeaderTableSize;
+ }
+
+ public void setLocalMaxHeaderTableSize(int localMaxHeaderTableSize)
+ {
+ _localMaxHeaderTableSize=localMaxHeaderTableSize;
}
public MetaData decode(ByteBuffer buffer)
@@ -188,6 +196,8 @@ public class HpackDecoder
int size = NBitInteger.decode(buffer,4);
if (LOG.isDebugEnabled())
LOG.debug("decode resize="+size);
+ if (size>_localMaxHeaderTableSize)
+ throw new IllegalArgumentException();
_context.resize(size);
}
else if (f==3)
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 ac21d4ce35..42e912d618 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
@@ -75,16 +75,24 @@ public class HpackEncoder
}
private final HpackContext _context;
+ private int _remoteMaxHeaderTableSize;
+ private int _localMaxHeaderTableSize;
-
public HpackEncoder()
{
- this(4096);
+ this(4096,4096);
+ }
+
+ public HpackEncoder(int localMaxHeaderTableSize)
+ {
+ this(localMaxHeaderTableSize,4096);
}
- public HpackEncoder(int maxHeaderTableSize)
+ public HpackEncoder(int localMaxHeaderTableSize,int remoteMaxHeaderTableSize)
{
- _context=new HpackContext(maxHeaderTableSize);
+ _context=new HpackContext(remoteMaxHeaderTableSize);
+ _remoteMaxHeaderTableSize=remoteMaxHeaderTableSize;
+ _localMaxHeaderTableSize=localMaxHeaderTableSize;
}
public HpackContext getContext()
@@ -92,6 +100,16 @@ public class HpackEncoder
return _context;
}
+ public void setRemoteMaxHeaderTableSize(int remoteMaxHeaderTableSize)
+ {
+ _remoteMaxHeaderTableSize=remoteMaxHeaderTableSize;
+ }
+
+ public void setLocalMaxHeaderTableSize(int localMaxHeaderTableSize)
+ {
+ _localMaxHeaderTableSize=localMaxHeaderTableSize;
+ }
+
public void encode(MetaData metadata,Lease lease)
{
ByteBuffer buffer = lease.acquire(8*1024,false); // TODO make size configurable
@@ -102,21 +120,15 @@ public class HpackEncoder
BufferUtil.flipToFlush(buffer,0);
}
- public void encodeMaxHeaderTableSize(ByteBuffer buffer, int maxHeaderTableSize)
- {
- _context.resize(maxHeaderTableSize);
- }
-
- public void encodeClearReferenceSet(ByteBuffer buffer)
- {
- // TODO
- _context.clearReferenceSet();
- }
public void encode(ByteBuffer buffer, MetaData metadata)
{
- // Add Request/response meta fields
+ // Check the header table sizes!
+ int maxHeaderTableSize=Math.min(_remoteMaxHeaderTableSize,_localMaxHeaderTableSize);
+ if (maxHeaderTableSize!=_context.getMaxHeaderTableSize())
+ encodeMaxHeaderTableSize(buffer,maxHeaderTableSize);
+ // Add Request/response meta fields
if (metadata.isRequest())
{
MetaData.Request request = (MetaData.Request)metadata;
@@ -147,6 +159,21 @@ public class HpackEncoder
_context.removedUnusedReferences(buffer);
}
+ public void encodeMaxHeaderTableSize(ByteBuffer buffer, int maxHeaderTableSize)
+ {
+ if (maxHeaderTableSize>_remoteMaxHeaderTableSize)
+ throw new IllegalArgumentException();
+ buffer.put((byte)0x20);
+ NBitInteger.encode(buffer,4,maxHeaderTableSize);
+ _context.resize(maxHeaderTableSize);
+ }
+
+ public void encodeClearReferenceSet(ByteBuffer buffer)
+ {
+ buffer.put((byte)0x30);
+ _context.clearReferenceSet();
+ }
+
private void encode(ByteBuffer buffer, HttpField field)
{
final int p=LOG.isDebugEnabled()?buffer.position():-1;
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 6657c7899e..49066b91b5 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
@@ -22,6 +22,7 @@ package org.eclipse.jetty.http2.hpack;
import java.util.ArrayList;
import java.util.List;
+import org.eclipse.jetty.http.HostPortHttpField;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpMethod;
import org.eclipse.jetty.http.HttpScheme;
@@ -83,7 +84,7 @@ public class MetaDataBuilder
case ":authority":
_authority=field.getValue();
- AuthorityHttpField afield=(field instanceof AuthorityHttpField)?((AuthorityHttpField)field):new AuthorityHttpField(field.getValue());
+ HostPortHttpField afield=(field instanceof HostPortHttpField)?((HostPortHttpField)field):new AuthorityHttpField(field.getValue());
_host=afield.getHost();
_port=afield.getPort();
break;
diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
index 09e9292358..e1e55e30c7 100644
--- a/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
+++ b/jetty-server/src/main/java/org/eclipse/jetty/server/HttpChannel.java
@@ -499,7 +499,7 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
}
@Override
- public boolean parsedHeader(HttpField field)
+ public void parsedHeader(HttpField field)
{
HttpHeader header=field.getHeader();
String value=field.getValue();
@@ -521,18 +521,16 @@ public class HttpChannel<T> implements HttpParser.RequestHandler<T>, Runnable
if (field.getName()!=null)
_request.getHttpFields().add(field);
- return false;
}
@Override
- public boolean parsedHostHeader(String host, int port)
+ public void parsedHostHeader(String host, int port)
{
if (_request.getUri().getHost()==null)
{
_request.setServerName(host);
_request.setServerPort(port);
}
- return false;
}
@Override
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 bede165bcf..a041f52582 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
@@ -95,7 +95,7 @@ class HttpChannelOverHttp extends HttpChannel<ByteBuffer> implements HttpParser.
@Override
- public boolean parsedHeader(HttpField field)
+ public void parsedHeader(HttpField field)
{
HttpHeader header=field.getHeader();
String value=field.getValue();
@@ -113,7 +113,7 @@ class HttpChannelOverHttp extends HttpChannel<ByteBuffer> implements HttpParser.
break;
default:
- String[] values = value.split(",");
+ String[] values = field.getValues();
for (int i = 0; values != null && i < values.length; i++)
{
expect = HttpHeaderValue.CACHE.get(values[i].trim());
@@ -136,7 +136,7 @@ class HttpChannelOverHttp extends HttpChannel<ByteBuffer> implements HttpParser.
}
}
}
- return super.parsedHeader(field);
+ super.parsedHeader(field);
}
/**
diff --git a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
index 17e1ec9af9..294c5ffc22 100644
--- a/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
+++ b/jetty-server/src/test/java/org/eclipse/jetty/server/RequestTest.java
@@ -129,41 +129,6 @@ public class RequestTest
}
@Test
- public void testEmptyHeaders() throws Exception
- {
- _handler._checker = new RequestTester()
- {
- @Override
- public boolean check(HttpServletRequest request,HttpServletResponse response)
- {
- assertNotNull(request.getLocale());
- assertTrue(request.getLocales().hasMoreElements());
- assertNull(request.getContentType());
- assertNull(request.getCharacterEncoding());
- assertEquals(0,request.getQueryString().length());
- assertEquals(-1,request.getContentLength());
- assertNull(request.getCookies());
- assertNull(request.getHeader("Name"));
- assertFalse(request.getHeaders("Name").hasMoreElements());
- assertEquals(-1,request.getDateHeader("Name"));
- return true;
- }
- };
-
- String request="GET /? HTTP/1.1\r\n"+
- "Host: whatever\r\n"+
- "Connection: close\n"+
- "Content-Type: \n"+
- "Accept-Language: \n"+
- "Cookie: \n"+
- "Name: \n"+
- "\n";
-
- String responses=_connector.getResponses(request);
- assertTrue(responses.startsWith("HTTP/1.1 200"));
- }
-
- @Test
public void testMultiPartNoConfig() throws Exception
{
_handler._checker = new RequestTester()
@@ -1058,78 +1023,6 @@ public class RequestTest
}
- @Test
- public void testCookieLeak() throws Exception
- {
- final String[] cookie=new String[10];
-
- _handler._checker = new RequestTester()
- {
- @Override
- public boolean check(HttpServletRequest request,HttpServletResponse response)
- {
- for (int i=0;i<cookie.length; i++)
- cookie[i]=null;
-
- Cookie[] cookies = request.getCookies();
- for (int i=0;cookies!=null && i<cookies.length; i++)
- {
- cookie[i]=cookies[i].getValue();
- }
- return true;
- }
- };
-
- String request="POST / HTTP/1.1\r\n"+
- "Host: whatever\r\n"+
- "Cookie: other=cookie\r\n"+
- "\r\n"
- +
- "POST / HTTP/1.1\r\n"+
- "Host: whatever\r\n"+
- "Cookie: name=value\r\n"+
- "Connection: close\r\n"+
- "\r\n";
-
- _connector.getResponses(request);
-
- assertEquals("value",cookie[0]);
- assertEquals(null,cookie[1]);
-
- request="POST / HTTP/1.1\r\n"+
- "Host: whatever\r\n"+
- "Cookie: name=value\r\n"+
- "\r\n"
- +
- "POST / HTTP/1.1\r\n"+
- "Host: whatever\r\n"+
- "Cookie:\r\n"+
- "Connection: close\r\n"+
- "\r\n";
-
- _connector.getResponses(request);
- assertEquals(null,cookie[0]);
- assertEquals(null,cookie[1]);
-
- request="POST / HTTP/1.1\r\n"+
- "Host: whatever\r\n"+
- "Cookie: name=value\r\n"+
- "Cookie: other=cookie\r\n"+
- "\r\n"
- +
- "POST / HTTP/1.1\r\n"+
- "Host: whatever\r\n"+
- "Cookie: name=value\r\n"+
- "Cookie:\r\n"+
- "Connection: close\r\n"+
- "\r\n";
-
- _connector.getResponses(request);
-
- assertEquals("value",cookie[0]);
- assertEquals(null,cookie[1]);
- }
-
@Test
public void testHashDOS() throws Exception
diff --git a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java
index 26c5b6b055..6a6ba1fad2 100644
--- a/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java
+++ b/jetty-spdy/spdy-http-server/src/main/java/org/eclipse/jetty/spdy/server/proxy/ProxyHTTPSPDYConnection.java
@@ -94,19 +94,17 @@ public class ProxyHTTPSPDYConnection extends HttpConnection implements HttpParse
}
@Override
- public boolean parsedHeader(HttpField field)
+ public void parsedHeader(HttpField field)
{
if (field.getHeader() == HttpHeader.HOST)
headers.put(HTTPSPDYHeader.HOST.name(version), field.getValue());
else
headers.put(field.getName(), field.getValue());
- return false;
}
@Override
- public boolean parsedHostHeader(String host, int port)
+ public void parsedHostHeader(String host, int port)
{
- return false;
}
@Override

Back to the top