Merged branch 'master' into 'jetty-9-alpn'.

This commit is contained in:
Simone Bordet 2014-03-20 16:34:44 +01:00
commit 3692ac17d4
12 changed files with 286 additions and 166 deletions

View File

@ -24,13 +24,17 @@
<module>spdy-example-webapp</module> <module>spdy-example-webapp</module>
</modules> </modules>
<dependencies> <profiles>
<dependency> <profile>
<groupId>org.eclipse.jetty.toolchain</groupId> <id>jdk7-npn</id>
<artifactId>jetty-test-helper</artifactId> <activation>
<scope>test</scope> <jdk>[1.7,1.8)</jdk>
</dependency> </activation>
</dependencies> <modules>
<module>spdy-npn-tests</module>
</modules>
</profile>
</profiles>
<build> <build>
<plugins> <plugins>
@ -71,16 +75,12 @@
</plugins> </plugins>
</build> </build>
<profiles> <dependencies>
<profile> <dependency>
<id>jdk7-npn</id> <groupId>org.eclipse.jetty.toolchain</groupId>
<activation> <artifactId>jetty-test-helper</artifactId>
<jdk>[1.7,1.8)</jdk> <scope>test</scope>
</activation> </dependency>
<modules> </dependencies>
<module>spdy-npn-tests</module>
</modules>
</profile>
</profiles>
</project> </project>

View File

@ -18,87 +18,28 @@
package org.eclipse.jetty.spdy.client; package org.eclipse.jetty.spdy.client;
import java.io.IOException;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.npn.NextProtoNego; import org.eclipse.jetty.npn.NextProtoNego;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.util.log.Logger;
public class NPNClientConnection extends AbstractConnection implements NextProtoNego.ClientProvider public class NPNClientConnection extends NegotiatingClientConnection implements NextProtoNego.ClientProvider
{ {
private final Logger LOG = Log.getLogger(getClass()); private static final Logger LOG = Log.getLogger(NPNClientConnection.class);
private final SPDYClient client;
private final ClientConnectionFactory connectionFactory;
private final SSLEngine engine;
private final Map<String, Object> context;
private volatile boolean completed;
public NPNClientConnection(EndPoint endPoint, SPDYClient client, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map<String, Object> context) private final String protocol;
{
super(endPoint, client.getFactory().getExecutor());
this.client = client;
this.connectionFactory = connectionFactory;
this.engine = sslEngine;
this.context = context;
NextProtoNego.put(engine, this);
}
@Override public NPNClientConnection(EndPoint endPoint, Executor executor, ClientConnectionFactory connectionFactory, SSLEngine sslEngine, Map<String, Object> context, String protocol)
public void onOpen()
{ {
super.onOpen(); super(endPoint, executor, sslEngine, connectionFactory, context);
try this.protocol = protocol;
{ NextProtoNego.put(sslEngine, this);
getEndPoint().flush(BufferUtil.EMPTY_BUFFER);
if (completed)
replaceConnection();
else
fillInterested();
}
catch(IOException e)
{
throw new RuntimeIOException(e);
}
}
@Override
public void onFillable()
{
while (true)
{
int filled = fill();
if (filled == 0 && !completed)
fillInterested();
if (filled <= 0 || completed)
break;
}
if (completed)
replaceConnection();
}
private int fill()
{
try
{
return getEndPoint().fill(BufferUtil.EMPTY_BUFFER);
}
catch (IOException x)
{
LOG.debug(x);
NextProtoNego.remove(engine);
close();
return -1;
}
} }
@Override @Override
@ -110,31 +51,31 @@ public class NPNClientConnection extends AbstractConnection implements NextProto
@Override @Override
public void unsupported() public void unsupported()
{ {
NextProtoNego.remove(engine); NextProtoNego.remove(getSSLEngine());
completed = true; completed();
} }
@Override @Override
public String selectProtocol(List<String> protocols) public String selectProtocol(List<String> protocols)
{ {
NextProtoNego.remove(engine); if (protocols.contains(protocol))
completed = true; {
return client.selectProtocol(protocols); NextProtoNego.remove(getSSLEngine());
completed();
return protocol;
}
else
{
LOG.info("Could not negotiate protocol: server {} - client {}", protocols, protocol);
close();
return null;
}
} }
private void replaceConnection() @Override
public void close()
{ {
EndPoint endPoint = getEndPoint(); NextProtoNego.remove(getSSLEngine());
try super.close();
{
Connection oldConnection = endPoint.getConnection();
Connection newConnection = connectionFactory.newConnection(endPoint, context);
ClientConnectionFactory.Helper.replaceConnection(oldConnection, newConnection);
}
catch (Throwable x)
{
LOG.debug(x);
close();
}
} }
} }

