diff --git a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java index 29a9e1baf5..e176ef1055 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java +++ b/codec-http/src/main/java/io/netty/handler/codec/spdy/SpdyOrHttpChooser.java @@ -16,18 +16,16 @@ package io.netty.handler.codec.spdy; import io.netty.buffer.ByteBuf; -import io.netty.channel.ChannelHandler; +import io.netty.channel.Channel; import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelInboundHandler; import io.netty.channel.ChannelPipeline; import io.netty.handler.codec.ByteToMessageDecoder; -import io.netty.handler.codec.http.HttpObjectAggregator; -import io.netty.handler.codec.http.HttpRequestDecoder; -import io.netty.handler.codec.http.HttpResponseEncoder; +import io.netty.handler.codec.http.HttpServerCodec; import io.netty.handler.ssl.SslHandler; -import io.netty.util.internal.StringUtil; +import io.netty.util.internal.logging.InternalLogger; +import io.netty.util.internal.logging.InternalLoggerFactory; -import javax.net.ssl.SSLEngine; import java.util.List; /** @@ -37,13 +35,14 @@ import java.util.List; */ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder { + private static final InternalLogger logger = InternalLoggerFactory.getInstance(SpdyOrHttpChooser.class); + // TODO: Replace with generic NPN handler public enum SelectedProtocol { SPDY_3_1("spdy/3.1"), HTTP_1_1("http/1.1"), - HTTP_1_0("http/1.0"), - UNKNOWN("Unknown"); + HTTP_1_0("http/1.0"); private final String name; @@ -58,9 +57,8 @@ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder { /** * Get an instance of this enum based on the protocol name returned by the NPN server provider * - * @param name - * the protocol name - * @return the SelectedProtocol instance + * @param name the protocol name + * @return the selected protocol or {@code null} if there is no match */ public static SelectedProtocol protocol(String name) { for (SelectedProtocol protocol : SelectedProtocol.values()) { @@ -68,36 +66,15 @@ public abstract class SpdyOrHttpChooser extends ByteToMessageDecoder { return protocol; } } - return UNKNOWN; + return null; } } - private final int maxSpdyContentLength; - private final int maxHttpContentLength; - - protected SpdyOrHttpChooser(int maxSpdyContentLength, int maxHttpContentLength) { - this.maxSpdyContentLength = maxSpdyContentLength; - this.maxHttpContentLength = maxHttpContentLength; - } - - /** - * Return the {@link SelectedProtocol} for the {@link SSLEngine}. If its not known yet implementations MUST return - * {@link SelectedProtocol#UNKNOWN}. - * - */ - protected SelectedProtocol getProtocol(SSLEngine engine) { - String[] protocol = StringUtil.split(engine.getSession().getProtocol(), ':'); - if (protocol.length < 2) { - // Use HTTP/1.1 as default - return SelectedProtocol.HTTP_1_1; - } - SelectedProtocol selectedProtocol = SelectedProtocol.protocol(protocol[1]); - return selectedProtocol; - } + protected SpdyOrHttpChooser() { } @Override protected void decode(ChannelHandlerContext ctx, ByteBuf in, List
+ */ + protected abstract void configureSpdy(ChannelHandlerContext ctx, SpdyVersion version) throws Exception; + + /** + * Configures the {@link Channel} of the specified {@code ctx} for HTTP/1. + *+ * A typical implementation of this method will look like the following: + *
+ * {@link ChannelPipeline} p = ctx.pipeline(); + * p.addLast(new {@link HttpServerCodec}()); + * p.addLast(new YourHttpRequestHandler()); + *+ * + */ + protected abstract void configureHttp1(ChannelHandlerContext ctx) throws Exception; + + @Override + public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception { + logger.warn("{} Failed to select the application-level protocol:", ctx.channel(), cause); + ctx.close(); } } diff --git a/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java b/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java index 080ad23d78..c6473465af 100644 --- a/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java +++ b/example/src/main/java/io/netty/example/spdy/client/SpdyClient.java @@ -50,9 +50,8 @@ import io.netty.handler.ssl.util.InsecureTrustManagerFactory; */ public final class SpdyClient { - static final boolean SSL = System.getProperty("ssl") != null; static final String HOST = System.getProperty("host", "127.0.0.1"); - static final int PORT = Integer.parseInt(System.getProperty("port", SSL? "8443" : "8080")); + static final int PORT = Integer.parseInt(System.getProperty("port", "8443")); public static void main(String[] args) throws Exception { // Configure SSL. @@ -98,9 +97,7 @@ public final class SpdyClient { // Wait until the connection is closed. channel.close().syncUninterruptibly(); } finally { - if (workerGroup != null) { - workerGroup.shutdownGracefully(); - } + workerGroup.shutdownGracefully(); } } } diff --git a/example/src/main/java/io/netty/example/spdy/server/SpdyOrHttpHandler.java b/example/src/main/java/io/netty/example/spdy/server/SpdyOrHttpHandler.java index e27a1d2a83..19490a2cd6 100644 --- a/example/src/main/java/io/netty/example/spdy/server/SpdyOrHttpHandler.java +++ b/example/src/main/java/io/netty/example/spdy/server/SpdyOrHttpHandler.java @@ -15,8 +15,17 @@ */ package io.netty.example.spdy.server; -import io.netty.channel.ChannelInboundHandler; +import io.netty.channel.ChannelHandlerContext; +import io.netty.channel.ChannelPipeline; +import io.netty.handler.codec.http.HttpObjectAggregator; +import io.netty.handler.codec.http.HttpServerCodec; +import io.netty.handler.codec.spdy.SpdyFrameCodec; +import io.netty.handler.codec.spdy.SpdyHttpDecoder; +import io.netty.handler.codec.spdy.SpdyHttpEncoder; +import io.netty.handler.codec.spdy.SpdyHttpResponseStreamIdHandler; import io.netty.handler.codec.spdy.SpdyOrHttpChooser; +import io.netty.handler.codec.spdy.SpdySessionHandler; +import io.netty.handler.codec.spdy.SpdyVersion; /** * Negotiates with the browser if SPDY or HTTP is going to be used. Once decided, the Netty pipeline is setup with @@ -26,17 +35,22 @@ public class SpdyOrHttpHandler extends SpdyOrHttpChooser { private static final int MAX_CONTENT_LENGTH = 1024 * 100; - public SpdyOrHttpHandler() { - this(MAX_CONTENT_LENGTH, MAX_CONTENT_LENGTH); - } - - public SpdyOrHttpHandler(int maxSpdyContentLength, int maxHttpContentLength) { - super(maxSpdyContentLength, maxHttpContentLength); + @Override + protected void configureSpdy(ChannelHandlerContext ctx, SpdyVersion version) throws Exception { + ChannelPipeline p = ctx.pipeline(); + p.addLast(new SpdyFrameCodec(version)); + p.addLast(new SpdySessionHandler(version, true)); + p.addLast(new SpdyHttpEncoder(version)); + p.addLast(new SpdyHttpDecoder(version, MAX_CONTENT_LENGTH)); + p.addLast(new SpdyHttpResponseStreamIdHandler()); + p.addLast(new SpdyServerHandler()); } @Override - protected ChannelInboundHandler createHttpRequestHandlerForHttp() { - return new SpdyServerHandler(); + protected void configureHttp1(ChannelHandlerContext ctx) throws Exception { + ChannelPipeline p = ctx.pipeline(); + p.addLast(new HttpServerCodec()); + p.addLast(new HttpObjectAggregator(MAX_CONTENT_LENGTH)); + p.addLast(new SpdyServerHandler()); } - } diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolAccessor.java b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolAccessor.java new file mode 100644 index 0000000000..812762db3c --- /dev/null +++ b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolAccessor.java @@ -0,0 +1,24 @@ +/* + * Copyright 2015 The Netty Project + * + * The Netty Project 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 io.netty.handler.ssl; + +/** + * Provides a way to get the application-level protocol name from ALPN or NPN. + */ +interface ApplicationProtocolAccessor { + String getApplicationProtocol(); +} diff --git a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolUtil.java b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolUtil.java index 0581e9b2c2..03a077ec69 100644 --- a/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolUtil.java +++ b/handler/src/main/java/io/netty/handler/ssl/ApplicationProtocolUtil.java @@ -22,6 +22,7 @@ import java.util.List; * Utility class for application protocol common operations. */ final class ApplicationProtocolUtil { + private static final int DEFAULT_LIST_SIZE = 2; private ApplicationProtocolUtil() { diff --git a/handler/src/main/java/io/netty/handler/ssl/JdkSslSession.java b/handler/src/main/java/io/netty/handler/ssl/JdkSslSession.java index ba54b9b83f..183c296632 100644 --- a/handler/src/main/java/io/netty/handler/ssl/JdkSslSession.java +++ b/handler/src/main/java/io/netty/handler/ssl/JdkSslSession.java @@ -24,7 +24,7 @@ import javax.security.cert.X509Certificate; import java.security.Principal; import java.security.cert.Certificate; -final class JdkSslSession implements SSLSession { +final class JdkSslSession implements SSLSession, ApplicationProtocolAccessor { private final SSLEngine engine; private volatile String applicationProtocol; @@ -32,39 +32,22 @@ final class JdkSslSession implements SSLSession { this.engine = engine; } - void setApplicationProtocol(String applicationProtocol) { - if (applicationProtocol != null) { - applicationProtocol = applicationProtocol.replace(':', '_'); - } - this.applicationProtocol = applicationProtocol; + private SSLSession unwrap() { + return engine.getSession(); } @Override public String getProtocol() { - final String protocol = unwrap().getProtocol(); - final String applicationProtocol = this.applicationProtocol; - - if (applicationProtocol == null) { - if (protocol != null) { - return protocol.replace(':', '_'); - } else { - return null; - } - } - - final StringBuilder buf = new StringBuilder(32); - if (protocol != null) { - buf.append(protocol.replace(':', '_')); - buf.append(':'); - } else { - buf.append("null:"); - } - buf.append(applicationProtocol); - return buf.toString(); + return unwrap().getProtocol(); } - private SSLSession unwrap() { - return engine.getSession(); + @Override + public String getApplicationProtocol() { + return applicationProtocol; + } + + void setApplicationProtocol(String applicationProtocol) { + this.applicationProtocol = applicationProtocol; } @Override diff --git a/handler/src/main/java/io/netty/handler/ssl/JettyNpnSslSession.java b/handler/src/main/java/io/netty/handler/ssl/JettyNpnSslSession.java deleted file mode 100644 index f4da3da1d5..0000000000 --- a/handler/src/main/java/io/netty/handler/ssl/JettyNpnSslSession.java +++ /dev/null @@ -1,170 +0,0 @@ -/* - * Copyright 2014 The Netty Project - * - * The Netty Project 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 io.netty.handler.ssl; - -import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLPeerUnverifiedException; -import javax.net.ssl.SSLSession; -import javax.net.ssl.SSLSessionContext; -import javax.security.cert.X509Certificate; -import java.security.Principal; -import java.security.cert.Certificate; - -final class JettyNpnSslSession implements SSLSession { - - private final SSLEngine engine; - private volatile String applicationProtocol; - - JettyNpnSslSession(SSLEngine engine) { - this.engine = engine; - } - - void setApplicationProtocol(String applicationProtocol) { - if (applicationProtocol != null) { - applicationProtocol = applicationProtocol.replace(':', '_'); - } - this.applicationProtocol = applicationProtocol; - } - - @Override - public String getProtocol() { - final String protocol = unwrap().getProtocol(); - final String applicationProtocol = this.applicationProtocol; - - if (applicationProtocol == null) { - if (protocol != null) { - return protocol.replace(':', '_'); - } else { - return null; - } - } - - final StringBuilder buf = new StringBuilder(32); - if (protocol != null) { - buf.append(protocol.replace(':', '_')); - buf.append(':'); - } else { - buf.append("null:"); - } - buf.append(applicationProtocol); - return buf.toString(); - } - - private SSLSession unwrap() { - return engine.getSession(); - } - - @Override - public byte[] getId() { - return unwrap().getId(); - } - - @Override - public SSLSessionContext getSessionContext() { - return unwrap().getSessionContext(); - } - - @Override - public long getCreationTime() { - return unwrap().getCreationTime(); - } - - @Override - public long getLastAccessedTime() { - return unwrap().getLastAccessedTime(); - } - - @Override - public void invalidate() { - unwrap().invalidate(); - } - - @Override - public boolean isValid() { - return unwrap().isValid(); - } - - @Override - public void putValue(String s, Object o) { - unwrap().putValue(s, o); - } - - @Override - public Object getValue(String s) { - return unwrap().getValue(s); - } - - @Override - public void removeValue(String s) { - unwrap().removeValue(s); - } - - @Override - public String[] getValueNames() { - return unwrap().getValueNames(); - } - - @Override - public Certificate[] getPeerCertificates() throws SSLPeerUnverifiedException { - return unwrap().getPeerCertificates(); - } - - @Override - public Certificate[] getLocalCertificates() { - return unwrap().getLocalCertificates(); - } - - @Override - public X509Certificate[] getPeerCertificateChain() throws SSLPeerUnverifiedException { - return unwrap().getPeerCertificateChain(); - } - - @Override - public Principal getPeerPrincipal() throws SSLPeerUnverifiedException { - return unwrap().getPeerPrincipal(); - } - - @Override - public Principal getLocalPrincipal() { - return unwrap().getLocalPrincipal(); - } - - @Override - public String getCipherSuite() { - return unwrap().getCipherSuite(); - } - - @Override - public String getPeerHost() { - return unwrap().getPeerHost(); - } - - @Override - public int getPeerPort() { - return unwrap().getPeerPort(); - } - - @Override - public int getPacketBufferSize() { - return unwrap().getPacketBufferSize(); - } - - @Override - public int getApplicationBufferSize() { - return unwrap().getApplicationBufferSize(); - } -} diff --git a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java index 264c345c12..53ef433446 100644 --- a/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java +++ b/handler/src/main/java/io/netty/handler/ssl/OpenSslEngine.java @@ -949,7 +949,7 @@ public final class OpenSslEngine extends SSLEngine { SSL.setOptions(ssl, SSL.SSL_OP_NO_TLSv1_2); } } else { - throw new IllegalStateException("failed to enable protocols: " + protocols); + throw new IllegalStateException("failed to enable protocols: " + Arrays.asList(protocols)); } } } @@ -1067,7 +1067,6 @@ public final class OpenSslEngine extends SSLEngine { private static String selectApplicationProtocol(List