Moving test from jetty-parent to jetty-client module
This commit is contained in:
parent
a82ed7a954
commit
3975ae74cb
|
@ -1,309 +0,0 @@
|
|||
//
|
||||
// ========================================================================
|
||||
// Copyright (c) 1995-2013 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.dummy;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.ServerSocket;
|
||||
import java.net.Socket;
|
||||
import java.net.SocketException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.Map;
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
import org.eclipse.jetty.util.IO;
|
||||
import org.eclipse.jetty.util.log.Log;
|
||||
import org.eclipse.jetty.util.log.Logger;
|
||||
import org.eclipse.jetty.websocket.WebSocketConnectionRFC6455;
|
||||
import org.junit.Assert;
|
||||
|
||||
/**
|
||||
* Simple ServerSocket server used to test oddball server scenarios encountered in the real world.
|
||||
*/
|
||||
public class DummyServer
|
||||
{
|
||||
public static class ServerConnection
|
||||
{
|
||||
private static final Logger LOG = Log.getLogger(ServerConnection.class);
|
||||
private final Socket socket;
|
||||
private InputStream in;
|
||||
private OutputStream out;
|
||||
|
||||
public ServerConnection(Socket socket)
|
||||
{
|
||||
this.socket = socket;
|
||||
}
|
||||
|
||||
public int read(ByteBuffer buf) throws IOException
|
||||
{
|
||||
int len = 0;
|
||||
while ((in.available() > 0) && (buf.remaining() > 0))
|
||||
{
|
||||
buf.put((byte)in.read());
|
||||
len++;
|
||||
}
|
||||
return len;
|
||||
}
|
||||
|
||||
public void disconnect()
|
||||
{
|
||||
LOG.debug("disconnect");
|
||||
IO.close(in);
|
||||
IO.close(out);
|
||||
if (socket != null)
|
||||
{
|
||||
try
|
||||
{
|
||||
socket.close();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public InputStream getInputStream() throws IOException
|
||||
{
|
||||
if (in == null)
|
||||
{
|
||||
in = socket.getInputStream();
|
||||
}
|
||||
return in;
|
||||
}
|
||||
|
||||
public OutputStream getOutputStream() throws IOException
|
||||
{
|
||||
if (out == null)
|
||||
{
|
||||
out = socket.getOutputStream();
|
||||
}
|
||||
return out;
|
||||
}
|
||||
|
||||
public void flush() throws IOException
|
||||
{
|
||||
LOG.debug("flush()");
|
||||
getOutputStream().flush();
|
||||
}
|
||||
|
||||
public String readRequest() throws IOException
|
||||
{
|
||||
LOG.debug("Reading client request");
|
||||
StringBuilder request = new StringBuilder();
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream()));
|
||||
for (String line = in.readLine(); line != null; line = in.readLine())
|
||||
{
|
||||
if (line.length() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
request.append(line).append("\r\n");
|
||||
LOG.debug("read line: {}",line);
|
||||
}
|
||||
|
||||
LOG.debug("Client Request:{}{}","\n",request);
|
||||
return request.toString();
|
||||
}
|
||||
|
||||
public void respond(String rawstr) throws IOException
|
||||
{
|
||||
LOG.debug("respond(){}{}","\n",rawstr);
|
||||
getOutputStream().write(rawstr.getBytes());
|
||||
flush();
|
||||
}
|
||||
|
||||
public void setSoTimeout(int ms) throws SocketException
|
||||
{
|
||||
socket.setSoTimeout(ms);
|
||||
}
|
||||
|
||||
public void upgrade(Map<String, String> extraResponseHeaders) throws IOException
|
||||
{
|
||||
@SuppressWarnings("unused")
|
||||
Pattern patExts = Pattern.compile("^Sec-WebSocket-Extensions: (.*)$",Pattern.CASE_INSENSITIVE);
|
||||
Pattern patKey = Pattern.compile("^Sec-WebSocket-Key: (.*)$",Pattern.CASE_INSENSITIVE);
|
||||
|
||||
LOG.debug("(Upgrade) Reading HTTP Request");
|
||||
Matcher mat;
|
||||
String key = "not sent";
|
||||
BufferedReader in = new BufferedReader(new InputStreamReader(getInputStream()));
|
||||
for (String line = in.readLine(); line != null; line = in.readLine())
|
||||
{
|
||||
if (line.length() == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO: Check for extensions
|
||||
// mat = patExts.matcher(line);
|
||||
// if (mat.matches())
|
||||
|
||||
// Check for Key
|
||||
mat = patKey.matcher(line);
|
||||
if (mat.matches())
|
||||
{
|
||||
key = mat.group(1);
|
||||
}
|
||||
}
|
||||
|
||||
LOG.debug("(Upgrade) Writing HTTP Response");
|
||||
// TODO: handle extensions?
|
||||
|
||||
// Setup Response
|
||||
StringBuilder resp = new StringBuilder();
|
||||
resp.append("HTTP/1.1 101 Upgrade\r\n");
|
||||
resp.append("Upgrade: websocket\r\n");
|
||||
resp.append("Connection: upgrade\r\n");
|
||||
resp.append("Sec-WebSocket-Accept: ");
|
||||
resp.append(WebSocketConnectionRFC6455.hashKey(key)).append("\r\n");
|
||||
// extra response headers.
|
||||
if (extraResponseHeaders != null)
|
||||
{
|
||||
for (Map.Entry<String,String> header : extraResponseHeaders.entrySet())
|
||||
{
|
||||
resp.append(header.getKey());
|
||||
resp.append(": ");
|
||||
resp.append(header.getValue());
|
||||
resp.append("\r\n");
|
||||
}
|
||||
}
|
||||
resp.append("\r\n");
|
||||
|
||||
// Write Response
|
||||
getOutputStream().write(resp.toString().getBytes());
|
||||
flush();
|
||||
}
|
||||
|
||||
public void write(byte[] bytes) throws IOException
|
||||
{
|
||||
LOG.debug("Writing {} bytes", bytes.length);
|
||||
getOutputStream().write(bytes);
|
||||
}
|
||||
|
||||
public void write(byte[] buf, int offset, int length) throws IOException
|
||||
{
|
||||
LOG.debug("Writing bytes[{}], offset={}, length={}", buf.length, offset, length);
|
||||
getOutputStream().write(buf,offset,length);
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException
|
||||
{
|
||||
LOG.debug("Writing int={}", b);
|
||||
getOutputStream().write(b);
|
||||
}
|
||||
}
|
||||
|
||||
private static final Logger LOG = Log.getLogger(DummyServer.class);
|
||||
private ServerSocket serverSocket;
|
||||
private URI wsUri;
|
||||
|
||||
public ServerConnection accept() throws IOException
|
||||
{
|
||||
LOG.debug(".accept()");
|
||||
assertIsStarted();
|
||||
Socket socket = serverSocket.accept();
|
||||
return new ServerConnection(socket);
|
||||
}
|
||||
|
||||
private void assertIsStarted()
|
||||
{
|
||||
Assert.assertThat("ServerSocket",serverSocket,notNullValue());
|
||||
Assert.assertThat("ServerSocket.isBound",serverSocket.isBound(),is(true));
|
||||
Assert.assertThat("ServerSocket.isClosed",serverSocket.isClosed(),is(false));
|
||||
|
||||
Assert.assertThat("WsUri",wsUri,notNullValue());
|
||||
}
|
||||
|
||||
public URI getWsUri()
|
||||
{
|
||||
return wsUri;
|
||||
}
|
||||
|
||||
public void respondToClient(Socket connection, String serverResponse) throws IOException
|
||||
{
|
||||
InputStream in = null;
|
||||
InputStreamReader isr = null;
|
||||
BufferedReader buf = null;
|
||||
OutputStream out = null;
|
||||
try
|
||||
{
|
||||
in = connection.getInputStream();
|
||||
isr = new InputStreamReader(in);
|
||||
buf = new BufferedReader(isr);
|
||||
String line;
|
||||
while ((line = buf.readLine()) != null)
|
||||
{
|
||||
// System.err.println(line);
|
||||
if (line.length() == 0)
|
||||
{
|
||||
// Got the "\r\n" line.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// System.out.println("[Server-Out] " + serverResponse);
|
||||
out = connection.getOutputStream();
|
||||
out.write(serverResponse.getBytes());
|
||||
out.flush();
|
||||
}
|
||||
finally
|
||||
{
|
||||
IO.close(buf);
|
||||
IO.close(isr);
|
||||
IO.close(in);
|
||||
IO.close(out);
|
||||
}
|
||||
}
|
||||
|
||||
public void start() throws IOException
|
||||
{
|
||||
serverSocket = new ServerSocket();
|
||||
InetAddress addr = InetAddress.getByName("localhost");
|
||||
InetSocketAddress endpoint = new InetSocketAddress(addr,0);
|
||||
serverSocket.bind(endpoint);
|
||||
int port = serverSocket.getLocalPort();
|
||||
String uri = String.format("ws://%s:%d/",addr.getHostAddress(),port);
|
||||
wsUri = URI.create(uri);
|
||||
LOG.debug("Server Started on {} -> {}",endpoint,wsUri);
|
||||
}
|
||||
|
||||
public void stop()
|
||||
{
|
||||
LOG.debug("Stopping Server");
|
||||
try
|
||||
{
|
||||
serverSocket.close();
|
||||
}
|
||||
catch (IOException ignore)
|
||||
{
|
||||
/* ignore */
|
||||
}
|
||||
}
|
||||
|
||||
}
|
|
@ -1,3 +0,0 @@
|
|||
# Setup default logging implementation for during testing
|
||||
org.eclipse.jetty.util.log.class=org.eclipse.jetty.util.log.StdErrLog
|
||||
#org.eclipse.jetty.websocket.LEVEL=DEBUG
|
|
@ -16,88 +16,90 @@
|
|||
// ========================================================================
|
||||
//
|
||||
|
||||
package org.eclipse.jetty.websocket;
|
||||
package org.eclipse.jetty.websocket.client;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.eclipse.jetty.util.thread.QueuedThreadPool;
|
||||
import org.eclipse.jetty.websocket.dummy.DummyServer;
|
||||
import org.eclipse.jetty.websocket.dummy.DummyServer.ServerConnection;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketAdapter;
|
||||
import org.eclipse.jetty.websocket.api.WebSocketConnection;
|
||||
import org.eclipse.jetty.websocket.client.blockhead.BlockheadServer;
|
||||
import org.eclipse.jetty.websocket.client.blockhead.BlockheadServer.ServerConnection;
|
||||
import org.junit.Assert;
|
||||
import org.junit.Ignore;
|
||||
import org.junit.Test;
|
||||
|
||||
public class TomcatServerQuirksTest
|
||||
{
|
||||
public static class LatchedSocket extends WebSocketAdapter
|
||||
{
|
||||
final CountDownLatch openLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
final CountDownLatch closeLatch = new CountDownLatch(1);
|
||||
|
||||
@Override
|
||||
public void onWebSocketClose(int statusCode, String reason)
|
||||
{
|
||||
closeLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketConnect(WebSocketConnection connection)
|
||||
{
|
||||
openLatch.countDown();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onWebSocketText(String message)
|
||||
{
|
||||
dataLatch.countDown();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for when encountering a "Transfer-Encoding: chunked" on a Upgrade Response header.
|
||||
* <ul>
|
||||
* <li><a href="https://bugs.eclipse.org/bugs/show_bug.cgi?id=393075">Eclipse Jetty Bug #393075</a></li>
|
||||
* <li><a href="https://issues.apache.org/bugzilla/show_bug.cgi?id=54067">Apache Tomcat Bug #54067</a></li>
|
||||
* </ul>
|
||||
* @throws IOException
|
||||
*
|
||||
* @throws IOException
|
||||
*/
|
||||
@Test
|
||||
public void testTomcat7_0_32_WithTransferEncoding() throws Exception
|
||||
public void testTomcat7_0_32_WithTransferEncoding() throws Exception
|
||||
{
|
||||
DummyServer server = new DummyServer();
|
||||
int bufferSize = 512;
|
||||
QueuedThreadPool threadPool = new QueuedThreadPool();
|
||||
WebSocketClientFactory factory = new WebSocketClientFactory(threadPool, new ZeroMaskGen(), bufferSize);
|
||||
|
||||
try
|
||||
BlockheadServer server = new BlockheadServer();
|
||||
WebSocketClient client = new WebSocketClient();
|
||||
|
||||
try
|
||||
{
|
||||
int bufferSize = 512;
|
||||
|
||||
server.start();
|
||||
|
||||
|
||||
// Setup Client Factory
|
||||
threadPool.start();
|
||||
factory.start();
|
||||
|
||||
// Create Client
|
||||
WebSocketClient client = new WebSocketClient(factory);
|
||||
client.start();
|
||||
|
||||
// Create End User WebSocket Class
|
||||
final CountDownLatch openLatch = new CountDownLatch(1);
|
||||
final CountDownLatch dataLatch = new CountDownLatch(1);
|
||||
WebSocket.OnTextMessage websocket = new WebSocket.OnTextMessage()
|
||||
{
|
||||
public void onOpen(Connection connection)
|
||||
{
|
||||
openLatch.countDown();
|
||||
}
|
||||
LatchedSocket websocket = new LatchedSocket();
|
||||
|
||||
public void onMessage(String data)
|
||||
{
|
||||
// System.out.println("data = " + data);
|
||||
dataLatch.countDown();
|
||||
}
|
||||
|
||||
public void onClose(int closeCode, String message)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
// Open connection
|
||||
URI wsURI = server.getWsUri();
|
||||
client.open(wsURI, websocket);
|
||||
client.connect(websocket,wsURI);
|
||||
|
||||
// Accept incoming connection
|
||||
ServerConnection socket = server.accept();
|
||||
socket.setSoTimeout(2000); // timeout
|
||||
|
||||
|
||||
// Issue upgrade
|
||||
Map<String,String> extraResponseHeaders = new HashMap<String, String>();
|
||||
extraResponseHeaders.put("Transfer-Encoding", "chunked"); // !! The problem !!
|
||||
socket.upgrade(extraResponseHeaders);
|
||||
|
||||
// Add the extra problematic header that triggers bug found in jetty-io
|
||||
socket.addResponseHeader("Transfer-Encoding","chunked");
|
||||
socket.upgrade();
|
||||
|
||||
// Wait for proper upgrade
|
||||
Assert.assertTrue("Timed out waiting for Client side WebSocket open event", openLatch.await(1, TimeUnit.SECONDS));
|
||||
Assert.assertTrue("Timed out waiting for Client side WebSocket open event",websocket.openLatch.await(1,TimeUnit.SECONDS));
|
||||
|
||||
// Have server write frame.
|
||||
int length = bufferSize / 2;
|
||||
|
@ -107,18 +109,19 @@ public class TomcatServerQuirksTest
|
|||
serverFrame.put((byte)(length >> 8)); // first length byte
|
||||
serverFrame.put((byte)(length & 0xFF)); // second length byte
|
||||
for (int i = 0; i < length; ++i)
|
||||
{
|
||||
serverFrame.put((byte)'x');
|
||||
}
|
||||
serverFrame.flip();
|
||||
byte buf[] = serverFrame.array();
|
||||
socket.write(buf,0,buf.length);
|
||||
socket.flush();
|
||||
|
||||
Assert.assertTrue(dataLatch.await(1000, TimeUnit.SECONDS));
|
||||
}
|
||||
finally
|
||||
Assert.assertTrue(websocket.dataLatch.await(1000,TimeUnit.SECONDS));
|
||||
}
|
||||
finally
|
||||
{
|
||||
factory.stop();
|
||||
threadPool.stop();
|
||||
client.stop();
|
||||
server.stop();
|
||||
}
|
||||
}
|
|
@ -33,7 +33,9 @@ import java.net.SocketException;
|
|||
import java.net.URI;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.concurrent.TimeoutException;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
|
@ -87,6 +89,7 @@ public class BlockheadServer
|
|||
private OutputStream out;
|
||||
private InputStream in;
|
||||
|
||||
private Map<String, String> extraResponseHeaders = new HashMap<>();
|
||||
private OutgoingFrames outgoing = this;
|
||||
|
||||
public ServerConnection(Socket socket)
|
||||
|
@ -101,6 +104,14 @@ public class BlockheadServer
|
|||
this.extensionRegistry = new WebSocketExtensionFactory(policy,bufferPool);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an extra header for the upgrade response (from the server). No extra work is done to ensure the key and value are sane for http.
|
||||
*/
|
||||
public void addResponseHeader(String rawkey, String rawvalue)
|
||||
{
|
||||
extraResponseHeaders.put(rawkey,rawvalue);
|
||||
}
|
||||
|
||||
public void close() throws IOException
|
||||
{
|
||||
write(new WebSocketFrame(OpCode.CLOSE));
|
||||
|
@ -412,6 +423,16 @@ public class BlockheadServer
|
|||
}
|
||||
resp.append("\r\n");
|
||||
}
|
||||
if (extraResponseHeaders.size() > 0)
|
||||
{
|
||||
for (Map.Entry<String, String> xheader : extraResponseHeaders.entrySet())
|
||||
{
|
||||
resp.append(xheader.getKey());
|
||||
resp.append(": ");
|
||||
resp.append(xheader.getValue());
|
||||
resp.append("\r\n");
|
||||
}
|
||||
}
|
||||
resp.append("\r\n");
|
||||
|
||||
// Write Response
|
||||
|
|
Loading…
Reference in New Issue