diff --git a/pom.xml b/pom.xml index 151a561c82e..310ed027d64 100644 --- a/pom.xml +++ b/pom.xml @@ -13,6 +13,7 @@ UTF-8 7.6.0.RC5 + 1.0.0-SNAPSHOT @@ -44,6 +45,16 @@ 1.7 + + maven-surefire-plugin + 2.9 + + + -Xbootclasspath/a:${settings.localRepository}/org/eclipse/jetty/jetty-npn-boot/${jetty.npn.version}/jetty-npn-boot-${jetty.npn.version}.jar + -javaagent:${settings.localRepository}/org/eclipse/jetty/jetty-npn-agent/${jetty.npn.version}/jetty-npn-agent-${jetty.npn.version}.jar + + + @@ -53,6 +64,11 @@ jetty-server ${jetty.version} + + org.eclipse.jetty + jetty-npn-boot + ${jetty.npn.version} + org.slf4j slf4j-api diff --git a/src/main/java/org/eclipse/jetty/spdy/nio/AsyncConnectionFactory.java b/src/main/java/org/eclipse/jetty/spdy/nio/AsyncConnectionFactory.java new file mode 100644 index 00000000000..743a97c30ca --- /dev/null +++ b/src/main/java/org/eclipse/jetty/spdy/nio/AsyncConnectionFactory.java @@ -0,0 +1,13 @@ +package org.eclipse.jetty.spdy.nio; + +import java.nio.channels.SocketChannel; + +import org.eclipse.jetty.io.AsyncEndPoint; +import org.eclipse.jetty.io.nio.AsyncConnection; + +public interface AsyncConnectionFactory +{ + public String getProtocol(); + + public AsyncConnection newAsyncConnection(SocketChannel channel, AsyncEndPoint endPoint, Object attachment); +} diff --git a/src/main/java/org/eclipse/jetty/spdy/nio/AsyncSPDYConnection.java b/src/main/java/org/eclipse/jetty/spdy/nio/AsyncSPDYConnection.java index 5b64f0bc836..4799e56fefe 100644 --- a/src/main/java/org/eclipse/jetty/spdy/nio/AsyncSPDYConnection.java +++ b/src/main/java/org/eclipse/jetty/spdy/nio/AsyncSPDYConnection.java @@ -19,8 +19,8 @@ import org.eclipse.jetty.spdy.parser.Parser; import org.slf4j.Logger; import org.slf4j.LoggerFactory; -public class AsyncSPDYConnection extends AbstractConnection implements AsyncConnection, Controller { - +public class AsyncSPDYConnection extends AbstractConnection implements AsyncConnection, Controller +{ private static final Logger logger = LoggerFactory.getLogger(AsyncSPDYConnection.class); private final Parser parser; private ByteBuffer buffer; diff --git a/src/main/java/org/eclipse/jetty/spdy/nio/NoProtocolConnection.java b/src/main/java/org/eclipse/jetty/spdy/nio/NoProtocolConnection.java new file mode 100644 index 00000000000..a72f2744ec1 --- /dev/null +++ b/src/main/java/org/eclipse/jetty/spdy/nio/NoProtocolConnection.java @@ -0,0 +1,51 @@ +package org.eclipse.jetty.spdy.nio; + +import java.io.IOException; + +import org.eclipse.jetty.io.AbstractConnection; +import org.eclipse.jetty.io.AsyncEndPoint; +import org.eclipse.jetty.io.Connection; +import org.eclipse.jetty.io.nio.AsyncConnection; + +public class NoProtocolConnection extends AbstractConnection implements AsyncConnection +{ + public NoProtocolConnection(AsyncEndPoint endPoint) + { + super(endPoint); + } + + public Connection handle() throws IOException + { + return this; + } + + @Override + public AsyncEndPoint getEndPoint() + { + return (AsyncEndPoint)super.getEndPoint(); + } + + @Override + public boolean isIdle() + { + return false; + } + + @Override + public boolean isSuspended() + { + return false; + } + + @Override + public void onClose() + { + // TODO + } + + @Override + public void onInputShutdown() throws IOException + { + // TODO + } +} diff --git a/src/main/java/org/eclipse/jetty/spdy/nio/SPDYClient.java b/src/main/java/org/eclipse/jetty/spdy/nio/SPDYClient.java index e9a6afa1286..c465194db95 100644 --- a/src/main/java/org/eclipse/jetty/spdy/nio/SPDYClient.java +++ b/src/main/java/org/eclipse/jetty/spdy/nio/SPDYClient.java @@ -2,6 +2,7 @@ * To change this template, choose Tools | Templates * and open the template in the editor. */ + package org.eclipse.jetty.spdy.nio; import java.io.IOException; @@ -9,6 +10,7 @@ import java.net.InetSocketAddress; import java.net.SocketAddress; import java.nio.channels.SelectionKey; import java.nio.channels.SocketChannel; +import java.util.List; import java.util.concurrent.CountDownLatch; import java.util.concurrent.ExecutionException; import java.util.concurrent.Future; @@ -24,10 +26,8 @@ import org.eclipse.jetty.io.nio.AsyncConnection; import org.eclipse.jetty.io.nio.SelectChannelEndPoint; import org.eclipse.jetty.io.nio.SelectorManager; import org.eclipse.jetty.io.nio.SslConnection; +import org.eclipse.jetty.npn.NextProtoNego; import org.eclipse.jetty.spdy.CompressionFactory; -import org.eclipse.jetty.spdy.CompressionFactory.Compressor; -import org.eclipse.jetty.spdy.CompressionFactory.Decompressor; -import org.eclipse.jetty.spdy.ISession; import org.eclipse.jetty.spdy.StandardCompressionFactory; import org.eclipse.jetty.spdy.StandardSession; import org.eclipse.jetty.spdy.api.Session; @@ -97,43 +97,24 @@ public class SPDYClient this.maxIdleTime = maxIdleTime; } + protected AsyncConnectionFactory selectAsyncConnectionFactory(List serverProtocols) + { + if (serverProtocols == null) + return new ClientSPDY2AsyncConnectionFactory(); + + // TODO: for each server protocol, lookup a connection factory in SPDYClient.Factory; + // TODO: if that's null, lookup a connection factory in SPDYClient; if that's null, return null. + + return new ClientSPDY2AsyncConnectionFactory(); + } + protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel) { - try - { - String peerHost = channel.socket().getInetAddress().getHostAddress(); - int peerPort = channel.socket().getPort(); - SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort); - engine.setUseClientMode(true); - engine.beginHandshake(); - return engine; - } - catch (SSLException x) - { - throw new RuntimeException(x); - } - } - - protected CompressionFactory newCompressionFactory() - { - return new StandardCompressionFactory(); - } - - protected Parser newParser(Decompressor decompressor) - { - return new Parser(decompressor); - } - - protected Generator newGenerator(Compressor compressor) - { - return new Generator(compressor); - } - - private Session newSession(ISession.Controller controller, FrameListener listener, Parser parser, Generator generator) - { - StandardSession session = new StandardSession(controller, 1, listener, generator); - parser.addListener(session); - return session; + String peerHost = channel.socket().getInetAddress().getHostAddress(); + int peerPort = channel.socket().getPort(); + SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort); + engine.setUseClientMode(true); + return engine; } public static class Factory extends AggregateLifeCycle @@ -223,45 +204,72 @@ public class SPDYClient } @Override - public AsyncConnection newConnection(SocketChannel channel, AsyncEndPoint endPoint, Object attachment) + public AsyncConnection newConnection(final SocketChannel channel, AsyncEndPoint endPoint, final Object attachment) { SessionFuture sessionFuture = (SessionFuture)attachment; - SPDYClient client = sessionFuture.client; + final SPDYClient client = sessionFuture.client; - CompressionFactory compressionFactory = client.newCompressionFactory(); - Parser parser = client.newParser(compressionFactory.newDecompressor()); - Generator generator = client.newGenerator(compressionFactory.newCompressor()); - - AsyncConnection result; - ISession.Controller controller; if (sslContextFactory != null) { SSLEngine engine = client.newSSLEngine(sslContextFactory, channel); SslConnection sslConnection = new SslConnection(engine, endPoint); endPoint.setConnection(sslConnection); - AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint(); - AsyncSPDYConnection connection = new AsyncSPDYConnection(sslEndPoint, parser); + final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint(); + + NextProtoNego.put(engine, new NextProtoNego.ClientProvider() + { + @Override + public boolean supports() + { + return true; + } + + @Override + public String selectProtocol(List protocols) + { + AsyncConnectionFactory connectionFactory = client.selectAsyncConnectionFactory(protocols); + if (connectionFactory == null) + return null; + + AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachment); + sslEndPoint.setConnection(connection); + + return connectionFactory.getProtocol(); +// return protocols == null ? null : connectionFactory.getProtocol(); + } + }); + + AsyncConnection connection = new NoProtocolConnection(sslEndPoint); sslEndPoint.setConnection(connection); - result = sslConnection; - controller = connection; + + startHandshake(engine); + + return sslConnection; } else { - AsyncSPDYConnection connection = new AsyncSPDYConnection(endPoint, parser); + AsyncConnectionFactory connectionFactory = new ClientSPDY2AsyncConnectionFactory(); + AsyncConnection connection = connectionFactory.newAsyncConnection(channel, endPoint, attachment); endPoint.setConnection(connection); - result = connection; - controller = connection; + return connection; } + } - Session session = client.newSession(controller, sessionFuture.listener, parser, generator); - sessionFuture.connected(session); - - return result; + private void startHandshake(SSLEngine engine) + { + try + { + engine.beginHandshake(); + } + catch (SSLException x) + { + throw new RuntimeException(x); + } } } } - private class SessionFuture implements Future + private static class SessionFuture implements Future { private final CountDownLatch latch = new CountDownLatch(1); private final SPDYClient client; @@ -330,4 +338,32 @@ public class SPDYClient latch.countDown(); } } + + private static class ClientSPDY2AsyncConnectionFactory implements AsyncConnectionFactory + { + @Override + public String getProtocol() + { + return "spdy/2"; + } + + @Override + public AsyncConnection newAsyncConnection(SocketChannel channel, AsyncEndPoint endPoint, Object attachment) + { + SessionFuture sessionFuture = (SessionFuture)attachment; + + CompressionFactory compressionFactory = new StandardCompressionFactory(); + Parser parser = new Parser(compressionFactory.newDecompressor()); + Generator generator = new Generator(compressionFactory.newCompressor()); + + AsyncSPDYConnection connection = new AsyncSPDYConnection(endPoint, parser); + endPoint.setConnection(connection); + + StandardSession session = new StandardSession(connection, 1, sessionFuture.listener, generator); + parser.addListener(session); + sessionFuture.connected(session); + + return connection; + } + } } diff --git a/src/main/java/org/eclipse/jetty/spdy/nio/SPDYServerConnector.java b/src/main/java/org/eclipse/jetty/spdy/nio/SPDYServerConnector.java index 73dc9964659..2085d31f079 100644 --- a/src/main/java/org/eclipse/jetty/spdy/nio/SPDYServerConnector.java +++ b/src/main/java/org/eclipse/jetty/spdy/nio/SPDYServerConnector.java @@ -2,7 +2,6 @@ package org.eclipse.jetty.spdy.nio; import java.nio.channels.SocketChannel; import javax.net.ssl.SSLEngine; -import javax.net.ssl.SSLException; import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.nio.AsyncConnection; @@ -66,7 +65,7 @@ public class SPDYServerConnector extends SelectChannelConnector Session session = newSession(controller, listener, parser, generator); - // TODO: this is called in the selector thread, which is not good + // TODO: this is called in the selector thread, which is not optimal // NPE guard to support tests if (listener != null) listener.onConnect(session); @@ -76,19 +75,11 @@ public class SPDYServerConnector extends SelectChannelConnector protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel) { - try - { - String peerHost = channel.socket().getInetAddress().getHostAddress(); - int peerPort = channel.socket().getPort(); - SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort); - engine.setUseClientMode(false); - engine.beginHandshake(); - return engine; - } - catch (SSLException x) - { - throw new RuntimeException(x); - } + String peerHost = channel.socket().getInetAddress().getHostAddress(); + int peerPort = channel.socket().getPort(); + SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort); + engine.setUseClientMode(false); + return engine; } protected CompressionFactory newCompressionFactory() diff --git a/src/test/java/org/eclipse/jetty/spdy/SPDYTest.java b/src/test/java/org/eclipse/jetty/spdy/SPDYTest.java index 9fe01409de6..dde31dcb5c8 100644 --- a/src/test/java/org/eclipse/jetty/spdy/SPDYTest.java +++ b/src/test/java/org/eclipse/jetty/spdy/SPDYTest.java @@ -22,6 +22,7 @@ public abstract class SPDYTest { server = new Server(); Connector connector = newSPDYServerConnector(listener); + connector.setPort(47443); server.addConnector(connector); server.start(); return new InetSocketAddress(connector.getLocalPort()); @@ -57,6 +58,7 @@ public abstract class SPDYTest sslContextFactory.setTrustStore("src/test/resources/truststore.jks"); sslContextFactory.setTrustStorePassword("storepwd"); sslContextFactory.setProtocol("TLSv1"); + sslContextFactory.setIncludeProtocols("TLSv1"); return sslContextFactory; } diff --git a/src/test/java/org/eclipse/jetty/spdy/SSLSPDYSynReplyTest.java b/src/test/java/org/eclipse/jetty/spdy/SSLSPDYSynReplyTest.java index d453dd56ff8..43387f1a51e 100644 --- a/src/test/java/org/eclipse/jetty/spdy/SSLSPDYSynReplyTest.java +++ b/src/test/java/org/eclipse/jetty/spdy/SSLSPDYSynReplyTest.java @@ -1,11 +1,13 @@ package org.eclipse.jetty.spdy; +import org.eclipse.jetty.npn.NextProtoNego; import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; import org.eclipse.jetty.spdy.nio.SPDYClient; import org.eclipse.jetty.spdy.nio.SPDYServerConnector; import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.thread.ThreadPool; +import org.junit.Before; public class SSLSPDYSynReplyTest extends SPDYSynReplyTest { @@ -22,4 +24,10 @@ public class SSLSPDYSynReplyTest extends SPDYSynReplyTest SslContextFactory sslContextFactory = newSslContextFactory(); return new SPDYClient.Factory(threadPool, sslContextFactory); } + + @Before + public void init() + { + NextProtoNego.debug = true; + } }