You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2019/12/23 13:57:24 UTC
[httpcomponents-core] 04/04: Support for status code 431 (Request
Header Fields Too Large)
This is an automated email from the ASF dual-hosted git repository.
olegk pushed a commit to branch development
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git
commit 04531ef0c0b3e58db25f00776ee58e1e97ce6790
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Dec 14 19:24:06 2019 +0100
Support for status code 431 (Request Header Fields Too Large)
---
.../core5/testing/classic/ClassicTestServer.java | 9 ++-
.../testing/classic/ClassicIntegrationTest.java | 77 ++++++++++++++++------
.../hc/core5/testing/nio/Http1IntegrationTest.java | 33 ++++++++++
.../http/RequestHeaderFieldsTooLargeException.java | 58 ++++++++++++++++
.../apache/hc/core5/http/impl/ServerSupport.java | 3 +
.../http/impl/io/DefaultHttpRequestParser.java | 14 ++++
.../apache/hc/core5/http/impl/io/HttpService.java | 6 +-
.../http/impl/nio/DefaultHttpRequestParser.java | 14 ++++
.../hc/core5/http/impl/io/TestRequestParser.java | 4 +-
9 files changed, 187 insertions(+), 31 deletions(-)
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
index c7d4ff4..4abe8b2 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
@@ -99,7 +99,10 @@ public class ClassicTestServer {
throw new IllegalStateException("Server not running");
}
- public void start(final HttpProcessor httpProcessor, final Decorator<HttpServerRequestHandler> handlerDecorator) throws IOException {
+ public void start(
+ final Http1Config http1Config,
+ final HttpProcessor httpProcessor,
+ final Decorator<HttpServerRequestHandler> handlerDecorator) throws IOException {
if (serverRef.get() == null) {
final HttpServerRequestHandler handler = new BasicHttpServerRequestHandler(registry);
final HttpService httpService = new HttpService(
@@ -115,7 +118,7 @@ public class ClassicTestServer {
sslContext != null ? sslContext.getServerSocketFactory() : ServerSocketFactory.getDefault(),
new LoggingBHttpServerConnectionFactory(
sslContext != null ? URIScheme.HTTPS.id : URIScheme.HTTP.id,
- Http1Config.DEFAULT,
+ http1Config != null ? http1Config : Http1Config.DEFAULT,
CharCodingConfig.DEFAULT),
null,
LoggingExceptionListener.INSTANCE);
@@ -128,7 +131,7 @@ public class ClassicTestServer {
}
public void start() throws IOException {
- start(null, null);
+ start(null, null, null);
}
public void shutdown(final CloseMode closeMode) {
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java
index 6971c4e..4ce02d6 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java
@@ -57,6 +57,7 @@ import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.HttpVersion;
import org.apache.hc.core5.http.Method;
import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.config.Http1Config;
import org.apache.hc.core5.http.io.HttpRequestHandler;
import org.apache.hc.core5.http.io.HttpServerRequestHandler;
import org.apache.hc.core5.http.io.SocketConfig;
@@ -90,8 +91,8 @@ public class ClassicIntegrationTest {
@Parameterized.Parameters(name = "{0}")
public static Collection<Object[]> protocols() {
return Arrays.asList(new Object[][]{
- { URIScheme.HTTP },
- { URIScheme.HTTPS }
+ {URIScheme.HTTP},
+ {URIScheme.HTTPS}
});
}
@@ -108,7 +109,7 @@ public class ClassicIntegrationTest {
@Override
protected void before() throws Throwable {
server = new ClassicTestServer(
- scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null,
+ scheme == URIScheme.HTTPS ? SSLTestContexts.createServerSSLContext() : null,
SocketConfig.custom()
.setSoTimeout(5, TimeUnit.SECONDS)
.build());
@@ -135,7 +136,7 @@ public class ClassicIntegrationTest {
@Override
protected void before() throws Throwable {
client = new ClassicTestClient(
- scheme == URIScheme.HTTPS ? SSLTestContexts.createClientSSLContext() : null,
+ scheme == URIScheme.HTTPS ? SSLTestContexts.createClientSSLContext() : null,
SocketConfig.custom()
.setSoTimeout(5, TimeUnit.SECONDS)
.build());
@@ -489,7 +490,7 @@ public class ClassicIntegrationTest {
});
- this.server.start(null, new Decorator<HttpServerRequestHandler>() {
+ this.server.start(null, null, new Decorator<HttpServerRequestHandler>() {
@Override
public HttpServerRequestHandler decorate(final HttpServerRequestHandler handler) {
@@ -602,22 +603,22 @@ public class ClassicIntegrationTest {
final String[] patterns = {
- "0123456789ABCDEF",
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
- "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that"
+ "0123456789ABCDEF",
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that-" +
+ "yadayada-blahblah-this-and-that-yadayada-blahblah-this-and-that"
};
// Initialize the server-side request handler
@@ -885,4 +886,38 @@ public class ClassicIntegrationTest {
}
}
+ @Test
+ public void testHeaderTooLarge() throws Exception {
+ this.server.registerHandler("*", new HttpRequestHandler() {
+
+ @Override
+ public void handle(
+ final ClassicHttpRequest request,
+ final ClassicHttpResponse response,
+ final HttpContext context) throws HttpException, IOException {
+ response.setEntity(new StringEntity("All is well", StandardCharsets.US_ASCII));
+ }
+
+ });
+
+ this.server.start(
+ Http1Config.custom()
+ .setMaxLineLength(100)
+ .build(),
+ null,
+ null);
+ this.client.start();
+
+ final HttpCoreContext context = HttpCoreContext.create();
+ final HttpHost host = new HttpHost(scheme.id, "localhost", this.server.getPort());
+
+ final BasicClassicHttpRequest get1 = new BasicClassicHttpRequest(Method.GET, "/");
+ get1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" +
+ "1234567890123456789012345678901234567890");
+ try (final ClassicHttpResponse response1 = this.client.execute(host, get1, context)) {
+ Assert.assertEquals(431, response1.getCode());
+ EntityUtils.consume(response1.getEntity());
+ }
+ }
+
}
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
index 6132f15..79c1dc0 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
@@ -1789,4 +1789,37 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
Assert.assertEquals("Boom!!!", entity1);
}
+ @Test
+ public void testHeaderTooLarge() throws Exception {
+ server.register("/hello", new Supplier<AsyncServerExchangeHandler>() {
+
+ @Override
+ public AsyncServerExchangeHandler get() {
+ return new SingleLineResponseHandler("Hi there");
+ }
+
+ });
+ final InetSocketAddress serverEndpoint = server.start(null, Http1Config.custom()
+ .setMaxLineLength(100)
+ .build());
+ client.start();
+
+ final Future<ClientSessionEndpoint> connectFuture = client.connect(
+ "localhost", serverEndpoint.getPort(), TIMEOUT);
+ final ClientSessionEndpoint streamEndpoint = connectFuture.get();
+
+ final HttpRequest request1 = new BasicHttpRequest(Method.GET, createRequestURI(serverEndpoint, "/hello"));
+ request1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" +
+ "1234567890123456789012345678901234567890");
+ final Future<Message<HttpResponse, String>> future1 = streamEndpoint.execute(
+ new BasicRequestProducer(request1, null),
+ new BasicResponseConsumer<>(new StringAsyncEntityConsumer()), null);
+ final Message<HttpResponse, String> result1 = future1.get(TIMEOUT.getDuration(), TIMEOUT.getTimeUnit());
+ Assert.assertNotNull(result1);
+ final HttpResponse response1 = result1.getHead();
+ Assert.assertNotNull(response1);
+ Assert.assertEquals(431, response1.getCode());
+ Assert.assertEquals("Maximum line length limit exceeded", result1.getBody());
+ }
+
}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/RequestHeaderFieldsTooLargeException.java b/httpcore5/src/main/java/org/apache/hc/core5/http/RequestHeaderFieldsTooLargeException.java
new file mode 100644
index 0000000..ac6cbe3
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/RequestHeaderFieldsTooLargeException.java
@@ -0,0 +1,58 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation. For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http;
+
+/**
+ * Signals request header field length or total field size violation.
+ *
+ * @since 5.0
+ */
+public class RequestHeaderFieldsTooLargeException extends ProtocolException {
+
+ private static final long serialVersionUID = 1L;
+
+ /**
+ * Creates a new RequestHeaderFieldsTooLargeException with the specified detail message.
+ *
+ * @param message The exception detail message
+ */
+ public RequestHeaderFieldsTooLargeException(final String message) {
+ super(message);
+ }
+
+ /**
+ * Creates a new RequestHeaderFieldsTooLargeException with the specified detail message and cause.
+ *
+ * @param message the exception detail message
+ * @param cause the {@code Throwable} that caused this exception, or {@code null}
+ * if the cause is unavailable, unknown, or not a {@code Throwable}
+ */
+ public RequestHeaderFieldsTooLargeException(final String message, final Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/ServerSupport.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/ServerSupport.java
index 29cca6b..df6bc12 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/ServerSupport.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/ServerSupport.java
@@ -34,6 +34,7 @@ import org.apache.hc.core5.http.HttpStatus;
import org.apache.hc.core5.http.MethodNotSupportedException;
import org.apache.hc.core5.http.NotImplementedException;
import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException;
import org.apache.hc.core5.http.UnsupportedHttpVersionException;
/**
@@ -70,6 +71,8 @@ public class ServerSupport {
code = HttpStatus.SC_HTTP_VERSION_NOT_SUPPORTED;
} else if (ex instanceof NotImplementedException) {
code = HttpStatus.SC_NOT_IMPLEMENTED;
+ } else if (ex instanceof RequestHeaderFieldsTooLargeException) {
+ code = HttpStatus.SC_REQUEST_HEADER_FIELDS_TOO_LARGE;
} else if (ex instanceof ProtocolException) {
code = HttpStatus.SC_BAD_REQUEST;
} else {
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpRequestParser.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpRequestParser.java
index a1e8e59..55e6caf 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpRequestParser.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/DefaultHttpRequestParser.java
@@ -28,12 +28,16 @@
package org.apache.hc.core5.http.impl.io;
import java.io.IOException;
+import java.io.InputStream;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ConnectionClosedException;
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequestFactory;
+import org.apache.hc.core5.http.MessageConstraintException;
+import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException;
import org.apache.hc.core5.http.config.Http1Config;
+import org.apache.hc.core5.http.io.SessionInputBuffer;
import org.apache.hc.core5.http.message.LineParser;
import org.apache.hc.core5.http.message.RequestLine;
import org.apache.hc.core5.util.CharArrayBuffer;
@@ -88,6 +92,16 @@ public class DefaultHttpRequestParser extends AbstractMessageParser<ClassicHttpR
}
@Override
+ public ClassicHttpRequest parse(
+ final SessionInputBuffer buffer, final InputStream inputStream) throws IOException, HttpException {
+ try {
+ return super.parse(buffer, inputStream);
+ } catch (final MessageConstraintException ex) {
+ throw new RequestHeaderFieldsTooLargeException(ex.getMessage(), ex);
+ }
+ }
+
+ @Override
protected ClassicHttpRequest createMessage(final CharArrayBuffer buffer) throws IOException, HttpException {
final RequestLine requestLine = getLineParser().parseRequestLine(buffer);
final ClassicHttpRequest request = this.requestFactory.newHttpRequest(requestLine.getMethod(), requestLine.getUri());
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
index 31dd3a1..72ef10d 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
@@ -266,11 +266,7 @@ public class HttpService {
*/
protected void handleException(final HttpException ex, final ClassicHttpResponse response) {
response.setCode(toStatusCode(ex));
- String message = ex.getMessage();
- if (message == null) {
- message = ex.toString();
- }
- response.setEntity(new StringEntity(message, ContentType.TEXT_PLAIN));
+ response.setEntity(new StringEntity(ServerSupport.toErrorMessage(ex), ContentType.TEXT_PLAIN));
}
protected int toStatusCode(final Exception ex) {
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestParser.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestParser.java
index 0f61845..67724df 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestParser.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/DefaultHttpRequestParser.java
@@ -27,12 +27,17 @@
package org.apache.hc.core5.http.impl.nio;
+import java.io.IOException;
+
import org.apache.hc.core5.http.HttpException;
import org.apache.hc.core5.http.HttpRequest;
import org.apache.hc.core5.http.HttpRequestFactory;
+import org.apache.hc.core5.http.MessageConstraintException;
+import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException;
import org.apache.hc.core5.http.config.Http1Config;
import org.apache.hc.core5.http.message.LineParser;
import org.apache.hc.core5.http.message.RequestLine;
+import org.apache.hc.core5.http.nio.SessionInputBuffer;
import org.apache.hc.core5.util.Args;
import org.apache.hc.core5.util.CharArrayBuffer;
@@ -79,6 +84,15 @@ public class DefaultHttpRequestParser<T extends HttpRequest> extends AbstractMes
}
@Override
+ public T parse(final SessionInputBuffer sessionBuffer, final boolean endOfStream) throws IOException, HttpException {
+ try {
+ return super.parse(sessionBuffer, endOfStream);
+ } catch (final MessageConstraintException ex) {
+ throw new RequestHeaderFieldsTooLargeException(ex.getMessage(), ex);
+ }
+ }
+
+ @Override
protected T createMessage(final CharArrayBuffer buffer) throws HttpException {
final RequestLine requestLine = getLineParser().parseRequestLine(buffer);
final T request = this.requestFactory.newHttpRequest(requestLine.getMethod(), requestLine.getUri());
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestRequestParser.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestRequestParser.java
index 918f7dd..0fe8a20 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestRequestParser.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestRequestParser.java
@@ -33,8 +33,8 @@ import java.nio.charset.StandardCharsets;
import org.apache.hc.core5.http.ClassicHttpRequest;
import org.apache.hc.core5.http.ConnectionClosedException;
import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.MessageConstraintException;
import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.http.RequestHeaderFieldsTooLargeException;
import org.apache.hc.core5.http.config.Http1Config;
import org.apache.hc.core5.http.io.SessionInputBuffer;
import org.junit.Assert;
@@ -95,7 +95,7 @@ public class TestRequestParser {
Assert.assertEquals(1, headers.length);
}
- @Test(expected = MessageConstraintException.class)
+ @Test(expected = RequestHeaderFieldsTooLargeException.class)
public void testBasicMessageParsingTooManyLeadingEmptyLines() throws Exception {
final String s =
"\r\n" +