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;
+ }
}