jetty http2 client parse error #726

This commit is contained in:
Greg Wilkins 2016-07-15 13:46:08 +10:00
parent 37d0caedee
commit 45ead1bb29
7 changed files with 92 additions and 57 deletions

View File

@ -206,6 +206,8 @@ public class HttpField
return false; return false;
if (_value==null) if (_value==null)
return false; return false;
if (search.equals(_value))
return true;
search = StringUtil.asciiToLowerCase(search); search = StringUtil.asciiToLowerCase(search);
@ -410,9 +412,10 @@ public class HttpField
@Override @Override
public int hashCode() public int hashCode()
{ {
int vhc = _value==null?0:_value.hashCode();
if (_header==null) if (_header==null)
return _value.hashCode() ^ nameHashCode(); return vhc ^ nameHashCode();
return _value.hashCode() ^ _header.hashCode(); return vhc ^ _header.hashCode();
} }
@Override @Override

View File

@ -161,7 +161,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i=_size;i-->0;) for (int i=_size;i-->0;)
{ {
HttpField f=_fields[i]; HttpField f=_fields[i];
if (f.isSameName(field) && f.contains(field.getValue())) if (f.isSameName(field) && (f.equals(field)||f.contains(field.getValue())))
return true; return true;
} }
return false; return false;

View File

