Multiple mixed in changes and improvements
Simplified HttpParser as per rfc7230 implemented local/remote hpack max table sizes
This commit is contained in:
parent
70223cbda9
commit
bbd61f8e19
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -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()
|
||||
{
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
@ -329,36 +331,6 @@ public class HttpParser
|
|||
return _state == state;
|
||||
}
|
||||
|
||||
/* ------------------------------------------------------------------------------- */
|
||||
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)
|
||||
{
|
||||
|
@ -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)
|
||||
// Handle known headers
|
||||
if (_header!=null)
|
||||
{
|
||||
boolean add_to_connection_trie=false;
|
||||
switch (_header)
|
||||
{
|
||||
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;)
|
||||
{
|
||||
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);
|
||||
}
|
||||
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"))
|
||||
{
|
||||
_closed=true;
|
||||
_connectionFields=null;
|
||||
}
|
||||
break;
|
||||
if (_contentLength <= 0)
|
||||
_endOfContent=EndOfContent.NO_CONTENT;
|
||||
else
|
||||
_endOfContent=EndOfContent.CONTENT_LENGTH;
|
||||
}
|
||||
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);
|
||||
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;
|
||||
}
|
||||
|
||||
if (add_to_connection_trie && !_connectionFields.isFull() && _header!=null && _valueString!=null)
|
||||
{
|
||||
if (_field==null)
|
||||
_field=new HttpField(_header,_strict?_headerString:_header.asString(),_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;
|
||||
|
||||
// End of headers!
|
||||
|
||||
// Was there a required host header?
|
||||
if (!_host && _version!=HttpVersion.HTTP_1_0 && _requestHandler!=null)
|
||||
{
|
||||
throw new BadMessage(HttpStatus.BAD_REQUEST_400,"No Host");
|
||||
}
|
||||
|
||||
// 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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
else if (ch<=HttpTokens.SPACE)
|
||||
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)
|
||||
{
|
||||
final String n;
|
||||
final String v;
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
final String n;
|
||||
final String v;
|
||||
|
||||
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
|
||||
{
|
||||
n=field.getName();
|
||||
v=field.getValue();
|
||||
v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1);
|
||||
field=new HttpField(field.getHeader(),n,v);
|
||||
}
|
||||
|
||||
_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
|
||||
{
|
||||
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);
|
||||
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
|
||||
{
|
||||
// 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()
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -247,9 +247,8 @@ public class HttpGeneratorServerHTTPTest
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean parsedHeader(HttpField field)
|
||||
public void parsedHeader(HttpField field)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 maxHeaderTableSize)
|
||||
public HpackEncoder(int localMaxHeaderTableSize)
|
||||
{
|
||||
_context=new HpackContext(maxHeaderTableSize);
|
||||
this(localMaxHeaderTableSize,4096);
|
||||
}
|
||||
|
||||
public HpackEncoder(int localMaxHeaderTableSize,int remoteMaxHeaderTableSize)
|
||||
{
|
||||
_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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -128,41 +128,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
|
||||
{
|
||||
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue