Implemented ALPNServerConnection and refactored common code with NPN.
This commit is contained in:
parent
119718d86f
commit
cf1c2451d6
|
@ -72,7 +72,7 @@ public class SpdyConnector
|
||||||
new HTTPSPDYServerConnectionFactory(3,https_config,new ReferrerPushStrategy());
|
new HTTPSPDYServerConnectionFactory(3,https_config,new ReferrerPushStrategy());
|
||||||
|
|
||||||
// NPN Factory
|
// NPN Factory
|
||||||
SPDYServerConnectionFactory.checkNPNAvailable();
|
SPDYServerConnectionFactory.checkProtocolNegotiationAvailable();
|
||||||
NPNServerConnectionFactory npn =
|
NPNServerConnectionFactory npn =
|
||||||
new NPNServerConnectionFactory(spdy3.getProtocol(),spdy2.getProtocol(),http.getDefaultProtocol());
|
new NPNServerConnectionFactory(spdy3.getProtocol(),spdy2.getProtocol(),http.getDefaultProtocol());
|
||||||
npn.setDefaultProtocol(http.getDefaultProtocol());
|
npn.setDefaultProtocol(http.getDefaultProtocol());
|
||||||
|
|
|
@ -110,7 +110,7 @@ public class SpdyServer
|
||||||
// Spdy Connector
|
// Spdy Connector
|
||||||
|
|
||||||
// Make sure that the required NPN implementations are available.
|
// Make sure that the required NPN implementations are available.
|
||||||
SPDYServerConnectionFactory.checkNPNAvailable();
|
SPDYServerConnectionFactory.checkProtocolNegotiationAvailable();
|
||||||
|
|
||||||
// A ReferrerPushStrategy is being initialized.
|
// A ReferrerPushStrategy is being initialized.
|
||||||
// See: http://www.eclipse.org/jetty/documentation/current/spdy-configuring-push.html for more details.
|
// See: http://www.eclipse.org/jetty/documentation/current/spdy-configuring-push.html for more details.
|
||||||
|
|
|
@ -91,6 +91,12 @@
|
||||||
<version>${npn.api.version}</version>
|
<version>${npn.api.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.eclipse.jetty.alpn</groupId>
|
||||||
|
<artifactId>alpn-api</artifactId>
|
||||||
|
<version>${alpn.api.version}</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
|
|
||||||
</project>
|
</project>
|
||||||
|
|
|
@ -0,0 +1,76 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.server;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.alpn.ALPN;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
public class ALPNServerConnection extends NegotiatingServerConnection implements ALPN.ServerProvider
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(ALPNServerConnection.class);
|
||||||
|
|
||||||
|
public ALPNServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
|
||||||
|
{
|
||||||
|
super(connector, endPoint, engine, protocols, defaultProtocol);
|
||||||
|
ALPN.put(engine, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void unsupported()
|
||||||
|
{
|
||||||
|
select(Collections.<String>emptyList());
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String select(List<String> clientProtocols)
|
||||||
|
{
|
||||||
|
List<String> serverProtocols = getProtocols();
|
||||||
|
String negotiated = null;
|
||||||
|
for (String clientProtocol : clientProtocols)
|
||||||
|
{
|
||||||
|
if (serverProtocols.contains(clientProtocol))
|
||||||
|
{
|
||||||
|
negotiated = clientProtocol;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (negotiated == null)
|
||||||
|
{
|
||||||
|
negotiated = getDefaultProtocol();
|
||||||
|
}
|
||||||
|
LOG.debug("{} protocol selected {}", this, negotiated);
|
||||||
|
setProtocol(negotiated);
|
||||||
|
ALPN.remove(getSSLEngine());
|
||||||
|
return negotiated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
ALPN.remove(getSSLEngine());
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,60 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.server;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.alpn.ALPN;
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.util.annotation.Name;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
public class ALPNServerConnectionFactory extends NegotiatingServerConnectionFactory
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(ALPNServerConnectionFactory.class);
|
||||||
|
|
||||||
|
public ALPNServerConnectionFactory(@Name("protocols") String... protocols)
|
||||||
|
{
|
||||||
|
super("alpn", protocols);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ClassLoader alpnClassLoader = ALPN.class.getClassLoader();
|
||||||
|
if (alpnClassLoader != null)
|
||||||
|
{
|
||||||
|
LOG.warn("ALPN must be in the boot classloader, not in: " + alpnClassLoader);
|
||||||
|
throw new IllegalStateException("ALPN must be in the boot classloader");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Throwable x)
|
||||||
|
{
|
||||||
|
LOG.warn("ALPN not available", x);
|
||||||
|
throw new IllegalStateException("ALPN not available", x);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
|
||||||
|
{
|
||||||
|
return new ALPNServerConnection(connector, endPoint, engine, protocols, defaultProtocol);
|
||||||
|
}
|
||||||
|
}
|
|
@ -18,146 +18,49 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.spdy.server;
|
package org.eclipse.jetty.spdy.server;
|
||||||
|
|
||||||
import java.io.IOException;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
import javax.net.ssl.SSLEngineResult;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.io.AbstractConnection;
|
|
||||||
import org.eclipse.jetty.io.Connection;
|
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.npn.NextProtoNego;
|
import org.eclipse.jetty.npn.NextProtoNego;
|
||||||
import org.eclipse.jetty.server.ConnectionFactory;
|
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
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 NPNServerConnection extends AbstractConnection implements NextProtoNego.ServerProvider
|
public class NPNServerConnection extends NegotiatingServerConnection implements NextProtoNego.ServerProvider
|
||||||
{
|
{
|
||||||
private final Logger LOG = Log.getLogger(getClass());
|
private static final Logger LOG = Log.getLogger(NPNServerConnection.class);
|
||||||
private final Connector connector;
|
|
||||||
private final SSLEngine engine;
|
|
||||||
private final List<String> protocols;
|
|
||||||
private final String defaultProtocol;
|
|
||||||
private String nextProtocol; // No need to be volatile: it is modified and read by the same thread
|
|
||||||
|
|
||||||
public NPNServerConnection(EndPoint endPoint, SSLEngine engine, Connector connector, List<String> protocols, String defaultProtocol)
|
public NPNServerConnection(EndPoint endPoint, SSLEngine engine, Connector connector, List<String> protocols, String defaultProtocol)
|
||||||
{
|
{
|
||||||
super(endPoint, connector.getExecutor());
|
super(connector, endPoint, engine, protocols, defaultProtocol);
|
||||||
this.connector = connector;
|
|
||||||
this.protocols = protocols;
|
|
||||||
this.defaultProtocol = defaultProtocol;
|
|
||||||
this.engine = engine;
|
|
||||||
NextProtoNego.put(engine, this);
|
NextProtoNego.put(engine, this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onOpen()
|
|
||||||
{
|
|
||||||
super.onOpen();
|
|
||||||
fillInterested();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void onFillable()
|
|
||||||
{
|
|
||||||
int filled = fill();
|
|
||||||
|
|
||||||
if (filled == 0)
|
|
||||||
{
|
|
||||||
if (nextProtocol == null)
|
|
||||||
{
|
|
||||||
if (engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
|
|
||||||
{
|
|
||||||
// Here the SSL handshake is finished, but while the client sent
|
|
||||||
// the NPN extension, the server application did not select the
|
|
||||||
// protocol; we need to close as the protocol cannot be negotiated.
|
|
||||||
LOG.debug("{} missing next protocol. SSLEngine: {}", this, engine);
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Here the SSL handshake is not finished yet but we filled 0 bytes,
|
|
||||||
// so we need to read more.
|
|
||||||
fillInterested();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ConnectionFactory connectionFactory = connector.getConnectionFactory(nextProtocol);
|
|
||||||
if (connectionFactory == null)
|
|
||||||
{
|
|
||||||
LOG.debug("{} application selected protocol '{}', but no correspondent {} has been configured",
|
|
||||||
this, nextProtocol, ConnectionFactory.class.getName());
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
EndPoint endPoint = getEndPoint();
|
|
||||||
Connection oldConnection = endPoint.getConnection();
|
|
||||||
Connection newConnection = connectionFactory.newConnection(connector, endPoint);
|
|
||||||
LOG.debug("{} switching from {} to {}", this, oldConnection, newConnection);
|
|
||||||
oldConnection.onClose();
|
|
||||||
endPoint.setConnection(newConnection);
|
|
||||||
getEndPoint().getConnection().onOpen();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (filled < 0)
|
|
||||||
{
|
|
||||||
// Something went bad, we need to close.
|
|
||||||
LOG.debug("{} closing on client close", this);
|
|
||||||
close();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Must never happen, since we fill using an empty buffer
|
|
||||||
throw new IllegalStateException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private int fill()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return getEndPoint().fill(BufferUtil.EMPTY_BUFFER);
|
|
||||||
}
|
|
||||||
catch (IOException x)
|
|
||||||
{
|
|
||||||
LOG.debug(x);
|
|
||||||
close();
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void unsupported()
|
public void unsupported()
|
||||||
{
|
{
|
||||||
protocolSelected(defaultProtocol);
|
protocolSelected(getDefaultProtocol());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public List<String> protocols()
|
public List<String> protocols()
|
||||||
{
|
{
|
||||||
return protocols;
|
return getProtocols();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void protocolSelected(String protocol)
|
public void protocolSelected(String protocol)
|
||||||
{
|
{
|
||||||
LOG.debug("{} protocol selected {}", this, protocol);
|
LOG.debug("{} protocol selected {}", this, protocol);
|
||||||
nextProtocol = protocol != null ? protocol : defaultProtocol;
|
setProtocol(protocol != null ? protocol : getDefaultProtocol());
|
||||||
NextProtoNego.remove(engine);
|
NextProtoNego.remove(getSSLEngine());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close()
|
public void close()
|
||||||
{
|
{
|
||||||
NextProtoNego.remove(engine);
|
NextProtoNego.remove(getSSLEngine());
|
||||||
EndPoint endPoint = getEndPoint();
|
super.close();
|
||||||
endPoint.shutdownOutput();
|
|
||||||
endPoint.close();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,106 +16,45 @@
|
||||||
// ========================================================================
|
// ========================================================================
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
package org.eclipse.jetty.spdy.server;
|
package org.eclipse.jetty.spdy.server;
|
||||||
|
|
||||||
import java.util.Arrays;
|
|
||||||
import java.util.Iterator;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
import javax.net.ssl.SSLEngine;
|
import javax.net.ssl.SSLEngine;
|
||||||
|
|
||||||
import org.eclipse.jetty.io.Connection;
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
import org.eclipse.jetty.io.EndPoint;
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
import org.eclipse.jetty.io.ssl.SslConnection;
|
|
||||||
import org.eclipse.jetty.npn.NextProtoNego;
|
import org.eclipse.jetty.npn.NextProtoNego;
|
||||||
import org.eclipse.jetty.server.AbstractConnectionFactory;
|
|
||||||
import org.eclipse.jetty.server.Connector;
|
import org.eclipse.jetty.server.Connector;
|
||||||
import org.eclipse.jetty.util.annotation.Name;
|
import org.eclipse.jetty.util.annotation.Name;
|
||||||
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 NPNServerConnectionFactory extends AbstractConnectionFactory
|
public class NPNServerConnectionFactory extends NegotiatingServerConnectionFactory
|
||||||
{
|
{
|
||||||
private static final Logger LOG = Log.getLogger(NPNServerConnectionFactory.class);
|
private static final Logger LOG = Log.getLogger(NPNServerConnectionFactory.class);
|
||||||
private final List<String> _protocols;
|
|
||||||
private String _defaultProtocol;
|
|
||||||
|
|
||||||
/* ------------------------------------------------------------ */
|
public NPNServerConnectionFactory(@Name("protocols") String... protocols)
|
||||||
/**
|
|
||||||
* @param protocols List of supported protocols in priority order
|
|
||||||
*/
|
|
||||||
public NPNServerConnectionFactory(@Name("protocols")String... protocols)
|
|
||||||
{
|
{
|
||||||
super("npn");
|
super("npn", protocols);
|
||||||
_protocols=Arrays.asList(protocols);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
if (NextProtoNego.class.getClassLoader()!=null)
|
ClassLoader npnClassLoader = NextProtoNego.class.getClassLoader();
|
||||||
|
if (npnClassLoader != null)
|
||||||
{
|
{
|
||||||
LOG.warn("NextProtoNego not from bootloader classloader: "+NextProtoNego.class.getClassLoader());
|
LOG.warn("NPN must be in the boot classloader, not in: " + npnClassLoader);
|
||||||
throw new IllegalStateException("NextProtoNego not on bootloader");
|
throw new IllegalStateException("NPN must be in the boot classloader");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch(Throwable th)
|
catch (Throwable x)
|
||||||
{
|
{
|
||||||
LOG.warn("NextProtoNego not available: "+th);
|
LOG.warn("NPN not available: " + x);
|
||||||
throw new IllegalStateException("NextProtoNego not available",th);
|
throw new IllegalStateException("NPN not available", x);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public String getDefaultProtocol()
|
|
||||||
{
|
|
||||||
return _defaultProtocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void setDefaultProtocol(String defaultProtocol)
|
|
||||||
{
|
|
||||||
_defaultProtocol = defaultProtocol;
|
|
||||||
}
|
|
||||||
|
|
||||||
public List<String> getProtocols()
|
|
||||||
{
|
|
||||||
return _protocols;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Connection newConnection(Connector connector, EndPoint endPoint)
|
protected AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol)
|
||||||
{
|
{
|
||||||
List<String> protocols=_protocols;
|
return new NPNServerConnection(endPoint, engine, connector, protocols, defaultProtocol);
|
||||||
if (protocols==null || protocols.size()==0)
|
|
||||||
{
|
|
||||||
protocols=connector.getProtocols();
|
|
||||||
for (Iterator<String> i=protocols.iterator();i.hasNext();)
|
|
||||||
{
|
|
||||||
String protocol=i.next();
|
|
||||||
if (protocol.startsWith("SSL-")||protocol.equals("NPN"))
|
|
||||||
i.remove();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
String dft=_defaultProtocol;
|
|
||||||
if (dft==null)
|
|
||||||
dft=_protocols.get(0);
|
|
||||||
|
|
||||||
SSLEngine engine=null;
|
|
||||||
EndPoint ep=endPoint;
|
|
||||||
while(engine==null && ep!=null)
|
|
||||||
{
|
|
||||||
// TODO make more generic
|
|
||||||
if (ep instanceof SslConnection.DecryptedEndPoint)
|
|
||||||
engine=((SslConnection.DecryptedEndPoint)ep).getSslConnection().getSSLEngine();
|
|
||||||
else
|
|
||||||
ep=null;
|
|
||||||
}
|
|
||||||
|
|
||||||
return configure(new NPNServerConnection(endPoint, engine, connector,protocols,_defaultProtocol),connector,endPoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public String toString()
|
|
||||||
{
|
|
||||||
return String.format("%s@%x{%s,%s,%s}",this.getClass().getSimpleName(),hashCode(),getProtocol(),getDefaultProtocol(),getProtocols());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,163 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.server;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
import javax.net.ssl.SSLEngineResult;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.server.ConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
import org.eclipse.jetty.util.BufferUtil;
|
||||||
|
import org.eclipse.jetty.util.log.Log;
|
||||||
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
|
|
||||||
|
public abstract class NegotiatingServerConnection extends AbstractConnection
|
||||||
|
{
|
||||||
|
private static final Logger LOG = Log.getLogger(NegotiatingServerConnection.class);
|
||||||
|
|
||||||
|
private final Connector connector;
|
||||||
|
private final SSLEngine engine;
|
||||||
|
private final List<String> protocols;
|
||||||
|
private final String defaultProtocol;
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
super(endPoint, connector.getExecutor());
|
||||||
|
this.connector = connector;
|
||||||
|
this.protocols = protocols;
|
||||||
|
this.defaultProtocol = defaultProtocol;
|
||||||
|
this.engine = engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected List<String> getProtocols()
|
||||||
|
{
|
||||||
|
return protocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getDefaultProtocol()
|
||||||
|
{
|
||||||
|
return defaultProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SSLEngine getSSLEngine()
|
||||||
|
{
|
||||||
|
return engine;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getProtocol()
|
||||||
|
{
|
||||||
|
return protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void setProtocol(String protocol)
|
||||||
|
{
|
||||||
|
this.protocol = protocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen()
|
||||||
|
{
|
||||||
|
super.onOpen();
|
||||||
|
fillInterested();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFillable()
|
||||||
|
{
|
||||||
|
int filled = fill();
|
||||||
|
|
||||||
|
if (filled == 0)
|
||||||
|
{
|
||||||
|
if (protocol == null)
|
||||||
|
{
|
||||||
|
if (engine.getHandshakeStatus() == SSLEngineResult.HandshakeStatus.NOT_HANDSHAKING)
|
||||||
|
{
|
||||||
|
// Here the SSL handshake is finished, but the protocol has not been negotiated.
|
||||||
|
LOG.debug("{} could not negotiate protocol, SSLEngine: {}", this, engine);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Here the SSL handshake is not finished yet but we filled 0 bytes,
|
||||||
|
// so we need to read more.
|
||||||
|
fillInterested();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ConnectionFactory connectionFactory = connector.getConnectionFactory(protocol);
|
||||||
|
if (connectionFactory == null)
|
||||||
|
{
|
||||||
|
LOG.debug("{} application selected protocol '{}', but no correspondent {} has been configured",
|
||||||
|
this, protocol, ConnectionFactory.class.getName());
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
EndPoint endPoint = getEndPoint();
|
||||||
|
Connection oldConnection = endPoint.getConnection();
|
||||||
|
Connection newConnection = connectionFactory.newConnection(connector, endPoint);
|
||||||
|
LOG.debug("{} switching from {} to {}", this, oldConnection, newConnection);
|
||||||
|
oldConnection.onClose();
|
||||||
|
endPoint.setConnection(newConnection);
|
||||||
|
getEndPoint().getConnection().onOpen();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (filled < 0)
|
||||||
|
{
|
||||||
|
// Something went bad, we need to close.
|
||||||
|
LOG.debug("{} closing on client close", this);
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Must never happen, since we fill using an empty buffer
|
||||||
|
throw new IllegalStateException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private int fill()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return getEndPoint().fill(BufferUtil.EMPTY_BUFFER);
|
||||||
|
}
|
||||||
|
catch (IOException x)
|
||||||
|
{
|
||||||
|
LOG.debug(x);
|
||||||
|
close();
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close()
|
||||||
|
{
|
||||||
|
// Gentler close for SSL.
|
||||||
|
getEndPoint().shutdownOutput();
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,103 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// 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.server;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import javax.net.ssl.SSLEngine;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.io.AbstractConnection;
|
||||||
|
import org.eclipse.jetty.io.Connection;
|
||||||
|
import org.eclipse.jetty.io.EndPoint;
|
||||||
|
import org.eclipse.jetty.io.ssl.SslConnection;
|
||||||
|
import org.eclipse.jetty.server.AbstractConnectionFactory;
|
||||||
|
import org.eclipse.jetty.server.Connector;
|
||||||
|
|
||||||
|
public abstract class NegotiatingServerConnectionFactory extends AbstractConnectionFactory
|
||||||
|
{
|
||||||
|
private final List<String> protocols;
|
||||||
|
private String defaultProtocol;
|
||||||
|
|
||||||
|
public NegotiatingServerConnectionFactory(String protocol, String... protocols)
|
||||||
|
{
|
||||||
|
super(protocol);
|
||||||
|
this.protocols = Arrays.asList(protocols);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getDefaultProtocol()
|
||||||
|
{
|
||||||
|
return defaultProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDefaultProtocol(String defaultProtocol)
|
||||||
|
{
|
||||||
|
this.defaultProtocol = defaultProtocol;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getProtocols()
|
||||||
|
{
|
||||||
|
return protocols;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Connection newConnection(Connector connector, EndPoint endPoint)
|
||||||
|
{
|
||||||
|
List<String> protocols = this.protocols;
|
||||||
|
if (protocols.isEmpty())
|
||||||
|
{
|
||||||
|
protocols = connector.getProtocols();
|
||||||
|
Iterator<String> i = protocols.iterator();
|
||||||
|
while (i.hasNext())
|
||||||
|
{
|
||||||
|
String protocol = i.next();
|
||||||
|
String prefix = "ssl-";
|
||||||
|
if (protocol.regionMatches(true, 0, prefix, 0, prefix.length()) || protocol.equalsIgnoreCase("alpn"))
|
||||||
|
{
|
||||||
|
i.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
String dft = defaultProtocol;
|
||||||
|
if (dft == null && !protocols.isEmpty())
|
||||||
|
dft = protocols.get(0);
|
||||||
|
|
||||||
|
SSLEngine engine = null;
|
||||||
|
EndPoint ep = endPoint;
|
||||||
|
while (engine == null && ep != null)
|
||||||
|
{
|
||||||
|
// TODO make more generic
|
||||||
|
if (ep instanceof SslConnection.DecryptedEndPoint)
|
||||||
|
engine = ((SslConnection.DecryptedEndPoint)ep).getSslConnection().getSSLEngine();
|
||||||
|
else
|
||||||
|
ep = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return configure(newServerConnection(connector, endPoint, engine, protocols, dft), connector, endPoint);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract AbstractConnection newServerConnection(Connector connector, EndPoint endPoint, SSLEngine engine, List<String> protocols, String defaultProtocol);
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString()
|
||||||
|
{
|
||||||
|
return String.format("%s@%x{%s,%s,%s}", getClass().getSimpleName(), hashCode(), getProtocol(), getDefaultProtocol(), getProtocols());
|
||||||
|
}
|
||||||
|
}
|
|
@ -47,19 +47,34 @@ import org.eclipse.jetty.util.annotation.ManagedObject;
|
||||||
@ManagedObject("SPDY Server Connection Factory")
|
@ManagedObject("SPDY Server Connection Factory")
|
||||||
public class SPDYServerConnectionFactory extends AbstractConnectionFactory
|
public class SPDYServerConnectionFactory extends AbstractConnectionFactory
|
||||||
{
|
{
|
||||||
// This method is placed here so as to provide a check for NPN before attempting to load any
|
/**
|
||||||
// NPN classes.
|
* @deprecated use {@link #checkProtocolNegotiationAvailable()} instead.
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
public static void checkNPNAvailable()
|
public static void checkNPNAvailable()
|
||||||
|
{
|
||||||
|
checkProtocolNegotiationAvailable();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void checkProtocolNegotiationAvailable()
|
||||||
|
{
|
||||||
|
if (!isAvailableInBootClassPath("org.eclipse.jetty.alpn.ALPN") &&
|
||||||
|
!isAvailableInBootClassPath("org.eclipse.jetty.npn.NextProtoNego"))
|
||||||
|
throw new IllegalStateException("No ALPN nor NPN classes available");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static boolean isAvailableInBootClassPath(String className)
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
Class<?> npn = ClassLoader.getSystemClassLoader().loadClass("org.eclipse.jetty.npn.NextProtoNego");
|
Class<?> klass = ClassLoader.getSystemClassLoader().loadClass(className);
|
||||||
if (npn.getClassLoader() != null)
|
if (klass.getClassLoader() != null)
|
||||||
throw new IllegalStateException("NextProtoNego must be on JVM boot path");
|
throw new IllegalStateException(className + " must be on JVM boot classpath");
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
catch (ClassNotFoundException e)
|
catch (ClassNotFoundException x)
|
||||||
{
|
{
|
||||||
throw new IllegalStateException("No NextProtoNego on boot path", e);
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -18,7 +18,8 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.spdy.server;
|
package org.eclipse.jetty.spdy.server;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.ConnectionFactory;
|
import java.util.Objects;
|
||||||
|
|
||||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||||
import org.eclipse.jetty.server.Server;
|
import org.eclipse.jetty.server.Server;
|
||||||
import org.eclipse.jetty.server.ServerConnector;
|
import org.eclipse.jetty.server.ServerConnector;
|
||||||
|
@ -30,23 +31,21 @@ public class SPDYServerConnector extends ServerConnector
|
||||||
{
|
{
|
||||||
public SPDYServerConnector(Server server, ServerSessionFrameListener listener)
|
public SPDYServerConnector(Server server, ServerSessionFrameListener listener)
|
||||||
{
|
{
|
||||||
this(server, null, listener);
|
super(server, (SslContextFactory)null, new SPDYServerConnectionFactory(SPDY.V2, listener));
|
||||||
}
|
}
|
||||||
|
|
||||||
public SPDYServerConnector(Server server, SslContextFactory sslContextFactory, ServerSessionFrameListener listener)
|
public SPDYServerConnector(Server server, SslContextFactory sslContextFactory, ServerSessionFrameListener listener)
|
||||||
{
|
{
|
||||||
super(server,
|
this(server, sslContextFactory, listener, new NPNServerConnectionFactory("spdy/3", "spdy/2", "http/1.1"));
|
||||||
sslContextFactory,
|
|
||||||
sslContextFactory==null
|
|
||||||
?new ConnectionFactory[]{new SPDYServerConnectionFactory(SPDY.V2, listener)}
|
|
||||||
:new ConnectionFactory[]{
|
|
||||||
new NPNServerConnectionFactory("spdy/3","spdy/2","http/1.1"),
|
|
||||||
new HttpConnectionFactory(),
|
|
||||||
new SPDYServerConnectionFactory(SPDY.V2, listener),
|
|
||||||
new SPDYServerConnectionFactory(SPDY.V3, listener)});
|
|
||||||
if (getConnectionFactory(NPNServerConnectionFactory.class)!=null)
|
|
||||||
getConnectionFactory(NPNServerConnectionFactory.class).setDefaultProtocol("http/1.1");
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public SPDYServerConnector(Server server, SslContextFactory sslContextFactory, ServerSessionFrameListener listener, NegotiatingServerConnectionFactory negotiator)
|
||||||
|
{
|
||||||
|
super(server, Objects.requireNonNull(sslContextFactory),
|
||||||
|
negotiator,
|
||||||
|
new SPDYServerConnectionFactory(SPDY.V3, listener),
|
||||||
|
new SPDYServerConnectionFactory(SPDY.V2, listener),
|
||||||
|
new HttpConnectionFactory());
|
||||||
|
negotiator.setDefaultProtocol("http/1.1");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
1
pom.xml
1
pom.xml
|
@ -17,6 +17,7 @@
|
||||||
<slf4j-version>1.6.1</slf4j-version>
|
<slf4j-version>1.6.1</slf4j-version>
|
||||||
<jetty-test-policy-version>1.2</jetty-test-policy-version>
|
<jetty-test-policy-version>1.2</jetty-test-policy-version>
|
||||||
<npn.api.version>1.1.0.v20120525</npn.api.version>
|
<npn.api.version>1.1.0.v20120525</npn.api.version>
|
||||||
|
<alpn.api.version>1.0.0-SNAPSHOT</alpn.api.version>
|
||||||
</properties>
|
</properties>
|
||||||
<scm>
|
<scm>
|
||||||
<connection>scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</connection>
|
<connection>scm:git:http://git.eclipse.org/gitroot/jetty/org.eclipse.jetty.project.git</connection>
|
||||||
|
|
|
@ -102,7 +102,7 @@ public class TestTransparentProxyServer
|
||||||
|
|
||||||
|
|
||||||
// Spdy Connector
|
// Spdy Connector
|
||||||
SPDYServerConnectionFactory.checkNPNAvailable();
|
SPDYServerConnectionFactory.checkProtocolNegotiationAvailable();
|
||||||
PushStrategy push = new ReferrerPushStrategy();
|
PushStrategy push = new ReferrerPushStrategy();
|
||||||
HTTPSPDYServerConnectionFactory spdy2 = new HTTPSPDYServerConnectionFactory(2,config,push);
|
HTTPSPDYServerConnectionFactory spdy2 = new HTTPSPDYServerConnectionFactory(2,config,push);
|
||||||
spdy2.setInputBufferSize(8192);
|
spdy2.setInputBufferSize(8192);
|
||||||
|
|
Loading…
Reference in New Issue