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: [email protected]
For additional commands, e-mail: [email protected]