diff options
18 files changed, 5 insertions, 2021 deletions
diff --git a/jdisc_http_service/pom.xml b/jdisc_http_service/pom.xml index 91e9b4336c6..a4f4ea6e88b 100644 --- a/jdisc_http_service/pom.xml +++ b/jdisc_http_service/pom.xml @@ -65,11 +65,6 @@ <scope>provided</scope> </dependency> <dependency> - <groupId>org.glassfish.grizzly</groupId> - <artifactId>grizzly-websockets</artifactId> - <scope>test</scope> - </dependency> - <dependency> <groupId>org.mockito</groupId> <artifactId>mockito-core</artifactId> <scope>test</scope> @@ -195,11 +190,6 @@ jetty-util-${jetty.version}.jar, org.apache.aries.spifly.dynamic.bundle-${aries.spifly.version}.jar, org.apache.aries.util-${aries.util.version}.jar, - websocket-api-${jetty.version}.jar, - websocket-client-${jetty.version}.jar, - websocket-common-${jetty.version}.jar, - websocket-server-${jetty.version}.jar, - websocket-servlet-${jetty.version}.jar, component-jar-with-dependencies.jar </discPreInstallBundle> </configuration> diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/WebSocketRequest.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/WebSocketRequest.java deleted file mode 100644 index 8a22b67b297..00000000000 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/WebSocketRequest.java +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http; - -import com.google.common.annotations.Beta; -import com.yahoo.jdisc.service.CurrentContainer; - -import java.net.SocketAddress; -import java.net.URI; - -/** - * Represents a WebSocket request. - * - * @author <a href="mailto:vikasp@yahoo-inc.com">Vikas Panwar</a> - */ -@Beta -public class WebSocketRequest extends HttpRequest { - - @SuppressWarnings("deprecation") - protected WebSocketRequest(CurrentContainer current, URI uri, Method method, Version version, - SocketAddress remoteAddress) { - super(current, uri, method, version, remoteAddress, null); - } - - public static WebSocketRequest newServerRequest(CurrentContainer current, URI uri, Method method, Version version, - SocketAddress remoteAddress) { - return new WebSocketRequest(current, uri, method, version, remoteAddress); - } -} diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/HttpClient.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/HttpClient.java index 495fd303ad2..5480e8fca87 100644 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/HttpClient.java +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/HttpClient.java @@ -53,7 +53,6 @@ public class HttpClient extends AbstractClientProvider { String TRANSFER_LATENCY = "ClientDataTransferLatency"; } - private static final String WEBSOCKET = "ws"; private static final String HTTP = "http"; private static final String HTTPS = "https"; @@ -89,8 +88,6 @@ public class HttpClient extends AbstractClientProvider { String uriScheme = request.getUri().getScheme(); switch (uriScheme) { - case WEBSOCKET: - return WebSocketClientRequest.executeRequest(ningClient, request, handler, metric, ctx); case HTTP: case HTTPS: HttpRequest.Method method = resolveMethod(request); diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketClientRequest.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketClientRequest.java deleted file mode 100644 index 9df75e93b92..00000000000 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketClientRequest.java +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.client; - -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.websocket.WebSocketUpgradeHandler; -import com.yahoo.jdisc.Metric; -import com.yahoo.jdisc.Request; -import com.yahoo.jdisc.handler.ContentChannel; -import com.yahoo.jdisc.handler.ResponseHandler; - -/** - * @author <a href="mailto:vikasp@yahoo-inc.com">Vikas Panwar</a> - */ -final class WebSocketClientRequest { - - private WebSocketClientRequest() { - // hide - } - - public static ContentChannel executeRequest(AsyncHttpClient client, Request request, - ResponseHandler responseHandler, Metric metric, Metric.Context ctx) { - return new WebSocketContent(client, request, new WebSocketUpgradeHandler.Builder() - .addWebSocketListener(new WebSocketHandler(request, responseHandler, metric, ctx)) - .build()); - } -} diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketContent.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketContent.java deleted file mode 100644 index 4331620513d..00000000000 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketContent.java +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.client; - -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.RequestBuilder; -import com.ning.http.client.websocket.WebSocket; -import com.ning.http.client.websocket.WebSocketUpgradeHandler; -import com.yahoo.jdisc.Request; -import com.yahoo.jdisc.handler.CompletionHandler; -import com.yahoo.jdisc.handler.ContentChannel; - -import java.nio.ByteBuffer; -import java.util.Objects; - -/** - * A content channel for interfacing with the web socket client. It accumulates the request data - * before dispatching it to the remote endpoint. - * - * @author <a href="mailto:vikasp@yahoo-inc.com">Vikas Panwar</a> - */ -class WebSocketContent implements ContentChannel { - - private final AsyncHttpClient client; - private final Request request; - private final WebSocketUpgradeHandler handler; - private final Object wsLock = new Object(); - private WebSocket websocket; - - WebSocketContent(AsyncHttpClient client, Request request, WebSocketUpgradeHandler handler) { - this.client = client; - this.request = request; - this.handler = handler; - this.websocket = null; - } - - @Override - public void write(ByteBuffer buf, CompletionHandler handler) { - Objects.requireNonNull(buf, "buf"); - - try { - executeRequest(buf.array()); - if (handler != null) { - handler.completed(); - } - } catch (Exception e) { - if (websocket != null) { - websocket.close(); - } - - throw new RuntimeException(e); - } - } - - @Override - public void close(CompletionHandler handler) { - if (websocket != null) { - websocket.close(); - } - - if (handler != null) { - handler.completed(); - } - } - - private void executeRequest(final byte[] content) throws Exception { - RequestBuilder builder = new RequestBuilder(); - builder.setUrl(request.getUri().toString()); - - synchronized (wsLock) { - if (websocket == null) { - websocket = client.executeRequest(builder.build(), handler).get(); - } - } - - if (websocket.isOpen()) { - websocket.sendMessage(content); - } - } -} diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketHandler.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketHandler.java deleted file mode 100644 index 9b1540881eb..00000000000 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/client/WebSocketHandler.java +++ /dev/null @@ -1,159 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.client; - -import com.ning.http.client.websocket.WebSocket; -import com.ning.http.client.websocket.WebSocketByteListener; -import com.yahoo.jdisc.Metric; -import com.yahoo.jdisc.Request; -import com.yahoo.jdisc.Response; -import com.yahoo.jdisc.handler.CompletionHandler; -import com.yahoo.jdisc.handler.ContentChannel; -import com.yahoo.jdisc.handler.ResponseHandler; -import com.yahoo.jdisc.http.HttpResponse; - -import java.net.ConnectException; -import java.nio.ByteBuffer; -import java.util.concurrent.TimeoutException; - -/** - * @author <a href="mailto:vikasp@yahoo-inc.com">Vikas Panwar</a> - */ -class WebSocketHandler implements WebSocketByteListener { - - private final CompletionHandler abortOnFailure = new AbortOnFailure(); - private final Metric metric; - private final Metric.Context metricCtx; - private final Request request; - private final ResponseHandler responseHandler; - private ContentChannel content; - private boolean aborted = false; - - public WebSocketHandler(Request request, ResponseHandler responseHandler, Metric metric, Metric.Context ctx) { - this.request = request; - this.responseHandler = responseHandler; - this.metric = metric; - this.metricCtx = ctx; - } - - @Override - public synchronized void onOpen(WebSocket webSocket) { - // ignore, open on first fragment to allow failures to propagate - } - - @Override - public synchronized void onMessage(byte[] bytes) { - if (aborted) { - return; - } - if (content == null) { - dispatchResponse(); - } - // need to copy the bytes into a new buffer since there is no declared ownership of the array - content.write((ByteBuffer)ByteBuffer.allocate(bytes.length).put(bytes).flip(), abortOnFailure); - } - - @Override - public synchronized void onFragment(byte[] bytes, boolean last) { - // ignore, write messages instead - } - - @Override - public synchronized void onClose(WebSocket webSocket) { - if (aborted) { - return; - } - if (content == null) { - dispatchResponse(); - } - content.close(abortOnFailure); - } - - @Override - public synchronized void onError(Throwable t) { - abort(t); - } - - private void dispatchResponse() { - content = responseHandler.handleResponse(HttpResponse.newInstance(Response.Status.OK)); - } - - private synchronized void abort(Throwable t) { - if (aborted) { - return; - } - aborted = true; - updateErrorMetric(t); - if (content == null) { - dispatchErrorResponse(t); - } - if (content != null) { - terminateContent(); - } - } - - private void updateErrorMetric(Throwable t) { - try { - if (t instanceof ConnectException) { - metric.add(HttpClient.Metrics.CONNECTION_EXCEPTIONS, 1, metricCtx); - } else if (t instanceof TimeoutException) { - metric.add(HttpClient.Metrics.TIMEOUT_EXCEPTIONS, 1, metricCtx); - } else { - metric.add(HttpClient.Metrics.OTHER_EXCEPTIONS, 1, metricCtx); - } - } catch (Exception e) { - // ignore - } - } - - private void dispatchErrorResponse(Throwable t) { - int status; - if (t instanceof ConnectException) { - status = com.yahoo.jdisc.Response.Status.SERVICE_UNAVAILABLE; - } else if (t instanceof TimeoutException) { - status = com.yahoo.jdisc.Response.Status.REQUEST_TIMEOUT; - } else { - status = com.yahoo.jdisc.Response.Status.BAD_REQUEST; - } - try { - content = responseHandler.handleResponse(HttpResponse.newError(request, status, t)); - } catch (Exception e) { - // ignore - } - } - - private void terminateContent() { - try { - content.close(IgnoreFailure.INSTANCE); - } catch (Exception e) { - // ignore - } - } - - private class AbortOnFailure implements CompletionHandler { - - @Override - public void completed() { - - } - - @Override - public void failed(Throwable t) { - abort(t); - } - } - - private static class IgnoreFailure implements CompletionHandler { - - final static IgnoreFailure INSTANCE = new IgnoreFailure(); - - @Override - public void completed() { - - } - - @Override - public void failed(Throwable t) { - - } - } -}
\ No newline at end of file diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java index 40db4bba661..2af7646e905 100644 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/JDiscHttpServlet.java @@ -4,36 +4,30 @@ package com.yahoo.jdisc.http.server.jetty; import com.yahoo.container.logging.AccessLogEntry; import com.yahoo.jdisc.Metric; import com.yahoo.jdisc.handler.OverloadException; - import org.eclipse.jetty.server.HttpConnection; -import org.eclipse.jetty.websocket.server.WebSocketServerFactory; -import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; -import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; -import org.eclipse.jetty.websocket.servlet.WebSocketCreator; -import org.eclipse.jetty.websocket.servlet.WebSocketServlet; -import org.eclipse.jetty.websocket.servlet.WebSocketServletFactory; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.annotation.WebServlet; +import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; - import java.io.IOException; import java.util.Arrays; import java.util.Enumeration; import java.util.HashSet; import java.util.Set; -import java.util.logging.Logger; import java.util.logging.Level; +import java.util.logging.Logger; import static com.yahoo.jdisc.http.server.jetty.ConnectorFactory.JDiscServerConnector; /** * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> + * @author bjorncs */ @WebServlet(asyncSupported = true, description = "Bridge between Servlet and JDisc APIs") -class JDiscHttpServlet extends WebSocketServlet { +class JDiscHttpServlet extends HttpServlet { public static final String ATTRIBUTE_NAME_ACCESS_LOG_ENTRY = JDiscHttpServlet.class.getName() + "_access-log-entry"; @@ -45,16 +39,6 @@ class JDiscHttpServlet extends WebSocketServlet { } @Override - public void init() throws ServletException { - // The parent class of this loads the WebSocketServerFactory class using Class.forName() in the current thread's - // context class loader. To make sure that the class is available when running on OSGi, we configure it - // explicitly. This also has the required side-effect of generating the appropriate Import-Package statement in - // our OSGi bundle's manifest. - Thread.currentThread().setContextClassLoader(WebSocketServerFactory.class.getClassLoader()); - super.init(); - } - - @Override protected void doGet(final HttpServletRequest request, final HttpServletResponse response) throws ServletException, IOException { dispatchHttpRequest(request, response); @@ -96,11 +80,6 @@ class JDiscHttpServlet extends WebSocketServlet { dispatchHttpRequest(request, response); } - @Override - public void configure(final WebSocketServletFactory factory) { - dispatchWebSocketRequest(factory); - } - private static final Set<String> JETTY_UNSUPPORTED_METHODS = new HashSet<>(Arrays.asList( "PATCH")); @@ -158,30 +137,6 @@ class JDiscHttpServlet extends WebSocketServlet { } } - private void dispatchWebSocketRequest(final WebSocketServletFactory factory) { - try { - // any configuration of the websocket factory goes here - factory.setCreator(new WebSocketCreator() { - - @Override - public Object createWebSocket( - final ServletUpgradeRequest request, - final ServletUpgradeResponse response) { - - if (true) { - log.warning("WebSocket is currently not supported for JDisc RequestHandlers when running on Jetty."); - return null; - } - return new WebSocketRequestDispatch(context.container, context.janitor, context.metric, - getMetricContext(request.getHttpServletRequest())) - .dispatch(request, response); - } - }); - } catch (RuntimeException e) { - throw new ExceptionWrapper(e); - } - } - private static Metric.Context getMetricContext(ServletRequest request) { return JDiscServerConnector.fromRequest(request) .getMetricContext(); diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java index 518c9f92ea8..e601b31f7ab 100644 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java +++ b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/MetricReporter.java @@ -11,7 +11,7 @@ import java.util.concurrent.atomic.AtomicBoolean; /** - * Responsible for metric reporting for JDisc http and web socket request handler support. + * Responsible for metric reporting for JDisc http request handler support. * @author tonytv */ public class MetricReporter { diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/WebSocketRequestDispatch.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/WebSocketRequestDispatch.java deleted file mode 100644 index 6b24996e23d..00000000000 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/WebSocketRequestDispatch.java +++ /dev/null @@ -1,417 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.server.jetty; - -import com.google.common.base.Preconditions; -import com.yahoo.jdisc.Metric; -import com.yahoo.jdisc.References; -import com.yahoo.jdisc.Request; -import com.yahoo.jdisc.ResourceReference; -import com.yahoo.jdisc.Response; -import com.yahoo.jdisc.handler.AbstractRequestHandler; -import com.yahoo.jdisc.handler.CompletionHandler; -import com.yahoo.jdisc.handler.ContentChannel; -import com.yahoo.jdisc.handler.RequestHandler; -import com.yahoo.jdisc.handler.ResponseHandler; -import com.yahoo.jdisc.http.HttpRequest; -import com.yahoo.jdisc.service.CurrentContainer; -import org.eclipse.jetty.websocket.api.Session; -import org.eclipse.jetty.websocket.api.StatusCode; -import org.eclipse.jetty.websocket.api.WebSocketAdapter; -import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; -import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; - -import javax.annotation.concurrent.GuardedBy; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.ArrayDeque; -import java.util.Deque; -import java.util.Objects; -import java.util.concurrent.Executor; -import java.util.concurrent.atomic.AtomicReference; -import java.util.logging.Level; -import java.util.logging.Logger; - -import static com.yahoo.jdisc.http.server.jetty.CompletionHandlerUtils.NOOP_COMPLETION_HANDLER; - -/** - * @author <a href="mailto:einarmr@yahoo-inc.com">Einar M R Rosenvinge</a> - * @since 5.17.0 - */ -class WebSocketRequestDispatch extends WebSocketAdapter { - - private final static Logger log = Logger.getLogger(WebSocketRequestDispatch.class.getName()); - private final static ByteBuffer EMPTY_BUFFER = ByteBuffer.allocate(0); - - private final AtomicReference<Object> responseRef = new AtomicReference<>(); - private final CurrentContainer container; - private final Executor janitor; - private final RequestHandler requestHandler; - private final Metric metric; - private final Metric.Context metricCtx; - private final Object lock = new Object(); - private final CompletionHandler failureHandlingCompletionHandler = new CompletionHandler() { - @Override - public void completed() { - } - - @Override - public void failed(final Throwable t) { - synchronized (lock) { - fail_holdingLock(t); - } - } - }; - - @GuardedBy("lock") - private final Deque<ResponseContentPart> responseContentQueue = new ArrayDeque<>(); - @GuardedBy("lock") - private ContentChannel requestContent; - @GuardedBy("lock") - private Throwable failure; - @GuardedBy("lock") - private boolean writingResponse = false; - @GuardedBy("lock") - private boolean connected; - - public WebSocketRequestDispatch( - final CurrentContainer container, - final Executor janitor, - final Metric metric, - final Metric.Context metricCtx) { - Objects.requireNonNull(janitor, "janitor"); - Objects.requireNonNull(metric, "metric"); - this.container = container; - this.requestHandler = new AbstractRequestHandler() { - @Override - public ContentChannel handleRequest(final Request request, final ResponseHandler handler) { - return request.connect(handler); - } - }; - this.janitor = janitor; - this.metric = metric; - this.metricCtx = metricCtx; - } - - @SuppressWarnings("try") - public WebSocketRequestDispatch dispatch(final ServletUpgradeRequest servletRequest, - final ServletUpgradeResponse servletResponse) { - final HttpRequest jdiscRequest = WebSocketRequestFactory.newJDiscRequest(container, servletRequest); - try (final ResourceReference ref = References.fromResource(jdiscRequest)) { - WebSocketRequestFactory.copyHeaders(servletRequest, jdiscRequest); - dispatchRequestWithoutThrowing(jdiscRequest); - } - final Response jdiscResponse = (Response)responseRef.getAndSet(new Object()); - if (jdiscResponse != null) { - log.finer("Applying sync " + jdiscResponse.getStatus() + " response to websocket response."); - servletResponse.setStatus(jdiscResponse.getStatus()); - WebSocketRequestFactory.copyHeaders(jdiscResponse, servletResponse); - } - return this; - } - - @Override - public void onWebSocketBinary(final byte[] arr, final int off, final int len) { - writeRequestContentWithoutThrowing(ByteBuffer.wrap(arr, off, len)); - } - - @Override - public void onWebSocketText(final String message) { - writeRequestContentWithoutThrowing(StandardCharsets.UTF_8.encode(message)); - } - - @Override - public void onWebSocketConnect(final Session session) { - super.onWebSocketConnect(session); - synchronized (lock) { - connected = true; - if (writingResponse) { - return; - } - writingResponse = true; - } - writeNextResponseContent(); - } - - /** - * This is ALWAYS called. - * ...if the remote side closes the connection - * ...if we c*ck up ourselves and throw an exception out of onWebSocketBinary() or onWebSocketText(), - * Jetty calls Session.close on our behalf (later followed by a call to onWebSocketError) - * - * TODO: Test below - * ...and also whenever we call Session.close() ourselves?? - * - * @param statusCode The {@link StatusCode} of the close. - * @param reason The reason text for the close. - */ - @Override - public void onWebSocketClose(final int statusCode, final String reason) { - super.onWebSocketClose(statusCode, reason); - final ContentChannel requestContentChannel; - synchronized (lock) { - Preconditions.checkState(requestContent != null || failure != null, - "requestContent should be non-null if we haven't had a failure"); - if (requestContent == null) { - return; - } - if (failure != null) { - // Request content will be closed as a result of the failure handling. - return; - } - requestContentChannel = requestContent; - requestContent = null; - } - try { - requestContentChannel.close(failureHandlingCompletionHandler); - } catch (final Throwable t) { - fail(t); - } - } - - /** - * <p>No need to call Session.close() here, that has been done or will be done by Jetty.</p> - * - * @param t The cause of the error. - */ - @Override - public void onWebSocketError(final Throwable t) { - fail(t); - } - - private void dispatchRequestWithoutThrowing(final Request request) { - final ContentChannel returnedContentChannel; - try { - returnedContentChannel = requestHandler.handleRequest(request, new GatedResponseHandler()); - } catch (final Throwable t) { - fail(t); - throw new IllegalStateException(t); - } - synchronized (lock) { - Preconditions.checkState(requestContent == null, "requestContent should be null"); - if (failure != null) { - // This means that request.connect() caused a synchronous failure. in this case - // the cleanup happened before requestContent was assigned, so we must clean it explicitly here - closeLater(returnedContentChannel); - throw new IllegalStateException(failure); - } - requestContent = returnedContentChannel; - } - } - - private void writeRequestContentWithoutThrowing(final ByteBuffer buf) { - int bytes_received = buf.remaining(); - metric.set(JettyHttpServer.Metrics.NUM_BYTES_RECEIVED, bytes_received, metricCtx); - metric.set(JettyHttpServer.Metrics.MANHATTAN_NUM_BYTES_RECEIVED, bytes_received, metricCtx); - final ContentChannel requestContentChannel; - synchronized (lock) { - Preconditions.checkState(requestContent != null, "requestContent should be non-null"); - if (failure != null) { - return; - } - requestContentChannel = requestContent; - } - try { - requestContentChannel.write(buf, failureHandlingCompletionHandler); - } catch (final Throwable t) { - fail(t); - } - } - - private void fail(final Throwable t) { - synchronized (lock) { - fail_holdingLock(t); - } - } - - private void tryWriteResponseContent(final ByteBuffer buf, final CompletionHandler handler) { - synchronized (lock) { - if (failure != null) { - failLater(handler, failure); - return; - } - responseContentQueue.addLast(new ResponseContentPart(buf, handler)); - if (writingResponse) { - return; - } - writingResponse = true; - } - writeNextResponseContent(); - } - - private void writeNextResponseContent() { - while (true) { - final ResponseContentPart part; - synchronized (lock) { - if (!connected) { - // We expect a later invocation of onWebSocketConnect(). That will invoke this method again. - writingResponse = false; - return; - } - if (responseContentQueue.isEmpty()) { - writingResponse = false; - return; // application will call later - } - part = responseContentQueue.poll(); - } - if (part.handler != null) { - try { - part.handler.completed(); - } catch (final Throwable t) { - fail(t); - return; - } - } - final boolean isClosePart = part.buf == null; - if (isClosePart) { - return; - } - try { - getRemote().sendBytesByFuture(part.buf); - } catch (final Throwable t) { - fail(t); - } - } - } - - private void fail_holdingLock(final Throwable failure) { - if (this.failure != null) { - return; - } - this.failure = failure; - if (requestContent != null) { - closeLater(requestContent); - } - requestContent = null; - for (ResponseContentPart part = responseContentQueue.poll(); part != null; part = responseContentQueue.poll()) { - failLater(part.handler, failure); - } - janitor.execute(() -> { - try { - getSession().close(StatusCode.SERVER_ERROR, failure.toString()); - } catch (final Throwable ignored) { - } - }); - } - - private void closeLater(final ContentChannel content) { - janitor.execute(() -> { - try { - content.close(NOOP_COMPLETION_HANDLER); - } catch (final Throwable ignored) { - } - }); - } - - private void failLater(final CompletionHandler handler, final Throwable failure) { - if (handler == null) { - return; - } - - final Throwable failureWithStack = new IllegalStateException(failure); - janitor.execute(() -> { - try { - handler.failed(failureWithStack); - } catch (final Throwable t) { - log.log(Level.WARNING, "Failure handling of " + failure + - " in application threw an exception.", t); - } - }); - } - - private class GatedResponseHandler implements ResponseHandler { - - @Override - public ContentChannel handleResponse(final Response response) { - synchronized (lock) { - if (failure != null) { - return new FailedResponseContent(new IllegalStateException(failure)); - } - } - final boolean firstToSetResponse = responseRef.compareAndSet(null, response); - if (!firstToSetResponse) { - log.finer("Ignoring async " + response.getStatus() + " response because sync websocket response has " + - "already been returned to client."); - // TODO(bakksjo): The message above is not necessarily correct. Getting here does not necessarily - // mean that a sync response has been returned to the client. It may just mean that dispatch() is - // finished, and the request handler's handleRequest() has been run. That does not mean that the - // request handler actually produced a sync response. If a response is produced asynchronously, we - // may get here and ignore that response. TODO: Analyze wire traffic. Maybe Jetty produces a response - // after dispatch(), even if we don't do it in our code. Besides, is the status code used for anything - // by the client anyway? Is it even available in client WebSocket implementations? - } - return new GatedResponseContent(); - } - } - - private class GatedResponseContent implements ContentChannel { - - @Override - public void write(final ByteBuffer raw, final CompletionHandler handler) { - final ByteBuffer buf = raw != null ? raw : EMPTY_BUFFER; - int bytesSent = buf.remaining(); - metric.set(JettyHttpServer.Metrics.NUM_BYTES_SENT, bytesSent, metricCtx); - metric.set(JettyHttpServer.Metrics.MANHATTAN_NUM_BYTES_SENT, bytesSent, metricCtx); - tryWriteResponseContent(buf, new MetricCompletionHandler(handler)); - } - - @Override - public void close(final CompletionHandler handler) { - // The only reason to let this synthetic 'part' go into the queue is to have the completion handler - // for close() invoked in order (after the completion handlers for enqueued parts. - tryWriteResponseContent(null, new MetricCompletionHandler(handler)); - } - } - - private class FailedResponseContent implements ContentChannel { - - final Throwable failure; - - FailedResponseContent(final Throwable failure) { - this.failure = failure; - } - - @Override - public void write(final ByteBuffer buf, final CompletionHandler handler) { - failLater(new MetricCompletionHandler(handler), failure); - } - - @Override - public void close(final CompletionHandler handler) { - failLater(new MetricCompletionHandler(handler), failure); - } - } - - private class MetricCompletionHandler implements CompletionHandler { - - final CompletionHandler delegate; - - MetricCompletionHandler(CompletionHandler delegate) { - this.delegate = delegate; - } - - @Override - public void completed() { - metric.add(JettyHttpServer.Metrics.NUM_SUCCESSFUL_WRITES, 1, metricCtx); - if (delegate != null) - delegate.completed(); - } - - @Override - public void failed(Throwable t) { - metric.add(JettyHttpServer.Metrics.NUM_FAILED_WRITES, 1, metricCtx); - if (delegate != null) - delegate.failed(t); - } - } - - private static class ResponseContentPart { - - final ByteBuffer buf; - final CompletionHandler handler; - - ResponseContentPart(final ByteBuffer buf, final CompletionHandler handler) { - this.buf = buf; - this.handler = handler; - } - } -} diff --git a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/WebSocketRequestFactory.java b/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/WebSocketRequestFactory.java deleted file mode 100644 index 8eebc11ce75..00000000000 --- a/jdisc_http_service/src/main/java/com/yahoo/jdisc/http/server/jetty/WebSocketRequestFactory.java +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.server.jetty; - -import com.yahoo.jdisc.Request; -import com.yahoo.jdisc.Response; -import com.yahoo.jdisc.http.HttpRequest; -import com.yahoo.jdisc.service.CurrentContainer; -import org.eclipse.jetty.websocket.servlet.ServletUpgradeRequest; -import org.eclipse.jetty.websocket.servlet.ServletUpgradeResponse; - -import java.net.InetSocketAddress; -import java.util.Map; - -/** - * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> - */ -class WebSocketRequestFactory { - - public static HttpRequest newJDiscRequest(final CurrentContainer container, - final ServletUpgradeRequest servletRequest) { - return HttpRequest.newServerRequest( - container, - servletRequest.getRequestURI(), - HttpRequest.Method.valueOf(servletRequest.getMethod()), - HttpRequest.Version.fromString(servletRequest.getHttpVersion()), - new InetSocketAddress(servletRequest.getRemoteAddress(), servletRequest.getRemotePort())); - } - - public static void copyHeaders(final ServletUpgradeRequest from, final Request to) { - to.headers().addAll(from.getHeaders()); - } - - public static void copyHeaders(final Response from, final ServletUpgradeResponse to) { - for (final Map.Entry<String, String> entry : from.headers().entries()) { - to.addHeader(entry.getKey(), entry.getValue()); - } - } -} diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketClientRequestTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketClientRequestTestCase.java deleted file mode 100644 index 3f9912fda33..00000000000 --- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketClientRequestTestCase.java +++ /dev/null @@ -1,30 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.client; - -import com.ning.http.client.AsyncHttpClient; -import com.yahoo.jdisc.Metric; -import com.yahoo.jdisc.Request; -import com.yahoo.jdisc.handler.ContentChannel; -import com.yahoo.jdisc.handler.ResponseHandler; -import org.mockito.Mockito; -import org.testng.annotations.Test; - -import static org.testng.AssertJUnit.assertTrue; - -/** - * @author <a href="mailto:vikasp@yahoo-inc.com">Vikas Panwar</a> - */ -public class WebSocketClientRequestTestCase { - - @Test(enabled = false) - public void testWebSocketRequestReturnsCorrectContentChannel() { - AsyncHttpClient client = Mockito.mock(AsyncHttpClient.class); - Request request = Mockito.mock(Request.class); - ResponseHandler respHandler = Mockito.mock(ResponseHandler.class); - Metric metric = Mockito.mock(Metric.class); - Metric.Context ctx = Mockito.mock(Metric.Context.class); - - ContentChannel cc = WebSocketClientRequest.executeRequest(client, request, respHandler, metric, ctx); - assertTrue(cc instanceof WebSocketContent); - } -} diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketContentTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketContentTestCase.java deleted file mode 100644 index 4ab851ac5b9..00000000000 --- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketContentTestCase.java +++ /dev/null @@ -1,97 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.client; - -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.ListenableFuture; -import com.ning.http.client.Request; -import com.ning.http.client.websocket.WebSocket; -import com.ning.http.client.websocket.WebSocketUpgradeHandler; -import com.yahoo.jdisc.handler.CompletionHandler; -import org.mockito.Mockito; -import org.testng.annotations.Test; - -import java.net.URI; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; - -import static org.testng.AssertJUnit.fail; - -/** - * @author <a href="mailto:vikasp@yahoo-inc.com">Vikas Panwar</a> - */ -@SuppressWarnings("unchecked") -public class WebSocketContentTestCase { - - private final byte[] TEST_DATA = "test data".getBytes(StandardCharsets.UTF_8); - - @Test(enabled = false) - public void testContentChannelWriteAndClose() throws Exception{ - AsyncHttpClient client = Mockito.mock(AsyncHttpClient.class); - com.yahoo.jdisc.Request request = Mockito.mock(com.yahoo.jdisc.Request.class); - Mockito.when(request.getUri()).thenReturn(new URI("")); - - WebSocket websocket = Mockito.mock(WebSocket.class); - Mockito.when(websocket.isOpen()).thenReturn(true); - ListenableFuture<WebSocket> future = Mockito.mock(ListenableFuture.class); - Mockito.when(client.executeRequest((Request)Mockito.isNotNull(), (WebSocketUpgradeHandler)Mockito.anyObject())) - .thenReturn(future); - Mockito.when(future.get()).thenReturn(websocket); - - WebSocketContent underTest = new WebSocketContent(client, request, Mockito.mock(WebSocketUpgradeHandler.class)); - - CompletionHandler completionHandler = Mockito.mock(CompletionHandler.class); - underTest.write(ByteBuffer.wrap(TEST_DATA),completionHandler); - - Mockito.verify(completionHandler,Mockito.atLeastOnce()).completed(); - - CompletionHandler closeHandler = Mockito.mock(CompletionHandler.class); - underTest.close(closeHandler); - Mockito.verify(closeHandler).completed(); - Mockito.verify(websocket).close(); - Mockito.verify(websocket).sendMessage(TEST_DATA); - } - - @Test(enabled = false) - public void testWritingToAClosedContentChannel() throws Exception{ - AsyncHttpClient client = Mockito.mock(AsyncHttpClient.class); - com.yahoo.jdisc.Request request = Mockito.mock(com.yahoo.jdisc.Request.class); - Mockito.when(request.getUri()).thenReturn(new URI("")); - WebSocket websocket = Mockito.mock(WebSocket.class); - ListenableFuture<WebSocket> future = Mockito.mock(ListenableFuture.class); - Mockito.when(client.executeRequest((Request)Mockito.isNotNull(), (WebSocketUpgradeHandler)Mockito.anyObject())) - .thenReturn(future); - Mockito.when(future.get()).thenReturn(websocket); - - WebSocketContent underTest = new WebSocketContent(client, request, Mockito.mock(WebSocketUpgradeHandler.class)); - underTest.close(Mockito.mock(CompletionHandler.class)); - - // opens a new websocket - underTest.write(ByteBuffer.wrap(TEST_DATA), Mockito.mock(CompletionHandler.class)); - } - - @Test(enabled = false) - public void testExceptionalPathInExecuteRequest() throws Exception{ - AsyncHttpClient client = Mockito.mock(AsyncHttpClient.class); - com.yahoo.jdisc.Request request = Mockito.mock(com.yahoo.jdisc.Request.class); - Mockito.when(request.getUri()).thenReturn(new URI("")); - - WebSocket websocket = Mockito.mock(WebSocket.class); - Mockito.when(websocket.isOpen()).thenReturn(true); - ListenableFuture<WebSocket> future = Mockito.mock(ListenableFuture.class); - Mockito.when(client.executeRequest((Request)Mockito.isNotNull(), (WebSocketUpgradeHandler)Mockito.anyObject())) - .thenReturn(future); - Mockito.when(future.get()).thenReturn(websocket); - Mockito.when(websocket.sendMessage((byte[])Mockito.any())).thenThrow(new RuntimeException()); - - WebSocketContent underTest = new WebSocketContent(client, request, Mockito.mock(WebSocketUpgradeHandler.class)); - - CompletionHandler completionHandler = Mockito.mock(CompletionHandler.class); - - try { - underTest.write(ByteBuffer.wrap(TEST_DATA),completionHandler); - fail(); - } catch(RuntimeException e) { - // Expected - } - } -} diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketHandlerTestCase.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketHandlerTestCase.java deleted file mode 100644 index 68283625579..00000000000 --- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/client/WebSocketHandlerTestCase.java +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.client; - -import com.ning.http.client.websocket.WebSocket; -import com.yahoo.jdisc.Metric; -import com.yahoo.jdisc.Request; -import com.yahoo.jdisc.Response; -import com.yahoo.jdisc.handler.CompletionHandler; -import com.yahoo.jdisc.handler.ContentChannel; -import com.yahoo.jdisc.handler.FutureResponse; -import com.yahoo.jdisc.handler.ReadableContentChannel; -import com.yahoo.jdisc.handler.ResponseHandler; -import com.yahoo.jdisc.http.HttpResponse; -import org.mockito.Mockito; -import org.testng.annotations.Test; - -import java.net.ConnectException; -import java.nio.ByteBuffer; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import static org.testng.AssertJUnit.assertEquals; -import static org.testng.AssertJUnit.assertFalse; -import static org.testng.AssertJUnit.assertNull; -import static org.testng.AssertJUnit.assertTrue; -import static org.testng.AssertJUnit.fail; - -/** - * @author <a href="mailto:vikasp@yahoo-inc.com">Vikas Panwar</a> - */ -public class WebSocketHandlerTestCase { - - @Test(enabled = false) - public void requireThatOnOpenDoesNothing() { - ResponseHandler responseHandler = Mockito.mock(ResponseHandler.class); - newSocketHandler(responseHandler).onOpen(Mockito.mock(WebSocket.class)); - Mockito.verifyZeroInteractions(responseHandler); - } - - @Test(enabled = false) - public void requireThatOnFragmentDoesNothing() { - ResponseHandler responseHandler = Mockito.mock(ResponseHandler.class); - newSocketHandler(responseHandler).onFragment(new byte[] { 6, 9 }, false); - Mockito.verifyZeroInteractions(responseHandler); - } - - @Test(enabled = false) - public void requireThatOnLastFragmentDoesNothing() { - ResponseHandler responseHandler = Mockito.mock(ResponseHandler.class); - newSocketHandler(responseHandler).onFragment(new byte[] { 6, 9 }, true); - Mockito.verifyZeroInteractions(responseHandler); - } - - @Test(enabled = false) - public void requireThatResponseIsDispatchedOnFirstMessage() throws Exception { - ReadableContentChannel content = new ReadableContentChannel(); - FutureResponse responseHandler = new FutureResponse(content); - try { - responseHandler.get(100, TimeUnit.MILLISECONDS); - fail(); - } catch (TimeoutException e) { - - } - newSocketHandler(responseHandler).onMessage(new byte[] { 6, 9 }); - Response response = responseHandler.get(60, TimeUnit.SECONDS); - assertTrue(response instanceof HttpResponse); - assertEquals(Response.Status.OK, response.getStatus()); - } - - @Test(enabled = false) - public void requireThatResponseBytesAreWritten() { - ReadableContentChannel content = new ReadableContentChannel(); - newSocketHandler(content).onMessage(new byte[] { 6, 9 }); - ByteBuffer buf = content.read(); - assertEquals(2, buf.remaining()); - assertEquals(6, buf.get()); - assertEquals(9, buf.get()); - } - - @Test(enabled = false) - public void requireThatEmptyResponsesCanBeSent() throws Exception { - ReadableContentChannel content = new ReadableContentChannel(); - FutureResponse responseHandler = new FutureResponse(content); - newSocketHandler(responseHandler).onClose(Mockito.mock(WebSocket.class)); - assertResponse(responseHandler, Response.Status.OK); - assertNull(content.read()); - } - - @Test(enabled = false) - public void requireThatEarlyErrorRespondsWithError() throws Exception { - assertErrorResponse(new ConnectException(), Response.Status.SERVICE_UNAVAILABLE); - assertErrorResponse(new TimeoutException(), Response.Status.REQUEST_TIMEOUT); - assertErrorResponse(new Throwable(), Response.Status.BAD_REQUEST); - } - - @Test(enabled = false) - public void requireThatWriteCompletionFailureClosesResponseContent() { - CloseableContentChannel content = new CloseableContentChannel(); - FutureResponse responseHandler = new FutureResponse(content); - WebSocketHandler socketHandler = newSocketHandler(responseHandler); - socketHandler.onMessage(new byte[] { 6, 9 }); - assertFalse(content.closed); - content.handler.failed(new Throwable()); - assertTrue(content.closed); - } - - private static void assertErrorResponse(Throwable t, int expectedStatus) throws Exception { - ReadableContentChannel content = new ReadableContentChannel(); - FutureResponse responseHandler = new FutureResponse(content); - newSocketHandler(responseHandler).onError(t); - assertResponse(responseHandler, expectedStatus); - assertNull(content.read()); - } - - private static void assertResponse(FutureResponse responseHandler, int expectedStatus) throws Exception { - Response response = responseHandler.get(60, TimeUnit.SECONDS); - assertTrue(response instanceof HttpResponse); - assertEquals(expectedStatus, response.getStatus()); - } - - private static WebSocketHandler newSocketHandler(ResponseHandler responseHandler) { - return new WebSocketHandler(Mockito.mock(Request.class), responseHandler, Mockito.mock(Metric.class), - Mockito.mock(Metric.Context.class)); - } - - private static WebSocketHandler newSocketHandler(ContentChannel responseContent) { - ResponseHandler responseHandler = Mockito.mock(ResponseHandler.class); - Mockito.when(responseHandler.handleResponse(Mockito.any(Response.class))).thenReturn(responseContent); - return new WebSocketHandler(Mockito.mock(Request.class), responseHandler, Mockito.mock(Metric.class), - Mockito.mock(Metric.Context.class)); - } - - private static class CloseableContentChannel implements ContentChannel { - - CompletionHandler handler; - boolean closed = false; - - @Override - public void write(ByteBuffer buf, CompletionHandler handler) { - this.handler = handler; - } - - @Override - public void close(CompletionHandler handler) { - closed = true; - } - } -} diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleWebSocketClient.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleWebSocketClient.java deleted file mode 100644 index 9d0aa02bbfe..00000000000 --- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/SimpleWebSocketClient.java +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.server.jetty; - -import com.ning.http.client.AsyncHttpClient; -import com.ning.http.client.AsyncHttpClientConfig; -import com.ning.http.client.ListenableFuture; -import com.ning.http.client.providers.grizzly.GrizzlyAsyncHttpProvider; -import com.ning.http.client.websocket.WebSocket; -import com.ning.http.client.websocket.WebSocketListener; -import com.ning.http.client.websocket.WebSocketUpgradeHandler; - -import javax.net.ssl.SSLContext; -import java.io.IOException; - -/** - * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> - */ -class SimpleWebSocketClient { - - private final AsyncHttpClient client; - private final String scheme; - private final int listenPort; - - public SimpleWebSocketClient(final TestDriver driver) { - this(driver.newSslContext(), driver.server().getListenPort()); - } - - public SimpleWebSocketClient(final SSLContext sslContext, final int listenPort) { - final AsyncHttpClientConfig config = new AsyncHttpClientConfig.Builder().setSSLContext(sslContext).build(); - this.client = new AsyncHttpClient(new GrizzlyAsyncHttpProvider(config), config); - this.scheme = sslContext != null ? "wss" : "ws"; - this.listenPort = listenPort; - } - - public ListenableFuture<WebSocket> executeRequest(final String path, final WebSocketListener listener) - throws IOException { - return client.prepareGet(scheme + "://localhost:" + listenPort + path) - .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(listener).build()); - } - - public boolean close() { - client.close(); - return true; - } -} diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/WebSocketServerConformanceTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/WebSocketServerConformanceTest.java deleted file mode 100644 index 8a6ea28f2f9..00000000000 --- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/WebSocketServerConformanceTest.java +++ /dev/null @@ -1,766 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.server.jetty; - -import com.google.common.util.concurrent.SettableFuture; -import com.google.inject.AbstractModule; -import com.google.inject.Module; -import com.google.inject.util.Modules; -import com.ning.http.client.websocket.WebSocket; -import com.ning.http.client.websocket.WebSocketByteListener; -import com.yahoo.jdisc.application.BindingRepository; -import com.yahoo.jdisc.http.ServerConfig; -import com.yahoo.jdisc.http.filter.RequestFilter; -import com.yahoo.jdisc.http.filter.ResponseFilter; -import com.yahoo.jdisc.http.guiceModules.ConnectorFactoryRegistryModule; -import com.yahoo.jdisc.http.server.FilterBindings; -import com.yahoo.jdisc.test.ServerProviderConformanceTest; -import org.glassfish.grizzly.websockets.HandshakeException; -import org.hamcrest.Matcher; -import org.testng.annotations.Test; - -import java.io.ByteArrayOutputStream; -import java.io.Closeable; -import java.io.IOException; -import java.nio.ByteBuffer; -import java.nio.charset.StandardCharsets; -import java.util.Collections; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.atomic.AtomicReference; - -import static org.hamcrest.CoreMatchers.instanceOf; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.CoreMatchers.nullValue; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> - */ -//Ignore: Broken by jetty 9.2.{3,4} -class WebSocketServerConformanceTestIgnored extends ServerProviderConformanceTest { - - /* Some tests here are disabled. What they have in common is that the scenario - * involves waiting for an event (response write) in the request content channel's close() - * method, but Jetty will sometimes use the thread that is supposed to generate that event - * (the thread that writes the response) to deliver the close() notification, causing a - * deadlock. - * - * All in all, the WebSocket protocol doesn't map beautifully to JDisc APIs, which makes - * it hard to do proper testing here. Specifically, in order to cause the request content - * channel to be closed, we have to close the socket from the client side, which means - * that all bets are off regarding what response the client will see. So, the tests here - * that close the socket early can do no verification at all. However, it will be - * verified by the test framework that the server-side request processing finishes - * without any unexpected side effects. - */ - - @Override - @Test - public void testContainerNotReadyException() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testBindingSetNotFoundException() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testNoBindingSetSelectedException() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testBindingNotFoundException() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testRequestHandlerWithSyncCloseResponse() throws Throwable { - new TestRunner().expectResponseContent(is("myResponseContent")) - .execute(); - } - - @Override - @Test - public void testRequestHandlerWithSyncWriteResponse() throws Throwable { - new TestRunner().expectResponseContent(is("myResponseContent")) - .execute(); - } - - @Override - @Test - public void testRequestHandlerWithSyncHandleResponse() throws Throwable { - new TestRunner().expectResponseContent(is("myResponseContent")) - .execute(); - } - - @Override - @Test - public void testRequestHandlerWithAsyncHandleResponse() throws Throwable { - new TestRunner().expectResponseContent(is("myResponseContent")) - .execute(); - } - - @Override - @Test - public void testRequestException() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testRequestExceptionWithSyncCloseResponse() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testRequestExceptionWithSyncWriteResponse() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testRequestNondeterministicExceptionWithSyncHandleResponse() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestExceptionBeforeResponseWriteWithSyncHandleResponse() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testRequestExceptionAfterResponseWriteWithSyncHandleResponse() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestNondeterministicExceptionWithAsyncHandleResponse() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestExceptionBeforeResponseWriteWithAsyncHandleResponse() throws Throwable { - new TestRunner().expectedError(instanceOf(HandshakeException.class)) - .execute(); - } - - @Override - @Test - public void testRequestExceptionAfterResponseCloseNoContentWithAsyncHandleResponse() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestExceptionAfterResponseWriteWithAsyncHandleResponse() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteWithSyncCompletion() throws Throwable { - new TestRunner().expectResponseContent(is("myResponseContent")) - .execute(); - } - - @Override - @Test - public void testRequestContentWriteWithAsyncCompletion() throws Throwable { - new TestRunner().expectResponseContent(is("myResponseContent")) - .execute(); - } - - @Override - @Test - public void testRequestContentWriteWithNondeterministicSyncFailure() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteWithSyncFailureBeforeResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteWithSyncFailureAfterResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteWithNondeterministicAsyncFailure() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteWithAsyncFailureBeforeResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteWithAsyncFailureAfterResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteWithAsyncFailureAfterResponseCloseNoContent() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteNondeterministicException() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionBeforeResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionAfterResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionAfterResponseCloseNoContent() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteNondeterministicExceptionWithSyncCompletion() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionBeforeResponseWriteWithSyncCompletion() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionAfterResponseWriteWithSyncCompletion() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionAfterResponseCloseNoContentWithSyncCompletion() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteNondeterministicExceptionWithAsyncCompletion() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionBeforeResponseWriteWithAsyncCompletion() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionAfterResponseWriteWithAsyncCompletion() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionAfterResponseCloseNoContentWithAsyncCompletion() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionWithNondeterministicSyncFailure() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionWithSyncFailureBeforeResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionWithSyncFailureAfterResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionWithSyncFailureAfterResponseCloseNoContent() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionWithNondeterministicAsyncFailure() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionWithAsyncFailureBeforeResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionWithAsyncFailureAfterResponseWrite() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentWriteExceptionWithAsyncFailureAfterResponseCloseNoContent() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentCloseWithSyncCompletion() throws Throwable { - new TestRunner().expectResponseContent(is("myResponseContent")) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseWithAsyncCompletion() throws Throwable { - new TestRunner().expectResponseContent(is("myResponseContent")) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseWithNondeterministicSyncFailure() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentCloseWithSyncFailureBeforeResponseWrite() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test(enabled = false) - public void testRequestContentCloseWithSyncFailureAfterResponseWrite() throws Throwable { - } - - @Override - @Test - public void testRequestContentCloseWithSyncFailureAfterResponseCloseNoContent() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseWithNondeterministicAsyncFailure() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseWithAsyncFailureBeforeResponseWrite() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test(enabled = false) - public void testRequestContentCloseWithAsyncFailureAfterResponseWrite() throws Throwable { - } - - @Override - @Test - public void testRequestContentCloseWithAsyncFailureAfterResponseCloseNoContent() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseNondeterministicException() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseExceptionBeforeResponseWrite() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test(enabled = false) - public void testRequestContentCloseExceptionAfterResponseWrite() throws Throwable { - } - - @Override - @Test - public void testRequestContentCloseExceptionAfterResponseCloseNoContent() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseNondeterministicExceptionWithSyncCompletion() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseExceptionBeforeResponseWriteWithSyncCompletion() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test(enabled = false) - public void testRequestContentCloseExceptionAfterResponseWriteWithSyncCompletion() throws Throwable { - } - - @Override - @Test - public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithSyncCompletion() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseNondeterministicExceptionWithAsyncCompletion() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseExceptionBeforeResponseWriteWithAsyncCompletion() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test(enabled = false) - public void testRequestContentCloseExceptionAfterResponseWriteWithAsyncCompletion() throws Throwable { - } - - @Override - @Test - public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithAsyncCompletion() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseNondeterministicExceptionWithSyncFailure() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentCloseExceptionBeforeResponseWriteWithSyncFailure() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test(enabled = false) - public void testRequestContentCloseExceptionAfterResponseWriteWithSyncFailure() throws Throwable { - } - - @Override - @Test - public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithSyncFailure() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testRequestContentCloseNondeterministicExceptionWithAsyncFailure() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testRequestContentCloseExceptionBeforeResponseWriteWithAsyncFailure() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test(enabled = false) - public void testRequestContentCloseExceptionAfterResponseWriteWithAsyncFailure() throws Throwable { - } - - @Override - @Test - public void testRequestContentCloseExceptionAfterResponseCloseNoContentWithAsyncFailure() throws Throwable { - new TestRunner().setCloseRequestEarly(true) - .execute(); - } - - @Override - @Test - public void testResponseWriteCompletionException() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testResponseCloseCompletionException() throws Throwable { - new TestRunner().execute(); - } - - @Override - @Test - public void testResponseCloseCompletionExceptionNoContent() throws Throwable { - new TestRunner().execute(); - } - - @SuppressWarnings("deprecation") - private class TestRunner implements Adapter<JettyHttpServer, WebSocketClient, Future<String>> { - - Matcher<String> expectedContent = null; - Matcher<Object> expectedError = null; - boolean closeRequestEarly; - - void execute() throws Throwable { - runTest(this); - } - - TestRunner expectResponseContent(final Matcher<String> matcher) { - assertThat(expectedError, is(nullValue())); - expectedContent = matcher; - return this; - } - - TestRunner expectedError(final Matcher<Object> matcher) { - assertThat(expectedContent, is(nullValue())); - expectedError = matcher; - return this; - } - - @Override - public Module newConfigModule() { - return Modules.combine( - new AbstractModule() { - @Override - protected void configure() { - bind(FilterBindings.class) - .toInstance(new FilterBindings( - new BindingRepository<RequestFilter>(), - new BindingRepository<ResponseFilter>())); - bind(ServerConfig.class) - .toInstance(new ServerConfig(new ServerConfig.Builder())); - } - }, - new ConnectorFactoryRegistryModule()); - } - - @Override - public Class<JettyHttpServer> getServerProviderClass() { - return JettyHttpServer.class; - } - - @Override - public WebSocketClient newClient(final JettyHttpServer server) throws Throwable { - return new WebSocketClient(server.getListenPort()); - } - - @Override - public Future<String> executeRequest( - final WebSocketClient client, - final boolean withRequestContent) throws Throwable { - final String requestContent = withRequestContent ? "myRequestContent" : null; - return client.executeRequest(requestContent, closeRequestEarly); - } - - @Override - public Iterable<ByteBuffer> newResponseContent() { - return Collections.singleton(StandardCharsets.UTF_8.encode("myResponseContent")); - } - - @Override - public void validateResponse(final Future<String> responseFuture) throws Throwable { - String content = null; - Throwable error = null; - try { - content = responseFuture.get(60, TimeUnit.SECONDS); - } catch (final ExecutionException e) { - error = e.getCause(); - } - if (expectedContent != null) { - assertThat(content, expectedContent); - } - if (expectedError != null) { - assertThat(error, expectedError); - } - } - - public TestRunner setCloseRequestEarly(final boolean closeRequestEarly) { - this.closeRequestEarly = closeRequestEarly; - return this; - } - } - - private static class WebSocketClient implements Closeable { - - final SimpleWebSocketClient delegate; - - WebSocketClient(final int listenPort) { - delegate = new SimpleWebSocketClient(null, listenPort); - } - - Future<String> executeRequest(final String requestContent, final boolean closeRequest) throws Exception { - final MyWebSocketListener listener = new MyWebSocketListener(requestContent, closeRequest); - delegate.executeRequest("/status.html", listener); - return listener.response; - } - - @Override - public void close() throws IOException { - delegate.close(); - } - } - - // You may find this class slightly ugly, with all the logic to do closing in various places. - // The reason this is necessary is the combination of several things: - // 1) The way WebSocket is implemented in JDisc and mapped to JDisc APIs, specifically: - // - When the client closes a socket, it is not guaranteed to receive anything more from the server - // (the protocol could support it, but neither the client nor server library that we use do) - // - The server won't close a socket until the client does, but by then it is too late for the - // server to send responses. - // 2) The conformance test framework is designed mostly for request-response protocols. It assumes that - // it is self-evident when communication is over, and only _then_ moves on to validating the response. - // - // The problem is that we cannot close the socket right after sending the request, as we are then - // not guaranteed to receive response data (nondeterministic behavior). We cannot close the socket when - // the conformance test framework asks us to validate the response, because we'd never get to that - // - the request processing isn't finished until some party closes the socket! So how do we decide when - // to close the socket? Well, what any "real" client would do is close it when we're satisfied with the - // response. And these tests never return anything more than a single response message, so if we get one, - // we consider ourselves done. Also if we get an error. - private static class MyWebSocketListener implements WebSocketByteListener { - - // This is used to temporarily concatenate response fragments until we have a complete response message. - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - - // This is used to signal that we have received a response, which may be data or an error. - // This is attempted set from multiple code locations, but the first one "wins" (the others are ignored). - final SettableFuture<String> response = SettableFuture.create(); - - // If this is true, the client will close the socket immediately after sending the request content. - // This means that there is no guarantee that the client will receive any response from the server. - // If this is false, the socket is closed after receiving a response from the server. Since the server - // response in theory can be infinitely long, we define "receive a response" as receiving a single message, - // since that is what is sent from the server in these tests. - final boolean closeEarly; - - final byte[] requestContent; - - // We need to be able to close the WebSocket in methods that are not handed the WebSocket instance. - // We use this to keep a reference to it. - private final AtomicReference<WebSocket> webSocketRef = new AtomicReference<>(null); - - MyWebSocketListener(final String requestContent, final boolean closeEarly) { - this.closeEarly = closeEarly; - this.requestContent = requestContent != null ? requestContent.getBytes(StandardCharsets.UTF_8) : null; - } - - @Override - public void onOpen(final WebSocket webSocket) { - this.webSocketRef.set(webSocket); - if (requestContent != null) { - webSocket.sendMessage(requestContent); - } - if (closeEarly) { - webSocket.close(); - } - } - - @Override - public void onClose(final WebSocket webSocket) { - response.set(""); - this.webSocketRef.set(null); - } - - @Override - public void onError(final Throwable t) { - response.setException(t); - closeSocket(); - } - - @Override - public void onMessage(final byte[] buf) { - final String message = new String(buf, StandardCharsets.UTF_8); - response.set(message); - closeSocket(); - } - - @Override - public void onFragment(final byte[] buf, final boolean last) { - try { - out.write(buf); - if (last) { - response.set(new String(out.toByteArray(), StandardCharsets.UTF_8)); - closeSocket(); - } - } catch (final IOException e) { - response.setException(e); - } - } - - private void closeSocket() { - final WebSocket webSocket = webSocketRef.get(); - if (webSocket != null) { - webSocket.close(); - } - } - } -} diff --git a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/WebSocketServerTest.java b/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/WebSocketServerTest.java deleted file mode 100644 index e2f94a1949d..00000000000 --- a/jdisc_http_service/src/test/java/com/yahoo/jdisc/http/server/jetty/WebSocketServerTest.java +++ /dev/null @@ -1,102 +0,0 @@ -// Copyright 2016 Yahoo Inc. Licensed under the terms of the Apache 2.0 license. See LICENSE in the project root. -package com.yahoo.jdisc.http.server.jetty; - -import com.google.common.util.concurrent.SettableFuture; -import com.ning.http.client.websocket.WebSocket; -import com.ning.http.client.websocket.WebSocketByteListener; -import com.yahoo.jdisc.Request; -import com.yahoo.jdisc.Response; -import com.yahoo.jdisc.handler.AbstractRequestHandler; -import com.yahoo.jdisc.handler.ContentChannel; -import com.yahoo.jdisc.handler.ResponseHandler; -import org.testng.annotations.Test; - -import java.io.ByteArrayOutputStream; -import java.io.IOException; -import java.nio.charset.StandardCharsets; -import java.util.concurrent.TimeUnit; - -import static com.yahoo.jdisc.Response.Status.OK; -import static org.hamcrest.CoreMatchers.is; -import static org.hamcrest.MatcherAssert.assertThat; - -/** - * @author <a href="mailto:simon@yahoo-inc.com">Simon Thoresen Hult</a> - */ -public class WebSocketServerTest { - - @Test(enabled = false) - public void requireThatServerCanRespondToRequest() throws Exception { - final TestDriver driver = TestDrivers.newInstance(new EchoRequestHandler()); - final SimpleWebSocketClient client = new SimpleWebSocketClient(driver); - final MyWebSocketListener listener = new MyWebSocketListener("Hello World!"); - client.executeRequest("/status.html", listener); - assertThat(listener.response.get(60, TimeUnit.SECONDS), is("Hello World!")); - assertThat(client.close(), is(true)); - assertThat(driver.close(), is(true)); - } - - //@Test Ignored: Broken in jetty 9.2.{3,4} - public void requireThatServerCanRespondToSslRequest() throws Exception { - final TestDriver driver = TestDrivers.newInstanceWithSsl(new EchoRequestHandler()); - final SimpleWebSocketClient client = new SimpleWebSocketClient(driver); - final MyWebSocketListener listener = new MyWebSocketListener("Hello World!"); - client.executeRequest("/status.html", listener); - assertThat(listener.response.get(60, TimeUnit.SECONDS), is("Hello World!")); - assertThat(client.close(), is(true)); - assertThat(driver.close(), is(true)); - } - - private static class EchoRequestHandler extends AbstractRequestHandler { - - @Override - public ContentChannel handleRequest(final Request request, final ResponseHandler handler) { - return handler.handleResponse(new Response(OK)); - } - } - - private static class MyWebSocketListener implements WebSocketByteListener { - - final ByteArrayOutputStream out = new ByteArrayOutputStream(); - final SettableFuture<String> response = SettableFuture.create(); - final byte[] requestContent; - - MyWebSocketListener(final String requestContent) { - this.requestContent = requestContent.getBytes(StandardCharsets.UTF_8); - } - - @Override - public void onOpen(final WebSocket webSocket) { - webSocket.sendMessage(requestContent); - webSocket.close(); - } - - @Override - public void onClose(final WebSocket webSocket) { - response.set(new String(out.toByteArray(), StandardCharsets.UTF_8)); - } - - @Override - public void onError(final Throwable t) { - response.setException(t); - } - - @Override - public void onMessage(final byte[] buf) { - try { - out.write(buf); - } catch (final IOException e) { - response.setException(e); - } - } - - @Override - public void onFragment(final byte[] buf, final boolean last) { - try { - out.write(buf); - } catch (final IOException e) { - response.setException(e); - } - } - } -} diff --git a/jdisc_jetty/pom.xml b/jdisc_jetty/pom.xml index b3e147bdac5..5887a6b7894 100644 --- a/jdisc_jetty/pom.xml +++ b/jdisc_jetty/pom.xml @@ -34,14 +34,6 @@ <groupId>org.eclipse.jetty</groupId> <artifactId>jetty-servlets</artifactId> </dependency> - <dependency> - <groupId>org.eclipse.jetty.websocket</groupId> - <artifactId>websocket-server</artifactId> - </dependency> - <dependency> - <groupId>org.eclipse.jetty.websocket</groupId> - <artifactId>websocket-servlet</artifactId> - </dependency> </dependencies> <build> <plugins> @@ -882,21 +882,6 @@ <version>${jetty.version}</version> </dependency> <dependency> - <groupId>org.eclipse.jetty.websocket</groupId> - <artifactId>websocket-server</artifactId> - <version>${jetty.version}</version> - </dependency> - <dependency> - <groupId>org.eclipse.jetty.websocket</groupId> - <artifactId>websocket-servlet</artifactId> - <version>${jetty.version}</version> - </dependency> - <dependency> - <groupId>org.glassfish.grizzly</groupId> - <artifactId>grizzly-websockets</artifactId> - <version>2.3.2</version> - </dependency> - <dependency> <groupId>org.hamcrest</groupId> <artifactId>hamcrest-all</artifactId> <version>1.3</version> |