diff --git a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java index 0fc38d65e03..e9b09d4167d 100644 --- a/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java +++ b/jetty-client/src/test/java/org/eclipse/jetty/client/http/HttpReceiverOverHTTPTest.java @@ -32,10 +32,10 @@ import org.eclipse.jetty.client.HttpExchange; import org.eclipse.jetty.client.HttpRequest; import org.eclipse.jetty.client.HttpResponseException; import org.eclipse.jetty.client.Origin; -import org.eclipse.jetty.client.api.Connection; import org.eclipse.jetty.client.api.Response; import org.eclipse.jetty.client.util.FutureResponseListener; import org.eclipse.jetty.http.HttpCompliance; +import org.eclipse.jetty.http.HttpComplianceSection; import org.eclipse.jetty.http.HttpFields; import org.eclipse.jetty.http.HttpHeader; import org.eclipse.jetty.http.HttpVersion; @@ -55,6 +55,11 @@ import org.junit.runners.Parameterized; @RunWith(Parameterized.class) public class HttpReceiverOverHTTPTest { + static + { + HttpCompliance.CUSTOM0.sections().remove(HttpComplianceSection.RFC7230_3_2_4_NO_WS_AFTER_FIELD_NAME); + } + @Rule public final TestTracker tracker = new TestTracker(); @@ -71,7 +76,7 @@ public class HttpReceiverOverHTTPTest { return Arrays.asList( new Object[] { HttpCompliance.LEGACY }, - new Object[] { HttpCompliance.WEAK }, + new Object[] { HttpCompliance.CUSTOM0 }, new Object[] { HttpCompliance.RFC2616 }, new Object[] { HttpCompliance.RFC7230 } ); @@ -245,7 +250,7 @@ public class HttpReceiverOverHTTPTest try { Response response = listener.get(5, TimeUnit.SECONDS); - Assert.assertThat(compliance, Matchers.lessThanOrEqualTo(HttpCompliance.WEAK)); + Assert.assertThat(compliance, Matchers.lessThanOrEqualTo(HttpCompliance.CUSTOM0)); Assert.assertNotNull(response); Assert.assertEquals(200, response.getStatus()); Assert.assertEquals("OK", response.getReason()); @@ -255,7 +260,7 @@ public class HttpReceiverOverHTTPTest Assert.assertEquals(1, headers.size()); Assert.assertEquals("0", headers.get(HttpHeader.CONTENT_LENGTH)); } catch (Exception e) { - Assert.assertThat(compliance, Matchers.greaterThan(HttpCompliance.WEAK)); + Assert.assertThat(compliance, Matchers.greaterThan(HttpCompliance.CUSTOM0)); Throwable cause = e.getCause(); Assert.assertThat(cause, CoreMatchers.instanceOf(HttpResponseException.class)); Assert.assertThat(cause.getMessage(), Matchers.containsString("HTTP protocol violation")); diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java index 0a499fd3c32..3ab9f05d5f0 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpCompliance.java @@ -19,6 +19,11 @@ package org.eclipse.jetty.http; import java.util.EnumSet; +import java.util.HashMap; +import java.util.Map; + +import org.eclipse.jetty.util.log.Log; +import org.eclipse.jetty.util.log.Logger; /** * HTTP compliance modes: @@ -31,29 +36,122 @@ import java.util.EnumSet; */ public enum HttpCompliance { - LEGACY(EnumSet.noneOf(HttpRFC.class)), - RFC2616(EnumSet.complementOf(EnumSet.of( - HttpRFC.RFC7230_3_2_4_WS_AFTER_FIELD_NAME, - HttpRFC.RFC7230_3_2_4_NO_FOLDING, - HttpRFC.RFC7230_A2_NO_HTTP_9))), - RFC7230(EnumSet.allOf(HttpRFC.class)), - ; + // TODO in Jetty-10 convert this enum to a class so that extra custom modes can be defined dynamically + LEGACY(sectionsBySpec("LEGACY")), + RFC2616(sectionsBySpec("RFC2616")), + RFC7230(sectionsBySpec("RFC7230,-RFC7230_3_1_1_METHOD_CASE_SENSITIVE")), // TODO fix in Jetty-10 + CUSTOM0(sectionsByProperty("CUSTOM0")), + CUSTOM1(sectionsByProperty("CUSTOM1")), + CUSTOM2(sectionsByProperty("CUSTOM2")), + CUSTOM3(sectionsByProperty("CUSTOM3")); + + private static final Logger LOG = Log.getLogger(HttpParser.class); + private static EnumSet sectionsByProperty(String property) + { + String s = System.getProperty(HttpCompliance.class.getName()+property); + return sectionsBySpec(s==null?"*":s); + } + + static EnumSet sectionsBySpec(String spec) + { + EnumSet sections; + String[] elements = spec.split("\\s*,\\s*"); + int i=0; + + switch(elements[i]) + { + case "RFC2616": + sections = EnumSet.complementOf(EnumSet.of( + HttpComplianceSection.RFC7230_3_2_4_NO_WS_AFTER_FIELD_NAME, + HttpComplianceSection.RFC7230_3_2_4_NO_FOLDING, + HttpComplianceSection.RFC7230_A2_NO_HTTP_9)); + i++; + break; + + case "RFC7230": + case "*": + i++; + sections = EnumSet.allOf(HttpComplianceSection.class); + break; + + case "LEGACY": + sections = EnumSet.of(HttpComplianceSection.RFC7230_3_1_1_METHOD_CASE_SENSITIVE); + i++; + break; + + case "0": + sections = EnumSet.noneOf(HttpComplianceSection.class); + i++; + break; + default: + sections = EnumSet.noneOf(HttpComplianceSection.class); + break; + } + + while(i _sections; + private final static Map __required = new HashMap<>(); + static + { + for (HttpComplianceSection section : HttpComplianceSection.values()) + { + for (HttpCompliance compliance : HttpCompliance.values()) + { + if (compliance.sections().contains(section)) + { + __required.put(section,compliance); + break; + } + } + } + } - HttpCompliance(EnumSet sections) + /** + * @param section The section to query + * @return The minimum compliance required to enable the section. + */ + public static HttpCompliance requiredCompliance(HttpComplianceSection section) + { + return __required.get(section); + } + + + + private final EnumSet _sections; + + private HttpCompliance(EnumSet sections) { _sections = sections; } - public EnumSet sections() + public EnumSet sections() { return _sections; } - public EnumSet excluding(EnumSet exclusions) + public EnumSet excluding(EnumSet exclusions) { - EnumSet sections = EnumSet.copyOf(_sections); + EnumSet sections = EnumSet.copyOf(_sections); sections.removeAll(exclusions); return sections; } diff --git a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpRFC.java b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpComplianceSection.java similarity index 81% rename from jetty-http/src/main/java/org/eclipse/jetty/http/HttpRFC.java rename to jetty-http/src/main/java/org/eclipse/jetty/http/HttpComplianceSection.java index 9b14c9b41e0..fae7c613f2b 100644 --- a/jetty-http/src/main/java/org/eclipse/jetty/http/HttpRFC.java +++ b/jetty-http/src/main/java/org/eclipse/jetty/http/HttpComplianceSection.java @@ -20,18 +20,18 @@ package org.eclipse.jetty.http; /** */ -public enum HttpRFC +public enum HttpComplianceSection { - CASE_SENSITIVE_FIELD_VALUES("","case-sensitive field values"), + USE_CASE_INSENSITIVE_FIELD_VALUE_CACHE("","Use case insensitive field value cache"), RFC7230_3_1_1_METHOD_CASE_SENSITIVE("https://tools.ietf.org/html/rfc7230#section-3.1.1","Method is case-sensitive"), RFC7230_3_2_FIELD_COLON("https://tools.ietf.org/html/rfc7230#section-3.2","Fields must have a Colon"), RFC7230_3_2_CASE_INSENSITIVE_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2","Field name is case-insensitive"), - RFC7230_3_2_4_WS_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2.4","Whitespace not allowed after field name"), + RFC7230_3_2_4_NO_WS_AFTER_FIELD_NAME("https://tools.ietf.org/html/rfc7230#section-3.2.4","Whitespace not allowed after field name"), RFC7230_3_2_4_NO_FOLDING("https://tools.ietf.org/html/rfc7230#section-3.2.4","No line Folding"), RFC7230_A2_NO_HTTP_9("https://tools.ietf.org/html/rfc7230#appendix-A.2","No HTTP/0.9"), ; - HttpRFC(String url,String description) + HttpComplianceSection(String url,String description) { } 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 d59a869fee3..f1578f4f396 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 @@ -35,7 +35,6 @@ import org.eclipse.jetty.util.Utf8StringBuilder; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; -import static org.eclipse.jetty.http.HttpCompliance.LEGACY; import static org.eclipse.jetty.http.HttpCompliance.RFC2616; import static org.eclipse.jetty.http.HttpCompliance.RFC7230; import static org.eclipse.jetty.http.HttpTokens.CARRIAGE_RETURN; @@ -117,7 +116,7 @@ public class HttpParser IN_NAME, VALUE, IN_VALUE, - OWS_AFTER_NAME, + WS_AFTER_NAME, } // States @@ -155,7 +154,8 @@ public class HttpParser private final ResponseHandler _responseHandler; private final ComplianceHandler _complianceHandler; private final int _maxHeaderBytes; - private final EnumSet _compliances; + private final HttpCompliance _compliance; + private final EnumSet _compliances; private HttpField _field; private HttpHeader _header; private String _headerString; @@ -294,23 +294,24 @@ public class HttpParser /* ------------------------------------------------------------------------------- */ public HttpParser(RequestHandler handler,int maxHeaderBytes,HttpCompliance compliance) { - this(handler,null,maxHeaderBytes,(compliance==null?compliance():compliance).sections()); + this(handler,null,maxHeaderBytes,compliance==null?compliance():compliance); } /* ------------------------------------------------------------------------------- */ public HttpParser(ResponseHandler handler,int maxHeaderBytes,HttpCompliance compliance) { - this(null,handler,maxHeaderBytes,(compliance==null?compliance():compliance).sections()); + this(null,handler,maxHeaderBytes,compliance==null?compliance():compliance); } /* ------------------------------------------------------------------------------- */ - private HttpParser(RequestHandler requestHandler,ResponseHandler responseHandler,int maxHeaderBytes,EnumSet compliances) + private HttpParser(RequestHandler requestHandler,ResponseHandler responseHandler,int maxHeaderBytes,HttpCompliance compliance) { _handler=requestHandler!=null?requestHandler:responseHandler; _requestHandler=requestHandler; _responseHandler=responseHandler; _maxHeaderBytes=maxHeaderBytes; - _compliances=compliances; + _compliance=compliance; + _compliances=compliance.sections(); _complianceHandler=(ComplianceHandler)(_handler instanceof ComplianceHandler?_handler:null); } @@ -322,36 +323,35 @@ public class HttpParser /* ------------------------------------------------------------------------------- */ /** Check RFC compliance violation - * @param compliance The compliance level violated + * @param violation The compliance section violation * @param reason The reason for the violation * @return True if the current compliance level is set so as to Not allow this violation */ - protected boolean complianceViolation(HttpCompliance compliance,String reason) + protected boolean complianceViolation(HttpComplianceSection violation, String reason) { - if (_complianceHandler==null) - return _compliance.ordinal()>=compliance.ordinal(); - if (_compliance.ordinal()=0) { // HTTP/0.9 - if (complianceViolation(RFC7230,"https://tools.ietf.org/html/rfc7230#appendix-A.2 HTTP/0.9")) + if (complianceViolation(HttpComplianceSection.RFC7230_A2_NO_HTTP_9,"No request version")) throw new BadMessageException("HTTP/0.9 not supported"); handle=_requestHandler.startRequest(_methodString,_uri.toString(), HttpVersion.HTTP_0_9); setState(State.END); @@ -851,7 +851,7 @@ public class HttpParser else { // HTTP/0.9 - if (complianceViolation(RFC7230,"https://tools.ietf.org/html/rfc7230#appendix-A.2 HTTP/0.9")) + if (complianceViolation(HttpComplianceSection.RFC7230_A2_NO_HTTP_9,"No request version")) throw new BadMessageException("HTTP/0.9 not supported"); handle=_requestHandler.startRequest(_methodString,_uri.toString(), HttpVersion.HTTP_0_9); @@ -969,7 +969,9 @@ public class HttpParser _host=true; if (!(_field instanceof HostPortHttpField) && _valueString!=null && !_valueString.isEmpty()) { - _field=new HostPortHttpField(_header,caseInsensitiveHeader(_headerString,_header.asString()),_valueString); + _field=new HostPortHttpField(_header, + _compliances.contains(HttpComplianceSection.RFC7230_3_2_CASE_INSENSITIVE_FIELD_NAME)?_header.asString():_headerString, + _valueString); add_to_connection_trie=_fieldCache!=null; } break; @@ -1066,7 +1068,7 @@ public class HttpParser case HttpTokens.SPACE: case HttpTokens.TAB: { - if (complianceViolation(RFC7230,"https://tools.ietf.org/html/rfc7230#section-3.2.4 folding")) + if (complianceViolation(HttpComplianceSection.RFC7230_3_2_4_NO_FOLDING,"Folded "+_headerString)) throw new BadMessageException(HttpStatus.BAD_REQUEST_400,"Header Folding"); // header value without name - continuation? @@ -1176,37 +1178,39 @@ public class HttpParser if (buffer.hasRemaining()) { // Try a look ahead for the known header name and value. - HttpField field=_fieldCache==null?null:_fieldCache.getBest(buffer,-1,buffer.remaining()); - if (field==null) - field=CACHE.getBest(buffer,-1,buffer.remaining()); + HttpField cached_field=_fieldCache==null?null:_fieldCache.getBest(buffer,-1,buffer.remaining()); + if (cached_field==null) + cached_field=CACHE.getBest(buffer,-1,buffer.remaining()); - if (field!=null) + if (cached_field!=null) { - final String n; - final String v; + String n = cached_field.getName(); + String v = cached_field.getValue(); - if (!_compliances.contains(HttpRFC.RFC7230_3_2_CASE_INSENSITIVE_FIELD_NAME)) + if (!_compliances.contains(HttpComplianceSection.RFC7230_3_2_CASE_INSENSITIVE_FIELD_NAME)) { // Have to get the fields exactly from the buffer to match case - String fn=field.getName(); - n=caseInsensitiveHeader(BufferUtil.toString(buffer,buffer.position()-1,fn.length(),StandardCharsets.US_ASCII),fn); - String fv=field.getValue(); - if (fv==null) - v=null; - else + String en = BufferUtil.toString(buffer,buffer.position()-1,n.length(),StandardCharsets.US_ASCII); + if (!n.equals(en)) { - XXX; - v=BufferUtil.toString(buffer,buffer.position()+fn.length()+1,fv.length(),StandardCharsets.ISO_8859_1); - field=new HttpField(field.getHeader(),n,v); + handleViolation(HttpComplianceSection.RFC7230_3_2_CASE_INSENSITIVE_FIELD_NAME,en); + n = en; + cached_field = new HttpField(cached_field.getHeader(),n,v); } } - else + + if (v!=null && !_compliances.contains(HttpComplianceSection.USE_CASE_INSENSITIVE_FIELD_VALUE_CACHE)) { - n=field.getName(); - v=field.getValue(); + String ev = BufferUtil.toString(buffer,buffer.position()+n.length()+1,v.length(),StandardCharsets.ISO_8859_1); + if (!v.equals(ev)) + { + handleViolation(HttpComplianceSection.USE_CASE_INSENSITIVE_FIELD_VALUE_CACHE,ev+"!="+v); + v = ev; + cached_field = new HttpField(cached_field.getHeader(),n,v); + } } - - _header=field.getHeader(); + + _header=cached_field.getHeader(); _headerString=n; if (v==null) @@ -1226,7 +1230,7 @@ public class HttpParser if (peek==HttpTokens.CARRIAGE_RETURN || peek==HttpTokens.LINE_FEED) { - _field=field; + _field=cached_field; _valueString=v; setState(FieldState.IN_VALUE); @@ -1255,7 +1259,6 @@ public class HttpParser _string.setLength(0); _string.append((char)b); _length=1; - } } break; @@ -1276,7 +1279,7 @@ public class HttpParser } // Fallthrough - case OWS_AFTER_NAME: + case WS_AFTER_NAME: if (b==HttpTokens.COLON) { if (_headerString==null) @@ -1290,7 +1293,7 @@ public class HttpParser break; } - if (b==HttpTokens.LINE_FEED && !complianceViolation(RFC2616,"https://tools.ietf.org/html/rfc2616#section-4.2 No colon")) + if (b==HttpTokens.LINE_FEED && !complianceViolation(HttpComplianceSection.RFC7230_3_2_FIELD_COLON,null)) { if (_headerString==null) { @@ -1306,9 +1309,9 @@ public class HttpParser } //Ignore trailing whitespaces - if (b==HttpTokens.SPACE && !complianceViolation(RFC2616,"https://tools.ietf.org/html/rfc2616#section-4.2 Invalid token in header name")) + if (b==HttpTokens.SPACE && !complianceViolation(HttpComplianceSection.RFC7230_3_2_4_NO_WS_AFTER_FIELD_NAME,null)) { - setState(FieldState.OWS_AFTER_NAME); + setState(FieldState.WS_AFTER_NAME); break; } @@ -1843,11 +1846,11 @@ public class HttpParser public interface ComplianceHandler extends HttpHandler { @Deprecated - public void onComplianceViolation(HttpCompliance compliance,HttpCompliance required,String reason); + public default void onComplianceViolation(HttpCompliance compliance, HttpCompliance required, String reason) {} - public default void onComplianceViolation(HttpRFC violation, String details) + public default void onComplianceViolation(HttpCompliance compliance, HttpComplianceSection violation, String details) { - + onComplianceViolation(compliance,HttpCompliance.requiredCompliance(violation), details); } } 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 2fa6e2c06bf..3386a4c8cfb 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 @@ -18,6 +18,8 @@ package org.eclipse.jetty.http; +import static org.hamcrest.Matchers.contains; + import java.nio.ByteBuffer; import java.nio.charset.StandardCharsets; import java.util.ArrayList; @@ -34,6 +36,11 @@ import org.junit.Test; public class HttpParserTest { + static + { + HttpCompliance.CUSTOM0.sections().remove(HttpComplianceSection.RFC7230_3_2_4_NO_WS_AFTER_FIELD_NAME); + } + /** * Parse until {@link State#END} state. * If the parser is already in the END state, then it is {@link HttpParser#reset()} and re-parsed. @@ -121,7 +128,7 @@ public class HttpParserTest Assert.assertEquals("/999", _uriOrStatus); Assert.assertEquals("HTTP/0.9", _versionOrReason); Assert.assertEquals(-1, _headers); - Assert.assertThat(_complianceViolation, Matchers.containsString("0.9")); + Assert.assertThat(_complianceViolation, contains(HttpComplianceSection.RFC7230_A2_NO_HTTP_9)); } @Test @@ -133,7 +140,7 @@ public class HttpParserTest HttpParser parser = new HttpParser(handler); parseAll(parser, buffer); Assert.assertEquals("HTTP/0.9 not supported", _bad); - Assert.assertNull(_complianceViolation); + Assert.assertThat(_complianceViolation,Matchers.empty()); } @Test @@ -150,7 +157,7 @@ public class HttpParserTest Assert.assertEquals("/222", _uriOrStatus); Assert.assertEquals("HTTP/0.9", _versionOrReason); Assert.assertEquals(-1, _headers); - Assert.assertThat(_complianceViolation, Matchers.containsString("0.9")); + Assert.assertThat(_complianceViolation, contains(HttpComplianceSection.RFC7230_A2_NO_HTTP_9)); } @Test @@ -163,7 +170,7 @@ public class HttpParserTest HttpParser parser = new HttpParser(handler); parseAll(parser, buffer); Assert.assertEquals("HTTP/0.9 not supported", _bad); - Assert.assertNull(_complianceViolation); + Assert.assertThat(_complianceViolation,Matchers.empty()); } @Test @@ -266,7 +273,7 @@ public class HttpParserTest Assert.assertEquals("Name", _hdr[1]); Assert.assertEquals("value extra", _val[1]); Assert.assertEquals(1, _headers); - Assert.assertThat(_complianceViolation, Matchers.containsString("folding")); + Assert.assertThat(_complianceViolation, contains(HttpComplianceSection.RFC7230_3_2_4_NO_FOLDING)); } @Test @@ -285,7 +292,7 @@ public class HttpParserTest Assert.assertThat(_bad, Matchers.notNullValue()); Assert.assertThat(_bad, Matchers.containsString("Header Folding")); - Assert.assertNull(_complianceViolation); + Assert.assertThat(_complianceViolation,Matchers.empty()); } @Test @@ -351,74 +358,52 @@ public class HttpParserTest } @Test - public void testNoColonWeak() throws Exception + public void testSpaceinNameCustom0() throws Exception { ByteBuffer buffer = BufferUtil.toBuffer( "GET / HTTP/1.0\r\n" + "Host: localhost\r\n" + - "Name\r\n" + + "Name with space: value\r\n" + "Other: value\r\n" + "\r\n"); HttpParser.RequestHandler handler = new Handler(); - HttpParser parser = new HttpParser(handler,HttpCompliance.WEAK); + HttpParser parser = new HttpParser(handler,HttpCompliance.CUSTOM0); parseAll(parser, buffer); - - Assert.assertTrue(_headerCompleted); - Assert.assertTrue(_messageCompleted); - Assert.assertEquals("GET", _methodOrVersion); - Assert.assertEquals("/", _uriOrStatus); - Assert.assertEquals("HTTP/1.0", _versionOrReason); - Assert.assertEquals("Host", _hdr[0]); - Assert.assertEquals("localhost", _val[0]); - Assert.assertEquals("Name", _hdr[1]); - Assert.assertEquals("", _val[1]); - Assert.assertEquals("Other", _hdr[2]); - Assert.assertEquals("value", _val[2]); - Assert.assertEquals(2, _headers); - Assert.assertThat(_complianceViolation, Matchers.containsString("No colon")); + + Assert.assertThat(_bad, Matchers.containsString("Illegal character")); + Assert.assertThat(_complianceViolation,contains(HttpComplianceSection.RFC7230_3_2_4_NO_WS_AFTER_FIELD_NAME)); } @Test - public void testNoColonWithWhitespaceWeak() throws Exception + public void testNoColonCustom0() throws Exception { ByteBuffer buffer = BufferUtil.toBuffer( "GET / HTTP/1.0\r\n" + "Host: localhost\r\n" + - "Name \r\n" + + "Name \r\n" + "Other: value\r\n" + "\r\n"); HttpParser.RequestHandler handler = new Handler(); - HttpParser parser = new HttpParser(handler,HttpCompliance.WEAK); + HttpParser parser = new HttpParser(handler,HttpCompliance.CUSTOM0); parseAll(parser, buffer); - - Assert.assertTrue(_headerCompleted); - Assert.assertTrue(_messageCompleted); - Assert.assertEquals("GET", _methodOrVersion); - Assert.assertEquals("/", _uriOrStatus); - Assert.assertEquals("HTTP/1.0", _versionOrReason); - Assert.assertEquals("Host", _hdr[0]); - Assert.assertEquals("localhost", _val[0]); - Assert.assertEquals("Name", _hdr[1]); - Assert.assertEquals("", _val[1]); - Assert.assertEquals("Other", _hdr[2]); - Assert.assertEquals("value", _val[2]); - Assert.assertEquals(2, _headers); - Assert.assertThat(_complianceViolation, Matchers.containsString("No colon")); + + Assert.assertThat(_bad, Matchers.containsString("Illegal character")); + Assert.assertThat(_complianceViolation,contains(HttpComplianceSection.RFC7230_3_2_4_NO_WS_AFTER_FIELD_NAME)); } @Test - public void testTrailingSpacesInHeaderNameInWeakMode() throws Exception + public void testTrailingSpacesInHeaderNameInCustom0Mode() throws Exception { ByteBuffer buffer = BufferUtil.toBuffer( "HTTP/1.1 204 No Content\r\n" + - "Access-Control-Allow-Headers : Origin\r\n" + - "Other: value\r\n" + - "\r\n"); + "Access-Control-Allow-Headers : Origin\r\n" + + "Other: value\r\n" + + "\r\n"); HttpParser.ResponseHandler handler = new Handler(); - HttpParser parser = new HttpParser(handler, -1, HttpCompliance.WEAK); + HttpParser parser = new HttpParser(handler, -1, HttpCompliance.CUSTOM0); parseAll(parser, buffer); Assert.assertTrue(_headerCompleted); @@ -437,11 +422,11 @@ public class HttpParserTest Assert.assertEquals("Other", _hdr[1]); Assert.assertEquals("value", _val[1]); - Assert.assertThat(_complianceViolation, Matchers.containsString("Invalid token in header name")); + Assert.assertThat(_complianceViolation, contains(HttpComplianceSection.RFC7230_3_2_4_NO_WS_AFTER_FIELD_NAME)); } @Test - public void testTrailingSpacesInHeaderNameNoWeak() throws Exception + public void testTrailingSpacesInHeaderNameNoCustom0() throws Exception { ByteBuffer buffer = BufferUtil.toBuffer( "HTTP/1.1 204 No Content\r\n" + @@ -472,7 +457,7 @@ public class HttpParserTest HttpParser parser = new HttpParser(handler,HttpCompliance.RFC7230); parseAll(parser, buffer); Assert.assertThat(_bad, Matchers.containsString("Illegal character")); - Assert.assertNull(_complianceViolation); + Assert.assertThat(_complianceViolation,Matchers.empty()); } @@ -767,7 +752,7 @@ public class HttpParserTest parseAll(parser, buffer); Assert.assertNull(_bad); Assert.assertEquals("GET", _methodOrVersion); - Assert.assertThat(_complianceViolation, Matchers.containsString("case insensitive method gEt")); + Assert.assertThat(_complianceViolation, contains(HttpComplianceSection.RFC7230_3_1_1_METHOD_CASE_SENSITIVE)); } @Test @@ -783,7 +768,7 @@ public class HttpParserTest parseAll(parser, buffer); Assert.assertNull(_bad); Assert.assertEquals("gEt", _methodOrVersion); - Assert.assertNull(_complianceViolation); + Assert.assertThat(_complianceViolation,Matchers.empty()); } @Test @@ -806,7 +791,7 @@ public class HttpParserTest Assert.assertEquals("Connection", _hdr[1]); Assert.assertEquals("close", _val[1]); Assert.assertEquals(1, _headers); - Assert.assertNull(_complianceViolation); + Assert.assertThat(_complianceViolation,Matchers.empty()); } @Test @@ -829,7 +814,7 @@ public class HttpParserTest Assert.assertEquals("cOnNeCtIoN", _hdr[1]); Assert.assertEquals("ClOsE", _val[1]); Assert.assertEquals(1, _headers); - Assert.assertThat(_complianceViolation, Matchers.containsString("case sensitive")); + Assert.assertThat(_complianceViolation, contains(HttpComplianceSection.RFC7230_3_2_CASE_INSENSITIVE_FIELD_NAME,HttpComplianceSection.RFC7230_3_2_CASE_INSENSITIVE_FIELD_NAME,HttpComplianceSection.USE_CASE_INSENSITIVE_FIELD_VALUE_CACHE)); } @Test @@ -2122,7 +2107,7 @@ public class HttpParserTest _headers = 0; _headerCompleted = false; _messageCompleted = false; - _complianceViolation = null; + _complianceViolation.clear(); } private String _host; @@ -2140,8 +2125,8 @@ public class HttpParserTest private boolean _early; private boolean _headerCompleted; private boolean _messageCompleted; - private String _complianceViolation; - + private final List _complianceViolation = new ArrayList<>(); + private class Handler implements HttpParser.RequestHandler, HttpParser.ResponseHandler, HttpParser.ComplianceHandler { @Override @@ -2249,9 +2234,9 @@ public class HttpParserTest } @Override - public void onComplianceViolation(HttpCompliance compliance, HttpCompliance required, String reason) + public void onComplianceViolation(HttpCompliance compliance, HttpComplianceSection violation, String reason) { - _complianceViolation=reason; + _complianceViolation.add(violation); } } } diff --git a/jetty-http/src/test/resources/jetty-logging.properties b/jetty-http/src/test/resources/jetty-logging.properties index adf68c7c337..753d5c7b2d8 100644 --- a/jetty-http/src/test/resources/jetty-logging.properties +++ b/jetty-http/src/test/resources/jetty-logging.properties @@ -1,3 +1,3 @@ org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog #org.eclipse.jetty.LEVEL=DEBUG -#org.eclipse.jetty.server.LEVEL=DEBUG +org.eclipse.jetty.server.LEVEL=DEBUG diff --git a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java index 72b1c26175e..92fc4a96348 100644 --- a/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java +++ b/jetty-server/src/main/java/org/eclipse/jetty/server/ServerConnector.java @@ -336,13 +336,6 @@ public class ServerConnector extends AbstractNetworkConnector return serverChannel; } - - @Override - public Future shutdown() - { - // shutdown all the connections - return super.shutdown(); - } @Override public void close()