Author: markt
Date: Thu Feb  7 17:34:46 2019
New Revision: 1853152

URL: http://svn.apache.org/viewvc?rev=1853152&view=rev
Log:
When performing an HTTP/1.1 upgrade to HTTP/2 (h2c) ensure that the hostname 
and port from the HTTP/1.1 Host header of the upgraded request are made 
available via the standard methods ServletRequest.getServerName() and 
ServletRequest.getServerPort()

Added:
    tomcat/trunk/test/org/apache/coyote/http2/TestHttp2InitialConnection.java   
(with props)
Modified:
    tomcat/trunk/java/org/apache/coyote/http2/Stream.java
    tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java
    tomcat/trunk/webapps/docs/changelog.xml

Modified: tomcat/trunk/java/org/apache/coyote/http2/Stream.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Stream.java?rev=1853152&r1=1853151&r2=1853152&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/coyote/http2/Stream.java (original)
+++ tomcat/trunk/java/org/apache/coyote/http2/Stream.java Thu Feb  7 17:34:46 
2019
@@ -103,8 +103,19 @@ class Stream extends AbstractStream impl
             // HTTP/1.1 upgrade
             this.coyoteRequest = coyoteRequest;
             this.inputBuffer = null;
-            // Headers have been populated by this point
+            // Headers have been read by this point
             state.receivedStartOfHeaders();
+            // Populate coyoteRequest from headers
+            try {
+                prepareRequest();
+            } catch (IllegalArgumentException iae) {
+                // Something in the headers is invalid
+                // Set correct return status
+                coyoteResponse.setStatus(400);
+                // Set error flag. This triggers error processing rather than
+                // the normal mapping
+                coyoteResponse.setError();
+            }
             // TODO Assuming the body has been read at this point is not valid
             state.receivedEndOfStream();
         }
@@ -118,6 +129,44 @@ class Stream extends AbstractStream impl
     }
 
 
