Merged branch 'jetty-9.2.x' into 'master'.
This commit is contained in:
commit
8a27385bc3
|
@ -2,23 +2,30 @@
|
||||||
.classpath
|
.classpath
|
||||||
.project
|
.project
|
||||||
.settings
|
.settings
|
||||||
.gitignore
|
|
||||||
|
|
||||||
# maven
|
# maven
|
||||||
target/
|
target/
|
||||||
*/src/main/java/META-INF/
|
*/src/main/java/META-INF/
|
||||||
*.versionsBackup
|
*.versionsBackup
|
||||||
|
*.releaseBackup
|
||||||
bin/
|
bin/
|
||||||
|
|
||||||
# common junk
|
# common junk
|
||||||
*.log
|
*.log
|
||||||
*.swp
|
|
||||||
*.diff
|
*.diff
|
||||||
*.patch
|
*.patch
|
||||||
~*
|
*.sw[a-p]
|
||||||
*~
|
*.bak
|
||||||
|
*.backup
|
||||||
|
*.debug
|
||||||
|
*.dump
|
||||||
|
|
||||||
# intellij
|
# vim
|
||||||
|
.*.sw[a-p]
|
||||||
|
*~
|
||||||
|
~*
|
||||||
|
|
||||||
|
# intellij / android studio
|
||||||
*.iml
|
*.iml
|
||||||
*.ipr
|
*.ipr
|
||||||
*.iws
|
*.iws
|
||||||
|
@ -34,12 +41,5 @@ bin/
|
||||||
# netbeans
|
# netbeans
|
||||||
/nbproject
|
/nbproject
|
||||||
|
|
||||||
# vim
|
|
||||||
.*.sw[a-p]
|
|
||||||
|
|
||||||
# merge tooling
|
# merge tooling
|
||||||
*.orig
|
*.orig
|
||||||
|
|
||||||
#maven
|
|
||||||
*.versionsBackup
|
|
||||||
*.releaseBackup
|
|
||||||
|
|
|
@ -134,10 +134,14 @@ public abstract class HttpConnection implements Connection
|
||||||
CookieStore cookieStore = getHttpClient().getCookieStore();
|
CookieStore cookieStore = getHttpClient().getCookieStore();
|
||||||
if (cookieStore != null)
|
if (cookieStore != null)
|
||||||
{
|
{
|
||||||
StringBuilder cookies = convertCookies(cookieStore.get(request.getURI()), null);
|
URI uri = request.getURI();
|
||||||
cookies = convertCookies(request.getCookies(), cookies);
|
if (uri != null)
|
||||||
if (cookies != null)
|
{
|
||||||
request.header(HttpHeader.COOKIE.asString(), cookies.toString());
|
StringBuilder cookies = convertCookies(cookieStore.get(uri), null);
|
||||||
|
cookies = convertCookies(request.getCookies(), cookies);
|
||||||
|
if (cookies != null)
|
||||||
|
request.header(HttpHeader.COOKIE.asString(), cookies.toString());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Authorization
|
// Authorization
|
||||||
|
|
|
@ -22,6 +22,7 @@ import java.io.IOException;
|
||||||
import java.io.UnsupportedEncodingException;
|
import java.io.UnsupportedEncodingException;
|
||||||
import java.net.HttpCookie;
|
import java.net.HttpCookie;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
import java.net.URLDecoder;
|
import java.net.URLDecoder;
|
||||||
import java.net.URLEncoder;
|
import java.net.URLEncoder;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
@ -57,6 +58,8 @@ import org.eclipse.jetty.util.Fields;
|
||||||
|
|
||||||
public class HttpRequest implements Request
|
public class HttpRequest implements Request
|
||||||
{
|
{
|
||||||
|
private static final URI NULL_URI = URI.create("null:0");
|
||||||
|
|
||||||
private final HttpFields headers = new HttpFields();
|
private final HttpFields headers = new HttpFields();
|
||||||
private final Fields params = new Fields(true);
|
private final Fields params = new Fields(true);
|
||||||
private final List<Response.ResponseListener> responseListeners = new ArrayList<>();
|
private final List<Response.ResponseListener> responseListeners = new ArrayList<>();
|
||||||
|
@ -158,22 +161,30 @@ public class HttpRequest implements Request
|
||||||
@Override
|
@Override
|
||||||
public Request path(String path)
|
public Request path(String path)
|
||||||
{
|
{
|
||||||
URI uri = URI.create(path);
|
URI uri = newURI(path);
|
||||||
String rawPath = uri.getRawPath();
|
if (uri == null)
|
||||||
if (uri.isOpaque())
|
|
||||||
rawPath = path;
|
|
||||||
if (rawPath == null)
|
|
||||||
rawPath = "";
|
|
||||||
this.path = rawPath;
|
|
||||||
String query = uri.getRawQuery();
|
|
||||||
if (query != null)
|
|
||||||
{
|
{
|
||||||
this.query = query;
|
this.path = path;
|
||||||
params.clear();
|
this.query = null;
|
||||||
extractParams(query);
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
String rawPath = uri.getRawPath();
|
||||||
|
if (uri.isOpaque())
|
||||||
|
rawPath = path;
|
||||||
|
if (rawPath == null)
|
||||||
|
rawPath = "";
|
||||||
|
this.path = rawPath;
|
||||||
|
String query = uri.getRawQuery();
|
||||||
|
if (query != null)
|
||||||
|
{
|
||||||
|
this.query = query;
|
||||||
|
params.clear();
|
||||||
|
extractParams(query);
|
||||||
|
}
|
||||||
|
if (uri.isAbsolute())
|
||||||
|
this.path = buildURI(false).toString();
|
||||||
}
|
}
|
||||||
if (uri.isAbsolute())
|
|
||||||
this.path = buildURI(false).toString();
|
|
||||||
this.uri = null;
|
this.uri = null;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
@ -187,9 +198,9 @@ public class HttpRequest implements Request
|
||||||
@Override
|
@Override
|
||||||
public URI getURI()
|
public URI getURI()
|
||||||
{
|
{
|
||||||
if (uri != null)
|
if (uri == null)
|
||||||
return uri;
|
uri = buildURI(true);
|
||||||
return uri = buildURI(true);
|
return uri == NULL_URI ? null : uri;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -762,12 +773,28 @@ public class HttpRequest implements Request
|
||||||
String query = getQuery();
|
String query = getQuery();
|
||||||
if (query != null && withQuery)
|
if (query != null && withQuery)
|
||||||
path += "?" + query;
|
path += "?" + query;
|
||||||
URI result = URI.create(path);
|
URI result = newURI(path);
|
||||||
|
if (result == null)
|
||||||
|
return NULL_URI;
|
||||||
if (!result.isAbsolute() && !result.isOpaque())
|
if (!result.isAbsolute() && !result.isOpaque())
|
||||||
result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
|
result = URI.create(new Origin(getScheme(), getHost(), getPort()).asString() + path);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private URI newURI(String uri)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return new URI(uri);
|
||||||
|
}
|
||||||
|
catch (URISyntaxException x)
|
||||||
|
{
|
||||||
|
// The "path" of a HTTP request may not be a URI,
|
||||||
|
// for example for CONNECT 127.0.0.1:8080 or OPTIONS *.
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public String toString()
|
public String toString()
|
||||||
{
|
{
|
||||||
|
|
|
@ -445,6 +445,11 @@ public class HttpClientTimeoutTest extends AbstractHttpClientServerTest
|
||||||
// Expected timeout during connect, continue the test.
|
// Expected timeout during connect, continue the test.
|
||||||
Assume.assumeTrue(true);
|
Assume.assumeTrue(true);
|
||||||
}
|
}
|
||||||
|
catch (Throwable x)
|
||||||
|
{
|
||||||
|
// Abort if any other exception happens.
|
||||||
|
Assume.assumeTrue(false);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class TimeoutHandler extends AbstractHandler
|
private class TimeoutHandler extends AbstractHandler
|
||||||
|
|
|
@ -18,8 +18,6 @@
|
||||||
|
|
||||||
package org.eclipse.jetty.proxy;
|
package org.eclipse.jetty.proxy;
|
||||||
|
|
||||||
import static org.junit.Assert.assertEquals;
|
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.ConnectException;
|
import java.net.ConnectException;
|
||||||
import java.net.Socket;
|
import java.net.Socket;
|
||||||
|
@ -28,7 +26,6 @@ import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.ExecutionException;
|
import java.util.concurrent.ExecutionException;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import java.util.concurrent.atomic.AtomicReference;
|
import java.util.concurrent.atomic.AtomicReference;
|
||||||
|
|
||||||
import javax.servlet.ServletException;
|
import javax.servlet.ServletException;
|
||||||
import javax.servlet.ServletOutputStream;
|
import javax.servlet.ServletOutputStream;
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
|
@ -64,6 +61,8 @@ import org.junit.Ignore;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
|
||||||
|
import static org.junit.Assert.assertEquals;
|
||||||
|
|
||||||
public class ProxyTunnellingTest
|
public class ProxyTunnellingTest
|
||||||
{
|
{
|
||||||
@Rule
|
@Rule
|
||||||
|
@ -148,8 +147,10 @@ public class ProxyTunnellingTest
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
|
// Use a numeric host to test the URI of the CONNECT request.
|
||||||
|
String host = "127.0.0.1";
|
||||||
String body = "BODY";
|
String body = "BODY";
|
||||||
ContentResponse response = httpClient.newRequest("localhost", serverConnector.getLocalPort())
|
ContentResponse response = httpClient.newRequest(host, serverConnector.getLocalPort())
|
||||||
.scheme(HttpScheme.HTTPS.asString())
|
.scheme(HttpScheme.HTTPS.asString())
|
||||||
.method(HttpMethod.GET)
|
.method(HttpMethod.GET)
|
||||||
.path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
|
.path("/echo?body=" + URLEncoder.encode(body, "UTF-8"))
|
||||||
|
|
|
@ -162,7 +162,7 @@ public class JsrEvents<T extends Annotation, C extends EndpointConfig>
|
||||||
onOpen.call(websocket,config);
|
onOpen.call(websocket,config);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void callPong(RemoteEndpoint.Async endpoint, Object websocket, ByteBuffer pong) throws DecodeException, IOException
|
public void callPong(RemoteEndpoint.Async endpoint, Object websocket, ByteBuffer pong)
|
||||||
{
|
{
|
||||||
if (onPong == null)
|
if (onPong == null)
|
||||||
{
|
{
|
||||||
|
|
|
@ -21,8 +21,6 @@ package org.eclipse.jetty.websocket.jsr356.annotations;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
import javax.websocket.DecodeException;
|
|
||||||
|
|
||||||
import org.eclipse.jetty.websocket.jsr356.JsrPongMessage;
|
import org.eclipse.jetty.websocket.jsr356.JsrPongMessage;
|
||||||
import org.eclipse.jetty.websocket.jsr356.JsrSession;
|
import org.eclipse.jetty.websocket.jsr356.JsrSession;
|
||||||
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
|
import org.eclipse.jetty.websocket.jsr356.annotations.Param.Role;
|
||||||
|
@ -45,7 +43,7 @@ public class OnMessagePongCallable extends OnMessageCallable
|
||||||
super(copy);
|
super(copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Object call(Object endpoint, ByteBuffer buf) throws DecodeException
|
public Object call(Object endpoint, ByteBuffer buf)
|
||||||
{
|
{
|
||||||
super.args[idxMessageObject] = new JsrPongMessage(buf);
|
super.args[idxMessageObject] = new JsrPongMessage(buf);
|
||||||
return super.call(endpoint,super.args);
|
return super.call(endpoint,super.args);
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.websocket.CloseReason;
|
import javax.websocket.CloseReason;
|
||||||
import javax.websocket.DecodeException;
|
import javax.websocket.DecodeException;
|
||||||
|
|
||||||
|
@ -123,7 +124,7 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
|
||||||
{
|
{
|
||||||
events.callBinaryStream(jsrsession.getAsyncRemote(),websocket,stream);
|
events.callBinaryStream(jsrsession.getAsyncRemote(),websocket,stream);
|
||||||
}
|
}
|
||||||
catch (DecodeException | IOException e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
onFatalError(e);
|
onFatalError(e);
|
||||||
}
|
}
|
||||||
|
@ -167,7 +168,7 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
|
||||||
// FIN is always true here
|
// FIN is always true here
|
||||||
events.callBinary(jsrsession.getAsyncRemote(),websocket,buf,true);
|
events.callBinary(jsrsession.getAsyncRemote(),websocket,buf,true);
|
||||||
}
|
}
|
||||||
catch (DecodeException e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
onFatalError(e);
|
onFatalError(e);
|
||||||
}
|
}
|
||||||
|
@ -188,7 +189,15 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable cause)
|
public void onError(Throwable cause)
|
||||||
{
|
{
|
||||||
events.callError(websocket,cause);
|
try
|
||||||
|
{
|
||||||
|
events.callError(websocket,cause);
|
||||||
|
}
|
||||||
|
catch (Throwable e)
|
||||||
|
{
|
||||||
|
LOG.warn("Unable to call onError with cause", cause);
|
||||||
|
LOG.warn("Call to onError resulted in exception", e);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void onFatalError(Throwable t)
|
private void onFatalError(Throwable t)
|
||||||
|
@ -203,15 +212,15 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onInputStream(InputStream stream)
|
public void onInputStream(InputStream stream) throws IOException
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
events.callBinaryStream(jsrsession.getAsyncRemote(),websocket,stream);
|
events.callBinaryStream(jsrsession.getAsyncRemote(),websocket,stream);
|
||||||
}
|
}
|
||||||
catch (DecodeException | IOException e)
|
catch (DecodeException e)
|
||||||
{
|
{
|
||||||
onFatalError(e);
|
throw new RuntimeException("Unable decode input stream", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -223,7 +232,7 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
|
||||||
}
|
}
|
||||||
catch (DecodeException e)
|
catch (DecodeException e)
|
||||||
{
|
{
|
||||||
onFatalError(e);
|
throw new RuntimeException("Unable decode partial binary message", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -235,46 +244,33 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
|
||||||
}
|
}
|
||||||
catch (DecodeException e)
|
catch (DecodeException e)
|
||||||
{
|
{
|
||||||
onFatalError(e);
|
throw new RuntimeException("Unable decode partial text message", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPing(ByteBuffer buffer)
|
public void onPing(ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
try
|
// Call pong, as there is no "onPing" method in the JSR
|
||||||
{
|
events.callPong(jsrsession.getAsyncRemote(),websocket,buffer);
|
||||||
events.callPong(jsrsession.getAsyncRemote(),websocket,buffer);
|
|
||||||
}
|
|
||||||
catch (DecodeException | IOException e)
|
|
||||||
{
|
|
||||||
onFatalError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onPong(ByteBuffer buffer)
|
public void onPong(ByteBuffer buffer)
|
||||||
{
|
{
|
||||||
try
|
events.callPong(jsrsession.getAsyncRemote(),websocket,buffer);
|
||||||
{
|
|
||||||
events.callPong(jsrsession.getAsyncRemote(),websocket,buffer);
|
|
||||||
}
|
|
||||||
catch (DecodeException | IOException e)
|
|
||||||
{
|
|
||||||
onFatalError(e);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReader(Reader reader)
|
public void onReader(Reader reader) throws IOException
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
events.callTextStream(jsrsession.getAsyncRemote(),websocket,reader);
|
events.callTextStream(jsrsession.getAsyncRemote(),websocket,reader);
|
||||||
}
|
}
|
||||||
catch (DecodeException | IOException e)
|
catch (DecodeException e)
|
||||||
{
|
{
|
||||||
onFatalError(e);
|
throw new RuntimeException("Unable decode reader", e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -343,7 +339,7 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
|
||||||
{
|
{
|
||||||
events.callTextStream(jsrsession.getAsyncRemote(),websocket,stream);
|
events.callTextStream(jsrsession.getAsyncRemote(),websocket,stream);
|
||||||
}
|
}
|
||||||
catch (DecodeException | IOException e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
onFatalError(e);
|
onFatalError(e);
|
||||||
}
|
}
|
||||||
|
@ -380,7 +376,7 @@ public class JsrAnnotatedEventDriver extends AbstractJsrEventDriver
|
||||||
// FIN is always true here
|
// FIN is always true here
|
||||||
events.callText(jsrsession.getAsyncRemote(),websocket,message,true);
|
events.callText(jsrsession.getAsyncRemote(),websocket,message,true);
|
||||||
}
|
}
|
||||||
catch (DecodeException e)
|
catch (Throwable e)
|
||||||
{
|
{
|
||||||
onFatalError(e);
|
onFatalError(e);
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,6 +23,7 @@ import java.io.InputStream;
|
||||||
import java.io.Reader;
|
import java.io.Reader;
|
||||||
import java.nio.ByteBuffer;
|
import java.nio.ByteBuffer;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import javax.websocket.CloseReason;
|
import javax.websocket.CloseReason;
|
||||||
import javax.websocket.Endpoint;
|
import javax.websocket.Endpoint;
|
||||||
import javax.websocket.MessageHandler;
|
import javax.websocket.MessageHandler;
|
||||||
|
@ -134,20 +135,23 @@ public class JsrEndpointEventDriver extends AbstractJsrEventDriver
|
||||||
{
|
{
|
||||||
LOG.debug("onConnect({}, {})",jsrsession,config);
|
LOG.debug("onConnect({}, {})",jsrsession,config);
|
||||||
}
|
}
|
||||||
try
|
|
||||||
{
|
// Let unhandled exceptions flow out
|
||||||
endpoint.onOpen(jsrsession,config);
|
endpoint.onOpen(jsrsession,config);
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
LOG.warn("Uncaught exception",t);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onError(Throwable cause)
|
public void onError(Throwable cause)
|
||||||
{
|
{
|
||||||
endpoint.onError(jsrsession,cause);
|
LOG.warn(cause);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
endpoint.onError(jsrsession,cause);
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
LOG.warn("Unable to report to onError due to exception",t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -0,0 +1,65 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2015 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.jsr356.misbehaving;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import javax.websocket.ClientEndpoint;
|
||||||
|
import javax.websocket.CloseReason;
|
||||||
|
import javax.websocket.EndpointConfig;
|
||||||
|
import javax.websocket.OnClose;
|
||||||
|
import javax.websocket.OnError;
|
||||||
|
import javax.websocket.OnOpen;
|
||||||
|
import javax.websocket.Session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JSR-356 Annotated that tosses a RuntimeException during its onOpen call
|
||||||
|
*/
|
||||||
|
@ClientEndpoint
|
||||||
|
public class AnnotatedRuntimeOnOpen
|
||||||
|
{
|
||||||
|
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||||
|
public CloseReason closeReason;
|
||||||
|
public LinkedList<Throwable> errors = new LinkedList<>();
|
||||||
|
|
||||||
|
@OnOpen
|
||||||
|
public void onOpen(Session session, EndpointConfig config)
|
||||||
|
{
|
||||||
|
// Intentional runtime exception.
|
||||||
|
int[] arr = new int[5];
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
arr[i] = 222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnClose
|
||||||
|
public void onClose(Session session, CloseReason closeReason)
|
||||||
|
{
|
||||||
|
this.closeReason = closeReason;
|
||||||
|
closeLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnError
|
||||||
|
public void onError(Session session, Throwable thr)
|
||||||
|
{
|
||||||
|
errors.add(thr);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,63 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2015 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.jsr356.misbehaving;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import javax.websocket.CloseReason;
|
||||||
|
import javax.websocket.Endpoint;
|
||||||
|
import javax.websocket.EndpointConfig;
|
||||||
|
import javax.websocket.Session;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A JSR-356 Endpoint that tosses a RuntimeException during its onOpen call
|
||||||
|
*/
|
||||||
|
public class EndpointRuntimeOnOpen extends Endpoint
|
||||||
|
{
|
||||||
|
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||||
|
public CloseReason closeReason;
|
||||||
|
public LinkedList<Throwable> errors = new LinkedList<>();
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOpen(Session session, EndpointConfig config)
|
||||||
|
{
|
||||||
|
// Intentional runtime exception.
|
||||||
|
int[] arr = new int[5];
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
arr[i] = 222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClose(Session session, CloseReason closeReason)
|
||||||
|
{
|
||||||
|
super.onClose(session,closeReason);
|
||||||
|
this.closeReason = closeReason;
|
||||||
|
closeLatch.countDown();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onError(Session session, Throwable thr)
|
||||||
|
{
|
||||||
|
super.onError(session,thr);
|
||||||
|
errors.add(thr);
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,127 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2015 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.jsr356.misbehaving;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import javax.websocket.ContainerProvider;
|
||||||
|
import javax.websocket.Session;
|
||||||
|
import javax.websocket.WebSocketContainer;
|
||||||
|
|
||||||
|
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.StacklessLogging;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.EchoHandler;
|
||||||
|
import org.eclipse.jetty.websocket.jsr356.endpoints.JsrEndpointEventDriver;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
public class MisbehavingClassTest
|
||||||
|
{
|
||||||
|
private static Server server;
|
||||||
|
private static EchoHandler handler;
|
||||||
|
private static URI serverUri;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void startServer() throws Exception
|
||||||
|
{
|
||||||
|
server = new Server();
|
||||||
|
ServerConnector connector = new ServerConnector(server);
|
||||||
|
server.addConnector(connector);
|
||||||
|
|
||||||
|
handler = new EchoHandler();
|
||||||
|
|
||||||
|
ContextHandler context = new ContextHandler();
|
||||||
|
context.setContextPath("/");
|
||||||
|
context.setHandler(handler);
|
||||||
|
server.setHandler(context);
|
||||||
|
|
||||||
|
// Start Server
|
||||||
|
server.start();
|
||||||
|
|
||||||
|
String host = connector.getHost();
|
||||||
|
if (host == null)
|
||||||
|
{
|
||||||
|
host = "localhost";
|
||||||
|
}
|
||||||
|
int port = connector.getLocalPort();
|
||||||
|
serverUri = new URI(String.format("ws://%s:%d/",host,port));
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stopServer()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
e.printStackTrace(System.err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testEndpointRuntimeOnOpen() throws Exception
|
||||||
|
{
|
||||||
|
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
|
||||||
|
EndpointRuntimeOnOpen socket = new EndpointRuntimeOnOpen();
|
||||||
|
|
||||||
|
try (StacklessLogging logging = new StacklessLogging(EndpointRuntimeOnOpen.class,JsrEndpointEventDriver.class))
|
||||||
|
{
|
||||||
|
// expecting ArrayIndexOutOfBoundsException during onOpen
|
||||||
|
Session session = container.connectToServer(socket,serverUri);
|
||||||
|
assertThat("Close should have occurred",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||||
|
|
||||||
|
// technically, the session object isn't invalid here.
|
||||||
|
assertThat("Session.isOpen",session.isOpen(),is(false));
|
||||||
|
assertThat("Should have only had 1 error",socket.errors.size(),is(1));
|
||||||
|
|
||||||
|
Throwable cause = socket.errors.pop();
|
||||||
|
assertThat("Error",cause,instanceOf(ArrayIndexOutOfBoundsException.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnnotatedRuntimeOnOpen() throws Exception
|
||||||
|
{
|
||||||
|
WebSocketContainer container = ContainerProvider.getWebSocketContainer();
|
||||||
|
AnnotatedRuntimeOnOpen socket = new AnnotatedRuntimeOnOpen();
|
||||||
|
|
||||||
|
try (StacklessLogging logging = new StacklessLogging(AnnotatedRuntimeOnOpen.class))
|
||||||
|
{
|
||||||
|
// expecting ArrayIndexOutOfBoundsException during onOpen
|
||||||
|
Session session = container.connectToServer(socket,serverUri);
|
||||||
|
assertThat("Close should have occurred",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||||
|
|
||||||
|
// technically, the session object isn't invalid here.
|
||||||
|
assertThat("Session.isOpen",session.isOpen(),is(false));
|
||||||
|
assertThat("Should have only had 1 error",socket.errors.size(),is(1));
|
||||||
|
|
||||||
|
Throwable cause = socket.errors.pop();
|
||||||
|
assertThat("Error",cause,instanceOf(ArrayIndexOutOfBoundsException.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -26,6 +26,7 @@ import java.lang.management.MemoryUsage;
|
||||||
import java.net.URI;
|
import java.net.URI;
|
||||||
import java.util.concurrent.CountDownLatch;
|
import java.util.concurrent.CountDownLatch;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
import javax.websocket.ContainerProvider;
|
import javax.websocket.ContainerProvider;
|
||||||
import javax.websocket.Endpoint;
|
import javax.websocket.Endpoint;
|
||||||
import javax.websocket.EndpointConfig;
|
import javax.websocket.EndpointConfig;
|
||||||
|
@ -74,6 +75,7 @@ public class MemoryUsageTest
|
||||||
server.stop();
|
server.stop();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
@Test
|
@Test
|
||||||
public void testMemoryUsage() throws Exception
|
public void testMemoryUsage() throws Exception
|
||||||
{
|
{
|
||||||
|
|
|
@ -32,6 +32,11 @@ public class CloseStatus
|
||||||
*/
|
*/
|
||||||
public static String trimMaxReasonLength(String reason)
|
public static String trimMaxReasonLength(String reason)
|
||||||
{
|
{
|
||||||
|
if (reason == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
if (reason.length() > MAX_REASON_PHRASE)
|
if (reason.length() > MAX_REASON_PHRASE)
|
||||||
{
|
{
|
||||||
return reason.substring(0,MAX_REASON_PHRASE);
|
return reason.substring(0,MAX_REASON_PHRASE);
|
||||||
|
|
|
@ -105,7 +105,7 @@ public class WebSocketSession extends ContainerLifeCycle implements Session, Inc
|
||||||
@Override
|
@Override
|
||||||
public void close(int statusCode, String reason)
|
public void close(int statusCode, String reason)
|
||||||
{
|
{
|
||||||
connection.close(statusCode,reason);
|
connection.close(statusCode,CloseStatus.trimMaxReasonLength(reason));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -49,13 +49,13 @@ public interface EventDriver extends IncomingFrames
|
||||||
|
|
||||||
public void onFrame(Frame frame);
|
public void onFrame(Frame frame);
|
||||||
|
|
||||||
public void onInputStream(InputStream stream);
|
public void onInputStream(InputStream stream) throws IOException;
|
||||||
|
|
||||||
public void onPing(ByteBuffer buffer);
|
public void onPing(ByteBuffer buffer);
|
||||||
|
|
||||||
public void onPong(ByteBuffer buffer);
|
public void onPong(ByteBuffer buffer);
|
||||||
|
|
||||||
public void onReader(Reader reader);
|
public void onReader(Reader reader) throws IOException;
|
||||||
|
|
||||||
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException;
|
public void onTextFrame(ByteBuffer buffer, boolean fin) throws IOException;
|
||||||
|
|
||||||
|
|
|
@ -86,7 +86,15 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
events.onBinary.call(websocket,session,msg);
|
try
|
||||||
|
{
|
||||||
|
events.onBinary.call(websocket,session,msg);
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
// dispatched calls need to be reported
|
||||||
|
onError(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
@ -188,7 +196,15 @@ public class JettyAnnotatedEventDriver extends AbstractEventDriver
|
||||||
@Override
|
@Override
|
||||||
public void run()
|
public void run()
|
||||||
{
|
{
|
||||||
events.onText.call(websocket,session,msg);
|
try
|
||||||
|
{
|
||||||
|
events.onText.call(websocket,session,msg);
|
||||||
|
}
|
||||||
|
catch (Throwable t)
|
||||||
|
{
|
||||||
|
// dispatched calls need to be reported
|
||||||
|
onError(t);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -22,7 +22,6 @@ import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Modifier;
|
import java.lang.reflect.Modifier;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
|
||||||
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
||||||
import org.eclipse.jetty.websocket.common.events.ParamList;
|
import org.eclipse.jetty.websocket.common.events.ParamList;
|
||||||
|
|
||||||
|
@ -39,7 +38,7 @@ public abstract class AbstractMethodAnnotationScanner<T>
|
||||||
StringBuilder err = new StringBuilder();
|
StringBuilder err = new StringBuilder();
|
||||||
err.append("Invalid declaration of ");
|
err.append("Invalid declaration of ");
|
||||||
err.append(method);
|
err.append(method);
|
||||||
err.append(StringUtil.__LINE_SEPARATOR);
|
err.append(System.lineSeparator());
|
||||||
|
|
||||||
err.append("Method modifier must be public");
|
err.append("Method modifier must be public");
|
||||||
|
|
||||||
|
@ -51,7 +50,7 @@ public abstract class AbstractMethodAnnotationScanner<T>
|
||||||
StringBuilder err = new StringBuilder();
|
StringBuilder err = new StringBuilder();
|
||||||
err.append("Invalid declaration of ");
|
err.append("Invalid declaration of ");
|
||||||
err.append(method);
|
err.append(method);
|
||||||
err.append(StringUtil.__LINE_SEPARATOR);
|
err.append(System.lineSeparator());
|
||||||
|
|
||||||
err.append("Method modifier may not be static");
|
err.append("Method modifier may not be static");
|
||||||
|
|
||||||
|
@ -66,7 +65,7 @@ public abstract class AbstractMethodAnnotationScanner<T>
|
||||||
StringBuilder err = new StringBuilder();
|
StringBuilder err = new StringBuilder();
|
||||||
err.append("Invalid declaration of ");
|
err.append("Invalid declaration of ");
|
||||||
err.append(method);
|
err.append(method);
|
||||||
err.append(StringUtil.__LINE_SEPARATOR);
|
err.append(System.lineSeparator());
|
||||||
|
|
||||||
err.append("Return type must be ").append(type);
|
err.append("Return type must be ").append(type);
|
||||||
|
|
||||||
|
@ -87,7 +86,7 @@ public abstract class AbstractMethodAnnotationScanner<T>
|
||||||
StringBuilder err = new StringBuilder();
|
StringBuilder err = new StringBuilder();
|
||||||
err.append("Duplicate @").append(annoClass.getSimpleName()).append(" declaration on ");
|
err.append("Duplicate @").append(annoClass.getSimpleName()).append(" declaration on ");
|
||||||
err.append(method);
|
err.append(method);
|
||||||
err.append(StringUtil.__LINE_SEPARATOR);
|
err.append(System.lineSeparator());
|
||||||
|
|
||||||
err.append("@").append(annoClass.getSimpleName()).append(" previously declared at ");
|
err.append("@").append(annoClass.getSimpleName()).append(" previously declared at ");
|
||||||
err.append(callable.getMethod());
|
err.append(callable.getMethod());
|
||||||
|
|
|
@ -24,7 +24,6 @@ import java.util.Objects;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.log.Log;
|
import org.eclipse.jetty.util.log.Log;
|
||||||
import org.eclipse.jetty.util.log.Logger;
|
import org.eclipse.jetty.util.log.Logger;
|
||||||
import org.eclipse.jetty.websocket.api.WebSocketException;
|
|
||||||
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
import org.eclipse.jetty.websocket.common.util.ReflectUtils;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -70,36 +69,58 @@ public class CallableMethod
|
||||||
{
|
{
|
||||||
return this.method.invoke(obj,args);
|
return this.method.invoke(obj,args);
|
||||||
}
|
}
|
||||||
catch (IllegalAccessException | IllegalArgumentException | InvocationTargetException e)
|
catch (Throwable t)
|
||||||
{
|
{
|
||||||
StringBuilder err = new StringBuilder();
|
String err = formatMethodCallError(args);
|
||||||
err.append("Cannot call method ");
|
throw unwrapRuntimeException(err,t);
|
||||||
err.append(ReflectUtils.toString(pojo,method));
|
|
||||||
err.append(" with args: [");
|
|
||||||
|
|
||||||
boolean delim = false;
|
|
||||||
for (Object arg : args)
|
|
||||||
{
|
|
||||||
if (delim)
|
|
||||||
{
|
|
||||||
err.append(", ");
|
|
||||||
}
|
|
||||||
if (arg == null)
|
|
||||||
{
|
|
||||||
err.append("<null>");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
err.append(arg.getClass().getName());
|
|
||||||
}
|
|
||||||
delim = true;
|
|
||||||
}
|
|
||||||
err.append("]");
|
|
||||||
|
|
||||||
throw new WebSocketException(err.toString(),e);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RuntimeException unwrapRuntimeException(String err, final Throwable t)
|
||||||
|
{
|
||||||
|
Throwable ret = t;
|
||||||
|
|
||||||
|
while (ret instanceof InvocationTargetException)
|
||||||
|
{
|
||||||
|
ret = ((InvocationTargetException)ret).getCause();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ret instanceof RuntimeException)
|
||||||
|
{
|
||||||
|
return (RuntimeException)ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new RuntimeException(err,ret);
|
||||||
|
}
|
||||||
|
|
||||||
|
public String formatMethodCallError(Object... args)
|
||||||
|
{
|
||||||
|
StringBuilder err = new StringBuilder();
|
||||||
|
err.append("Cannot call method ");
|
||||||
|
err.append(ReflectUtils.toString(pojo,method));
|
||||||
|
err.append(" with args: [");
|
||||||
|
|
||||||
|
boolean delim = false;
|
||||||
|
for (Object arg : args)
|
||||||
|
{
|
||||||
|
if (delim)
|
||||||
|
{
|
||||||
|
err.append(", ");
|
||||||
|
}
|
||||||
|
if (arg == null)
|
||||||
|
{
|
||||||
|
err.append("<null>");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
err.append(arg.getClass().getName());
|
||||||
|
}
|
||||||
|
delim = true;
|
||||||
|
}
|
||||||
|
err.append("]");
|
||||||
|
return err.toString();
|
||||||
|
}
|
||||||
|
|
||||||
public Method getMethod()
|
public Method getMethod()
|
||||||
{
|
{
|
||||||
return method;
|
return method;
|
||||||
|
|
|
@ -21,7 +21,6 @@ package org.eclipse.jetty.websocket.common.events.annotated;
|
||||||
import java.lang.annotation.Annotation;
|
import java.lang.annotation.Annotation;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import org.eclipse.jetty.util.StringUtil;
|
|
||||||
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
import org.eclipse.jetty.websocket.api.InvalidWebSocketException;
|
||||||
import org.eclipse.jetty.websocket.common.events.ParamList;
|
import org.eclipse.jetty.websocket.common.events.ParamList;
|
||||||
|
|
||||||
|
@ -34,7 +33,7 @@ public class InvalidSignatureException extends InvalidWebSocketException
|
||||||
StringBuilder err = new StringBuilder();
|
StringBuilder err = new StringBuilder();
|
||||||
err.append("Invalid declaration of ");
|
err.append("Invalid declaration of ");
|
||||||
err.append(method);
|
err.append(method);
|
||||||
err.append(StringUtil.__LINE_SEPARATOR);
|
err.append(System.lineSeparator());
|
||||||
|
|
||||||
err.append("Acceptable method declarations for @");
|
err.append("Acceptable method declarations for @");
|
||||||
err.append(annoClass.getSimpleName());
|
err.append(annoClass.getSimpleName());
|
||||||
|
@ -43,7 +42,7 @@ public class InvalidSignatureException extends InvalidWebSocketException
|
||||||
{
|
{
|
||||||
for (Class<?>[] params : validParams)
|
for (Class<?>[] params : validParams)
|
||||||
{
|
{
|
||||||
err.append(StringUtil.__LINE_SEPARATOR);
|
err.append(System.lineSeparator());
|
||||||
err.append("public void ").append(method.getName());
|
err.append("public void ").append(method.getName());
|
||||||
err.append('(');
|
err.append('(');
|
||||||
boolean delim = false;
|
boolean delim = false;
|
||||||
|
|
|
@ -0,0 +1,70 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2015 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.server.misbehaving;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketClose;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketConnect;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.OnWebSocketError;
|
||||||
|
import org.eclipse.jetty.websocket.api.annotations.WebSocket;
|
||||||
|
|
||||||
|
@WebSocket
|
||||||
|
public class AnnotatedRuntimeOnConnectSocket
|
||||||
|
{
|
||||||
|
public LinkedList<Throwable> errors = new LinkedList<>();
|
||||||
|
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||||
|
public int closeStatusCode;
|
||||||
|
public String closeReason;
|
||||||
|
|
||||||
|
@OnWebSocketConnect
|
||||||
|
public void onWebSocketConnect(Session sess)
|
||||||
|
{
|
||||||
|
// Intentional runtime exception.
|
||||||
|
int[] arr = new int[5];
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
arr[i] = 222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketClose
|
||||||
|
public void onWebSocketClose(int statusCode, String reason)
|
||||||
|
{
|
||||||
|
closeLatch.countDown();
|
||||||
|
closeStatusCode = statusCode;
|
||||||
|
closeReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@OnWebSocketError
|
||||||
|
public void onWebSocketError(Throwable cause)
|
||||||
|
{
|
||||||
|
this.errors.add(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
this.closeLatch = new CountDownLatch(1);
|
||||||
|
this.closeStatusCode = -1;
|
||||||
|
this.closeReason = null;
|
||||||
|
this.errors.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,56 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2015 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.server.misbehaving;
|
||||||
|
|
||||||
|
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;
|
||||||
|
|
||||||
|
@SuppressWarnings("serial")
|
||||||
|
public class BadSocketsServlet extends WebSocketServlet implements WebSocketCreator
|
||||||
|
{
|
||||||
|
public ListenerRuntimeOnConnectSocket listenerRuntimeConnect;
|
||||||
|
public AnnotatedRuntimeOnConnectSocket annotatedRuntimeConnect;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void configure(WebSocketServletFactory factory)
|
||||||
|
{
|
||||||
|
factory.setCreator(this);
|
||||||
|
|
||||||
|
this.listenerRuntimeConnect = new ListenerRuntimeOnConnectSocket();
|
||||||
|
this.annotatedRuntimeConnect = new AnnotatedRuntimeOnConnectSocket();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object createWebSocket(ServletUpgradeRequest req, ServletUpgradeResponse resp)
|
||||||
|
{
|
||||||
|
if (req.hasSubProtocol("listener-runtime-connect"))
|
||||||
|
{
|
||||||
|
return this.listenerRuntimeConnect;
|
||||||
|
}
|
||||||
|
else if (req.hasSubProtocol("annotated-runtime-connect"))
|
||||||
|
{
|
||||||
|
return this.annotatedRuntimeConnect;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,74 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2015 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.server.misbehaving;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.concurrent.CountDownLatch;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.websocket.api.Session;
|
||||||
|
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||||
|
|
||||||
|
public class ListenerRuntimeOnConnectSocket extends WebSocketAdapter
|
||||||
|
{
|
||||||
|
public LinkedList<Throwable> errors = new LinkedList<>();
|
||||||
|
public CountDownLatch closeLatch = new CountDownLatch(1);
|
||||||
|
public int closeStatusCode;
|
||||||
|
public String closeReason;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketConnect(Session sess)
|
||||||
|
{
|
||||||
|
super.onWebSocketConnect(sess);
|
||||||
|
|
||||||
|
// Intentional runtime exception.
|
||||||
|
int[] arr = new int[5];
|
||||||
|
for (int i = 0; i < 10; i++)
|
||||||
|
{
|
||||||
|
arr[i] = 222;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketClose(int statusCode, String reason)
|
||||||
|
{
|
||||||
|
closeLatch.countDown();
|
||||||
|
closeStatusCode = statusCode;
|
||||||
|
closeReason = reason;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketError(Throwable cause)
|
||||||
|
{
|
||||||
|
this.errors.add(cause);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onWebSocketText(String message)
|
||||||
|
{
|
||||||
|
getRemote().sendStringByFuture(message);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reset()
|
||||||
|
{
|
||||||
|
this.closeLatch = new CountDownLatch(1);
|
||||||
|
this.closeStatusCode = -1;
|
||||||
|
this.closeReason = null;
|
||||||
|
this.errors.clear();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,133 @@
|
||||||
|
//
|
||||||
|
// ========================================================================
|
||||||
|
// Copyright (c) 1995-2015 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.server.misbehaving;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
|
||||||
|
import java.util.concurrent.TimeUnit;
|
||||||
|
|
||||||
|
import org.eclipse.jetty.toolchain.test.EventQueue;
|
||||||
|
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||||
|
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||||
|
import org.eclipse.jetty.websocket.common.CloseInfo;
|
||||||
|
import org.eclipse.jetty.websocket.common.OpCode;
|
||||||
|
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||||
|
import org.eclipse.jetty.websocket.common.events.AbstractEventDriver;
|
||||||
|
import org.eclipse.jetty.websocket.common.test.BlockheadClient;
|
||||||
|
import org.eclipse.jetty.websocket.server.SimpleServletServer;
|
||||||
|
import org.junit.AfterClass;
|
||||||
|
import org.junit.BeforeClass;
|
||||||
|
import org.junit.Test;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Testing badly behaving Socket class implementations to get the best
|
||||||
|
* error messages and state out of the websocket implementation.
|
||||||
|
*/
|
||||||
|
public class MisbehavingClassTest
|
||||||
|
{
|
||||||
|
private static SimpleServletServer server;
|
||||||
|
private static BadSocketsServlet badSocketsServlet;
|
||||||
|
|
||||||
|
@BeforeClass
|
||||||
|
public static void startServer() throws Exception
|
||||||
|
{
|
||||||
|
badSocketsServlet = new BadSocketsServlet();
|
||||||
|
server = new SimpleServletServer(badSocketsServlet);
|
||||||
|
server.start();
|
||||||
|
}
|
||||||
|
|
||||||
|
@AfterClass
|
||||||
|
public static void stopServer()
|
||||||
|
{
|
||||||
|
server.stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testListenerRuntimeOnConnect() throws Exception
|
||||||
|
{
|
||||||
|
try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
|
||||||
|
{
|
||||||
|
client.setProtocols("listener-runtime-connect");
|
||||||
|
client.setTimeout(1,TimeUnit.SECONDS);
|
||||||
|
try (StacklessLogging scope = new StacklessLogging(AbstractEventDriver.class))
|
||||||
|
{
|
||||||
|
ListenerRuntimeOnConnectSocket socket = badSocketsServlet.listenerRuntimeConnect;
|
||||||
|
socket.reset();
|
||||||
|
|
||||||
|
client.connect();
|
||||||
|
client.sendStandardRequest();
|
||||||
|
client.expectUpgradeResponse();
|
||||||
|
|
||||||
|
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
|
||||||
|
WebSocketFrame frame = frames.poll();
|
||||||
|
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||||
|
CloseInfo close = new CloseInfo(frame);
|
||||||
|
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
|
||||||
|
|
||||||
|
client.write(close.asFrame()); // respond with close
|
||||||
|
|
||||||
|
// ensure server socket got close event
|
||||||
|
assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||||
|
assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
|
||||||
|
|
||||||
|
// Validate errors
|
||||||
|
assertThat("socket.onErrors",socket.errors.size(),is(1));
|
||||||
|
Throwable cause = socket.errors.pop();
|
||||||
|
assertThat("Error type",cause,instanceOf(ArrayIndexOutOfBoundsException.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testAnnotatedRuntimeOnConnect() throws Exception
|
||||||
|
{
|
||||||
|
try (BlockheadClient client = new BlockheadClient(server.getServerUri()))
|
||||||
|
{
|
||||||
|
client.setProtocols("annotated-runtime-connect");
|
||||||
|
client.setTimeout(1,TimeUnit.SECONDS);
|
||||||
|
try (StacklessLogging scope = new StacklessLogging(AbstractEventDriver.class))
|
||||||
|
{
|
||||||
|
AnnotatedRuntimeOnConnectSocket socket = badSocketsServlet.annotatedRuntimeConnect;
|
||||||
|
socket.reset();
|
||||||
|
|
||||||
|
client.connect();
|
||||||
|
client.sendStandardRequest();
|
||||||
|
client.expectUpgradeResponse();
|
||||||
|
|
||||||
|
EventQueue<WebSocketFrame> frames = client.readFrames(1,1,TimeUnit.SECONDS);
|
||||||
|
WebSocketFrame frame = frames.poll();
|
||||||
|
assertThat("frames[0].opcode",frame.getOpCode(),is(OpCode.CLOSE));
|
||||||
|
CloseInfo close = new CloseInfo(frame);
|
||||||
|
assertThat("Close Status Code",close.getStatusCode(),is(StatusCode.SERVER_ERROR));
|
||||||
|
|
||||||
|
client.write(close.asFrame()); // respond with close
|
||||||
|
|
||||||
|
// ensure server socket got close event
|
||||||
|
assertThat("Close Latch",socket.closeLatch.await(1,TimeUnit.SECONDS),is(true));
|
||||||
|
assertThat("closeStatusCode",socket.closeStatusCode,is(StatusCode.SERVER_ERROR));
|
||||||
|
|
||||||
|
// Validate errors
|
||||||
|
assertThat("socket.onErrors",socket.errors.size(),is(1));
|
||||||
|
Throwable cause = socket.errors.pop();
|
||||||
|
assertThat("Error type",cause,instanceOf(ArrayIndexOutOfBoundsException.class));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue