Author: remm Date: Mon May 2 14:46:29 2016 New Revision: 1741984 URL: http://svn.apache.org/viewvc?rev=1741984&view=rev Log: 59421: Allow direct (plain text most likely) connection to HTTP/2. The performance cost of the preface matching at this stage should be minimal. Probably not polished enough, so no 8.5 port for now.
Modified: tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java tomcat/trunk/java/org/apache/coyote/http11/Http11InputBuffer.java tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorBase.java tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java tomcat/trunk/webapps/docs/changelog.xml Modified: tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/AbstractProtocol.java Mon May 2 14:46:29 2016 @@ -424,6 +424,16 @@ public abstract class AbstractProtocol<S /** + * Find a suitable handler for the protocol upgraded name specified. This + * is used for direct connection protocol selection. + * @param name The name of the requested negotiated protocol. + * @return The instance where {@link UpgradeProtocol#getAlpnName()} matches + * the requested protocol + */ + protected abstract UpgradeProtocol getUpgradeProtocol(String name); + + + /** * Create and configure a new Processor instance for the current protocol * implementation. * @@ -433,7 +443,7 @@ public abstract class AbstractProtocol<S protected abstract Processor createUpgradeProcessor( - SocketWrapperBase<?> socket, ByteBuffer leftoverInput, + SocketWrapperBase<?> socket, UpgradeToken upgradeToken); @@ -780,32 +790,44 @@ public abstract class AbstractProtocol<S if (state == SocketState.UPGRADING) { // Get the HTTP upgrade handler UpgradeToken upgradeToken = processor.getUpgradeToken(); - HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler(); // Retrieve leftover input - ByteBuffer leftoverInput = processor.getLeftoverInput(); - // Release the Http11 processor to be re-used - release(processor); - // Create the upgrade processor - processor = getProtocol().createUpgradeProcessor( - wrapper, leftoverInput, upgradeToken); - // Mark the connection as upgraded - wrapper.setUpgraded(true); - // Associate with the processor with the connection - connections.put(socket, processor); - // Initialise the upgrade handler (which may trigger - // some IO using the new protocol which is why the lines - // above are necessary) - // This cast should be safe. If it fails the error - // handling for the surrounding try/catch will deal with - // it. - if (upgradeToken.getInstanceManager() == null) { - httpUpgradeHandler.init((WebConnection) processor); + ByteBuffer leftOverInput = processor.getLeftoverInput(); + if (upgradeToken == null) { + // Assume direct HTTP/2 connection + UpgradeProtocol upgradeProtocol = getProtocol().getUpgradeProtocol("h2c"); + if (upgradeProtocol != null) { + processor = upgradeProtocol.getProcessor( + wrapper, getProtocol().getAdapter()); + wrapper.unRead(leftOverInput); + // Associate with the processor with the connection + connections.put(socket, processor); + } } else { - ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null); - try { + HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler(); + // Release the Http11 processor to be re-used + release(processor); + // Create the upgrade processor + processor = getProtocol().createUpgradeProcessor(wrapper, upgradeToken); + wrapper.unRead(leftOverInput); + // Mark the connection as upgraded + wrapper.setUpgraded(true); + // Associate with the processor with the connection + connections.put(socket, processor); + // Initialise the upgrade handler (which may trigger + // some IO using the new protocol which is why the lines + // above are necessary) + // This cast should be safe. If it fails the error + // handling for the surrounding try/catch will deal with + // it. + if (upgradeToken.getInstanceManager() == null) { httpUpgradeHandler.init((WebConnection) processor); - } finally { - upgradeToken.getContextBind().unbind(false, oldCL); + } else { + ClassLoader oldCL = upgradeToken.getContextBind().bind(false, null); + try { + httpUpgradeHandler.init((WebConnection) processor); + } finally { + upgradeToken.getContextBind().unbind(false, oldCL); + } } } } Modified: tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java Mon May 2 14:46:29 2016 @@ -16,8 +16,6 @@ */ package org.apache.coyote.ajp; -import java.nio.ByteBuffer; - import org.apache.coyote.AbstractProtocol; import org.apache.coyote.Processor; import org.apache.coyote.UpgradeProtocol; @@ -82,6 +80,16 @@ public abstract class AbstractAjpProtoco } + /** + * {@inheritDoc} + * + * AJP does not support protocol upgrade so this always returns null. + */ + @Override + protected UpgradeProtocol getUpgradeProtocol(String name) { + return null; + } + // ------------------------------------------------- AJP specific properties // ------------------------------------------ managed in the ProtocolHandler @@ -190,7 +198,7 @@ public abstract class AbstractAjpProtoco @Override protected Processor createUpgradeProcessor(SocketWrapperBase<?> socket, - ByteBuffer leftoverInput, UpgradeToken upgradeToken) { + UpgradeToken upgradeToken) { throw new IllegalStateException(sm.getString("ajpprotocol.noUpgradeHandler", upgradeToken.getHttpUpgradeHandler().getClass().getName())); } Modified: tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/AbstractHttp11Protocol.java Mon May 2 14:46:29 2016 @@ -16,7 +16,6 @@ */ package org.apache.coyote.http11; -import java.nio.ByteBuffer; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; @@ -351,6 +350,10 @@ public abstract class AbstractHttp11Prot public UpgradeProtocol getNegotiatedProtocol(String negotiatedName) { return negotiatedProtocols.get(negotiatedName); } + @Override + public UpgradeProtocol getUpgradeProtocol(String upgradedName) { + return httpUpgradeProtocols.get(upgradedName); + } // ------------------------------------------------ HTTP specific properties @@ -643,13 +646,13 @@ public abstract class AbstractHttp11Prot @Override protected Processor createUpgradeProcessor( - SocketWrapperBase<?> socket, ByteBuffer leftoverInput, + SocketWrapperBase<?> socket, UpgradeToken upgradeToken) { HttpUpgradeHandler httpUpgradeHandler = upgradeToken.getHttpUpgradeHandler(); if (httpUpgradeHandler instanceof InternalHttpUpgradeHandler) { - return new UpgradeProcessorInternal(socket, leftoverInput, upgradeToken); + return new UpgradeProcessorInternal(socket, upgradeToken); } else { - return new UpgradeProcessorExternal(socket, leftoverInput, upgradeToken); + return new UpgradeProcessorExternal(socket, upgradeToken); } } } Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11InputBuffer.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11InputBuffer.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11InputBuffer.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11InputBuffer.java Mon May 2 14:46:29 2016 @@ -97,6 +97,9 @@ public class Http11InputBuffer implement } + private static final byte[] CLIENT_PREFACE_START = + "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n".getBytes(StandardCharsets.ISO_8859_1); + /** * Associated Coyote request. */ @@ -407,6 +410,25 @@ public class Http11InputBuffer implement // Switch to the socket timeout. wrapper.setReadTimeout(wrapper.getEndpoint().getSoTimeout()); } + if (!keptAlive) { + for (int i = 0; i < CLIENT_PREFACE_START.length; i++) { + if (i == lastValid) { + // Need more data to know if this is HTTP/2 + if (!fill(false)) { + // A read is pending, so no longer in initial state + parsingRequestLinePhase = 1; + return false; + } + } + if (CLIENT_PREFACE_START[i] != buf[i]) { + break; + } else if (i == CLIENT_PREFACE_START.length - 1) { + // HTTP/2 preface matched + parsingRequestLinePhase = -1; + return false; + } + } + } // Set the start time once we start reading data (even if it is // just skipping blank lines) if (request.getStartTime() < 0) { Modified: tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/Http11Processor.java Mon May 2 14:46:29 2016 @@ -984,7 +984,9 @@ public class Http11Processor extends Abs // Parsing the request header try { if (!inputBuffer.parseRequestLine(keptAlive)) { - if (handleIncompleteRequestLineRead()) { + if (inputBuffer.getParsingRequestLinePhase() == -1) { + return SocketState.UPGRADING; + } else if (handleIncompleteRequestLineRead()) { break; } } Modified: tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorBase.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorBase.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorBase.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorBase.java Mon May 2 14:46:29 2016 @@ -33,10 +33,9 @@ public abstract class UpgradeProcessorBa private final UpgradeToken upgradeToken; - public UpgradeProcessorBase(SocketWrapperBase<?> wrapper, ByteBuffer leftOverInput, + public UpgradeProcessorBase(SocketWrapperBase<?> wrapper, UpgradeToken upgradeToken) { this.upgradeToken = upgradeToken; - wrapper.unRead(leftOverInput); } Modified: tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorExternal.java Mon May 2 14:46:29 2016 @@ -17,7 +17,6 @@ package org.apache.coyote.http11.upgrade; import java.io.IOException; -import java.nio.ByteBuffer; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; @@ -40,13 +39,12 @@ public class UpgradeProcessorExternal ex private final UpgradeServletOutputStream upgradeServletOutputStream; - public UpgradeProcessorExternal(SocketWrapperBase<?> wrapper, ByteBuffer leftOverInput, + public UpgradeProcessorExternal(SocketWrapperBase<?> wrapper, UpgradeToken upgradeToken) { - super(wrapper, leftOverInput, upgradeToken); + super(wrapper, upgradeToken); this.upgradeServletInputStream = new UpgradeServletInputStream(this, wrapper); this.upgradeServletOutputStream = new UpgradeServletOutputStream(this, wrapper); - wrapper.unRead(leftOverInput); /* * Leave timeouts in the hands of the upgraded protocol. */ Modified: tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java (original) +++ tomcat/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessorInternal.java Mon May 2 14:46:29 2016 @@ -17,7 +17,6 @@ package org.apache.coyote.http11.upgrade; import java.io.IOException; -import java.nio.ByteBuffer; import javax.servlet.ServletInputStream; import javax.servlet.ServletOutputStream; @@ -36,9 +35,9 @@ public class UpgradeProcessorInternal ex private final InternalHttpUpgradeHandler internalHttpUpgradeHandler; - public UpgradeProcessorInternal(SocketWrapperBase<?> wrapper, ByteBuffer leftOverInput, + public UpgradeProcessorInternal(SocketWrapperBase<?> wrapper, UpgradeToken upgradeToken) { - super(wrapper, leftOverInput, upgradeToken); + super(wrapper, upgradeToken); this.internalHttpUpgradeHandler = (InternalHttpUpgradeHandler) upgradeToken.getHttpUpgradeHandler(); /* * Leave timeouts in the hands of the upgraded protocol. Modified: tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java (original) +++ tomcat/trunk/java/org/apache/coyote/http2/Http2Protocol.java Mon May 2 14:46:29 2016 @@ -72,7 +72,7 @@ public class Http2Protocol implements Up @Override public Processor getProcessor(SocketWrapperBase<?> socketWrapper, Adapter adapter) { - UpgradeProcessorInternal processor = new UpgradeProcessorInternal(socketWrapper, null, + UpgradeProcessorInternal processor = new UpgradeProcessorInternal(socketWrapper, new UpgradeToken(getInternalUpgradeHandler(adapter, null), null, null)); return processor; } Modified: tomcat/trunk/webapps/docs/changelog.xml URL: http://svn.apache.org/viewvc/tomcat/trunk/webapps/docs/changelog.xml?rev=1741984&r1=1741983&r2=1741984&view=diff ============================================================================== --- tomcat/trunk/webapps/docs/changelog.xml (original) +++ tomcat/trunk/webapps/docs/changelog.xml Mon May 2 14:46:29 2016 @@ -219,6 +219,9 @@ known issue in OpenSSL</a> that does not permit the TLS handshake to be failed if the ALPN negotiation fails. (markt) </fix> + <update> + <bug>59421</bug>: Add direct HTTP/2 connection support. (remm) + </update> </changelog> </subsection> <subsection name="WebSocket"> --------------------------------------------------------------------- To unsubscribe, e-mail: dev-unsubscr...@tomcat.apache.org For additional commands, e-mail: dev-h...@tomcat.apache.org