Introduced NPN support in the client.

This commit is contained in:
Simone Bordet 2012-01-30 16:23:59 +01:00
parent 000b635a12
commit 233c32e9f3
8 changed files with 192 additions and 75 deletions

16
pom.xml
View File

@ -13,6 +13,7 @@
<properties> <properties>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<jetty.version>7.6.0.RC5</jetty.version> <jetty.version>7.6.0.RC5</jetty.version>
<jetty.npn.version>1.0.0-SNAPSHOT</jetty.npn.version>
</properties> </properties>
<build> <build>
@ -44,6 +45,16 @@
<target>1.7</target> <target>1.7</target>
</configuration> </configuration>
</plugin> </plugin>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<version>2.9</version>
<configuration>
<argLine>
-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
</argLine>
</configuration>
</plugin>
</plugins> </plugins>
</build> </build>
@ -53,6 +64,11 @@
<artifactId>jetty-server</artifactId> <artifactId>jetty-server</artifactId>
<version>${jetty.version}</version> <version>${jetty.version}</version>
</dependency> </dependency>
<dependency>
<groupId>org.eclipse.jetty</groupId>
<artifactId>jetty-npn-boot</artifactId>
<version>${jetty.npn.version}</version>
</dependency>
<dependency> <dependency>
<groupId>org.slf4j</groupId> <groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId> <artifactId>slf4j-api</artifactId>

View File

@ -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);
}

View File

@ -19,8 +19,8 @@ import org.eclipse.jetty.spdy.parser.Parser;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; 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 static final Logger logger = LoggerFactory.getLogger(AsyncSPDYConnection.class);
private final Parser parser; private final Parser parser;
private ByteBuffer buffer; private ByteBuffer buffer;

View File

@ -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
}
}

View File

@ -2,6 +2,7 @@
* To change this template, choose Tools | Templates * To change this template, choose Tools | Templates
* and open the template in the editor. * and open the template in the editor.
*/ */
package org.eclipse.jetty.spdy.nio; package org.eclipse.jetty.spdy.nio;
import java.io.IOException; import java.io.IOException;
@ -9,6 +10,7 @@ import java.net.InetSocketAddress;
import java.net.SocketAddress; import java.net.SocketAddress;
import java.nio.channels.SelectionKey; import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import java.util.List;
import java.util.concurrent.CountDownLatch; import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutionException; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future; 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.SelectChannelEndPoint;
import org.eclipse.jetty.io.nio.SelectorManager; import org.eclipse.jetty.io.nio.SelectorManager;
import org.eclipse.jetty.io.nio.SslConnection; 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;
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.StandardCompressionFactory;
import org.eclipse.jetty.spdy.StandardSession; import org.eclipse.jetty.spdy.StandardSession;
import org.eclipse.jetty.spdy.api.Session; import org.eclipse.jetty.spdy.api.Session;
@ -97,44 +97,25 @@ public class SPDYClient
this.maxIdleTime = maxIdleTime; this.maxIdleTime = maxIdleTime;
} }
protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel) protected AsyncConnectionFactory selectAsyncConnectionFactory(List<String> serverProtocols)
{ {
try 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)
{ {
String peerHost = channel.socket().getInetAddress().getHostAddress(); String peerHost = channel.socket().getInetAddress().getHostAddress();
int peerPort = channel.socket().getPort(); int peerPort = channel.socket().getPort();
SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort); SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort);
engine.setUseClientMode(true); engine.setUseClientMode(true);
engine.beginHandshake();
return engine; 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;
}
public static class Factory extends AggregateLifeCycle public static class Factory extends AggregateLifeCycle
{ {
@ -223,45 +204,72 @@ public class SPDYClient
} }
@Override @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; 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) if (sslContextFactory != null)
{ {
SSLEngine engine = client.newSSLEngine(sslContextFactory, channel); SSLEngine engine = client.newSSLEngine(sslContextFactory, channel);
SslConnection sslConnection = new SslConnection(engine, endPoint); SslConnection sslConnection = new SslConnection(engine, endPoint);
endPoint.setConnection(sslConnection); endPoint.setConnection(sslConnection);
AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint(); final AsyncEndPoint sslEndPoint = sslConnection.getSslEndPoint();
AsyncSPDYConnection connection = new AsyncSPDYConnection(sslEndPoint, parser);
NextProtoNego.put(engine, new NextProtoNego.ClientProvider()
{
@Override
public boolean supports()
{
return true;
}
@Override
public String selectProtocol(List<String> protocols)
{
AsyncConnectionFactory connectionFactory = client.selectAsyncConnectionFactory(protocols);
if (connectionFactory == null)
return null;
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, sslEndPoint, attachment);
sslEndPoint.setConnection(connection); sslEndPoint.setConnection(connection);
result = sslConnection;
controller = connection; return connectionFactory.getProtocol();
// return protocols == null ? null : connectionFactory.getProtocol();
}
});
AsyncConnection connection = new NoProtocolConnection(sslEndPoint);
sslEndPoint.setConnection(connection);
startHandshake(engine);
return sslConnection;
} }
else else
{ {
AsyncSPDYConnection connection = new AsyncSPDYConnection(endPoint, parser); AsyncConnectionFactory connectionFactory = new ClientSPDY2AsyncConnectionFactory();
AsyncConnection connection = connectionFactory.newAsyncConnection(channel, endPoint, attachment);
endPoint.setConnection(connection); endPoint.setConnection(connection);
result = connection; return connection;
controller = connection; }
} }
Session session = client.newSession(controller, sessionFuture.listener, parser, generator); private void startHandshake(SSLEngine engine)
sessionFuture.connected(session); {
try
return result; {
engine.beginHandshake();
}
catch (SSLException x)
{
throw new RuntimeException(x);
}
} }
} }
} }
private class SessionFuture implements Future<Session> private static class SessionFuture implements Future<Session>
{ {
private final CountDownLatch latch = new CountDownLatch(1); private final CountDownLatch latch = new CountDownLatch(1);
private final SPDYClient client; private final SPDYClient client;
@ -330,4 +338,32 @@ public class SPDYClient
latch.countDown(); 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;
}
}
} }

