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;
if (_value==null)
return false;
if (search.equals(_value))
return true;
search = StringUtil.asciiToLowerCase(search);
@ -410,9 +412,10 @@ public class HttpField
@Override
public int hashCode()
{
int vhc = _value==null?0:_value.hashCode();
if (_header==null)
return _value.hashCode() ^ nameHashCode();
return _value.hashCode() ^ _header.hashCode();
return vhc ^ nameHashCode();
return vhc ^ _header.hashCode();
}
@Override

View File

@ -161,7 +161,7 @@ public class HttpFields implements Iterable<HttpField>
for (int i=_size;i-->0;)
{
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 false;

View File

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

View File

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

View File

@ -175,6 +175,9 @@ public class HpackEncoder
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;
String encoding=null;

View File

@ -23,14 +23,22 @@ import java.nio.ByteBuffer;
import java.util.Iterator;
import org.eclipse.jetty.http.HttpField;
import org.eclipse.jetty.http.HttpFields;
import org.eclipse.jetty.http.HttpHeader;
import org.eclipse.jetty.http.HttpScheme;
import org.eclipse.jetty.http.HttpVersion;
import org.eclipse.jetty.http.MetaData;
import org.eclipse.jetty.util.BufferUtil;
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 static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertThat;
import static org.junit.Assert.assertTrue;
public class HpackDecoderTest
@ -159,4 +167,24 @@ public class HpackDecoderTest
assertEquals("www.example.com",request.getURI().getHost());
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();
fields0.add(HttpHeader.CONTENT_TYPE,"text/html");
fields0.add(HttpHeader.CONTENT_LENGTH,"1024");
fields0.add(new HttpField(HttpHeader.CONTENT_ENCODING,(String)null));
fields0.add(ServerJetty);
fields0.add(XPowerJetty);
fields0.add(Date);
@ -65,7 +66,7 @@ public class HpackTest
encoder.encode(buffer,original0);
BufferUtil.flipToFlush(buffer,0);
Response decoded0 = (Response)decoder.decode(buffer);
original0.getFields().put(new HttpField(HttpHeader.CONTENT_ENCODING,""));
assertMetadataSame(original0,decoded0);
// Same again?
@ -79,9 +80,10 @@ public class HpackTest
HttpFields fields1 = new HttpFields();
fields1.add(HttpHeader.CONTENT_TYPE,"text/plain");
fields1.add(HttpHeader.CONTENT_LENGTH,"1234");
fields1.add(HttpHeader.CONTENT_ENCODING," ");
fields1.add(ServerJetty);
fields0.add(XPowerJetty);
fields0.add(Date);
fields1.add(XPowerJetty);
fields1.add(Date);
fields1.add("Custom-Key","Other-Value");
Response original1 = new MetaData.Response(HttpVersion.HTTP_2,200,fields1);