+    private void prepareRequest() {
+        MessageBytes hostValueMB = 
coyoteRequest.getMimeHeaders().getUniqueValue("host");
+        if (hostValueMB == null) {
+            throw new IllegalArgumentException();
+        }
+        // This processing expects bytes. Server push will have used a String
+        // to trigger a conversion if required.
+        hostValueMB.toBytes();
+        ByteChunk valueBC = hostValueMB.getByteChunk();
+        byte[] valueB = valueBC.getBytes();
+        int valueL = valueBC.getLength();
+        int valueS = valueBC.getStart();
+
+        int colonPos = Host.parse(hostValueMB);
+        if (colonPos != -1) {
+            int port = 0;
+            for (int i = colonPos + 1; i < valueL; i++) {
+                char c = (char) valueB[i + valueS];
+                if (c < '0' || c > '9') {
+                    throw new IllegalArgumentException();
+                }
+                port = port * 10 + c - '0';
+            }
+            coyoteRequest.setServerPort(port);
+
+            // Only need to copy the host name up to the :
+            valueL = colonPos;
+        }
+
+        // Extract the host name
+        char[] hostNameC = new char[valueL];
+        for (int i = 0; i < valueL; i++) {
+            hostNameC[i] = (char) valueB[i + valueS];
+        }
+        coyoteRequest.serverName().setChars(hostNameC, 0, valueL);
+    }
+
+
     final void rePrioritise(AbstractStream parent, boolean exclusive, int 
weight) {
         if (log.isDebugEnabled()) {
             log.debug(sm.getString("stream.reprioritisation.debug",

Modified: tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java?rev=1853152&r1=1853151&r2=1853152&view=diff
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java (original)
+++ tomcat/trunk/test/org/apache/coyote/http2/Http2TestBase.java Thu Feb  7 
17:34:46 2019
@@ -476,25 +476,35 @@ public abstract class Http2TestBase exte
 
 
     protected String getCookieResponseTrace(int streamId, int cookieCount) {
-        return getResponseBodyFrameTrace(streamId, "text/plain;charset=UTF-8",
+        return getResponseBodyFrameTrace(streamId, 200, 
"text/plain;charset=UTF-8", null,
                 "Cookie count: " + cookieCount, null);
     }
 
 
-    private String getResponseBodyFrameTrace(int streamId, String body) {
-        return getResponseBodyFrameTrace(streamId, "application/octet-stream", 
body, body);
+    protected String getResponseBodyFrameTrace(int streamId, String body) {
+        return getResponseBodyFrameTrace(streamId, 200, 
"application/octet-stream", null, body, body);
     }
 
-    private String getResponseBodyFrameTrace(int streamId, String contentType, 
String body, String cl) {
+
+    protected String getResponseBodyFrameTrace(int streamId, int status, 
String contentType,
+            String contentLanguage, String body, String cl) {
         StringBuilder result = new StringBuilder();
         result.append(streamId);
         result.append("-HeadersStart\n");
         result.append(streamId);
-        result.append("-Header-[:status]-[200]\n");
+        result.append("-Header-[:status]-[");
+        result.append(status);
+        result.append("]\n");
         result.append(streamId);
         result.append("-Header-[content-type]-[");
         result.append(contentType);
         result.append("]\n");
+        if (contentLanguage != null) {
+            result.append(streamId);
+            result.append("-Header-[content-language]-[");
+            result.append(contentLanguage);
+            result.append("]\n");
+        }
         if (cl != null) {
             result.append(streamId);
             result.append("-Header-[content-length]-[");

Added: tomcat/trunk/test/org/apache/coyote/http2/TestHttp2InitialConnection.java
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/coyote/http2/TestHttp2InitialConnection.java?rev=1853152&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/coyote/http2/TestHttp2InitialConnection.java 
(added)
+++ tomcat/trunk/test/org/apache/coyote/http2/TestHttp2InitialConnection.java 
Thu Feb  7 17:34:46 2019
@@ -0,0 +1,131 @@
+/*
+ *  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.
+ */
+package org.apache.coyote.http2;
+
+import java.io.IOException;
+import java.nio.charset.StandardCharsets;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestHttp2InitialConnection extends Http2TestBase {
+
+    private TestData testData;
+
+
+    @Test
+    public void testValidHostHeader() throws Exception {
+        List<String> hostHeaders = new ArrayList<>(1);
+        hostHeaders.add("localhost:8080");
+
+        testData = new TestData(hostHeaders, 200);
+
+        http2Connect();
+    }
+
+
+    @Test
+    public void testMultipleHostHeaders() throws Exception {
+        List<String> hostHeaders = new ArrayList<>(1);
+        hostHeaders.add("localhost:8080");
+        hostHeaders.add("localhost:8081");
+
+        testData = new TestData(hostHeaders, 400);
+
+        http2Connect();
+    }
+
+
+    @Test
+    public void testNoHostHeader() throws Exception {
+        List<String> hostHeaders = new ArrayList<>(1);
+
+        testData = new TestData(hostHeaders, 400);
+
+        http2Connect();
+    }
+
+
+    @Override
+    protected void doHttpUpgrade(String connection, String upgrade, String 
settings,
+            boolean validate) throws IOException {
+        StringBuilder request = new StringBuilder();
+        request.append("GET /simple HTTP/1.1\r\n");
+        for (String hostHeader : testData.getHostHeaders()) {
+            request.append("Host: ");
+            request.append(hostHeader);
+            request.append("\r\n");
+        }
+        // Connection
+        request.append("Connection: ");
+        request.append(connection);
+        request.append("\r\n");
+        // Upgrade
+        request.append("Upgrade: ");
+        request.append(upgrade);
+        request.append("\r\n");
+        // Settings
+        request.append(settings);
+        // Request terminator
+        request.append("\r\n");
+
+        byte[] upgradeRequest = 
request.toString().getBytes(StandardCharsets.ISO_8859_1);
+        os.write(upgradeRequest);
+        os.flush();
+
+        if (validate) {
+            Assert.assertTrue("Failed to read HTTP Upgrade response",
+                    readHttpUpgradeResponse());
+        }
+    }
+
+
+    @Override
+    protected String getResponseBodyFrameTrace(int streamId, String body) {
+        if (testData.getExpectedStatus() == 200) {
+            return super.getResponseBodyFrameTrace(streamId, body);
+        } else if (testData.getExpectedStatus() == 400) {
+            return getResponseBodyFrameTrace(streamId, 
testData.getExpectedStatus(),
+                    "text/html;charset=utf-8", "en", "1136", "1136");
+        } else {
+            Assert.fail();
+            // To keep the IDE happy
+            return null;
+        }
+    }
+
+
+    private static class TestData {
+        private final List<String> hostHeaders;
+        private final int expectedStatus;
+
+        public TestData(List<String> hostHeaders, int expectedStatus) {
+            this.hostHeaders = hostHeaders;
+            this.expectedStatus = expectedStatus;
+        }
+
+        public List<String> getHostHeaders() {
+            return hostHeaders;
+        }
+
+        public int getExpectedStatus() {
+            return expectedStatus;
+        }
+    }
+}

Propchange: 
tomcat/trunk/test/org/apache/coyote/http2/TestHttp2InitialConnection.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: tomcat/trunk/webapps/docs/changelog.xml
URL: 
http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1853152&r1=1853151&r2=1853152&view=diff
==============================================================================
--- tomcat/trunk/webapps/docs/changelog.xml (original)
+++ tomcat/trunk/webapps/docs/changelog.xml Thu Feb  7 17:34:46 2019
@@ -55,6 +55,17 @@
       </fix>
     </changelog>
   </subsection>
+  <subsection name="Coyote">
+    <changelog>
+      <fix>
+        When performing an HTTP/1.1 upgrade to HTTP/2 (h2c) ensure that the 
hostname
+        and port from the HTTP/1.1 Host header of the upgraded request are made
+        available via the standard methods
+        <code>ServletRequest.getServerName()</code> and
+        <code>ServletRequest.getServerPort()</code>. (markt)
+      </fix>
+    </changelog>
+  </subsection>
   <subsection name="WebSocket">
     <changelog>
       <scode>



---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org
For additional commands, e-mail: dev-h...@tomcat.apache.org

Reply via email to