View File

@ -2,7 +2,6 @@ package org.eclipse.jetty.spdy.nio;
import java.nio.channels.SocketChannel; import java.nio.channels.SocketChannel;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLException;
import org.eclipse.jetty.io.AsyncEndPoint; import org.eclipse.jetty.io.AsyncEndPoint;
import org.eclipse.jetty.io.nio.AsyncConnection; import org.eclipse.jetty.io.nio.AsyncConnection;
@ -66,7 +65,7 @@ public class SPDYServerConnector extends SelectChannelConnector
Session session = newSession(controller, listener, parser, generator); 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 // NPE guard to support tests
if (listener != null) if (listener != null)
listener.onConnect(session); listener.onConnect(session);
@ -75,21 +74,13 @@ public class SPDYServerConnector extends SelectChannelConnector
} }
protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel) protected SSLEngine newSSLEngine(SslContextFactory sslContextFactory, SocketChannel channel)
{
try
{ {
String peerHost = channel.socket().getInetAddress().getHostAddress(); String peerHost = channel.socket().getInetAddress().getHostAddress();
int peerPort = channel.socket().getPort(); int peerPort = channel.socket().getPort();
SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort); SSLEngine engine = sslContextFactory.newSslEngine(peerHost, peerPort);
engine.setUseClientMode(false); engine.setUseClientMode(false);
engine.beginHandshake();
return engine; return engine;
} }
catch (SSLException x)
{
throw new RuntimeException(x);
}
}
protected CompressionFactory newCompressionFactory() protected CompressionFactory newCompressionFactory()
{ {

View File

@ -22,6 +22,7 @@ public abstract class SPDYTest
{ {
server = new Server(); server = new Server();
Connector connector = newSPDYServerConnector(listener); Connector connector = newSPDYServerConnector(listener);
connector.setPort(47443);
server.addConnector(connector); server.addConnector(connector);
server.start(); server.start();
return new InetSocketAddress(connector.getLocalPort()); return new InetSocketAddress(connector.getLocalPort());
@ -57,6 +58,7 @@ public abstract class SPDYTest
sslContextFactory.setTrustStore("src/test/resources/truststore.jks"); sslContextFactory.setTrustStore("src/test/resources/truststore.jks");
sslContextFactory.setTrustStorePassword("storepwd"); sslContextFactory.setTrustStorePassword("storepwd");
sslContextFactory.setProtocol("TLSv1"); sslContextFactory.setProtocol("TLSv1");
sslContextFactory.setIncludeProtocols("TLSv1");
return sslContextFactory; return sslContextFactory;
} }

View File

@ -1,11 +1,13 @@
package org.eclipse.jetty.spdy; package org.eclipse.jetty.spdy;
import org.eclipse.jetty.npn.NextProtoNego;
import org.eclipse.jetty.server.Connector; import org.eclipse.jetty.server.Connector;
import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener; import org.eclipse.jetty.spdy.api.server.ServerSessionFrameListener;
import org.eclipse.jetty.spdy.nio.SPDYClient; import org.eclipse.jetty.spdy.nio.SPDYClient;
import org.eclipse.jetty.spdy.nio.SPDYServerConnector; import org.eclipse.jetty.spdy.nio.SPDYServerConnector;
import org.eclipse.jetty.util.ssl.SslContextFactory; import org.eclipse.jetty.util.ssl.SslContextFactory;
import org.eclipse.jetty.util.thread.ThreadPool; import org.eclipse.jetty.util.thread.ThreadPool;
import org.junit.Before;
public class SSLSPDYSynReplyTest extends SPDYSynReplyTest public class SSLSPDYSynReplyTest extends SPDYSynReplyTest
{ {
@ -22,4 +24,10 @@ public class SSLSPDYSynReplyTest extends SPDYSynReplyTest
SslContextFactory sslContextFactory = newSslContextFactory(); SslContextFactory sslContextFactory = newSslContextFactory();
return new SPDYClient.Factory(threadPool, sslContextFactory); return new SPDYClient.Factory(threadPool, sslContextFactory);
} }
@Before
public void init()
{
NextProtoNego.debug = true;
}
} }