View File

@ -20,7 +20,7 @@ package org.eclipse.jetty.spdy.client;
import java.io.IOException; import java.io.IOException;
import java.util.Map; import java.util.Map;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine; import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.ClientConnectionFactory; import org.eclipse.jetty.io.ClientConnectionFactory;
@ -28,21 +28,22 @@ import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint; import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.ssl.SslClientConnectionFactory; import org.eclipse.jetty.io.ssl.SslClientConnectionFactory;
public class NPNClientConnectionFactory implements ClientConnectionFactory public class NPNClientConnectionFactory extends NegotiatingClientConnectionFactory
{ {
private final SPDYClient client; private final Executor executor;
private final ClientConnectionFactory connectionFactory; private final String protocol;
public NPNClientConnectionFactory(SPDYClient client, ClientConnectionFactory connectionFactory) public NPNClientConnectionFactory(Executor executor, ClientConnectionFactory connectionFactory, String protocol)
{ {
this.client = client; super(connectionFactory);
this.connectionFactory = connectionFactory; this.executor = executor;
this.protocol = protocol;
} }
@Override @Override
public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException public Connection newConnection(EndPoint endPoint, Map<String, Object> context) throws IOException
{ {
return new NPNClientConnection(endPoint, client, connectionFactory, return new NPNClientConnection(endPoint, executor, getClientConnectionFactory(),
(SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context); (SSLEngine)context.get(SslClientConnectionFactory.SSL_ENGINE_CONTEXT_KEY), context, protocol);
} }
} }

View File

@ -0,0 +1,133 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.spdy.client;
import java.io.IOException;
import java.util.Map;
import java.util.concurrent.Executor;
import javax.net.ssl.SSLEngine;
import org.eclipse.jetty.io.AbstractConnection;
import org.eclipse.jetty.io.ClientConnectionFactory;
import org.eclipse.jetty.io.Connection;
import org.eclipse.jetty.io.EndPoint;
import org.eclipse.jetty.io.RuntimeIOException;
import org.eclipse.jetty.util.BufferUtil;
import org.eclipse.jetty.util.log.Log;
import org.eclipse.jetty.util.log.Logger;
public abstract class NegotiatingClientConnection extends AbstractConnection
{
private static final Logger LOG = Log.getLogger(NegotiatingClientConnection.class);
private final SSLEngine engine;
private final ClientConnectionFactory connectionFactory;
private final Map<String, Object> context;
private volatile boolean completed;
protected NegotiatingClientConnection(EndPoint endp, Executor executor, SSLEngine sslEngine, ClientConnectionFactory connectionFactory, Map<String, Object> context)
{
super(endp, executor);
this.engine = sslEngine;
this.connectionFactory = connectionFactory;
this.context = context;
}
protected SSLEngine getSSLEngine()
{
return engine;
}
protected void completed()
{
completed = true;
}
@Override
public void onOpen()
{
super.onOpen();
try
{
getEndPoint().flush(BufferUtil.EMPTY_BUFFER);
if (completed)
replaceConnection();
else
fillInterested();
}
catch (IOException x)
{
close();
throw new RuntimeIOException(x);
}
}
@Override
public void onFillable()
{
while (true)
{
int filled = fill();
if (filled == 0 && !completed)
fillInterested();
if (filled <= 0 || completed)
break;
}
if (completed)
replaceConnection();
}
private int fill()
{
try
{
return getEndPoint().fill(BufferUtil.EMPTY_BUFFER);
}
catch (IOException x)
{
LOG.debug(x);
close();
return -1;
}
}
private void replaceConnection()
{
EndPoint endPoint = getEndPoint();
try
{
Connection oldConnection = endPoint.getConnection();
Connection newConnection = connectionFactory.newConnection(endPoint, context);
ClientConnectionFactory.Helper.replaceConnection(oldConnection, newConnection);
}
catch (Throwable x)
{
LOG.debug(x);
close();
}
}
@Override
public void close()
{
// Gentler close for SSL.
getEndPoint().shutdownOutput();
super.close();
}
}

View File

@ -0,0 +1,36 @@
//
// ========================================================================
// Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
// ------------------------------------------------------------------------
// All rights reserved. This program and the accompanying materials
// are made available under the terms of the Eclipse Public License v1.0
// and Apache License v2.0 which accompanies this distribution.
//
// The Eclipse Public License is available at
// http://www.eclipse.org/legal/epl-v10.html
//
// The Apache License v2.0 is available at
// http://www.opensource.org/licenses/apache2.0.php
//
// You may elect to redistribute this code under either of these licenses.
// ========================================================================
//
package org.eclipse.jetty.spdy.client;
import org.eclipse.jetty.io.ClientConnectionFactory;
public abstract class NegotiatingClientConnectionFactory implements ClientConnectionFactory
{
private final ClientConnectionFactory connectionFactory;
protected NegotiatingClientConnectionFactory(ClientConnectionFactory connectionFactory)
{
this.connectionFactory = connectionFactory;
}
public ClientConnectionFactory getClientConnectionFactory()
{
return connectionFactory;
}
}

View File

@ -26,7 +26,6 @@ import java.nio.channels.SocketChannel;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Queue; import java.util.Queue;
import java.util.concurrent.ConcurrentLinkedQueue; import java.util.concurrent.ConcurrentLinkedQueue;
@ -86,10 +85,6 @@ public class SPDYClient
this.factory = factory; this.factory = factory;
setInitialWindowSize(65536); setInitialWindowSize(65536);
setDispatchIO(true); setDispatchIO(true);
ClientConnectionFactory connectionFactory = new SPDYClientConnectionFactory();
if (factory.sslContextFactory != null)
connectionFactory = new SslClientConnectionFactory(factory.getSslContextFactory(), factory.getByteBufferPool(), factory.getExecutor(), new NPNClientConnectionFactory(this, connectionFactory));
setClientConnectionFactory(connectionFactory);
} }
public short getVersion() public short getVersion()
@ -239,17 +234,6 @@ public class SPDYClient
this.connectionFactory = connectionFactory; this.connectionFactory = connectionFactory;
} }
protected String selectProtocol(List<String> serverProtocols)
{
String protocol = "spdy/" + version;
for (String serverProtocol : serverProtocols)
{
if (serverProtocol.equals(protocol))
return protocol;
}
return null;
}
protected FlowControlStrategy newFlowControlStrategy() protected FlowControlStrategy newFlowControlStrategy()
{ {
return FlowControlStrategyFactory.newFlowControlStrategy(version); return FlowControlStrategyFactory.newFlowControlStrategy(version);
@ -357,7 +341,19 @@ public class SPDYClient
public SPDYClient newSPDYClient(short version) public SPDYClient newSPDYClient(short version)
{ {
return new SPDYClient(version, this); return newSPDYClient(version, new NPNClientConnectionFactory(getExecutor(), new SPDYClientConnectionFactory(), "spdy/" + version));
}
public SPDYClient newSPDYClient(short version, NegotiatingClientConnectionFactory negotiatingFactory)
{
SPDYClient client = new SPDYClient(version, this);
ClientConnectionFactory connectionFactory = negotiatingFactory.getClientConnectionFactory();
if (sslContextFactory != null)
connectionFactory = new SslClientConnectionFactory(getSslContextFactory(), getByteBufferPool(), getExecutor(), negotiatingFactory);
client.setClientConnectionFactory(connectionFactory);
return client;
} }
@Override @Override

View File

@ -77,16 +77,15 @@ import static org.junit.Assert.assertThat;
@RunWith(value = Parameterized.class) @RunWith(value = Parameterized.class)
public abstract class ProxySPDYToHTTPTest public abstract class ProxySPDYToHTTPTest
{ {
@Rule
public final TestTracker tracker = new TestTracker();
private final short version;
@Parameterized.Parameters @Parameterized.Parameters
public static Collection<Short[]> parameters() public static Collection<Short[]> parameters()
{ {
return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3}); return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
} }
@Rule
public final TestTracker tracker = new TestTracker();
private final short version;
private SPDYClient.Factory factory; private SPDYClient.Factory factory;
private Server server; private Server server;
private Server proxy; private Server proxy;

View File

@ -67,16 +67,15 @@ import static org.junit.Assert.assertThat;
@RunWith(value = Parameterized.class) @RunWith(value = Parameterized.class)
public abstract class ProxySPDYToSPDYLoadTest public abstract class ProxySPDYToSPDYLoadTest
{ {
@Rule
public final TestTracker tracker = new TestTracker();
private final short version;
@Parameterized.Parameters @Parameterized.Parameters
public static Collection<Short[]> parameters() public static Collection<Short[]> parameters()
{ {
return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3}); return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
} }
@Rule
public final TestTracker tracker = new TestTracker();
private final short version;
private static final String UUID_HEADER_NAME = "uuidHeader"; private static final String UUID_HEADER_NAME = "uuidHeader";
private static final String SERVER_ID_HEADER = "serverId"; private static final String SERVER_ID_HEADER = "serverId";
private SPDYClient.Factory factory; private SPDYClient.Factory factory;

View File

@ -68,16 +68,15 @@ import static org.junit.Assert.assertThat;
@RunWith(value = Parameterized.class) @RunWith(value = Parameterized.class)
public abstract class ProxySPDYToSPDYTest public abstract class ProxySPDYToSPDYTest
{ {
@Rule
public final TestTracker tracker = new TestTracker();
private final short version;
@Parameterized.Parameters @Parameterized.Parameters
public static Collection<Short[]> parameters() public static Collection<Short[]> parameters()
{ {
return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3}); return Arrays.asList(new Short[]{SPDY.V2}, new Short[]{SPDY.V3});
} }
@Rule
public final TestTracker tracker = new TestTracker();
private final short version;
private SPDYClient.Factory factory; private SPDYClient.Factory factory;
private Server server; private Server server;
private Server proxy; private Server proxy;

View File

@ -42,7 +42,7 @@ public abstract class NegotiatingServerConnection extends AbstractConnection
private final String defaultProtocol; private final String defaultProtocol;
private String protocol; // No need to be volatile: it is modified and read by the same thread private String protocol; // No need to be volatile: it is modified and read by the same thread
public NegotiatingServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol) protected NegotiatingServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
{ {
super(endPoint, connector.getExecutor()); super(endPoint, connector.getExecutor());
this.connector = connector; this.connector = connector;

View File

@ -18,7 +18,7 @@
package org.eclipse.jetty.xml; package org.eclipse.jetty.xml;
import java.util.HashMap; import java.util.LinkedHashMap;
import java.util.Map; import java.util.Map;
import org.junit.Assert; import org.junit.Assert;
@ -31,7 +31,7 @@ public class XmlAppendableTest
{ {
StringBuilder b = new StringBuilder(); StringBuilder b = new StringBuilder();
XmlAppendable out = new XmlAppendable(b); XmlAppendable out = new XmlAppendable(b);
Map<String,String> attr = new HashMap<>(); Map<String, String> attr = new LinkedHashMap<>();
out.open("test"); out.open("test");
@ -48,7 +48,23 @@ public class XmlAppendableTest
out.close(); out.close();
String s = b.toString(); String expected = "" +
Assert.assertEquals("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<test>\n <tag/>\n <tag quotes=\"&apos;&quot;\" name=\"attr value\" noval=\"\"/>\n <tag quotes=\"&apos;&quot;\" name=\"attr value\" noval=\"\">content</tag>\n <level1>\n <tag>content</tag>\n <tag>content</tag>\n </level1>\n <level1 quotes=\"&apos;&quot;\" name=\"attr value\" noval=\"\">\n <level2>\n <tag>content</tag>\n <tag>content</tag>\n </level2>\n </level1>\n</test>\n",s); "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" +
"<test>\n" +
" <tag/>\n" +
" <tag name=\"attr value\" noval=\"\" quotes=\"&apos;&quot;\"/>\n" +
" <tag name=\"attr value\" noval=\"\" quotes=\"&apos;&quot;\">content</tag>\n" +
" <level1>\n" +
" <tag>content</tag>\n" +
" <tag>content</tag>\n" +
" </level1>\n" +
" <level1 name=\"attr value\" noval=\"\" quotes=\"&apos;&quot;\">\n" +
" <level2>\n" +
" <tag>content</tag>\n" +
" <tag>content</tag>\n" +
" </level2>\n" +
" </level1>\n" +
"</test>\n";
Assert.assertEquals(expected, b.toString());
} }
} }