Introduced NPN support in the client.
This commit is contained in:
parent
000b635a12
commit
233c32e9f3
16
pom.xml
16
pom.xml
|
@ -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>
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
|
@ -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;
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
{
|
{
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue