You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tomcat.apache.org by ma...@apache.org on 2012/12/05 20:21:48 UTC
svn commit: r1417595 - in /tomcat/trunk:
java/org/apache/tomcat/websocket/LocalStrings.properties
java/org/apache/tomcat/websocket/WsFrame.java
java/org/apache/tomcat/websocket/WsProtocolHandler.java
test/org/apache/tomcat/websocket/TestWsFrame.java
Author: markt
Date: Wed Dec 5 19:21:47 2012
New Revision: 1417595
URL: http://svn.apache.org/viewvc?rev=1417595&view=rev
Log:
WebSocket 1.0 implementation part 9 of many
Now able to receive short payloads. Lots of hacks in this code that need to be addressed.
Added:
tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java (with props)
tomcat/trunk/test/org/apache/tomcat/websocket/TestWsFrame.java (with props)
Modified:
tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java
Modified: tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties?rev=1417595&r1=1417594&r2=1417595&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/LocalStrings.properties Wed Dec 5 19:21:47 2012
@@ -18,4 +18,8 @@ serverContainer.missingEndpoint=An Endpo
serverContainer.pojoDeploy=POJO class [{0}] deploying to path [{1}] in ServletContext [{2}]
serverContainer.servletContextMismatch=Attempted to register a POJO annotated for WebSocket at path [{0}] in the ServletContext with context path [{1}] when the WebSocket ServerContainer is allocated to the ServletContext with context path [{2}]
serverContainer.servletContextMissing=No ServletContext was specified
-uriTemplate.noMatch=The input template [{0}] generated the pattern [{1}] which did not match the supplied pathInfo [{2}]
\ No newline at end of file
+uriTemplate.noMatch=The input template [{0}] generated the pattern [{1}] which did not match the supplied pathInfo [{2}]
+wsFrame.byteToLongFail=Too many bytes ([{0}]) were provided to be converted into a long
+wsFrame.controlPayloadTooBig=A control frame was sent with a payload of length [{0}] which is larger than the maximum length permitted of 125 bytes
+wsFrame.controlNoFin=A control frame was sent that did not have the fin bit set. Control frames are not permitted to use continuation frames.
+wsFrame.notMasked=The client frame was not masked but all client frames must be masked
\ No newline at end of file
Added: tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java?rev=1417595&view=auto
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java (added)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java Wed Dec 5 19:21:47 2012
@@ -0,0 +1,210 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket;
+
+import java.io.EOFException;
+import java.io.IOException;
+import java.nio.charset.Charset;
+
+import javax.servlet.ServletInputStream;
+
+import org.apache.tomcat.util.res.StringManager;
+
+/**
+ * Takes the ServletInputStream and converts the received data into WebSocket
+ * frames.
+ */
+public class WsFrame {
+
+ private static StringManager sm = StringManager.getManager(
+ Constants.PACKAGE_NAME);
+
+ private final ServletInputStream sis;
+ private final WsSession wsSession;
+ private final byte[] inputBuffer;
+ private int pos = 0;
+
+ private State state = State.NEW_FRAME;
+ private boolean fin;
+ private int rsv;
+ private byte opCode;
+ private byte[] mask = new byte[4];
+ private long payloadLength = -1;
+ private int headerLength = -1;
+
+
+ public WsFrame(ServletInputStream sis, WsSession wsSession) {
+ this.sis = sis;
+ this.wsSession = wsSession;
+
+ inputBuffer = new byte[8192];
+ }
+
+
+ /**
+ * Called when there is data in the ServletInputStream to process.
+ */
+ public void onDataAvailable() throws IOException {
+ while (sis.isReady()) {
+ // Fill up the input buffer with as much data as we can
+ int read = sis.read(inputBuffer, pos, inputBuffer.length - pos);
+ if (read == 0) {
+ return;
+ }
+ if (read == -1) {
+ throw new EOFException();
+ }
+ pos += read;
+
+ while (true) {
+ if (state == State.NEW_FRAME) {
+ if (!processInitialHeader()) {
+ break;
+ }
+ }
+ if (state == State.PARTIAL_HEADER) {
+ if (!processRemainingHeader()) {
+ break;
+ }
+ }
+ if (state == State.DATA) {
+ if (!processData()) {
+ break;
+ }
+ }
+ }
+ }
+ }
+
+
+ private boolean processInitialHeader() throws IOException {
+ // Need at least two bytes of data to do this
+ if (pos < 2) {
+ return false;
+ }
+
+ int b = inputBuffer[0];
+ fin = (b & 0x80) > 0;
+ rsv = (b & 0x70) >>> 4;
+ opCode = (byte) (b & 0x0F);
+
+ b = inputBuffer[1];
+ // Client data must be masked
+ if ((b & 0x80) == 0) {
+ throw new IOException(sm.getString("wsFrame.notMasked.notMasked"));
+ }
+
+ payloadLength = b & 0x7F;
+
+ state = State.PARTIAL_HEADER;
+ return true;
+ }
+
+
+ private boolean processRemainingHeader() throws IOException {
+ // Initial 2 bytes already read + 4 for the mask
+ headerLength = 6;
+
+ // Add additional bytes depending on length
+ if (payloadLength == 126) {
+ headerLength +=2;
+ } else if (payloadLength == 127) {
+ headerLength +=8;
+ }
+
+ if (pos < headerLength) {
+ return false;
+ }
+
+ // Calculate new payload length if necessary
+ if (payloadLength == 126) {
+ payloadLength = byteArrayToLong(inputBuffer, 2, 2);
+ } else if (payloadLength == 127) {
+ payloadLength = byteArrayToLong(inputBuffer, 2, 8);
+ }
+
+ if (isControl()) {
+ if (payloadLength > 125) {
+ throw new IOException(sm.getString(
+ "wsFrame.controlPayloadTooBig",
+ Long.valueOf(payloadLength)));
+ }
+ if (!fin) {
+ throw new IOException("wsFrame.controlNoFin");
+ }
+ }
+
+ System.arraycopy(inputBuffer, headerLength - 4, mask, 0, 4);
+
+ state = State.DATA;
+ return true;
+ }
+
+
+ private boolean processData() {
+ if (pos < (headerLength + payloadLength)) {
+ // TODO Check if partial messages supported
+ if (inputBuffer.length - pos > 0) {
+ return false;
+ }
+ throw new UnsupportedOperationException();
+ } else {
+ // Unmask the data
+ for (int i = 0; i < payloadLength; i++) {
+ inputBuffer[headerLength + i] = (byte)
+ ((inputBuffer[headerLength + i] ^ mask[i % 4]) & 0xFF);
+ }
+ // TODO Handle incoming data properly
+ System.out.println(new String(inputBuffer, headerLength,
+ (int) payloadLength, Charset.forName("UTF-8")));
+ }
+
+ state = State.NEW_FRAME;
+ pos = 0;
+ return true;
+ }
+
+
+ private boolean isControl() {
+ return (opCode & 0x08) > 0;
+ }
+
+
+ protected static long byteArrayToLong(byte[] b, int start, int len)
+ throws IOException {
+
+ if (len > 8) {
+ throw new IOException(
+ sm.getString("wsFrame.byteToLongFail", Long.valueOf(len)));
+ }
+
+ int shift = 0;
+ long result = 0;
+ for (int i = start + len - 1; i >= start; i--) {
+ result = result + ((b[i] & 0xFF) << shift);
+ shift += 8;
+ }
+
+ return result;
+ }
+
+ private static enum State {
+ NEW_FRAME,
+ PARTIAL_HEADER,
+ DATA
+ }
+}
Propchange: tomcat/trunk/java/org/apache/tomcat/websocket/WsFrame.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java?rev=1417595&r1=1417594&r2=1417595&view=diff
==============================================================================
--- tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java (original)
+++ tomcat/trunk/java/org/apache/tomcat/websocket/WsProtocolHandler.java Wed Dec 5 19:21:47 2012
@@ -33,12 +33,12 @@ public class WsProtocolHandler implement
private final Endpoint ep;
private final ClassLoader applicationClassLoader;
- private final WsSession session;
+ private final WsSession wsSession;
public WsProtocolHandler(Endpoint ep) {
this.ep = ep;
applicationClassLoader = Thread.currentThread().getContextClassLoader();
- session = new WsSession();
+ wsSession = new WsSession();
}
@Override
@@ -49,7 +49,7 @@ public class WsProtocolHandler implement
ClassLoader cl = t.getContextClassLoader();
t.setContextClassLoader(applicationClassLoader);
try {
- ep.onOpen(session);
+ ep.onOpen(wsSession);
} finally {
t.setContextClassLoader(cl);
}
@@ -63,7 +63,9 @@ public class WsProtocolHandler implement
throw new IllegalStateException(e);
}
- sis.setReadListener(new WsReadListener(this));
+ WsFrame wsFrame = new WsFrame(sis, wsSession);
+
+ sis.setReadListener(new WsReadListener(this, wsFrame));
sos.setWriteListener(new WsWriteListener(this));
}
@@ -83,15 +85,21 @@ public class WsProtocolHandler implement
private static class WsReadListener implements ReadListener {
private final WsProtocolHandler wsProtocolHandler;
+ private final WsFrame wsFrame;
- private WsReadListener(WsProtocolHandler wsProtocolHandler) {
+ private WsReadListener(WsProtocolHandler wsProtocolHandler,
+ WsFrame wsFrame) {
this.wsProtocolHandler = wsProtocolHandler;
+ this.wsFrame = wsFrame;
}
@Override
public void onDataAvailable() {
- // TODO Auto-generated method stub
-
+ try {
+ wsFrame.onDataAvailable();
+ } catch (IOException e) {
+ onError(e);
+ }
}
@Override
Added: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsFrame.java
URL: http://svn.apache.org/viewvc/tomcat/trunk/test/org/apache/tomcat/websocket/TestWsFrame.java?rev=1417595&view=auto
==============================================================================
--- tomcat/trunk/test/org/apache/tomcat/websocket/TestWsFrame.java (added)
+++ tomcat/trunk/test/org/apache/tomcat/websocket/TestWsFrame.java Wed Dec 5 19:21:47 2012
@@ -0,0 +1,49 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.tomcat.websocket;
+
+import java.io.IOException;
+
+import org.junit.Assert;
+import org.junit.Test;
+
+public class TestWsFrame {
+
+ @Test
+ public void testByteArrayToLong() throws IOException {
+ Assert.assertEquals(0L, WsFrame.byteArrayToLong(new byte[] { 0 }, 0, 1));
+ Assert.assertEquals(1L, WsFrame.byteArrayToLong(new byte[] { 1 }, 0, 1));
+ Assert.assertEquals(0xFF, WsFrame.byteArrayToLong(new byte[] { -1 }, 0, 1));
+ Assert.assertEquals(0xFFFF,
+ WsFrame.byteArrayToLong(new byte[] { -1, -1 }, 0, 2));
+ Assert.assertEquals(0xFFFFFF,
+ WsFrame.byteArrayToLong(new byte[] { -1, -1, -1 }, 0, 3));
+ }
+
+
+ @Test
+ public void testByteArrayToLongOffset() throws IOException {
+ Assert.assertEquals(0L, WsFrame.byteArrayToLong(new byte[] { 20, 0 }, 1, 1));
+ Assert.assertEquals(1L, WsFrame.byteArrayToLong(new byte[] { 20, 1 }, 1, 1));
+ Assert.assertEquals(0xFF, WsFrame.byteArrayToLong(new byte[] { 20, -1 }, 1, 1));
+ Assert.assertEquals(0xFFFF,
+ WsFrame.byteArrayToLong(new byte[] { 20, -1, -1 }, 1, 2));
+ Assert.assertEquals(0xFFFFFF,
+ WsFrame.byteArrayToLong(new byte[] { 20, -1, -1, -1 }, 1, 3));
+ }
+
+}
Propchange: tomcat/trunk/test/org/apache/tomcat/websocket/TestWsFrame.java
------------------------------------------------------------------------------
svn:eol-style = native
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org