Reduced code duplication in Handshakers and Negotiations.
Signed-off-by: Simone Bordet <simone.bordet@gmail.com>
This commit is contained in:
parent
06ce13e226
commit
98574f28a0
|
@ -36,146 +36,25 @@ import org.eclipse.jetty.websocket.core.ExtensionConfig;
|
|||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.internal.ExtensionStack;
|
||||
|
||||
public class Negotiation
|
||||
public abstract class Negotiation
|
||||
{
|
||||
private final Request baseRequest;
|
||||
private final HttpServletRequest request;
|
||||
private final HttpServletResponse response;
|
||||
private final List<ExtensionConfig> offeredExtensions;
|
||||
private final List<String> offeredSubprotocols;
|
||||
private final WebSocketComponents components;
|
||||
private final String version;
|
||||
private final Boolean upgrade;
|
||||
private final String key;
|
||||
|
||||
private String version;
|
||||
private List<ExtensionConfig> offeredExtensions;
|
||||
private List<ExtensionConfig> negotiatedExtensions;
|
||||
private String subprotocol;
|
||||
private List<String> offeredProtocols;
|
||||
private ExtensionStack extensionStack;
|
||||
private String protocol;
|
||||
|
||||
/**
|
||||
* @throws BadMessageException if there is any errors parsing the upgrade request
|
||||
*/
|
||||
public Negotiation(
|
||||
Request baseRequest,
|
||||
HttpServletRequest request,
|
||||
HttpServletResponse response,
|
||||
WebSocketComponents components) throws BadMessageException
|
||||
public Negotiation(Request baseRequest, HttpServletRequest request, HttpServletResponse response, WebSocketComponents webSocketComponents)
|
||||
{
|
||||
this.baseRequest = baseRequest;
|
||||
this.request = request;
|
||||
this.response = response;
|
||||
this.components = components;
|
||||
|
||||
Boolean upgrade = null;
|
||||
String key = null;
|
||||
String version = null;
|
||||
QuotedCSV connectionCSVs = null;
|
||||
QuotedCSV extensions = null;
|
||||
QuotedCSV subprotocols = null;
|
||||
|
||||
try
|
||||
{
|
||||
for (HttpField field : baseRequest.getHttpFields())
|
||||
{
|
||||
if (field.getHeader() != null)
|
||||
{
|
||||
switch (field.getHeader())
|
||||
{
|
||||
case UPGRADE:
|
||||
if (upgrade == null && "websocket".equalsIgnoreCase(field.getValue()))
|
||||
upgrade = Boolean.TRUE;
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
if (connectionCSVs == null)
|
||||
connectionCSVs = new QuotedCSV();
|
||||
connectionCSVs.addValue(field.getValue());
|
||||
break;
|
||||
|
||||
case SEC_WEBSOCKET_KEY:
|
||||
key = field.getValue();
|
||||
break;
|
||||
|
||||
case SEC_WEBSOCKET_VERSION:
|
||||
version = field.getValue();
|
||||
break;
|
||||
|
||||
case SEC_WEBSOCKET_EXTENSIONS:
|
||||
if (extensions == null)
|
||||
extensions = new QuotedCSV(field.getValue());
|
||||
else
|
||||
extensions.addValue(field.getValue());
|
||||
break;
|
||||
|
||||
case SEC_WEBSOCKET_SUBPROTOCOL:
|
||||
if (subprotocols == null)
|
||||
subprotocols = new QuotedCSV(field.getValue());
|
||||
else
|
||||
subprotocols.addValue(field.getValue());
|
||||
break;
|
||||
|
||||
default:
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
this.version = version;
|
||||
this.key = key;
|
||||
this.upgrade = upgrade != null && connectionCSVs != null && connectionCSVs.getValues().stream().anyMatch(s -> s.equalsIgnoreCase("Upgrade"));
|
||||
|
||||
Set<String> available = components.getExtensionRegistry().getAvailableExtensionNames();
|
||||
offeredExtensions = extensions == null
|
||||
? Collections.emptyList()
|
||||
: extensions.getValues().stream()
|
||||
.map(ExtensionConfig::parse)
|
||||
.filter(ec -> available.contains(ec.getName().toLowerCase()) && !ec.getName().startsWith("@"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
offeredSubprotocols = subprotocols == null
|
||||
? Collections.emptyList()
|
||||
: subprotocols.getValues();
|
||||
|
||||
negotiatedExtensions = new ArrayList<>();
|
||||
for (ExtensionConfig config : offeredExtensions)
|
||||
{
|
||||
long matches = negotiatedExtensions.stream()
|
||||
.filter(negotiatedConfig -> negotiatedConfig.getName().equals(config.getName())).count();
|
||||
if (matches == 0)
|
||||
negotiatedExtensions.add(config);
|
||||
}
|
||||
}
|
||||
catch (Throwable t)
|
||||
{
|
||||
throw new BadMessageException("Invalid Handshake Request", t);
|
||||
}
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
|
||||
public List<ExtensionConfig> getOfferedExtensions()
|
||||
{
|
||||
return offeredExtensions;
|
||||
}
|
||||
|
||||
public void setNegotiatedExtensions(List<ExtensionConfig> extensions)
|
||||
{
|
||||
if (extensions == offeredExtensions)
|
||||
return;
|
||||
negotiatedExtensions = extensions == null ? null : new ArrayList<>(extensions);
|
||||
extensionStack = null;
|
||||
}
|
||||
|
||||
public List<ExtensionConfig> getNegotiatedExtensions()
|
||||
{
|
||||
return negotiatedExtensions;
|
||||
}
|
||||
|
||||
public List<String> getOfferedSubprotocols()
|
||||
{
|
||||
return offeredSubprotocols;
|
||||
this.components = webSocketComponents;
|
||||
}
|
||||
|
||||
public Request getBaseRequest()
|
||||
|
@ -193,25 +72,113 @@ public class Negotiation
|
|||
return response;
|
||||
}
|
||||
|
||||
public void setSubprotocol(String subprotocol)
|
||||
public void negotiate() throws BadMessageException
|
||||
{
|
||||
this.subprotocol = subprotocol;
|
||||
response.setHeader(HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL.asString(), subprotocol);
|
||||
try
|
||||
{
|
||||
negotiateHeaders(getBaseRequest());
|
||||
}
|
||||
catch (Throwable x)
|
||||
{
|
||||
throw new BadMessageException("Invalid upgrade request", x);
|
||||
}
|
||||
}
|
||||
|
||||
public String getSubprotocol()
|
||||
protected void negotiateHeaders(Request baseRequest)
|
||||
{
|
||||
return subprotocol;
|
||||
QuotedCSV extensions = null;
|
||||
QuotedCSV protocols = null;
|
||||
for (HttpField field : baseRequest.getHttpFields())
|
||||
{
|
||||
if (field.getHeader() != null)
|
||||
{
|
||||
switch (field.getHeader())
|
||||
{
|
||||
case SEC_WEBSOCKET_VERSION:
|
||||
version = field.getValue();
|
||||
break;
|
||||
|
||||
case SEC_WEBSOCKET_EXTENSIONS:
|
||||
if (extensions == null)
|
||||
extensions = new QuotedCSV(field.getValue());
|
||||
else
|
||||
extensions.addValue(field.getValue());
|
||||
break;
|
||||
|
||||
case SEC_WEBSOCKET_SUBPROTOCOL:
|
||||
if (protocols == null)
|
||||
protocols = new QuotedCSV(field.getValue());
|
||||
else
|
||||
protocols.addValue(field.getValue());
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Set<String> available = components.getExtensionRegistry().getAvailableExtensionNames();
|
||||
offeredExtensions = extensions == null
|
||||
? Collections.emptyList()
|
||||
: extensions.getValues().stream()
|
||||
.map(ExtensionConfig::parse)
|
||||
.filter(ec -> available.contains(ec.getName().toLowerCase()) && !ec.getName().startsWith("@"))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
offeredProtocols = protocols == null
|
||||
? Collections.emptyList()
|
||||
: protocols.getValues();
|
||||
|
||||
negotiatedExtensions = new ArrayList<>();
|
||||
for (ExtensionConfig config : offeredExtensions)
|
||||
{
|
||||
long matches = negotiatedExtensions.stream()
|
||||
.filter(negotiatedConfig -> negotiatedConfig.getName().equals(config.getName())).count();
|
||||
if (matches == 0)
|
||||
negotiatedExtensions.add(config);
|
||||
}
|
||||
}
|
||||
|
||||
public abstract boolean isSuccessful();
|
||||
|
||||
public String getVersion()
|
||||
{
|
||||
return version;
|
||||
}
|
||||
|
||||
public boolean isUpgrade()
|
||||
public String getSubprotocol()
|
||||
{
|
||||
return upgrade;
|
||||
return protocol;
|
||||
}
|
||||
|
||||
public void setSubprotocol(String protocol)
|
||||
{
|
||||
this.protocol = protocol;
|
||||
response.setHeader(HttpHeader.SEC_WEBSOCKET_SUBPROTOCOL.asString(), protocol);
|
||||
}
|
||||
|
||||
public List<String> getOfferedSubprotocols()
|
||||
{
|
||||
return offeredProtocols;
|
||||
}
|
||||
|
||||
public List<ExtensionConfig> getOfferedExtensions()
|
||||
{
|
||||
return offeredExtensions;
|
||||
}
|
||||
|
||||
public List<ExtensionConfig> getNegotiatedExtensions()
|
||||
{
|
||||
return negotiatedExtensions;
|
||||
}
|
||||
|
||||
public void setNegotiatedExtensions(List<ExtensionConfig> extensions)
|
||||
{
|
||||
if (extensions == offeredExtensions)
|
||||
return;
|
||||
negotiatedExtensions = extensions;
|
||||
extensionStack = null;
|
||||
}
|
||||
|
||||
public ExtensionStack getExtensionStack()
|
||||
|
@ -229,14 +196,14 @@ public class Negotiation
|
|||
else
|
||||
baseRequest.getResponse().setHeader(HttpHeader.SEC_WEBSOCKET_EXTENSIONS, null);
|
||||
}
|
||||
|
||||
return extensionStack;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString()
|
||||
{
|
||||
return String.format("Negotiation@%x{uri=%s,oe=%s,op=%s}",
|
||||
return String.format("%s@%x{uri=%s,oe=%s,op=%s}",
|
||||
getClass().getSimpleName(),
|
||||
hashCode(),
|
||||
getRequest().getRequestURI(),
|
||||
getOfferedExtensions(),
|
||||
|
|
|
@ -0,0 +1,211 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2019 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.websocket.core.server.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.Executor;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpTransport;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.websocket.core.Behavior;
|
||||
import org.eclipse.jetty.websocket.core.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketConstants;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketException;
|
||||
import org.eclipse.jetty.websocket.core.internal.ExtensionStack;
|
||||
import org.eclipse.jetty.websocket.core.internal.Negotiated;
|
||||
import org.eclipse.jetty.websocket.core.internal.WebSocketConnection;
|
||||
import org.eclipse.jetty.websocket.core.internal.WebSocketCoreSession;
|
||||
import org.eclipse.jetty.websocket.core.server.Handshaker;
|
||||
import org.eclipse.jetty.websocket.core.server.Negotiation;
|
||||
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
|
||||
|
||||
public abstract class AbstractHandshaker implements Handshaker
|
||||
{
|
||||
protected static final Logger LOG = Log.getLogger(RFC8441Handshaker.class);
|
||||
private static final HttpField SERVER_VERSION = new PreEncodedHttpField(HttpHeader.SERVER, HttpConfiguration.SERVER_VERSION);
|
||||
|
||||
@Override
|
||||
public boolean upgradeRequest(WebSocketNegotiator negotiator, HttpServletRequest request, HttpServletResponse response, FrameHandler.Customizer defaultCustomizer) throws IOException
|
||||
{
|
||||
if (!validateRequest(request))
|
||||
return false;
|
||||
|
||||
Negotiation negotiation = newNegotiation(request, response, new WebSocketComponents());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("negotiation {}", negotiation);
|
||||
negotiation.negotiate();
|
||||
|
||||
if (!validateNegotiation(negotiation))
|
||||
return false;
|
||||
|
||||
// Negotiate the FrameHandler
|
||||
FrameHandler handler = negotiator.negotiate(negotiation);
|
||||
if (handler == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: no frame handler provided {}", request);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Handle error responses
|
||||
Request baseRequest = negotiation.getBaseRequest();
|
||||
if (response.isCommitted())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: response committed {}", request);
|
||||
baseRequest.setHandled(true);
|
||||
return false;
|
||||
}
|
||||
int httpStatus = response.getStatus();
|
||||
if (httpStatus > 200)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: invalid http code {} {}", httpStatus, request);
|
||||
response.flushBuffer();
|
||||
baseRequest.setHandled(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Validate negotiated protocol
|
||||
String protocol = negotiation.getSubprotocol();
|
||||
List<String> offeredProtocols = negotiation.getOfferedSubprotocols();
|
||||
if (protocol != null)
|
||||
{
|
||||
if (!offeredProtocols.contains(protocol))
|
||||
throw new WebSocketException("not upgraded: selected a protocol not present in offered protocols");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!offeredProtocols.isEmpty())
|
||||
throw new WebSocketException("not upgraded: no protocol selected from offered protocols");
|
||||
}
|
||||
|
||||
// validate negotiated extensions
|
||||
for (ExtensionConfig config : negotiation.getNegotiatedExtensions())
|
||||
{
|
||||
if (config.getName().startsWith("@"))
|
||||
continue;
|
||||
|
||||
long matches = negotiation.getOfferedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
|
||||
if (matches < 1)
|
||||
throw new WebSocketException("Upgrade failed: negotiated extension not requested");
|
||||
|
||||
matches = negotiation.getNegotiatedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
|
||||
if (matches > 1)
|
||||
throw new WebSocketException("Upgrade failed: multiple negotiated extensions of the same name");
|
||||
}
|
||||
|
||||
// Create and Negotiate the ExtensionStack
|
||||
ExtensionStack extensionStack = negotiation.getExtensionStack();
|
||||
|
||||
Negotiated negotiated = new Negotiated(baseRequest.getHttpURI().toURI(), protocol, baseRequest.isSecure(), extensionStack, WebSocketConstants.SPEC_VERSION_STRING);
|
||||
|
||||
// Create the Session
|
||||
WebSocketCoreSession coreSession = newWebSocketCoreSession(handler, negotiated);
|
||||
if (defaultCustomizer != null)
|
||||
defaultCustomizer.customize(coreSession);
|
||||
negotiator.customize(coreSession);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("session {}", coreSession);
|
||||
|
||||
WebSocketConnection connection = createWebSocketConnection(baseRequest, coreSession);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("connection {}", connection);
|
||||
if (connection == null)
|
||||
throw new WebSocketException("not upgraded: no connection");
|
||||
|
||||
HttpChannel httpChannel = baseRequest.getHttpChannel();
|
||||
HttpConfiguration httpConfig = httpChannel.getHttpConfiguration();
|
||||
connection.setUseInputDirectByteBuffers(httpConfig.isUseInputDirectByteBuffers());
|
||||
connection.setUseOutputDirectByteBuffers(httpChannel.isUseOutputDirectByteBuffers());
|
||||
|
||||
httpChannel.getConnector().getEventListeners().forEach(connection::addEventListener);
|
||||
|
||||
coreSession.setWebSocketConnection(connection);
|
||||
|
||||
Response baseResponse = baseRequest.getResponse();
|
||||
prepareResponse(baseResponse, negotiation);
|
||||
if (httpConfig.getSendServerVersion())
|
||||
baseResponse.getHttpFields().put(SERVER_VERSION);
|
||||
baseResponse.flushBuffer();
|
||||
baseRequest.setHandled(true);
|
||||
|
||||
baseRequest.setAttribute(HttpTransport.UPGRADE_CONNECTION_ATTRIBUTE, connection);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("upgrade connection={} session={} framehandler={}", connection, coreSession, handler);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected abstract boolean validateRequest(HttpServletRequest request);
|
||||
|
||||
protected abstract Negotiation newNegotiation(HttpServletRequest request, HttpServletResponse response, WebSocketComponents webSocketComponents);
|
||||
|
||||
protected boolean validateNegotiation(Negotiation negotiation)
|
||||
{
|
||||
if (!negotiation.isSuccessful())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: no upgrade header or connection upgrade", negotiation.getBaseRequest());
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WebSocketConstants.SPEC_VERSION_STRING.equals(negotiation.getVersion()))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: unsupported version {} {}", negotiation.getVersion(), negotiation.getBaseRequest());
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected WebSocketCoreSession newWebSocketCoreSession(FrameHandler handler, Negotiated negotiated)
|
||||
{
|
||||
return new WebSocketCoreSession(handler, Behavior.SERVER, negotiated);
|
||||
}
|
||||
|
||||
protected abstract WebSocketConnection createWebSocketConnection(Request baseRequest, WebSocketCoreSession coreSession);
|
||||
|
||||
protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, WebSocketCoreSession coreSession)
|
||||
{
|
||||
return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, coreSession);
|
||||
}
|
||||
|
||||
protected abstract void prepareResponse(Response response, Negotiation negotiation);
|
||||
}
|
|
@ -18,54 +18,33 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core.server.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpFields;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.server.ConnectionFactory;
|
||||
import org.eclipse.jetty.server.Connector;
|
||||
import org.eclipse.jetty.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.HttpTransport;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.websocket.core.Behavior;
|
||||
import org.eclipse.jetty.websocket.core.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketConstants;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketException;
|
||||
import org.eclipse.jetty.websocket.core.internal.ExtensionStack;
|
||||
import org.eclipse.jetty.websocket.core.internal.Negotiated;
|
||||
import org.eclipse.jetty.websocket.core.internal.WebSocketConnection;
|
||||
import org.eclipse.jetty.websocket.core.internal.WebSocketCore;
|
||||
import org.eclipse.jetty.websocket.core.internal.WebSocketCoreSession;
|
||||
import org.eclipse.jetty.websocket.core.server.Handshaker;
|
||||
import org.eclipse.jetty.websocket.core.server.Negotiation;
|
||||
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
|
||||
|
||||
public final class RFC6455Handshaker implements Handshaker
|
||||
public final class RFC6455Handshaker extends AbstractHandshaker
|
||||
{
|
||||
static final Logger LOG = Log.getLogger(RFC6455Handshaker.class);
|
||||
private static final HttpField UPGRADE_WEBSOCKET = new PreEncodedHttpField(HttpHeader.UPGRADE, "WebSocket");
|
||||
private static final HttpField CONNECTION_UPGRADE = new PreEncodedHttpField(HttpHeader.CONNECTION, HttpHeader.UPGRADE.asString());
|
||||
private static final HttpField SERVER_VERSION = new PreEncodedHttpField(HttpHeader.SERVER, HttpConfiguration.SERVER_VERSION);
|
||||
|
||||
public boolean upgradeRequest(WebSocketNegotiator negotiator, HttpServletRequest request, HttpServletResponse response,
|
||||
FrameHandler.Customizer defaultCustomizer) throws IOException
|
||||
@Override
|
||||
protected boolean validateRequest(HttpServletRequest request)
|
||||
{
|
||||
if (!HttpMethod.GET.is(request.getMethod()))
|
||||
{
|
||||
|
@ -74,176 +53,48 @@ public final class RFC6455Handshaker implements Handshaker
|
|||
return false;
|
||||
}
|
||||
|
||||
final Request baseRequest = Request.getBaseRequest(request);
|
||||
if (!HttpVersion.HTTP_1_1.equals(baseRequest.getHttpVersion()))
|
||||
if (!HttpVersion.HTTP_1_1.is(request.getProtocol()))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded version!=1.1 {}", baseRequest);
|
||||
LOG.debug("not upgraded version!=1.1 {}", request);
|
||||
return false;
|
||||
}
|
||||
|
||||
Negotiation negotiation = new Negotiation(baseRequest, request, response, new WebSocketComponents());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("negotiation {}", negotiation);
|
||||
|
||||
if (!negotiation.isUpgrade())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: no upgrade header or connection upgrade", baseRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WebSocketConstants.SPEC_VERSION_STRING.equals(negotiation.getVersion()))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: unsupported version {} {}", negotiation.getVersion(), baseRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (negotiation.getKey() == null)
|
||||
throw new BadMessageException("Missing request header 'Sec-WebSocket-Key'");
|
||||
|
||||
// Negotiate the FrameHandler
|
||||
FrameHandler handler = negotiator.negotiate(negotiation);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("negotiated handler {}", handler);
|
||||
|
||||
// Handle error responses
|
||||
if (response.isCommitted())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: response committed {}", baseRequest);
|
||||
baseRequest.setHandled(true);
|
||||
return false;
|
||||
}
|
||||
if (response.getStatus() > 200)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: error sent {} {}", response.getStatus(), baseRequest);
|
||||
response.flushBuffer();
|
||||
baseRequest.setHandled(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for handler
|
||||
if (handler == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: no frame handler provided {}", baseRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate negotiated subprotocol
|
||||
String subprotocol = negotiation.getSubprotocol();
|
||||
if (subprotocol != null)
|
||||
{
|
||||
if (!negotiation.getOfferedSubprotocols().contains(subprotocol))
|
||||
throw new WebSocketException("not upgraded: selected a subprotocol not present in offered subprotocols");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!negotiation.getOfferedSubprotocols().isEmpty())
|
||||
throw new WebSocketException("not upgraded: no subprotocol selected from offered subprotocols");
|
||||
}
|
||||
|
||||
// validate negotiated extensions
|
||||
for (ExtensionConfig config : negotiation.getNegotiatedExtensions())
|
||||
{
|
||||
if (config.getName().startsWith("@"))
|
||||
continue;
|
||||
|
||||
long matches = negotiation.getOfferedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
|
||||
if (matches < 1)
|
||||
throw new WebSocketException("Upgrade failed: negotiated extension not requested");
|
||||
|
||||
matches = negotiation.getNegotiatedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
|
||||
if (matches > 1)
|
||||
throw new WebSocketException("Upgrade failed: multiple negotiated extensions of the same name");
|
||||
}
|
||||
|
||||
// Create and Negotiate the ExtensionStack
|
||||
ExtensionStack extensionStack = negotiation.getExtensionStack();
|
||||
|
||||
Negotiated negotiated = new Negotiated(
|
||||
baseRequest.getHttpURI().toURI(),
|
||||
subprotocol,
|
||||
baseRequest.isSecure(),
|
||||
extensionStack,
|
||||
WebSocketConstants.SPEC_VERSION_STRING);
|
||||
|
||||
// Create the Session
|
||||
WebSocketCoreSession coreSession = newWebSocketCoreSession(handler, negotiated);
|
||||
if (defaultCustomizer != null)
|
||||
defaultCustomizer.customize(coreSession);
|
||||
negotiator.customize(coreSession);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("session {}", coreSession);
|
||||
|
||||
// Create a connection
|
||||
HttpChannel httpChannel = baseRequest.getHttpChannel();
|
||||
Connector connector = httpChannel.getConnector();
|
||||
WebSocketConnection connection = newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession);
|
||||
// TODO: perhaps use of direct buffers should be WebSocket specific
|
||||
// rather than inheriting the setting from HttpConfiguration.
|
||||
HttpConfiguration httpConfig = httpChannel.getHttpConfiguration();
|
||||
connection.setUseInputDirectByteBuffers(httpConfig.isUseInputDirectByteBuffers());
|
||||
connection.setUseOutputDirectByteBuffers(httpChannel.isUseOutputDirectByteBuffers());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("connection {}", connection);
|
||||
if (connection == null)
|
||||
throw new WebSocketException("not upgraded: no connection");
|
||||
|
||||
connector.getEventListeners().forEach(connection::addEventListener);
|
||||
|
||||
coreSession.setWebSocketConnection(connection);
|
||||
|
||||
// send upgrade response
|
||||
Response baseResponse = baseRequest.getResponse();
|
||||
baseResponse.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
|
||||
baseResponse.getHttpFields().put(UPGRADE_WEBSOCKET);
|
||||
baseResponse.getHttpFields().put(CONNECTION_UPGRADE);
|
||||
baseResponse.getHttpFields().put(HttpHeader.SEC_WEBSOCKET_ACCEPT, WebSocketCore.hashKey(negotiation.getKey()));
|
||||
|
||||
// See bugs.eclipse.org/485969
|
||||
if (getSendServerVersion(connector))
|
||||
{
|
||||
baseResponse.getHttpFields().put(SERVER_VERSION);
|
||||
}
|
||||
|
||||
baseResponse.flushBuffer();
|
||||
baseRequest.setHandled(true);
|
||||
|
||||
// upgrade
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("upgrade connection={} session={}", connection, coreSession);
|
||||
|
||||
baseRequest.setAttribute(HttpTransport.UPGRADE_CONNECTION_ATTRIBUTE, connection);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected WebSocketCoreSession newWebSocketCoreSession(FrameHandler handler, Negotiated negotiated)
|
||||
@Override
|
||||
protected Negotiation newNegotiation(HttpServletRequest request, HttpServletResponse response, WebSocketComponents webSocketComponents)
|
||||
{
|
||||
return new WebSocketCoreSession(handler, Behavior.SERVER, negotiated);
|
||||
return new RFC6544Negotiation(Request.getBaseRequest(request), request, response, webSocketComponents);
|
||||
}
|
||||
|
||||
protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, WebSocketCoreSession coreSession)
|
||||
@Override
|
||||
protected boolean validateNegotiation(Negotiation negotiation)
|
||||
{
|
||||
return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, coreSession);
|
||||
}
|
||||
|
||||
private boolean getSendServerVersion(Connector connector)
|
||||
{
|
||||
ConnectionFactory connFactory = connector.getConnectionFactory(HttpVersion.HTTP_1_1.asString());
|
||||
if (connFactory == null)
|
||||
boolean result = super.validateNegotiation(negotiation);
|
||||
if (!result)
|
||||
return false;
|
||||
if (((RFC6544Negotiation)negotiation).getKey() == null)
|
||||
throw new BadMessageException("Missing request header 'Sec-WebSocket-Key'");
|
||||
return true;
|
||||
}
|
||||
|
||||
if (connFactory instanceof HttpConnectionFactory)
|
||||
{
|
||||
HttpConfiguration httpConf = ((HttpConnectionFactory)connFactory).getHttpConfiguration();
|
||||
if (httpConf != null)
|
||||
return httpConf.getSendServerVersion();
|
||||
}
|
||||
return false;
|
||||
@Override
|
||||
protected WebSocketConnection createWebSocketConnection(Request baseRequest, WebSocketCoreSession coreSession)
|
||||
{
|
||||
HttpChannel httpChannel = baseRequest.getHttpChannel();
|
||||
Connector connector = httpChannel.getConnector();
|
||||
return newWebSocketConnection(httpChannel.getEndPoint(), connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void prepareResponse(Response response, Negotiation negotiation)
|
||||
{
|
||||
response.setStatus(HttpServletResponse.SC_SWITCHING_PROTOCOLS);
|
||||
HttpFields responseFields = response.getHttpFields();
|
||||
responseFields.put(UPGRADE_WEBSOCKET);
|
||||
responseFields.put(CONNECTION_UPGRADE);
|
||||
responseFields.put(HttpHeader.SEC_WEBSOCKET_ACCEPT, WebSocketCore.hashKey(((RFC6544Negotiation)negotiation).getKey()));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,90 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2019 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.websocket.core.server.internal;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.QuotedCSV;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.server.Negotiation;
|
||||
|
||||
public class RFC6544Negotiation extends Negotiation
|
||||
{
|
||||
private boolean successful;
|
||||
private String key;
|
||||
|
||||
public RFC6544Negotiation(Request baseRequest, HttpServletRequest request, HttpServletResponse response, WebSocketComponents components) throws BadMessageException
|
||||
{
|
||||
super(baseRequest, request, response, components);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void negotiateHeaders(Request baseRequest)
|
||||
{
|
||||
super.negotiateHeaders(baseRequest);
|
||||
|
||||
boolean upgrade = false;
|
||||
QuotedCSV connectionCSVs = null;
|
||||
for (HttpField field : baseRequest.getHttpFields())
|
||||
{
|
||||
HttpHeader header = field.getHeader();
|
||||
if (header != null)
|
||||
{
|
||||
switch (header)
|
||||
{
|
||||
case UPGRADE:
|
||||
upgrade = "websocket".equalsIgnoreCase(field.getValue());
|
||||
break;
|
||||
|
||||
case CONNECTION:
|
||||
if (connectionCSVs == null)
|
||||
connectionCSVs = new QuotedCSV();
|
||||
connectionCSVs.addValue(field.getValue());
|
||||
break;
|
||||
|
||||
case SEC_WEBSOCKET_KEY:
|
||||
key = field.getValue();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
successful = upgrade && connectionCSVs != null &&
|
||||
connectionCSVs.getValues().stream().anyMatch(s -> s.equalsIgnoreCase("upgrade"));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isSuccessful()
|
||||
{
|
||||
return successful;
|
||||
}
|
||||
|
||||
public String getKey()
|
||||
{
|
||||
return key;
|
||||
}
|
||||
}
|
|
@ -18,220 +18,62 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.core.server.internal;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.concurrent.Executor;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.HttpField;
|
||||
import org.eclipse.jetty.http.HttpHeader;
|
||||
import org.eclipse.jetty.http.HttpMethod;
|
||||
import org.eclipse.jetty.http.HttpStatus;
|
||||
import org.eclipse.jetty.http.HttpVersion;
|
||||
import org.eclipse.jetty.http.PreEncodedHttpField;
|
||||
import org.eclipse.jetty.io.ByteBufferPool;
|
||||
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.server.HttpChannel;
|
||||
import org.eclipse.jetty.server.HttpConfiguration;
|
||||
import org.eclipse.jetty.server.HttpConnectionFactory;
|
||||
import org.eclipse.jetty.server.HttpTransport;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.server.Response;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.websocket.core.Behavior;
|
||||
import org.eclipse.jetty.websocket.core.ExtensionConfig;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketConstants;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketException;
|
||||
import org.eclipse.jetty.websocket.core.internal.ExtensionStack;
|
||||
import org.eclipse.jetty.websocket.core.internal.Negotiated;
|
||||
import org.eclipse.jetty.websocket.core.internal.WebSocketConnection;
|
||||
import org.eclipse.jetty.websocket.core.internal.WebSocketCoreSession;
|
||||
import org.eclipse.jetty.websocket.core.server.Handshaker;
|
||||
import org.eclipse.jetty.websocket.core.server.Negotiation;
|
||||
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
|
||||
|
||||
public class RFC8441Handshaker implements Handshaker
|
||||
public class RFC8441Handshaker extends AbstractHandshaker
|
||||
{
|
||||
static final Logger LOG = Log.getLogger(RFC8441Handshaker.class);
|
||||
private static final HttpField SERVER_VERSION = new PreEncodedHttpField(HttpHeader.SERVER, HttpConfiguration.SERVER_VERSION);
|
||||
|
||||
@Override
|
||||
public boolean upgradeRequest(WebSocketNegotiator negotiator, HttpServletRequest request, HttpServletResponse response, FrameHandler.Customizer defaultCustomizer) throws IOException
|
||||
protected boolean validateRequest(HttpServletRequest request)
|
||||
{
|
||||
if (!HttpMethod.CONNECT.is(request.getMethod()))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded method!=GET {}", request.toString());
|
||||
LOG.debug("not upgraded method!=GET {}", request);
|
||||
return false;
|
||||
}
|
||||
|
||||
Request baseRequest = Request.getBaseRequest(request);
|
||||
if (!HttpVersion.HTTP_2.equals(baseRequest.getHttpVersion()))
|
||||
if (!HttpVersion.HTTP_2.is(request.getProtocol()))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded HttpVersion!=2 {}", baseRequest);
|
||||
LOG.debug("not upgraded HttpVersion!=2 {}", request);
|
||||
return false;
|
||||
}
|
||||
|
||||
Negotiation negotiation = new RFC8441Negotiation(baseRequest, request, response, new WebSocketComponents());
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("negotiation {}", negotiation);
|
||||
|
||||
if (!negotiation.isUpgrade())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: no upgrade header or connection upgrade", baseRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!WebSocketConstants.SPEC_VERSION_STRING.equals(negotiation.getVersion()))
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: unsupported version {} {}", negotiation.getVersion(), baseRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Negotiate the FrameHandler
|
||||
FrameHandler handler = negotiator.negotiate(negotiation);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("negotiated handler {}", handler);
|
||||
|
||||
// Handle error responses
|
||||
if (response.isCommitted())
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: response committed {}", baseRequest);
|
||||
baseRequest.setHandled(true);
|
||||
return false;
|
||||
}
|
||||
if (response.getStatus() > 200)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: error sent {} {}", response.getStatus(), baseRequest);
|
||||
response.flushBuffer();
|
||||
baseRequest.setHandled(true);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Check for handler
|
||||
if (handler == null)
|
||||
{
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("not upgraded: no frame handler provided {}", baseRequest);
|
||||
return false;
|
||||
}
|
||||
|
||||
// validate negotiated subprotocol
|
||||
String subprotocol = negotiation.getSubprotocol();
|
||||
if (subprotocol != null)
|
||||
{
|
||||
if (!negotiation.getOfferedSubprotocols().contains(subprotocol))
|
||||
throw new WebSocketException("not upgraded: selected a subprotocol not present in offered subprotocols");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!negotiation.getOfferedSubprotocols().isEmpty())
|
||||
throw new WebSocketException("not upgraded: no subprotocol selected from offered subprotocols");
|
||||
}
|
||||
|
||||
// validate negotiated extensions
|
||||
for (ExtensionConfig config : negotiation.getNegotiatedExtensions())
|
||||
{
|
||||
if (config.getName().startsWith("@"))
|
||||
continue;
|
||||
|
||||
long matches = negotiation.getOfferedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
|
||||
if (matches < 1)
|
||||
throw new WebSocketException("Upgrade failed: negotiated extension not requested");
|
||||
|
||||
matches = negotiation.getNegotiatedExtensions().stream().filter(c -> config.getName().equalsIgnoreCase(c.getName())).count();
|
||||
if (matches > 1)
|
||||
throw new WebSocketException("Upgrade failed: multiple negotiated extensions of the same name");
|
||||
}
|
||||
|
||||
// Create and Negotiate the ExtensionStack
|
||||
ExtensionStack extensionStack = negotiation.getExtensionStack();
|
||||
|
||||
Negotiated negotiated = new Negotiated(
|
||||
baseRequest.getHttpURI().toURI(),
|
||||
subprotocol,
|
||||
baseRequest.isSecure(),
|
||||
extensionStack,
|
||||
WebSocketConstants.SPEC_VERSION_STRING);
|
||||
|
||||
// Create the Channel
|
||||
WebSocketCoreSession coreSession = newWebSocketCoreSession(handler, negotiated);
|
||||
if (defaultCustomizer != null)
|
||||
defaultCustomizer.customize(coreSession);
|
||||
negotiator.customize(coreSession);
|
||||
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("coreSession {}", coreSession);
|
||||
|
||||
// Create the Connection
|
||||
HttpChannel httpChannel = baseRequest.getHttpChannel();
|
||||
Connector connector = httpChannel.getConnector();
|
||||
EndPoint endPoint = httpChannel.getTunnellingEndPoint();
|
||||
WebSocketConnection connection = newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession);
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("connection {}", connection);
|
||||
if (connection == null)
|
||||
throw new WebSocketException("not upgraded: no connection");
|
||||
|
||||
for (Connection.Listener listener : connector.getBeans(Connection.Listener.class))
|
||||
{
|
||||
connection.addEventListener(listener);
|
||||
}
|
||||
|
||||
coreSession.setWebSocketConnection(connection);
|
||||
|
||||
// send upgrade response
|
||||
Response baseResponse = baseRequest.getResponse();
|
||||
baseResponse.setStatus(HttpStatus.OK_200);
|
||||
|
||||
// See bugs.eclipse.org/485969
|
||||
if (getSendServerVersion(connector))
|
||||
baseResponse.getHttpFields().put(SERVER_VERSION);
|
||||
|
||||
baseRequest.setHandled(true);
|
||||
|
||||
// upgrade
|
||||
if (LOG.isDebugEnabled())
|
||||
LOG.debug("upgrade connection={} session={}", connection, coreSession);
|
||||
|
||||
baseRequest.setAttribute(HttpTransport.UPGRADE_CONNECTION_ATTRIBUTE, connection);
|
||||
return true;
|
||||
}
|
||||
|
||||
protected WebSocketCoreSession newWebSocketCoreSession(FrameHandler handler, Negotiated negotiated)
|
||||
@Override
|
||||
protected Negotiation newNegotiation(HttpServletRequest request, HttpServletResponse response, WebSocketComponents webSocketComponents)
|
||||
{
|
||||
return new WebSocketCoreSession(handler, Behavior.SERVER, negotiated);
|
||||
return new RFC8441Negotiation(Request.getBaseRequest(request), request, response, webSocketComponents);
|
||||
}
|
||||
|
||||
protected WebSocketConnection newWebSocketConnection(EndPoint endPoint, Executor executor, Scheduler scheduler, ByteBufferPool byteBufferPool, WebSocketCoreSession coreSession)
|
||||
@Override
|
||||
protected WebSocketConnection createWebSocketConnection(Request baseRequest, WebSocketCoreSession coreSession)
|
||||
{
|
||||
return new WebSocketConnection(endPoint, executor, scheduler, byteBufferPool, coreSession);
|
||||
HttpChannel httpChannel = baseRequest.getHttpChannel();
|
||||
Connector connector = httpChannel.getConnector();
|
||||
EndPoint endPoint = httpChannel.getTunnellingEndPoint();
|
||||
return newWebSocketConnection(endPoint, connector.getExecutor(), connector.getScheduler(), connector.getByteBufferPool(), coreSession);
|
||||
}
|
||||
|
||||
private boolean getSendServerVersion(Connector connector)
|
||||
@Override
|
||||
protected void prepareResponse(Response response, Negotiation negotiation)
|
||||
{
|
||||
ConnectionFactory connFactory = connector.getConnectionFactory(HttpVersion.HTTP_2.asString());
|
||||
if (connFactory == null)
|
||||
return false;
|
||||
|
||||
if (connFactory instanceof HttpConnectionFactory)
|
||||
{
|
||||
HttpConfiguration httpConf = ((HttpConnectionFactory)connFactory).getHttpConfiguration();
|
||||
if (httpConf != null)
|
||||
return httpConf.getSendServerVersion();
|
||||
}
|
||||
return false;
|
||||
response.setStatus(HttpStatus.OK_200);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ import javax.servlet.http.HttpServletRequest;
|
|||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import org.eclipse.jetty.http.BadMessageException;
|
||||
import org.eclipse.jetty.http.MetaData;
|
||||
import org.eclipse.jetty.server.Request;
|
||||
import org.eclipse.jetty.websocket.core.WebSocketComponents;
|
||||
import org.eclipse.jetty.websocket.core.server.Negotiation;
|
||||
|
@ -34,11 +35,11 @@ public class RFC8441Negotiation extends Negotiation
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean isUpgrade()
|
||||
public boolean isSuccessful()
|
||||
{
|
||||
if (!getBaseRequest().hasMetaData())
|
||||
MetaData.Request metaData = getBaseRequest().getMetaData();
|
||||
if (metaData == null)
|
||||
return false;
|
||||
|
||||
return "websocket".equals(getBaseRequest().getMetaData().getProtocol());
|
||||
return "websocket".equals(metaData.getProtocol());
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ public class WebSocketNegotiationTest extends WebSocketTester
|
|||
break;
|
||||
|
||||
case "testNotAcceptingExtensions":
|
||||
negotiation.setNegotiatedExtensions(Collections.EMPTY_LIST);
|
||||
negotiation.setNegotiatedExtensions(Collections.emptyList());
|
||||
break;
|
||||
|
||||
case "testNoSubProtocolSelected":
|
||||
|
@ -353,4 +353,4 @@ public class WebSocketNegotiationTest extends WebSocketTester
|
|||
|
||||
assertThat(response, containsString("400 Bad Request"));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,15 +26,12 @@ import org.eclipse.jetty.server.NetworkConnector;
|
|||
import org.eclipse.jetty.server.Server;
|
||||
import org.eclipse.jetty.server.ServerConnector;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.server.Negotiation;
|
||||
import org.eclipse.jetty.websocket.core.server.WebSocketNegotiator;
|
||||
import org.eclipse.jetty.websocket.core.server.WebSocketUpgradeHandler;
|
||||
|
||||
public class WebSocketServer
|
||||
{
|
||||
private static Logger LOG = Log.getLogger(WebSocketServer.class);
|
||||
private final Server server;
|
||||
private URI serverUri;
|
||||
|
||||
|
@ -59,12 +56,12 @@ public class WebSocketServer
|
|||
return server;
|
||||
}
|
||||
|
||||
public WebSocketServer(FrameHandler frameHandler) throws Exception
|
||||
public WebSocketServer(FrameHandler frameHandler)
|
||||
{
|
||||
this(new DefaultNegotiator(frameHandler));
|
||||
}
|
||||
|
||||
public WebSocketServer(WebSocketNegotiator negotiator) throws Exception
|
||||
public WebSocketServer(WebSocketNegotiator negotiator)
|
||||
{
|
||||
server = new Server();
|
||||
ServerConnector connector = new ServerConnector(server);
|
||||
|
|
|
@ -22,7 +22,6 @@ import java.io.IOException;
|
|||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
@ -34,8 +33,6 @@ import org.eclipse.jetty.server.ServerConnector;
|
|||
import org.eclipse.jetty.server.handler.AbstractHandler;
|
||||
import org.eclipse.jetty.server.handler.ContextHandler;
|
||||
import org.eclipse.jetty.util.Callback;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.core.CloseStatus;
|
||||
import org.eclipse.jetty.websocket.core.FrameHandler;
|
||||
import org.eclipse.jetty.websocket.core.MessageHandler;
|
||||
|
@ -47,8 +44,6 @@ import static org.eclipse.jetty.util.Callback.NOOP;
|
|||
|
||||
public class ChatWebSocketServer
|
||||
{
|
||||
private static Logger LOG = Log.getLogger(ChatWebSocketServer.class);
|
||||
|
||||
private Set<MessageHandler> members = new HashSet<>();
|
||||
|
||||
private FrameHandler negotiate(Negotiation negotiation)
|
||||
|
@ -77,7 +72,7 @@ public class ChatWebSocketServer
|
|||
{
|
||||
members.add(this);
|
||||
callback.succeeded();
|
||||
}, x -> callback.failed(x)));
|
||||
}, callback::failed));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
Loading…
Reference in New Issue