@ -47,11 +47,11 @@ import org.eclipse.jetty.util.log.Logger;
public class HpackContext public class HpackContext
{ {
public static final Logger LOG = Log.getLogger(HpackContext.class); public static final Logger LOG = Log.getLogger(HpackContext.class);
private static final String EMPTY = "";
public static final String[][] STATIC_TABLE = public static final String[][] STATIC_TABLE =
{ {
{null,null}, {null,null},
/* 1 */ {":authority",null}, /* 1 */ {":authority",EMPTY},
/* 2 */ {":method","GET"}, /* 2 */ {":method","GET"},
/* 3 */ {":method","POST"}, /* 3 */ {":method","POST"},
/* 4 */ {":path","/"}, /* 4 */ {":path","/"},
@ -65,53 +65,53 @@ public class HpackContext
/* 12 */ {":status","400"}, /* 12 */ {":status","400"},
/* 13 */ {":status","404"}, /* 13 */ {":status","404"},
/* 14 */ {":status","500"}, /* 14 */ {":status","500"},
/* 15 */ {"accept-charset",null}, /* 15 */ {"accept-charset",EMPTY},
/* 16 */ {"accept-encoding","gzip, deflate"}, /* 16 */ {"accept-encoding","gzip, deflate"},
/* 17 */ {"accept-language",null}, /* 17 */ {"accept-language",EMPTY},
/* 18 */ {"accept-ranges",null}, /* 18 */ {"accept-ranges",EMPTY},
/* 19 */ {"accept",null}, /* 19 */ {"accept",EMPTY},
/* 20 */ {"access-control-allow-origin",null}, /* 20 */ {"access-control-allow-origin",EMPTY},
/* 21 */ {"age",null}, /* 21 */ {"age",EMPTY},
/* 22 */ {"allow",null}, /* 22 */ {"allow",EMPTY},
/* 23 */ {"authorization",null}, /* 23 */ {"authorization",EMPTY},
/* 24 */ {"cache-control",null}, /* 24 */ {"cache-control",EMPTY},
/* 25 */ {"content-disposition",null}, /* 25 */ {"content-disposition",EMPTY},
/* 26 */ {"content-encoding",null}, /* 26 */ {"content-encoding",EMPTY},
/* 27 */ {"content-language",null}, /* 27 */ {"content-language",EMPTY},
/* 28 */ {"content-length",null}, /* 28 */ {"content-length",EMPTY},
/* 29 */ {"content-location",null}, /* 29 */ {"content-location",EMPTY},
/* 30 */ {"content-range",null}, /* 30 */ {"content-range",EMPTY},
/* 31 */ {"content-type",null}, /* 31 */ {"content-type",EMPTY},
/* 32 */ {"cookie",null}, /* 32 */ {"cookie",EMPTY},
/* 33 */ {"date",null}, /* 33 */ {"date",EMPTY},
/* 34 */ {"etag",null}, /* 34 */ {"etag",EMPTY},
/* 35 */ {"expect",null}, /* 35 */ {"expect",EMPTY},
/* 36 */ {"expires",null}, /* 36 */ {"expires",EMPTY},
/* 37 */ {"from",null}, /* 37 */ {"from",EMPTY},
/* 38 */ {"host",null}, /* 38 */ {"host",EMPTY},
/* 39 */ {"if-match",null}, /* 39 */ {"if-match",EMPTY},
/* 40 */ {"if-modified-since",null}, /* 40 */ {"if-modified-since",EMPTY},
/* 41 */ {"if-none-match",null}, /* 41 */ {"if-none-match",EMPTY},
/* 42 */ {"if-range",null}, /* 42 */ {"if-range",EMPTY},
/* 43 */ {"if-unmodified-since",null}, /* 43 */ {"if-unmodified-since",EMPTY},
/* 44 */ {"last-modified",null}, /* 44 */ {"last-modified",EMPTY},
/* 45 */ {"link",null}, /* 45 */ {"link",EMPTY},
/* 46 */ {"location",null}, /* 46 */ {"location",EMPTY},
/* 47 */ {"max-forwards",null}, /* 47 */ {"max-forwards",EMPTY},
/* 48 */ {"proxy-authenticate",null}, /* 48 */ {"proxy-authenticate",EMPTY},
/* 49 */ {"proxy-authorization",null}, /* 49 */ {"proxy-authorization",EMPTY},
/* 50 */ {"range",null}, /* 50 */ {"range",EMPTY},
/* 51 */ {"referer",null}, /* 51 */ {"referer",EMPTY},
/* 52 */ {"refresh",null}, /* 52 */ {"refresh",EMPTY},
/* 53 */ {"retry-after",null}, /* 53 */ {"retry-after",EMPTY},
/* 54 */ {"server",null}, /* 54 */ {"server",EMPTY},
/* 55 */ {"set-cookie",null}, /* 55 */ {"set-cookie",EMPTY},
/* 56 */ {"strict-transport-security",null}, /* 56 */ {"strict-transport-security",EMPTY},
/* 57 */ {"transfer-encoding",null}, /* 57 */ {"transfer-encoding",EMPTY},
/* 58 */ {"user-agent",null}, /* 58 */ {"user-agent",EMPTY},
/* 59 */ {"vary",null}, /* 59 */ {"vary",EMPTY},
/* 60 */ {"via",null}, /* 60 */ {"via",EMPTY},
/* 61 */ {"www-authenticate",null}, /* 61 */ {"www-authenticate",EMPTY},
}; };
private static final Map<HttpField,Entry> __staticFieldMap = new HashMap<>(); private static final Map<HttpField,Entry> __staticFieldMap = new HashMap<>();

View File

@ -78,10 +78,9 @@ public class HpackDecoder
while(buffer.hasRemaining()) while(buffer.hasRemaining())
{ {
if (LOG.isDebugEnabled()) if (LOG.isDebugEnabled() && buffer.hasArray())
{ {
int l=Math.min(buffer.remaining(),16); int l=Math.min(buffer.remaining(),32);
// TODO: not guaranteed the buffer has a backing array !
LOG.debug("decode {}{}", LOG.debug("decode {}{}",
TypeUtil.toHexString(buffer.array(),buffer.arrayOffset()+buffer.position(),l), TypeUtil.toHexString(buffer.array(),buffer.arrayOffset()+buffer.position(),l),
l<buffer.remaining()?"...":""); l<buffer.remaining()?"...":"");

View File

@ -175,6 +175,9 @@ public class HpackEncoder
public void encode(ByteBuffer buffer, HttpField field) public void encode(ByteBuffer buffer, HttpField field)
{ {
if (field.getValue()==null)
field = new HttpField(field.getHeader(),field.getName(),"");
final int p=_debug?buffer.position():-1; final int p=_debug?buffer.position():-1;
String encoding=null; String encoding=null;

View File

@ -23,14 +23,22 @@ import java.nio.ByteBuffer;
import java.util.Iterator; import java.util.Iterator;
import org.eclipse.jetty.http.HttpField; import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme; import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData; import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.TypeUtil; import org.eclipse.jetty.util.TypeUtil;
import org.eclipse.jetty.util.log.Log;
import org.hamcrest.Matchers;
import org.junit.Assert;
import org.junit.Test; import org.junit.Test;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse; import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue; import static org.junit.Assert.assertTrue;
public class HpackDecoderTest public class HpackDecoderTest
@ -159,4 +167,24 @@ public class HpackDecoderTest
assertEquals("www.example.com",request.getURI().getHost()); assertEquals("www.example.com",request.getURI().getHost());
assertFalse(request.iterator().hasNext()); assertFalse(request.iterator().hasNext());
} }
@Test
public void testNghttpx()
{
// Response encoded by nghttpx
String encoded="886196C361Be940b6a65B6850400B8A00571972e080a62D1Bf5f87497cA589D34d1f9a0f0d0234327690Aa69D29aFcA954D3A5358980Ae112e0f7c880aE152A9A74a6bF3";
ByteBuffer buffer = ByteBuffer.wrap(TypeUtil.fromHexString(encoded));
HpackDecoder decoder = new HpackDecoder(4096,8192);
MetaData.Response response = (MetaData.Response)decoder.decode(buffer);
assertThat(response.getStatus(),is(200));
assertThat(response.getFields().size(),is(6));
assertTrue(response.getFields().contains(new HttpField(HttpHeader.DATE,"Fri, 15 Jul 2016 02:36:20 GMT")));
assertTrue(response.getFields().contains(new HttpField(HttpHeader.CONTENT_TYPE,"text/html")));
assertTrue(response.getFields().contains(new HttpField(HttpHeader.CONTENT_ENCODING,"")));
assertTrue(response.getFields().contains(new HttpField(HttpHeader.CONTENT_LENGTH,"42")));
assertTrue(response.getFields().contains(new HttpField(HttpHeader.SERVER,"nghttpx nghttp2/1.12.0")));
assertTrue(response.getFields().contains(new HttpField(HttpHeader.VIA,"1.1 nghttpx")));
}
} }

View File

@ -54,6 +54,7 @@ public class HpackTest
HttpFields fields0 = new HttpFields(); HttpFields fields0 = new HttpFields();
fields0.add(HttpHeader.CONTENT_TYPE,"text/html"); fields0.add(HttpHeader.CONTENT_TYPE,"text/html");
fields0.add(HttpHeader.CONTENT_LENGTH,"1024"); fields0.add(HttpHeader.CONTENT_LENGTH,"1024");
fields0.add(new HttpField(HttpHeader.CONTENT_ENCODING,(String)null));
fields0.add(ServerJetty); fields0.add(ServerJetty);
fields0.add(XPowerJetty); fields0.add(XPowerJetty);
fields0.add(Date); fields0.add(Date);
@ -65,7 +66,7 @@ public class HpackTest
encoder.encode(buffer,original0); encoder.encode(buffer,original0);
BufferUtil.flipToFlush(buffer,0); BufferUtil.flipToFlush(buffer,0);
Response decoded0 = (Response)decoder.decode(buffer); Response decoded0 = (Response)decoder.decode(buffer);
original0.getFields().put(new HttpField(HttpHeader.CONTENT_ENCODING,""));
assertMetadataSame(original0,decoded0); assertMetadataSame(original0,decoded0);
// Same again? // Same again?
@ -79,9 +80,10 @@ public class HpackTest
HttpFields fields1 = new HttpFields(); HttpFields fields1 = new HttpFields();
fields1.add(HttpHeader.CONTENT_TYPE,"text/plain"); fields1.add(HttpHeader.CONTENT_TYPE,"text/plain");
fields1.add(HttpHeader.CONTENT_LENGTH,"1234"); fields1.add(HttpHeader.CONTENT_LENGTH,"1234");
fields1.add(HttpHeader.CONTENT_ENCODING," ");
fields1.add(ServerJetty); fields1.add(ServerJetty);
fields0.add(XPowerJetty); fields1.add(XPowerJetty);
fields0.add(Date); fields1.add(Date);
fields1.add("Custom-Key","Other-Value"); fields1.add("Custom-Key","Other-Value");
Response original1 = new MetaData.Response(HttpVersion.HTTP_2,200,fields1); Response original1 = new MetaData.Response(HttpVersion.HTTP_2,200,fields1);