Support for status code 431 (Request Header Fields Too Large)
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 @@
         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 @@
                     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 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..7e2e507 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.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;
@@ -108,7 +109,7 @@
         @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());
@@ -489,7 +490,7 @@
 
         });
 
-        this.server.start(null, new Decorator<HttpServerRequestHandler>() {
+        this.server.start(null, null, new Decorator<HttpServerRequestHandler>() {
 
             @Override
             public HttpServerRequestHandler decorate(final HttpServerRequestHandler handler) {
@@ -885,4 +886,79 @@
         }
     }
 
+    @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());
+        }
+    }
+
+    @Test
+    public void testHeaderTooLargePost() 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(
+                new DefaultHttpProcessor(new RequestContent(), new RequestTargetHost(), new RequestConnControl()));
+
+        final HttpCoreContext context = HttpCoreContext.create();
+        final HttpHost host = new HttpHost(scheme.id, "localhost", this.server.getPort());
+
+        final ClassicHttpRequest post1 = new BasicClassicHttpRequest(Method.POST, "/");
+        post1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" +
+                "1234567890123456789012345678901234567890");
+        final byte[] b = new byte[2048];
+        for (int i = 0; i < b.length; i++) {
+            b[i] = (byte) ('a' + i % 10);
+        }
+        post1.setEntity(new ByteArrayEntity(b, ContentType.TEXT_PLAIN));
+
+        try (final ClassicHttpResponse response1 = this.client.execute(host, post1, 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..ea990a0 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
@@ -122,6 +122,7 @@
 import org.apache.hc.core5.http.protocol.HttpProcessor;
 import org.apache.hc.core5.http.protocol.RequestConnControl;
 import org.apache.hc.core5.http.protocol.RequestContent;
+import org.apache.hc.core5.http.protocol.RequestTargetHost;
 import org.apache.hc.core5.http.protocol.RequestValidateHost;
 import org.apache.hc.core5.reactor.IOReactorConfig;
 import org.apache.hc.core5.reactor.IOSession;
@@ -1789,4 +1790,77 @@
         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());
+    }
+
+    @Test
+    public void testHeaderTooLargePost() 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(
+                new DefaultHttpProcessor(new RequestContent(), new RequestTargetHost(), new RequestConnControl()), null);
+
+        final Future<ClientSessionEndpoint> connectFuture = client.connect(
+                "localhost", serverEndpoint.getPort(), TIMEOUT);
+        final ClientSessionEndpoint streamEndpoint = connectFuture.get();
+
+        final HttpRequest request1 = new BasicHttpRequest(Method.POST, createRequestURI(serverEndpoint, "/hello"));
+        request1.setHeader("big-f-header", "1234567890123456789012345678901234567890123456789012345678901234567890" +
+                "1234567890123456789012345678901234567890");
+
+        final byte[] b = new byte[2048];
+        for (int i = 0; i < b.length; i++) {
+            b[i] = (byte) ('a' + i % 10);
+        }
+
+        final Future<Message<HttpResponse, String>> future1 = streamEndpoint.execute(
+                new BasicRequestProducer(request1, AsyncEntityProducers.create(b, ContentType.TEXT_PLAIN)),
+                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.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 @@
             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 @@
     }
 
     @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 @@
      */
     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 @@
     }
 
     @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 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 @@
         Assert.assertEquals(1, headers.length);
     }
 
-    @Test(expected = MessageConstraintException.class)
+    @Test(expected = RequestHeaderFieldsTooLargeException.class)
     public void testBasicMessageParsingTooManyLeadingEmptyLines() throws Exception {
         final String s =
                 "\r\n" +