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

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
* 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<String> 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<String> 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<Session>
private static class SessionFuture implements Future<Session>
{
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;
}
}
}

View File

@ -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()

View File

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

View File

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