Issue #207 - Close Handshake Test refactor
This commit is contained in:
parent
a8503a2932
commit
b8e10ce9ef
|
@ -135,10 +135,8 @@ public abstract class AbstractWebSocketConnection extends AbstractConnection imp
|
|||
// close FrameFlusher, we cannot write anymore at this point.
|
||||
flusher.close();
|
||||
|
||||
// We need to gently close first, to allow
|
||||
// SSL close alerts to be sent by Jetty
|
||||
getEndPoint().close();
|
||||
closed.set(true);
|
||||
close();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -0,0 +1,719 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2017 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.tests.client;
|
||||
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.channels.SelectableChannel;
|
||||
import java.nio.channels.SelectionKey;
|
||||
import java.nio.channels.SocketChannel;
|
||||
import java.util.Arrays;
|
||||
import java.util.concurrent.CompletableFuture;
|
||||
import java.util.concurrent.Future;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.ManagedSelector;
|
||||
import org.eclipse.jetty.io.SelectorManager;
|
||||
import org.eclipse.jetty.io.SocketChannelEndPoint;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.ProtocolException;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.Parser;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.RawFrameBuilder;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.UntrustedWSServer;
|
||||
import org.eclipse.jetty.websocket.tests.UntrustedWSSession;
|
||||
import org.junit.After;
|
||||
import org.junit.Before;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.TestName;
|
||||
|
||||
public class ClientCloseHandshakeTest
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ClientCloseHandshakeTest.class);
|
||||
|
||||
@Rule
|
||||
public TestName testname = new TestName();
|
||||
|
||||
private UntrustedWSServer server;
|
||||
private WebSocketClient client;
|
||||
|
||||
public static class TestClientTransportOverHTTP extends HttpClientTransportOverHTTP
|
||||
{
|
||||
@Override
|
||||
protected SelectorManager newSelectorManager(HttpClient client)
|
||||
{
|
||||
return new ClientSelectorManager(client, 1)
|
||||
{
|
||||
@Override
|
||||
protected EndPoint newEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key)
|
||||
{
|
||||
ClientCloseTest.TestEndPoint endPoint = new ClientCloseTest.TestEndPoint(channel, selector, key, getScheduler());
|
||||
endPoint.setIdleTimeout(client.getIdleTimeout());
|
||||
return endPoint;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public static class TestEndPoint extends SocketChannelEndPoint
|
||||
{
|
||||
public AtomicBoolean congestedFlush = new AtomicBoolean(false);
|
||||
|
||||
public TestEndPoint(SelectableChannel channel, ManagedSelector selector, SelectionKey key, Scheduler scheduler)
|
||||
{
|
||||
super((SocketChannel) channel, selector, key, scheduler);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean flush(ByteBuffer... buffers) throws IOException
|
||||
{
|
||||
boolean flushed = super.flush(buffers);
|
||||
congestedFlush.set(!flushed);
|
||||
// TODO: if true, toss exception (different use case)
|
||||
return flushed;
|
||||
}
|
||||
}
|
||||
|
||||
@Before
|
||||
public void startClient() throws Exception
|
||||
{
|
||||
HttpClient httpClient = new HttpClient(new TestClientTransportOverHTTP(), null);
|
||||
client = new WebSocketClient(httpClient);
|
||||
client.addBean(httpClient);
|
||||
client.start();
|
||||
}
|
||||
|
||||
@Before
|
||||
public void startServer() throws Exception
|
||||
{
|
||||
server = new UntrustedWSServer();
|
||||
server.start();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopClient() throws Exception
|
||||
{
|
||||
client.stop();
|
||||
}
|
||||
|
||||
@After
|
||||
public void stopServer() throws Exception
|
||||
{
|
||||
server.stop();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Client Initiated - no data
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* TCP Connect TCP Accept
|
||||
* WS Handshake Request >
|
||||
* < WS Handshake Response
|
||||
* OnOpen() OnOpen()
|
||||
* close(Normal)
|
||||
* send:Close/Normal >
|
||||
* OnFrame(Close)
|
||||
* OnClose(normal)
|
||||
* exit onClose()
|
||||
* < send:Close/Normal
|
||||
* OnFrame(Close) close.success(disconnect())
|
||||
* OnClose(normal)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClientInitiated_NoData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Initiated - no data - alternate close code
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* TCP Connect TCP Accept
|
||||
* WS Handshake Request >
|
||||
* < WS Handshake Response
|
||||
* OnOpen() OnOpen()
|
||||
* close(Normal)
|
||||
* send:Close/Normal >
|
||||
* OnFrame(Close)
|
||||
* OnClose(normal)
|
||||
* close(Shutdown)
|
||||
* < send:Close/Shutdown (queue)
|
||||
* cb.success() -> disconnect()
|
||||
* exit onClose()
|
||||
* OnFrame(Close)
|
||||
* OnClose(Shutdown)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClientInitiated_NoData_ChangeClose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Initiated - async send (complete message) during onClose
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* TCP Connect TCP Accept
|
||||
* WS Handshake Request >
|
||||
* < WS Handshake Response
|
||||
* OnOpen() OnOpen()
|
||||
* close(Normal)
|
||||
* send:Close/Normal >
|
||||
* OnFrame(Close)
|
||||
* OnClose(normal)
|
||||
* sendAsync:Text (queued to extension stack)
|
||||
* exit onClose()
|
||||
* < send:Close/Normal (queued)
|
||||
* OnFrame(Close) disconnect()
|
||||
* OnClose(normal)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClientInitiated_AsyncDataDuringClose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Initiated - partial send data during on close
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* TCP Connect TCP Accept
|
||||
* WS Handshake Request >
|
||||
* < WS Handshake Response
|
||||
* OnOpen() OnOpen()
|
||||
* close(Normal)
|
||||
* send:Close/Normal >
|
||||
* OnFrame(Close)
|
||||
* OnClose(normal)
|
||||
* session.getRemote().writePartial(msg, fin=false)
|
||||
* exit onClose()
|
||||
* < send:Close/Normal (queued)
|
||||
* OnFrame(Close) disconnect()
|
||||
* OnClose(normal)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClientInitiated_PartialDataDuringClose()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Initiated - async streaming during on close
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* TCP Connect TCP Accept
|
||||
* WS Handshake Request >
|
||||
* < WS Handshake Response
|
||||
* OnOpen() OnOpen()
|
||||
* close(Normal)
|
||||
* send:Close/Normal >
|
||||
* OnFrame(Close)
|
||||
* OnClose(normal)
|
||||
* session.getRemote().getOutputStream()
|
||||
* new Thread()
|
||||
* send movie to client
|
||||
* exit onClose()
|
||||
* < send:Close/Normal
|
||||
* OnFrame(Close) disconnect()
|
||||
* OnClose(normal)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClientInitiated_AsyncStreamingDuringClose()
|
||||
{
|
||||
// TODO: dubious
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Initiated - server is streaming data
|
||||
* <pre>
|
||||
* Client Server Server Thread
|
||||
* ---------- ---------- --------------------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* new Thread() send(Text/!fin)
|
||||
* send(Continuation/!fin)
|
||||
* close(Normal)
|
||||
* send(Close/Normal) >
|
||||
* OnFrame(Close)
|
||||
* OnClose(normal)
|
||||
* exit onClose()
|
||||
* send(Continuation/!fin)
|
||||
* < send(Close/Normal)
|
||||
* send(Continuation/fin) - FAIL
|
||||
* OnFrame(Close) disconnect()
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClientInitiated_ServerStreamingData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Initiated - client is streaming data
|
||||
* <pre>
|
||||
* Client Client Thread Server
|
||||
* ---------- ------------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* new Thread() send(Text/!fin) - (streaming here)
|
||||
* send(Continuation/!fin)
|
||||
* close(Normal)
|
||||
* send:Close/Normal
|
||||
* send(Continuation/!fin) FAIL
|
||||
* send(Continuation/fin) FAIL - (whole here)
|
||||
* OnFrame(Close)
|
||||
*
|
||||
* OnClose(normal)
|
||||
* exit onClose()
|
||||
* send:Close/Normal
|
||||
* OnFrame(Close)
|
||||
* OnClose(normal) disconnect()
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClientInitiated_ClientStreamingData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Server Initiated - no data
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* close(Normal)
|
||||
* < send(Close/Normal)
|
||||
* OnFrame(Close)
|
||||
* OnClose(normal)
|
||||
* exit onClose()
|
||||
* send:Close/Normal) >
|
||||
* OnFrame(Close)
|
||||
* disconnect() OnClose(normal)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testServerInitiated_NoData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Server Initiated - server is streaming data
|
||||
* <pre>
|
||||
* Client Server Server Thread
|
||||
* ---------- ---------- --------------------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* new Thread() send(Text/!fin)
|
||||
* send(Continuation/!fin)
|
||||
* close(Normal)
|
||||
* send(Continuation/!fin)
|
||||
* send(Continuation/fin)
|
||||
* < send(Close/Normal)
|
||||
* OnFrame(Close)
|
||||
* send(Close/Normal) >
|
||||
* OnClose(normal) OnFrame(Close)
|
||||
* disconnect() OnClose(normal)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testServerInitiated_ServerStreamingData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Server Initiated - client is streaming data
|
||||
* <pre>
|
||||
* Client Client Thread Server
|
||||
* ---------- ------------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* new Thread() send(Text/!fin)
|
||||
* send(Continuation/!fin)
|
||||
* close(Normal)
|
||||
* send(Close/Normal)
|
||||
* OnFrame(Close)
|
||||
* send(Continuation/!fin)
|
||||
* send(Continuation/fin)
|
||||
* send(Close/Normal)
|
||||
* OnClose(normal) OnFrame(Close)
|
||||
* disconnect() OnClose(normal)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testServerInitiated_ClientStreamingData()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Read IOException
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* ....
|
||||
* disconnect - (accidental)
|
||||
* conn.onError(EOF)
|
||||
* OnClose(ABNORMAL)
|
||||
* disconnect()
|
||||
* read -> IOException
|
||||
* conn.onError(IOException)
|
||||
* OnClose(ABNORMAL)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClient_Read_IOException()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Reads -1
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* ...(some time later)...
|
||||
* fillAndParse() disconnect - (no-close-handshake)
|
||||
* read = -1
|
||||
* // no close frame received?
|
||||
* OnClose(ABNORMAL)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Needs work")
|
||||
public void testClient_Read_Minus1() throws Exception
|
||||
{
|
||||
// Set client timeout
|
||||
final int timeout = 1000;
|
||||
client.setMaxIdleTimeout(timeout);
|
||||
|
||||
URI wsUri = server.getUntrustedWsUri(this.getClass(), testname);
|
||||
CompletableFuture<UntrustedWSSession> serverSessionFut = new CompletableFuture<>();
|
||||
server.registerOnOpenFuture(wsUri, serverSessionFut);
|
||||
|
||||
// Client connects
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri);
|
||||
|
||||
// Server accepts connect
|
||||
UntrustedWSSession serverSession = serverSessionFut.get(10, TimeUnit.SECONDS);
|
||||
|
||||
// Wait for client connect on via future
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
clientSession.getRemote().setBatchMode(BatchMode.OFF);
|
||||
|
||||
// Wait for client connect via client websocket
|
||||
assertThat("Client WebSocket is Open", clientSocket.openLatch.await(Defaults.OPEN_EVENT_TIMEOUT_MS, TimeUnit.MILLISECONDS), is(true));
|
||||
|
||||
try (StacklessLogging ignored = new StacklessLogging(clientSocket.LOG))
|
||||
{
|
||||
// client sends close frame
|
||||
final String origCloseReason = "Normal Close";
|
||||
clientSocket.close(StatusCode.NORMAL, origCloseReason);
|
||||
|
||||
// server receives close frame
|
||||
serverSession.getUntrustedEndpoint().awaitCloseEvent("Server");
|
||||
serverSession.getUntrustedEndpoint().assertCloseInfo("Server", StatusCode.NORMAL, is(origCloseReason));
|
||||
|
||||
// client should not have received close message (yet)
|
||||
clientSocket.assertNotClosed("Client");
|
||||
|
||||
// server shuts down connection (no frame reply)
|
||||
serverSession.disconnect();
|
||||
|
||||
// client reads -1 (EOF)
|
||||
clientSocket.assertErrorEvent("Client", instanceOf(IOException.class), containsString("EOF"));
|
||||
// client triggers close event on client ws-endpoint
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.ABNORMAL, containsString("Disconnected"));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Idle Timeout
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* ...(some time later)...
|
||||
* onIdleTimeout()
|
||||
* conn.onError(TimeoutException)
|
||||
* send:Close/Shutdown >
|
||||
* OnFrame(Close)
|
||||
* OnClose(Shutdown)
|
||||
* exit onClose()
|
||||
* < send(Close/Shutdown)
|
||||
* OnFrame(Close)
|
||||
* OnClose(Shutdown) disconnect()
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClient_IdleTimeout()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Idle Timeout
|
||||
* <pre>
|
||||
* Client Server
|
||||
* ---------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* ...(some time later)...
|
||||
* close(Normal)
|
||||
* send:Close/Normal >
|
||||
* (state unknown)
|
||||
* ...(some time later)...
|
||||
* onIdleTimeout()
|
||||
* conn.onError(TimeoutException)
|
||||
* OnClose(ABNORMAL/IdleTimeout)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testClient_IdleTimeout_Alt()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Client ProtocolViolation
|
||||
* <pre>
|
||||
* Bad Client Server
|
||||
* ---------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* ....
|
||||
* send:Text(BadFormat)
|
||||
* close(ProtocolViolation)
|
||||
* send(Close/ProtocolViolation)
|
||||
* OnFrame(Close) disconnect()
|
||||
* OnClose(ProtocolViolation)
|
||||
* send(Close/ProtocolViolation) > FAILS
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
@Ignore("Needs work")
|
||||
public void testClient_ProtocolViolation_Received() throws Exception
|
||||
{
|
||||
// Set client timeout
|
||||
final int timeout = 1000;
|
||||
client.setMaxIdleTimeout(timeout);
|
||||
|
||||
URI wsUri = server.getUntrustedWsUri(this.getClass(), testname);
|
||||
CompletableFuture<UntrustedWSSession> serverSessionFut = new CompletableFuture<>();
|
||||
server.registerOnOpenFuture(wsUri, serverSessionFut);
|
||||
|
||||
// Client connects
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri);
|
||||
|
||||
// Server accepts connect
|
||||
UntrustedWSSession serverSession = serverSessionFut.get(10, TimeUnit.SECONDS);
|
||||
|
||||
// Wait for client connect on via future
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
clientSession.getRemote().setBatchMode(BatchMode.OFF);
|
||||
|
||||
// Wait for client connect via client websocket
|
||||
assertThat("Client WebSocket is Open", clientSocket.openLatch.await(Defaults.OPEN_EVENT_TIMEOUT_MS, TimeUnit.MILLISECONDS), is(true));
|
||||
|
||||
// client should not have received close message (yet)
|
||||
clientSocket.assertNotClosed("Client");
|
||||
|
||||
// server sends bad close frame (too big of a reason message)
|
||||
byte msg[] = new byte[400];
|
||||
Arrays.fill(msg, (byte) 'x');
|
||||
ByteBuffer bad = ByteBuffer.allocate(500);
|
||||
RawFrameBuilder.putOpFin(bad, OpCode.CLOSE, true);
|
||||
RawFrameBuilder.putLength(bad, msg.length + 2, false);
|
||||
bad.putShort((short) StatusCode.NORMAL);
|
||||
bad.put(msg);
|
||||
BufferUtil.flipToFlush(bad, 0);
|
||||
try (StacklessLogging ignored = new StacklessLogging(Parser.class))
|
||||
{
|
||||
serverSession.getUntrustedConnection().writeRaw(bad);
|
||||
|
||||
// client should have noticed the error
|
||||
clientSocket.assertErrorEvent("Client", instanceOf(ProtocolException.class), containsString("Invalid control frame"));
|
||||
|
||||
// client parse invalid frame, notifies server of close (protocol error)
|
||||
serverSession.getUntrustedEndpoint().awaitCloseEvent("Server");
|
||||
serverSession.getUntrustedEndpoint().assertCloseInfo("Server", StatusCode.PROTOCOL, allOf(containsString("Invalid control frame"), containsString("length")));
|
||||
}
|
||||
|
||||
// server disconnects
|
||||
serverSession.disconnect();
|
||||
|
||||
// client close event on ws-endpoint
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.PROTOCOL, allOf(containsString("Invalid control frame"), containsString("length")));
|
||||
}
|
||||
|
||||
/**
|
||||
* Client Exception during Write
|
||||
* <pre>
|
||||
* Bad Client Server
|
||||
* ---------- ----------
|
||||
* Connect Accept
|
||||
* Handshake Request >
|
||||
* < Handshake Response
|
||||
* OnOpen OnOpen
|
||||
* ....
|
||||
* send:Text()
|
||||
* write -> IOException
|
||||
* conn.onError(IOException)
|
||||
* OnClose(Shutdown)
|
||||
* disconnect()
|
||||
* read -> IOException
|
||||
* conn.onError(IOException)
|
||||
* OnClose(ABNORMAL)
|
||||
* disconnect()
|
||||
* </pre>
|
||||
*/
|
||||
@Test
|
||||
public void testWriteException() throws Exception
|
||||
{
|
||||
// Set client timeout
|
||||
final int timeout = 1000;
|
||||
client.setMaxIdleTimeout(timeout);
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
URI wsUri = server.getUntrustedWsUri(this.getClass(), testname);
|
||||
CompletableFuture<UntrustedWSSession> serverSessionFut = new CompletableFuture<>();
|
||||
server.registerOnOpenFuture(wsUri, serverSessionFut);
|
||||
|
||||
// Client connects
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri);
|
||||
|
||||
// Server accepts connect
|
||||
UntrustedWSSession serverSession = serverSessionFut.get(10, TimeUnit.SECONDS);
|
||||
|
||||
// client confirms connection via echo
|
||||
// Wait for client connect on via future
|
||||
Session clientSession = clientConnectFuture.get(Defaults.CONNECT_TIMEOUT_MS, TimeUnit.MILLISECONDS);
|
||||
clientSession.getRemote().setBatchMode(BatchMode.OFF);
|
||||
|
||||
// Wait for client connect via client websocket
|
||||
assertThat("Client WebSocket is Open", clientSocket.openLatch.await(Defaults.OPEN_EVENT_TIMEOUT_MS, TimeUnit.MILLISECONDS), is(true));
|
||||
|
||||
// setup client endpoint for write failure (test only)
|
||||
EndPoint endp = clientSocket.getJettyEndPoint();
|
||||
endp.shutdownOutput();
|
||||
|
||||
// client enqueue close frame
|
||||
// client write failure
|
||||
final String origCloseReason = "Normal Close";
|
||||
clientSocket.close(StatusCode.NORMAL, origCloseReason);
|
||||
|
||||
assertThat("OnError", clientSocket.error.get(), instanceOf(EofException.class));
|
||||
|
||||
// client triggers close event on client ws-endpoint
|
||||
// assert - close code==1006 (abnormal)
|
||||
// assert - close reason message contains (write failure)
|
||||
assertTrue("Client onClose not called", clientSocket.closeLatch.getCount() > 0);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDecoderError_CallsOnError()
|
||||
{
|
||||
// TODO: put in JSR specific layers of tests
|
||||
}
|
||||
}
|
|
@ -18,14 +18,12 @@
|
|||
|
||||
package org.eclipse.jetty.websocket.tests.client;
|
||||
|
||||
import static org.hamcrest.Matchers.allOf;
|
||||
import static org.hamcrest.Matchers.anything;
|
||||
import static org.hamcrest.Matchers.containsString;
|
||||
import static org.hamcrest.Matchers.instanceOf;
|
||||
import static org.hamcrest.Matchers.is;
|
||||
import static org.hamcrest.Matchers.nullValue;
|
||||
import static org.junit.Assert.assertThat;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.SocketTimeoutException;
|
||||
|
@ -43,26 +41,20 @@ import java.util.concurrent.atomic.AtomicBoolean;
|
|||
import org.eclipse.jetty.client.HttpClient;
|
||||
import org.eclipse.jetty.client.http.HttpClientTransportOverHTTP;
|
||||
import org.eclipse.jetty.io.EndPoint;
|
||||
import org.eclipse.jetty.io.EofException;
|
||||
import org.eclipse.jetty.io.ManagedSelector;
|
||||
import org.eclipse.jetty.io.SelectorManager;
|
||||
import org.eclipse.jetty.io.SocketChannelEndPoint;
|
||||
import org.eclipse.jetty.toolchain.test.TestTracker;
|
||||
import org.eclipse.jetty.util.BufferUtil;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.util.log.StacklessLogging;
|
||||
import org.eclipse.jetty.util.thread.Scheduler;
|
||||
import org.eclipse.jetty.websocket.api.BatchMode;
|
||||
import org.eclipse.jetty.websocket.api.ProtocolException;
|
||||
import org.eclipse.jetty.websocket.api.Session;
|
||||
import org.eclipse.jetty.websocket.api.StatusCode;
|
||||
import org.eclipse.jetty.websocket.client.WebSocketClient;
|
||||
import org.eclipse.jetty.websocket.common.OpCode;
|
||||
import org.eclipse.jetty.websocket.common.Parser;
|
||||
import org.eclipse.jetty.websocket.common.WebSocketFrame;
|
||||
import org.eclipse.jetty.websocket.tests.Defaults;
|
||||
import org.eclipse.jetty.websocket.tests.RawFrameBuilder;
|
||||
import org.eclipse.jetty.websocket.tests.TrackingEndpoint;
|
||||
import org.eclipse.jetty.websocket.tests.UntrustedWSConnection;
|
||||
import org.eclipse.jetty.websocket.tests.UntrustedWSEndpoint;
|
||||
|
@ -155,6 +147,7 @@ public class ClientCloseTest
|
|||
{
|
||||
boolean flushed = super.flush(buffers);
|
||||
congestedFlush.set(!flushed);
|
||||
// TODO: if true, toss exception (different use case)
|
||||
return flushed;
|
||||
}
|
||||
}
|
||||
|
@ -235,104 +228,6 @@ public class ClientCloseTest
|
|||
clientSocket.assertErrorEvent("Client", instanceOf(SocketTimeoutException.class), anything());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testProtocolException() throws Exception
|
||||
{
|
||||
// Set client timeout
|
||||
final int timeout = 1000;
|
||||
client.setMaxIdleTimeout(timeout);
|
||||
|
||||
URI wsUri = server.getUntrustedWsUri(this.getClass(), testname);
|
||||
CompletableFuture<UntrustedWSSession> serverSessionFut = new CompletableFuture<>();
|
||||
server.registerOnOpenFuture(wsUri, serverSessionFut);
|
||||
|
||||
// Client connects
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri);
|
||||
|
||||
// Server accepts connect
|
||||
UntrustedWSSession serverSession = serverSessionFut.get(10, TimeUnit.SECONDS);
|
||||
|
||||
// client confirms connection via echo
|
||||
confirmConnection(clientSocket, clientConnectFuture, serverSession);
|
||||
|
||||
// client should not have received close message (yet)
|
||||
clientSocket.assertNotClosed("Client");
|
||||
|
||||
// server sends bad close frame (too big of a reason message)
|
||||
byte msg[] = new byte[400];
|
||||
Arrays.fill(msg, (byte) 'x');
|
||||
ByteBuffer bad = ByteBuffer.allocate(500);
|
||||
RawFrameBuilder.putOpFin(bad, OpCode.CLOSE, true);
|
||||
RawFrameBuilder.putLength(bad, msg.length + 2, false);
|
||||
bad.putShort((short) StatusCode.NORMAL);
|
||||
bad.put(msg);
|
||||
BufferUtil.flipToFlush(bad, 0);
|
||||
try (StacklessLogging ignored = new StacklessLogging(Parser.class))
|
||||
{
|
||||
serverSession.getUntrustedConnection().writeRaw(bad);
|
||||
|
||||
// client should have noticed the error
|
||||
clientSocket.assertErrorEvent("Client", instanceOf(ProtocolException.class), containsString("Invalid control frame"));
|
||||
|
||||
// client parse invalid frame, notifies server of close (protocol error)
|
||||
serverSession.getUntrustedEndpoint().awaitCloseEvent("Server");
|
||||
serverSession.getUntrustedEndpoint().assertCloseInfo("Server", StatusCode.PROTOCOL, allOf(containsString("Invalid control frame"), containsString("length")));
|
||||
}
|
||||
|
||||
// server disconnects
|
||||
serverSession.disconnect();
|
||||
|
||||
// client close event on ws-endpoint
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.PROTOCOL, allOf(containsString("Invalid control frame"), containsString("length")));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testReadEOF() throws Exception
|
||||
{
|
||||
// Set client timeout
|
||||
final int timeout = 1000;
|
||||
client.setMaxIdleTimeout(timeout);
|
||||
|
||||
URI wsUri = server.getUntrustedWsUri(this.getClass(), testname);
|
||||
CompletableFuture<UntrustedWSSession> serverSessionFut = new CompletableFuture<>();
|
||||
server.registerOnOpenFuture(wsUri, serverSessionFut);
|
||||
|
||||
// Client connects
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri);
|
||||
|
||||
// Server accepts connect
|
||||
UntrustedWSSession serverSession = serverSessionFut.get(10, TimeUnit.SECONDS);
|
||||
|
||||
// client confirms connection via echo
|
||||
confirmConnection(clientSocket, clientConnectFuture, serverSession);
|
||||
|
||||
try (StacklessLogging ignored = new StacklessLogging(clientSocket.LOG))
|
||||
{
|
||||
// client sends close frame
|
||||
final String origCloseReason = "Normal Close";
|
||||
clientSocket.close(StatusCode.NORMAL, origCloseReason);
|
||||
|
||||
// server receives close frame
|
||||
serverSession.getUntrustedEndpoint().awaitCloseEvent("Server");
|
||||
serverSession.getUntrustedEndpoint().assertCloseInfo("Server", StatusCode.NORMAL, is(origCloseReason));
|
||||
|
||||
// client should not have received close message (yet)
|
||||
clientSocket.assertNotClosed("Client");
|
||||
|
||||
// server shuts down connection (no frame reply)
|
||||
serverSession.disconnect();
|
||||
|
||||
// client reads -1 (EOF)
|
||||
clientSocket.assertErrorEvent("Client", instanceOf(IOException.class), containsString("EOF"));
|
||||
// client triggers close event on client ws-endpoint
|
||||
clientSocket.awaitCloseEvent("Client");
|
||||
clientSocket.assertCloseInfo("Client", StatusCode.ABNORMAL, containsString("Disconnected"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testServerNoCloseHandshake() throws Exception
|
||||
{
|
||||
|
@ -423,42 +318,4 @@ public class ClientCloseTest
|
|||
clientSockets[i].assertCloseInfo("Client[" + i + "]", StatusCode.SHUTDOWN, containsString("Shutdown"));
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testWriteException() throws Exception
|
||||
{
|
||||
// Set client timeout
|
||||
final int timeout = 1000;
|
||||
client.setMaxIdleTimeout(timeout);
|
||||
|
||||
TrackingEndpoint clientSocket = new TrackingEndpoint(testname.getMethodName());
|
||||
URI wsUri = server.getUntrustedWsUri(this.getClass(), testname);
|
||||
CompletableFuture<UntrustedWSSession> serverSessionFut = new CompletableFuture<>();
|
||||
server.registerOnOpenFuture(wsUri, serverSessionFut);
|
||||
|
||||
// Client connects
|
||||
Future<Session> clientConnectFuture = client.connect(clientSocket, wsUri);
|
||||
|
||||
// Server accepts connect
|
||||
UntrustedWSSession serverSession = serverSessionFut.get(10, TimeUnit.SECONDS);
|
||||
|
||||
// client confirms connection via echo
|
||||
confirmConnection(clientSocket, clientConnectFuture, serverSession);
|
||||
|
||||
// setup client endpoint for write failure (test only)
|
||||
EndPoint endp = clientSocket.getJettyEndPoint();
|
||||
endp.shutdownOutput();
|
||||
|
||||
// client enqueue close frame
|
||||
// client write failure
|
||||
final String origCloseReason = "Normal Close";
|
||||
clientSocket.close(StatusCode.NORMAL, origCloseReason);
|
||||
|
||||
assertThat("OnError", clientSocket.error.get(), instanceOf(EofException.class));
|
||||
|
||||
// client triggers close event on client ws-endpoint
|
||||
// assert - close code==1006 (abnormal)
|
||||
// assert - close reason message contains (write failure)
|
||||
assertTrue("Client onClose not called", clientSocket.closeLatch.getCount() > 0);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue