diff --git a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/frames/BinaryFrame.java b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/frames/BinaryFrame.java index e5e733c6fa5..12e29806df1 100644 --- a/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/frames/BinaryFrame.java +++ b/jetty-websocket/websocket-core/src/main/java/org/eclipse/jetty/websocket/frames/BinaryFrame.java @@ -16,6 +16,12 @@ public class BinaryFrame extends DataFrame super(OpCode.BINARY); } + public BinaryFrame( byte[] payload ) + { + this(); + setPayload(payload); + } + @Override public String toString() { diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1_1.java similarity index 98% rename from jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1.java rename to jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1_1.java index 783d7ba1457..731a89dc71b 100644 --- a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1.java +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1_1.java @@ -10,17 +10,15 @@ import org.eclipse.jetty.websocket.ByteBufferAssert; import org.eclipse.jetty.websocket.Debug; import org.eclipse.jetty.websocket.api.WebSocketBehavior; import org.eclipse.jetty.websocket.api.WebSocketPolicy; -import org.eclipse.jetty.websocket.frames.BinaryFrame; import org.eclipse.jetty.websocket.frames.TextFrame; import org.eclipse.jetty.websocket.generator.Generator; import org.eclipse.jetty.websocket.parser.FrameParseCapture; import org.eclipse.jetty.websocket.parser.Parser; import org.eclipse.jetty.websocket.parser.TextPayloadParser; import org.junit.Assert; -import org.junit.Ignore; import org.junit.Test; -public class ABCase1 +public class ABCase1_1 { WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); @@ -421,8 +419,8 @@ public class ABCase1 @Test public void testParse65535ByteTextCase1_1_6() { - Debug.enableDebugLogging(Parser.class); - Debug.enableDebugLogging(TextPayloadParser.class); + //Debug.enableDebugLogging(Parser.class); + //Debug.enableDebugLogging(TextPayloadParser.class); int length = 65535; diff --git a/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1_2.java b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1_2.java new file mode 100644 index 00000000000..63217d75b80 --- /dev/null +++ b/jetty-websocket/websocket-core/src/test/java/org/eclipse/jetty/websocket/ab/ABCase1_2.java @@ -0,0 +1,544 @@ +package org.eclipse.jetty.websocket.ab; + + +import static org.hamcrest.Matchers.is; + +import java.nio.ByteBuffer; + +import org.eclipse.jetty.io.StandardByteBufferPool; +import org.eclipse.jetty.util.BufferUtil; +import org.eclipse.jetty.websocket.ByteBufferAssert; +import org.eclipse.jetty.websocket.Debug; +import org.eclipse.jetty.websocket.api.WebSocketBehavior; +import org.eclipse.jetty.websocket.api.WebSocketPolicy; +import org.eclipse.jetty.websocket.frames.BinaryFrame; +import org.eclipse.jetty.websocket.frames.TextFrame; +import org.eclipse.jetty.websocket.generator.Generator; +import org.eclipse.jetty.websocket.parser.FrameParseCapture; +import org.eclipse.jetty.websocket.parser.Parser; +import org.eclipse.jetty.websocket.parser.TextPayloadParser; +import org.junit.Assert; +import org.junit.Test; + +public class ABCase1_2 +{ + StandardByteBufferPool bufferPool = new StandardByteBufferPool(); + WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); + + + @Test + public void testGenerateEmptyBinaryCase1_2_1() + { + BinaryFrame binaryFrame = new BinaryFrame(new byte[]{}); + binaryFrame.setFin(true); + + Generator generator = new Generator(bufferPool,policy); + ByteBuffer actual = generator.generate(binaryFrame); + + ByteBuffer expected = ByteBuffer.allocate(5); + + expected.put(new byte[] + { (byte)0x82, (byte)0x00 }); + + actual.flip(); + expected.flip(); + + ByteBufferAssert.assertEquals("buffers do not match",expected,actual); + + } + + @Test + public void testParseEmptyBinaryCase1_2_1() + { + + ByteBuffer expected = ByteBuffer.allocate(5); + + expected.put(new byte[] + { (byte)0x82, (byte)0x00 }); + + expected.flip(); + + Parser parser = new Parser(policy); + FrameParseCapture capture = new FrameParseCapture(); + parser.addListener(capture); + parser.parse(expected); + + capture.assertNoErrors(); + capture.assertHasFrame(BinaryFrame.class,1); + + BinaryFrame pActual = (BinaryFrame)capture.getFrames().get(0); + Assert.assertThat("BinaryFrame.payloadLength",pActual.getPayloadLength(),is(0)); + ByteBufferAssert.assertSize("BinaryFrame.payload",0,pActual.getPayload()); + } + + @Test + public void testGenerate125ByteBinaryCase1_2_2() + { + int length = 125; + + ByteBuffer bb = ByteBuffer.allocate(length); + + for ( int i = 0 ; i < length ; ++i) + { + bb.put("*".getBytes()); + } + + bb.flip(); + + BinaryFrame binaryFrame = new BinaryFrame( BufferUtil.toArray(bb) ); + binaryFrame.setFin(true); + + Generator generator = new Generator(bufferPool,policy); + ByteBuffer actual = generator.generate(binaryFrame); + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + + byte b = 0x00; // no masking + b |= length & 0x7F; + expected.put(b); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + actual.flip(); + expected.flip(); + + ByteBufferAssert.assertEquals("buffers do not match",expected,actual); + + } + + @Test + public void testParse125ByteBinaryCase1_2_2() + { + int length = 125; + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + byte b = 0x00; // no masking + b |= length & 0x7F; + expected.put(b); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + expected.flip(); + + Parser parser = new Parser(policy); + FrameParseCapture capture = new FrameParseCapture(); + parser.addListener(capture); + parser.parse(expected); + + capture.assertNoErrors(); + capture.assertHasFrame(BinaryFrame.class,1); + + BinaryFrame pActual = (BinaryFrame)capture.getFrames().get(0); + Assert.assertThat("BinaryFrame.payloadLength",pActual.getPayloadLength(),is(length)); + ByteBufferAssert.assertSize("BinaryFrame.payload",length,pActual.getPayload()); + } + + @Test + public void testGenerate126ByteBinaryCase1_2_3() + { + int length = 126; + + ByteBuffer bb = ByteBuffer.allocate(length); + + for ( int i = 0 ; i < length ; ++i) + { + bb.put("*".getBytes()); + } + + bb.flip(); + + BinaryFrame binaryFrame = new BinaryFrame(BufferUtil.toArray(bb)); + binaryFrame.setFin(true); + + Generator generator = new Generator(bufferPool,policy); + ByteBuffer actual = generator.generate(binaryFrame); + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + + byte b = 0x00; // no masking + b |= length & 0x7E; + expected.put(b); + + //expected.put((byte)((length>>8) & 0xFF)); + //expected.put((byte)(length & 0xFF)); + expected.putShort((short)length); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + actual.flip(); + expected.flip(); + + ByteBufferAssert.assertEquals("buffers do not match",expected,actual); + + } + + @Test + public void testParse126ByteBinaryCase1_2_3() + { + int length = 126; + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + byte b = 0x00; // no masking + b |= length & 0x7E; + expected.put(b); + expected.putShort((short)length); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + expected.flip(); + + Parser parser = new Parser(policy); + FrameParseCapture capture = new FrameParseCapture(); + parser.addListener(capture); + parser.parse(expected); + + capture.assertNoErrors(); + capture.assertHasFrame(BinaryFrame.class,1); + + BinaryFrame pActual = (BinaryFrame)capture.getFrames().get(0); + Assert.assertThat("BinaryFrame.payloadLength",pActual.getPayloadLength(),is(length)); + ByteBufferAssert.assertSize("BinaryFrame.payload",length,pActual.getPayload()); + } + + @Test + public void testGenerate127ByteBinaryCase1_2_4() + { + int length = 127; + + ByteBuffer bb = ByteBuffer.allocate(length); + + for ( int i = 0 ; i < length ; ++i) + { + bb.put("*".getBytes()); + + } + + bb.flip(); + + BinaryFrame binaryFrame = new BinaryFrame(BufferUtil.toArray(bb)); + binaryFrame.setFin(true); + + Generator generator = new Generator(bufferPool,policy); + ByteBuffer actual = generator.generate(binaryFrame); + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + + byte b = 0x00; // no masking + b |= length & 0x7E; + expected.put(b); + + //expected.put((byte)((length>>8) & 0xFF)); + //expected.put((byte)(length & 0xFF)); + expected.putShort((short)length); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + actual.flip(); + expected.flip(); + + ByteBufferAssert.assertEquals("buffers do not match",expected,actual); + + } + + @Test + public void testParse127ByteBinaryCase1_2_4() + { + int length = 127; + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + byte b = 0x00; // no masking + b |= length & 0x7E; + expected.put(b); + expected.putShort((short)length); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + expected.flip(); + + Parser parser = new Parser(policy); + FrameParseCapture capture = new FrameParseCapture(); + parser.addListener(capture); + parser.parse(expected); + + capture.assertNoErrors(); + capture.assertHasFrame(BinaryFrame.class,1); + + BinaryFrame pActual = (BinaryFrame)capture.getFrames().get(0); + Assert.assertThat("BinaryFrame.payloadLength",pActual.getPayloadLength(),is(length)); + ByteBufferAssert.assertSize("BinaryFrame.payload",length,pActual.getPayload()); + } + + @Test + public void testGenerate128ByteBinaryCase1_2_5() + { + int length = 128; + + ByteBuffer bb = ByteBuffer.allocate(length); + + for ( int i = 0 ; i < length ; ++i) + { + bb.put("*".getBytes()); + + } + + bb.flip(); + BinaryFrame textFrame = new BinaryFrame(BufferUtil.toArray(bb)); + textFrame.setFin(true); + + Generator generator = new Generator(bufferPool,policy); + ByteBuffer actual = generator.generate(textFrame); + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + + byte b = 0x00; // no masking + b |= 0x7E; + expected.put(b); + + expected.put((byte)(length>>8)); + expected.put((byte)(length & 0xFF)); + //expected.putShort((short)length); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + actual.flip(); + expected.flip(); + + ByteBufferAssert.assertEquals("buffers do not match",expected,actual); + + } + + @Test + public void testParse128ByteBinaryCase1_2_5() + { + int length = 128; + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + byte b = 0x00; // no masking + b |= 0x7E; + expected.put(b); + expected.putShort((short)length); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + expected.flip(); + + Parser parser = new Parser(policy); + FrameParseCapture capture = new FrameParseCapture(); + parser.addListener(capture); + parser.parse(expected); + + capture.assertNoErrors(); + capture.assertHasFrame(BinaryFrame.class,1); + + BinaryFrame pActual = (BinaryFrame)capture.getFrames().get(0); + Assert.assertThat("BinaryFrame.payloadLength",pActual.getPayloadLength(),is(length)); + ByteBufferAssert.assertSize("BinaryFrame.payload",length,pActual.getPayload()); + } + + @Test + public void testGenerate65535ByteBinaryCase1_2_6() + { + int length = 65535; + + ByteBuffer bb = ByteBuffer.allocate(length); + + for ( int i = 0 ; i < length ; ++i) + { + bb.put("*".getBytes()); + + } + + bb.flip(); + + BinaryFrame textFrame = new BinaryFrame(BufferUtil.toArray(bb)); + textFrame.setFin(true); + + Generator generator = new Generator(bufferPool,policy); + ByteBuffer actual = generator.generate(textFrame); + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + + byte b = 0x00; // no masking + b |= 0x7E; + expected.put(b); + expected.put(new byte[]{ (byte)0xff, (byte)0xff}); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + actual.flip(); + expected.flip(); + + ByteBufferAssert.assertEquals("buffers do not match",expected,actual); + + } + + @Test + public void testParse65535ByteBinaryCase1_2_6() + { + int length = 65535; + + ByteBuffer expected = ByteBuffer.allocate(length + 5); + + expected.put(new byte[] + { (byte)0x82 }); + byte b = 0x00; // no masking + b |= 0x7E; + expected.put(b); + expected.put(new byte[]{ (byte)0xff, (byte)0xff}); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + expected.flip(); + WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); + policy.setMaxTextMessageSize(length); + Parser parser = new Parser(policy); + FrameParseCapture capture = new FrameParseCapture(); + parser.addListener(capture); + parser.parse(expected); + + capture.assertNoErrors(); + capture.assertHasFrame(BinaryFrame.class,1); + + BinaryFrame pActual = (BinaryFrame)capture.getFrames().get(0); + Assert.assertThat("BinaryFrame.payloadLength",pActual.getPayloadLength(),is(length)); + ByteBufferAssert.assertSize("BinaryFrame.payload",length,pActual.getPayload()); + } + + + @Test + public void testGenerate65536ByteBinaryCase1_2_7() + { + int length = 65536; + + ByteBuffer bb = ByteBuffer.allocate(length); + + for ( int i = 0 ; i < length ; ++i) + { + bb.put("*".getBytes()); + + } + + bb.flip(); + + BinaryFrame textFrame = new BinaryFrame(BufferUtil.toArray(bb)); + textFrame.setFin(true); + + Generator generator = new Generator(bufferPool,policy); + ByteBuffer actual = generator.generate(textFrame); + + ByteBuffer expected = ByteBuffer.allocate(length + 11); + + expected.put(new byte[] + { (byte)0x82 }); + + byte b = 0x00; // no masking + b |= 0x7F; + expected.put(b); + expected.put(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}); + + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + actual.flip(); + expected.flip(); + + ByteBufferAssert.assertEquals("buffers do not match",expected,actual); + + } + + @Test + public void testParse65536ByteBinaryCase1_2_7() + { + int length = 65536; + + ByteBuffer expected = ByteBuffer.allocate(length + 11); + + expected.put(new byte[] + { (byte)0x82 }); + byte b = 0x00; // no masking + b |= 0x7F; + expected.put(b); + expected.put(new byte[]{0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00}); + + for ( int i = 0 ; i < length ; ++i ) + { + expected.put("*".getBytes()); + } + + expected.flip(); + + WebSocketPolicy policy = new WebSocketPolicy(WebSocketBehavior.SERVER); + policy.setMaxTextMessageSize(length); + Parser parser = new Parser(policy); + FrameParseCapture capture = new FrameParseCapture(); + parser.addListener(capture); + parser.parse(expected); + + capture.assertNoErrors(); + capture.assertHasFrame(BinaryFrame.class,1); + + BinaryFrame pActual = (BinaryFrame)capture.getFrames().get(0); + Assert.assertThat("BinaryFrame.payloadLength",pActual.getPayloadLength(),is(length)); + ByteBufferAssert.assertSize("BinaryFrame.payload",length,pActual.getPayload()); + } +} diff --git a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java index 3fa5d222373..f96dcc86d6c 100644 --- a/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java +++ b/jetty-websocket/websocket-server/src/main/java/org/eclipse/jetty/websocket/server/WebSocketServerFactory.java @@ -15,101 +15,87 @@ package org.eclipse.jetty.websocket.server; import java.io.IOException; import java.util.ArrayList; -import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.Queue; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.Executor; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.eclipse.jetty.http.HttpStatus; import org.eclipse.jetty.io.AsyncEndPoint; -import org.eclipse.jetty.io.ByteBufferPool; -import org.eclipse.jetty.io.StandardByteBufferPool; import org.eclipse.jetty.server.HttpConnection; import org.eclipse.jetty.util.QuotedStringTokenizer; import org.eclipse.jetty.util.component.AbstractLifeCycle; import org.eclipse.jetty.util.log.Log; import org.eclipse.jetty.util.log.Logger; import org.eclipse.jetty.websocket.WebSocket; -import org.eclipse.jetty.websocket.api.ExtensionConfig; import org.eclipse.jetty.websocket.api.WebSocketPolicy; import org.eclipse.jetty.websocket.extensions.Extension; import org.eclipse.jetty.websocket.extensions.deflate.DeflateFrameExtension; import org.eclipse.jetty.websocket.extensions.fragment.FragmentExtension; import org.eclipse.jetty.websocket.extensions.identity.IdentityExtension; -import org.eclipse.jetty.websocket.server.handshake.HandshakeHixie76; -import org.eclipse.jetty.websocket.server.handshake.HandshakeRFC6455; /** * Factory to create WebSocket connections */ public class WebSocketServerFactory extends AbstractLifeCycle { + public interface Acceptor + { + /* ------------------------------------------------------------ */ + /** + *
Checks the origin of an incoming WebSocket handshake request.
+ * @param request the incoming HTTP upgrade request + * @param origin the origin URI + * @return boolean to indicate that the origin is acceptable. + */ + boolean checkOrigin(HttpServletRequest request, String origin); + + /* ------------------------------------------------------------ */ + /** + *Factory method that applications needs to implement to return a + * {@link WebSocket} object.
+ * @param request the incoming HTTP upgrade request + * @param protocol the websocket sub protocol + * @return a new {@link WebSocket} object that will handle websocket events. + */ + WebSocket doWebSocketConnect(HttpServletRequest request, String protocol); + } private static final Logger LOG = Log.getLogger(WebSocketServerFactory.class); - private static final int RESPONSE_BUFFER_SIZE = 8192; - private final Queue- * This method will not normally return, but will instead throw a UpgradeConnectionException, to exit HTTP handling and initiate WebSocket handling of the - * connection. - * - * @param request - * The request to upgrade - * @param response - * The response to upgrade - * @param websocket - * The websocket handler implementation to use - * @param acceptedSubProtocol - * The accepted websocket sub protocol - * @throws IOException - * in case of I/O errors + *
This method will not normally return, but will instead throw a
+ * UpgradeConnectionException, to exit HTTP handling and initiate
+ * WebSocket handling of the connection.
+ *
+ * @param request The request to upgrade
+ * @param response The response to upgrade
+ * @param websocket The websocket handler implementation to use
+ * @param protocol The websocket protocol
+ * @throws IOException in case of I/O errors
*/
- public void upgrade(HttpServletRequest request, HttpServletResponse response, WebSocket websocket, String acceptedSubProtocol) throws IOException
+ public void upgrade(HttpServletRequest request, HttpServletResponse response, WebSocket websocket, String protocol)
+ throws IOException
{
if (!"websocket".equalsIgnoreCase(request.getHeader("Upgrade")))
{
- throw new IllegalStateException("Not a 'WebSocket: Ugprade' request");
+ throw new IllegalStateException("!Upgrade:websocket");
}
if (!"HTTP/1.1".equals(request.getProtocol()))
{
- throw new IllegalStateException("Not a 'HTTP/1.1' request");
+ throw new IllegalStateException("!HTTP/1.1");
}
- int version = request.getIntHeader("Sec-WebSocket-Version");
- if (version < 0)
- {
+ int draft = request.getIntHeader("Sec-WebSocket-Version");
+ if (draft < 0) {
// Old pre-RFC version specifications (header not present in RFC-6455)
- version = request.getIntHeader("Sec-WebSocket-Draft");
+ draft = request.getIntHeader("Sec-WebSocket-Draft");
}
+ HttpConnection http = HttpConnection.getCurrentConnection();
+ AsyncEndPoint endp = http.getEndPoint();
- List