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:23 UTC

[httpcomponents-core] 03/04: Corrected handling of illegal or invalid request heads by async server side protocol handler

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 337401f41da3bfdf10cce0975f7780fea437cdb5
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Mon Dec 23 14:27:06 2019 +0100

    Corrected handling of illegal or invalid request heads by async server side protocol handler
---
 .../http/impl/nio/AbstractHttp1StreamDuplexer.java | 12 +++-
 .../http/impl/nio/ServerHttp1StreamDuplexer.java   | 36 +++++++++++
 .../http/impl/nio/ServerHttp1StreamHandler.java    | 73 +++++++++++++---------
 3 files changed, 88 insertions(+), 33 deletions(-)

diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
index 24aa6b8..70807f3 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/AbstractHttp1StreamDuplexer.java
@@ -234,6 +234,14 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
         processCommands();
     }
 
+    IncomingMessage parseMessageHead(final boolean endOfStream) throws IOException, HttpException {
+        final IncomingMessage messageHead = incomingMessageParser.parse(inbuf, endOfStream);
+        if (messageHead != null) {
+            incomingMessageParser.reset();
+        }
+        return messageHead;
+    }
+
     public final void onInput(final ByteBuffer src) throws HttpException, IOException {
         if (src != null) {
             inbuf.put(src);
@@ -256,10 +264,8 @@ abstract class AbstractHttp1StreamDuplexer<IncomingMessage extends HttpMessage,
         do {
             if (incomingMessage == null) {
 
-                final IncomingMessage messageHead = incomingMessageParser.parse(inbuf, endOfStream);
+                final IncomingMessage messageHead = parseMessageHead(endOfStream);
                 if (messageHead != null) {
-                    incomingMessageParser.reset();
-
                     this.version = messageHead.getVersion();
 
                     updateInputMetrics(messageHead, connMetrics);
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
index 850d645..50161f7 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamDuplexer.java
@@ -298,6 +298,42 @@ public class ServerHttp1StreamDuplexer extends AbstractHttp1StreamDuplexer<HttpR
     }
 
     @Override
+    HttpRequest parseMessageHead(final boolean endOfStream) throws IOException, HttpException {
+        try {
+            return super.parseMessageHead(endOfStream);
+        } catch (final HttpException ex) {
+            terminateExchange(ex);
+            return null;
+        }
+    }
+
+    void terminateExchange(final HttpException ex) throws HttpException, IOException {
+        final ServerHttp1StreamHandler streamHandler;
+        final HttpCoreContext context = HttpCoreContext.create();
+        context.setAttribute(HttpCoreContext.SSL_SESSION, getSSLSession());
+        context.setAttribute(HttpCoreContext.CONNECTION_ENDPOINT, getEndpointDetails());
+        if (outgoing == null) {
+            streamHandler = new ServerHttp1StreamHandler(
+                    outputChannel,
+                    httpProcessor,
+                    connectionReuseStrategy,
+                    exchangeHandlerFactory,
+                    context);
+            outgoing = streamHandler;
+        } else {
+            streamHandler = new ServerHttp1StreamHandler(
+                    new DelayedOutputChannel(outputChannel),
+                    httpProcessor,
+                    connectionReuseStrategy,
+                    exchangeHandlerFactory,
+                    context);
+            pipeline.add(streamHandler);
+        }
+        streamHandler.terminateExchange(ex);
+        incoming = streamHandler;
+    }
+
+    @Override
     void consumeHeader(final HttpRequest request, final EntityDetails entityDetails) throws HttpException, IOException {
         if (streamListener != null) {
             streamListener.onRequestHead(this, request);
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
index 6c7cb1b..0515179 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
@@ -58,12 +58,12 @@ import org.apache.hc.core5.http.nio.support.ImmediateResponseExchangeHandler;
 import org.apache.hc.core5.http.protocol.HttpContext;
 import org.apache.hc.core5.http.protocol.HttpCoreContext;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
-import org.apache.hc.core5.util.Asserts;
 
 class ServerHttp1StreamHandler implements ResourceHolder {
 
     private final Http1StreamChannel<HttpResponse> outputChannel;
     private final DataStreamChannel internalDataChannel;
+    private final ResponseChannel responseChannel;
     private final HttpProcessor httpProcessor;
     private final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory;
     private final ConnectionReuseStrategy connectionReuseStrategy;
@@ -112,6 +112,33 @@ class ServerHttp1StreamHandler implements ResourceHolder {
 
         };
 
+        this.responseChannel = new ResponseChannel() {
+
+            @Override
+            public void sendInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException {
+                commitInformation(response);
+            }
+
+            @Override
+            public void sendResponse(
+                    final HttpResponse response, final EntityDetails responseEntityDetails, final HttpContext httpContext) throws HttpException, IOException {
+                ServerSupport.validateResponse(response, responseEntityDetails);
+                commitResponse(response, responseEntityDetails);
+            }
+
+            @Override
+            public void pushPromise(
+                    final HttpRequest promise, final AsyncPushProducer pushProducer, final HttpContext httpContext) throws HttpException, IOException {
+                commitPromise();
+            }
+
+            @Override
+            public String toString() {
+                return super.toString() + " " + ServerHttp1StreamHandler.this.toString();
+            }
+
+        };
+
         this.httpProcessor = httpProcessor;
         this.connectionReuseStrategy = connectionReuseStrategy;
         this.exchangeHandlerFactory = exchangeHandlerFactory;
@@ -138,12 +165,11 @@ class ServerHttp1StreamHandler implements ResourceHolder {
                 throw new HttpException("Invalid response: " + status);
             }
 
-            Asserts.notNull(receivedRequest, "Received request");
-            final String method = receivedRequest.getMethod();
             context.setAttribute(HttpCoreContext.HTTP_RESPONSE, response);
             httpProcessor.process(response, responseEntityDetails, context);
 
-            final boolean endStream = responseEntityDetails == null || Method.HEAD.isSame(method);
+            final boolean endStream = responseEntityDetails == null ||
+                    (receivedRequest != null && Method.HEAD.isSame(receivedRequest.getMethod()));
 
             if (!connectionReuseStrategy.keepAlive(receivedRequest, response, context)) {
                 keepAlive = false;
@@ -195,6 +221,19 @@ class ServerHttp1StreamHandler implements ResourceHolder {
         return requestState == MessageState.COMPLETE && responseState == MessageState.COMPLETE;
     }
 
+    void terminateExchange(final HttpException ex) throws HttpException, IOException {
+        if (done.get() || requestState != MessageState.HEADERS) {
+            throw new ProtocolException("Unexpected message head");
+        }
+        receivedRequest = null;
+        requestState = MessageState.COMPLETE;
+        final AsyncResponseProducer responseProducer = new BasicResponseProducer(
+                ServerSupport.toStatusCode(ex),
+                ServerSupport.toErrorMessage(ex));
+        exchangeHandler = new ImmediateResponseExchangeHandler(responseProducer);
+        exchangeHandler.handleRequest(null, null, responseChannel, context);
+    }
+
     void consumeHeader(final HttpRequest request, final EntityDetails requestEntityDetails) throws HttpException, IOException {
         if (done.get() || requestState != MessageState.HEADERS) {
             throw new ProtocolException("Unexpected message head");
@@ -223,32 +262,6 @@ class ServerHttp1StreamHandler implements ResourceHolder {
         context.setProtocolVersion(transportVersion);
         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
-        final ResponseChannel responseChannel = new ResponseChannel() {
-
-            @Override
-            public void sendInformation(final HttpResponse response, final HttpContext httpContext) throws HttpException, IOException {
-                commitInformation(response);
-            }
-
-            @Override
-            public void sendResponse(
-                    final HttpResponse response, final EntityDetails responseEntityDetails, final HttpContext httpContext) throws HttpException, IOException {
-                ServerSupport.validateResponse(response, responseEntityDetails);
-                commitResponse(response, responseEntityDetails);
-            }
-
-            @Override
-            public void pushPromise(
-                    final HttpRequest promise, final AsyncPushProducer pushProducer, final HttpContext httpContext) throws HttpException, IOException {
-                commitPromise();
-            }
-
-            @Override
-            public String toString() {
-                return super.toString() + " " + ServerHttp1StreamHandler.this.toString();
-            }
-
-        };
         try {
             httpProcessor.process(request, requestEntityDetails, context);
             exchangeHandler.handleRequest(request, requestEntityDetails, responseChannel, context);