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/03/16 00:39:49 UTC
svn commit: r1301270 [1/2] - in /tomcat/tc7.0.x/trunk: ./
java/org/apache/catalina/websocket/ java/org/apache/coyote/
java/org/apache/coyote/ajp/ java/org/apache/coyote/http11/
java/org/apache/coyote/http11/upgrade/ test/org/apache/catalina/websocket/
...
Author: markt
Date: Thu Mar 15 23:39:48 2012
New Revision: 1301270
URL: http://svn.apache.org/viewvc?rev=1301270&view=rev
Log:
More WebSocket
Added:
tomcat/tc7.0.x/trunk/test/org/apache/catalina/websocket/TestUtf8.java
- copied unchanged from r1295997, tomcat/trunk/test/org/apache/catalina/websocket/TestUtf8.java
tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/
- copied from r1297716, tomcat/trunk/webapps/examples/WEB-INF/classes/websocket/snake/
tomcat/tc7.0.x/trunk/webapps/examples/websocket/snake.html (contents, props changed)
- copied, changed from r1297716, tomcat/trunk/webapps/examples/websocket/snake.html
Modified:
tomcat/tc7.0.x/trunk/ (props changed)
tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/LocalStrings.properties
tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrame.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java
tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/LocalStrings.properties
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeInbound.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java
tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java
tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java
tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java (contents, props changed)
tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java (contents, props changed)
tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java (contents, props changed)
tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java (contents, props changed)
tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml
tomcat/tc7.0.x/trunk/webapps/examples/websocket/echo.html
tomcat/tc7.0.x/trunk/webapps/examples/websocket/index.html
Propchange: tomcat/tc7.0.x/trunk/
------------------------------------------------------------------------------
--- svn:mergeinfo (original)
+++ svn:mergeinfo Thu Mar 15 23:39:48 2012
@@ -1 +1 @@
-/tomcat/trunk:1156115-1157160,1157162-1157859,1157862-1157942,1157945-1160347,1160349-1163716,1163718-1166689,1166691-1174340,1174342-1175596,1175598-1175611,1175613-1175932,1175934-1177783,1177785-1177980,1178006-1180720,1180722-1183094,1183096-1187753,1187755,1187775,1187801,1187806,1187809,1187826-1188312,1188314-1188401,1188646-1188840,1188842-1190176,1190178-1195223,1195225-1195953,1195955,1195957-1201238,1201240-1203345,1203347-1206623,1206625-1208046,1208073,1208096,1208114,1208145,1208772,1209194-1212125,1212127-1220291,1220293,1220295-1221321,1221323-1222328,1222332-1222401,1222405-1222795,1222850-1222950,1222969-1225326,1225328-1225463,1225465,1225627,1225629-1226534,1226536-1228908,1228911-1228923,1228927-1229532,1229534-1230766,1230768-1231625,1231627-1233414,1233419-1235207,1235209-1237425,1237427,1237429-1237977,1237981,1237985,1237995,1238070,1238073,1239024-1239048,1239050-1239062,1239135,1239256,1239258-1239485,1239785-1240046,1240101,1240106,1240109,1240112
,1240114,1240116,1240118,1240121,1240329,1240474-1240850,1240857,1241087,1241160,1241408-1241822,1241908-1241909,1241912-1242110,1242371-1292130,1292134-1292458,1292464-1292670,1292672-1292776,1292780-1293392,1293831-1293832,1295998,1296284,1297014-1297015,1297017,1297158,1297177,1297202,1297209,1297213,1297717,1297722,1297729,1297768,1297778,1297818,1297828,1297979,1297987,1298121,1298140,1298590,1298592,1298628-1298629,1298794,1298983-1298984,1299020,1299034,1299819,1300154-1300155,1300569,1300948
+/tomcat/trunk:1156115-1157160,1157162-1157859,1157862-1157942,1157945-1160347,1160349-1163716,1163718-1166689,1166691-1174340,1174342-1175596,1175598-1175611,1175613-1175932,1175934-1177783,1177785-1177980,1178006-1180720,1180722-1183094,1183096-1187753,1187755,1187775,1187801,1187806,1187809,1187826-1188312,1188314-1188401,1188646-1188840,1188842-1190176,1190178-1195223,1195225-1195953,1195955,1195957-1201238,1201240-1203345,1203347-1206623,1206625-1208046,1208073,1208096,1208114,1208145,1208772,1209194-1212125,1212127-1220291,1220293,1220295-1221321,1221323-1222328,1222332-1222401,1222405-1222795,1222850-1222950,1222969-1225326,1225328-1225463,1225465,1225627,1225629-1226534,1226536-1228908,1228911-1228923,1228927-1229532,1229534-1230766,1230768-1231625,1231627-1233414,1233419-1235207,1235209-1237425,1237427,1237429-1237977,1237981,1237985,1237995,1238070,1238073,1239024-1239048,1239050-1239062,1239135,1239256,1239258-1239485,1239785-1240046,1240101,1240106,1240109,1240112
,1240114,1240116,1240118,1240121,1240329,1240474-1240850,1240857,1241087,1241160,1241408-1241822,1241908-1241909,1241912-1242110,1242371-1292130,1292134-1292458,1292464-1292670,1292672-1292776,1292780-1293392,1293397-1297017,1297019-1297963,1297965-1299820,1300154-1300155,1300569,1300948
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/Constants.java Thu Mar 15 23:39:48 2012
@@ -29,4 +29,93 @@ public class Constants {
public static final byte OPCODE_CLOSE = 0x08;
public static final byte OPCODE_PING = 0x09;
public static final byte OPCODE_PONG = 0x0A;
+
+ // Status Codes
+ // Definitions as per RFC 6455 (http://tools.ietf.org/html/rfc6455)
+ /**
+ * 1000 indicates a normal closure, meaning whatever purpose the
+ * connection was established for has been fulfilled.
+ */
+ public static final int STATUS_CLOSE_NORMAL = 1000;
+
+ /**
+ * 1001 indicates that an endpoint is "going away", such as a server
+ * going down, or a browser having navigated away from a page.
+ */
+ public static final int STATUS_SHUTDOWN = 1001;
+
+ /**
+ * 1002 indicates that an endpoint is terminating the connection due
+ * to a protocol error.
+ */
+ public static final int STATUS_PROTOCOL_ERROR = 1002;
+
+ /**
+ * 1003 indicates that an endpoint is terminating the connection
+ * because it has received a type of data it cannot accept (e.g. an
+ * endpoint that understands only text data MAY send this if it
+ * receives a binary message).
+ */
+ public static final int STATUS_UNEXPECTED_DATA_TYPE = 1003;
+
+ // 1004 is reserved. The specific meaning might be defined in the future.
+
+ /**
+ * 1005 is a reserved value and MUST NOT be set as a status code in a
+ * Close control frame by an endpoint. It is designated for use in
+ * applications expecting a status code to indicate that no status
+ * code was actually present.
+ */
+ public static final int STATUS_CODE_MISSING = 1005;
+
+ /**
+ * 1006 is a reserved value and MUST NOT be set as a status code in a
+ * Close control frame by an endpoint. It is designated for use in
+ * applications expecting a status code to indicate that the
+ * connection was closed abnormally, e.g. without sending or
+ * receiving a Close control frame.
+ */
+ public static final int STATUS_CLOSED_UNEXPECTEDLY = 1006;
+
+ /**
+ * 1007 indicates that an endpoint is terminating the connection
+ * because it has received data within a message that was not
+ * consistent with the type of the message (e.g., non-UTF-8 [RFC3629]
+ * data within a text message).
+ */
+ public static final int STATUS_BAD_DATA = 1007;
+
+ /**
+ * 1008 indicates that an endpoint is terminating the connection
+ * because it has received a message that violates its policy. This
+ * is a generic status code that can be returned when there is no
+ * other more suitable status code (e.g. 1003 or 1009), or if there
+ * is a need to hide specific details about the policy.
+ */
+ public static final int STATUS_POLICY_VIOLATION = 1008;
+
+ /**
+ * 1009 indicates that an endpoint is terminating the connection
+ * because it has received a message which is too big for it to
+ * process.
+ */
+ public static final int STATUS_MESSAGE_TOO_LARGE = 1009;
+
+ /**
+ * 1010 indicates that an endpoint (client) is terminating the
+ * connection because it has expected the server to negotiate one or
+ * more extension, but the server didn't return them in the response
+ * message of the WebSocket handshake. The list of extensions which
+ * are needed SHOULD appear in the /reason/ part of the Close frame.
+ * Note that this status code is not used by the server, because it
+ * can fail the WebSocket handshake instead.
+ */
+ public static final int STATUS_REQUIRED_EXTENSION = 1010;
+
+ /**
+ * 1011 indicates that a server is terminating the connection because it
+ * encountered an unexpected condition that prevented it from fulfilling the
+ * request.
+ */
+ public static final int STATUS_UNEXPECTED_CONDITION = 1011;
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/LocalStrings.properties?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/LocalStrings.properties (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/LocalStrings.properties Thu Mar 15 23:39:48 2012
@@ -15,9 +15,11 @@
frame.eos=The end of the stream was reached before the expected number of payload bytes could be read
frame.invalidUtf8=A sequence of bytes was received that did not represent valid UTF-8
+frame.readFailed=Failed to read the first byte of the next WebSocket frame. The return value from the read was [{0}]
+frame.readEos=The end of the stream was reached when trying to read the first byte of a new WebSocket frame
frame.notMasked=The client frame was not masked but all client frames must be masked
-is.notContinutation=A frame with the OpCode [{0}] was recieved when a continuation frame was expected
+is.notContinuation=A frame with the OpCode [{0}] was received when a continuation frame was expected
is.unknownOpCode=A frame with the unrecognized OpCode [{0}] was received
message.bufferTooSmall=The buffer is not big enough to contain the message currently being processed
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/StreamInbound.java Thu Mar 15 23:39:48 2012
@@ -20,6 +20,7 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
+import java.nio.ByteBuffer;
import java.nio.charset.MalformedInputException;
import java.nio.charset.UnmappableCharacterException;
@@ -38,10 +39,51 @@ public abstract class StreamInbound impl
private UpgradeProcessor<?> processor = null;
private WsOutbound outbound;
+ private int outboundByteBufferSize = WsOutbound.DEFAULT_BUFFER_SIZE;
+ private int outboundCharBufferSize = WsOutbound.DEFAULT_BUFFER_SIZE;
+
+
+
+ public int getOutboundByteBufferSize() {
+ return outboundByteBufferSize;
+ }
+
+
+ /**
+ * This only applies to the {@link WsOutbound} instance returned from
+ * {@link #getWsOutbound()} created by a subsequent call to
+ * {@link #setUpgradeOutbound(UpgradeOutbound)}. The current
+ * {@link WsOutbound} instance, if any, is not affected.
+ *
+ * @param outboundByteBufferSize
+ */
+ public void setOutboundByteBufferSize(int outboundByteBufferSize) {
+ this.outboundByteBufferSize = outboundByteBufferSize;
+ }
+
+
+ public int getOutboundCharBufferSize() {
+ return outboundCharBufferSize;
+ }
+
+
+ /**
+ * This only applies to the {@link WsOutbound} instance returned from
+ * {@link #getWsOutbound()} created by a subsequent call to
+ * {@link #setUpgradeOutbound(UpgradeOutbound)}. The current
+ * {@link WsOutbound} instance, if any, is not affected.
+ *
+ * @param outboundCharBufferSize
+ */
+ public void setOutboundCharBufferSize(int outboundCharBufferSize) {
+ this.outboundCharBufferSize = outboundCharBufferSize;
+ }
+
@Override
public final void setUpgradeOutbound(UpgradeOutbound upgradeOutbound) {
- outbound = new WsOutbound(upgradeOutbound);
+ outbound = new WsOutbound(upgradeOutbound, outboundByteBufferSize,
+ outboundCharBufferSize);
}
@@ -62,59 +104,102 @@ public abstract class StreamInbound impl
@Override
public final SocketState onData() throws IOException {
- // Must be start the start of a frame or series of frames
+ // Must be start the start of a message (which may consist of multiple
+ // frames)
+ WsInputStream wsIs = new WsInputStream(processor, getWsOutbound());
try {
- WsInputStream wsIs = new WsInputStream(processor, getWsOutbound());
-
- WsFrame frame = wsIs.getFrame();
-
- // TODO User defined extensions may define values for rsv
- if (frame.getRsv() > 0) {
- getWsOutbound().close(1002, null);
- return SocketState.CLOSED;
- }
-
- byte opCode = frame.getOpCode();
+ WsFrame frame = wsIs.nextFrame(true);
- if (opCode == Constants.OPCODE_BINARY) {
- onBinaryData(wsIs);
- return SocketState.UPGRADED;
- } else if (opCode == Constants.OPCODE_TEXT) {
- InputStreamReader r =
- new InputStreamReader(wsIs, new Utf8Decoder());
- onTextData(r);
- return SocketState.UPGRADED;
+ while (frame != null) {
+ // TODO User defined extensions may define values for rsv
+ if (frame.getRsv() > 0) {
+ closeOutboundConnection(
+ Constants.STATUS_PROTOCOL_ERROR, null);
+ return SocketState.CLOSED;
+ }
+
+ byte opCode = frame.getOpCode();
+
+ if (opCode == Constants.OPCODE_BINARY) {
+ onBinaryData(wsIs);
+ } else if (opCode == Constants.OPCODE_TEXT) {
+ InputStreamReader r =
+ new InputStreamReader(wsIs, new Utf8Decoder());
+ onTextData(r);
+ } else if (opCode == Constants.OPCODE_CLOSE){
+ closeOutboundConnection(frame);
+ return SocketState.CLOSED;
+ } else if (opCode == Constants.OPCODE_PING) {
+ getWsOutbound().pong(frame.getPayLoad());
+ } else if (opCode == Constants.OPCODE_PONG) {
+ // NO-OP
+ } else {
+ // Unknown OpCode
+ closeOutboundConnection(
+ Constants.STATUS_PROTOCOL_ERROR, null);
+ return SocketState.CLOSED;
+ }
+ frame = wsIs.nextFrame(false);
}
-
- if (opCode == Constants.OPCODE_CLOSE){
- getWsOutbound().close(frame);
- return SocketState.CLOSED;
- } else if (opCode == Constants.OPCODE_PING) {
- getWsOutbound().pong(frame.getPayLoad());
- return SocketState.UPGRADED;
- } else if (opCode == Constants.OPCODE_PONG) {
- // NO-OP
- return SocketState.UPGRADED;
- }
-
- // Unknown OpCode
- getWsOutbound().close(1002, null);
- return SocketState.CLOSED;
} catch (MalformedInputException mie) {
// Invalid UTF-8
- getWsOutbound().close(1007, null);
+ closeOutboundConnection(Constants.STATUS_BAD_DATA, null);
return SocketState.CLOSED;
} catch (UnmappableCharacterException uce) {
// Invalid UTF-8
- getWsOutbound().close(1007, null);
+ closeOutboundConnection(Constants.STATUS_BAD_DATA, null);
return SocketState.CLOSED;
} catch (IOException ioe) {
- // Given something must have gone to reach this point, this might
- // not work but try it anyway.
- getWsOutbound().close(1002, null);
+ // Given something must have gone to reach this point, this
+ // might not work but try it anyway.
+ closeOutboundConnection(Constants.STATUS_PROTOCOL_ERROR, null);
return SocketState.CLOSED;
}
+ return SocketState.UPGRADED;
+ }
+
+ private void closeOutboundConnection(int status, ByteBuffer data) throws IOException {
+ try {
+ getWsOutbound().close(status, data);
+ } finally {
+ onClose(status);
+ }
+ }
+
+ private void closeOutboundConnection(WsFrame frame) throws IOException {
+ try {
+ getWsOutbound().close(frame);
+ } finally {
+ onClose(Constants.OPCODE_CLOSE);
+ }
+ }
+
+ @Override
+ public void onUpgradeComplete() {
+ onOpen(outbound);
+ }
+
+ /**
+ * Intended to be overridden by sub-classes that wish to be notified
+ * when the outbound connection is established. The default implementation
+ * is a NO-OP.
+ *
+ * @param outbound The outbound WebSocket connection.
+ */
+ protected void onOpen(WsOutbound outbound) {
+ // NO-OP
+ }
+
+ /**
+ * Intended to be overridden by sub-classes that wish to be notified
+ * when the outbound connection is closed. The default implementation
+ * is a NO-OP.
+ *
+ * @param status The status code of the close reason.
+ */
+ protected void onClose(int status) {
+ // NO-OP
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrame.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrame.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrame.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsFrame.java Thu Mar 15 23:39:48 2012
@@ -16,6 +16,7 @@
*/
package org.apache.catalina.websocket;
+import java.io.EOFException;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
@@ -46,6 +47,7 @@ public class WsFrame {
* Create the new WebSocket frame, reading data from the processor as
* necessary.
*
+ * @param first First byte of data for this frame
* @param processor Processor associated with the WebSocket connection on
* which the frame has been sent
*
@@ -53,14 +55,15 @@ public class WsFrame {
* exception will trigger the closing of the WebSocket
* connection.
*/
- public WsFrame(UpgradeProcessor<?> processor) throws IOException {
+ private WsFrame(byte first,
+ UpgradeProcessor<?> processor) throws IOException {
- int b = processorRead(processor);
+ int b = first & 0xFF;
fin = (b & 0x80) > 0;
rsv = (b & 0x70) >>> 4;
opCode = (byte) (b & 0x0F);
- b = processorRead(processor);
+ b = blockingRead(processor);
// Client data must be masked
if ((b & 0x80) == 0) {
throw new IOException(sm.getString("frame.notMasked"));
@@ -69,11 +72,11 @@ public class WsFrame {
payloadLength = b & 0x7F;
if (payloadLength == 126) {
byte[] extended = new byte[2];
- processorRead(processor, extended);
+ blockingRead(processor, extended);
payloadLength = Conversions.byteArrayToLong(extended);
} else if (payloadLength == 127) {
byte[] extended = new byte[8];
- processorRead(processor, extended);
+ blockingRead(processor, extended);
payloadLength = Conversions.byteArrayToLong(extended);
}
@@ -86,12 +89,12 @@ public class WsFrame {
}
}
- processorRead(processor, mask);
+ blockingRead(processor, mask);
if (isControl()) {
// Note: Payload limited to <= 125 bytes by test above
payload = ByteBuffer.allocate((int) payloadLength);
- processorRead(processor, payload);
+ blockingRead(processor, payload);
if (opCode == Constants.OPCODE_CLOSE && payloadLength > 2) {
// Check close payload - if present - is valid UTF-8
@@ -138,9 +141,10 @@ public class WsFrame {
}
- // ----------------------------------- Guaranteed read methods for processor
-
- private int processorRead(UpgradeProcessor<?> processor)
+ /*
+ * Blocks until a aingle byte has been read
+ */
+ private int blockingRead(UpgradeProcessor<?> processor)
throws IOException {
int result = processor.read();
if (result == -1) {
@@ -150,12 +154,15 @@ public class WsFrame {
}
- private void processorRead(UpgradeProcessor<?> processor, byte[] bytes)
+ /*
+ * Blocks until the byte array has been filled.
+ */
+ private void blockingRead(UpgradeProcessor<?> processor, byte[] bytes)
throws IOException {
int read = 0;
int last = 0;
while (read < bytes.length) {
- last = processor.read(bytes, read, bytes.length - read);
+ last = processor.read(true, bytes, read, bytes.length - read);
if (last == -1) {
throw new IOException(sm.getString("frame.eos"));
}
@@ -165,9 +172,10 @@ public class WsFrame {
/*
- * Intended to read whole payload. Therefore able to unmask.
+ * Intended to read whole payload and blocks until it has. Therefore able to
+ * unmask the payload data.
*/
- private void processorRead(UpgradeProcessor<?> processor, ByteBuffer bb)
+ private void blockingRead(UpgradeProcessor<?> processor, ByteBuffer bb)
throws IOException {
int last = 0;
while (bb.hasRemaining()) {
@@ -179,4 +187,39 @@ public class WsFrame {
}
bb.flip();
}
+
+
+ /**
+ * Read the next WebSocket frame, reading data from the processor as
+ * necessary.
+ *
+ * @param processor Processor associated with the WebSocket connection on
+ * which the frame has been sent
+ *
+ * @param block Should this method block until a frame is presented if no
+ * data is currently available to process. Note that is a
+ * single byte is available, this method will block until the
+ * complete frame (excluding payload for non-control frames) is
+ * available.
+ *
+ * @throws IOException If a problem occurs processing the frame. Any
+ * exception will trigger the closing of the WebSocket
+ * connection.
+ */
+ public static WsFrame nextFrame(UpgradeProcessor<?> processor,
+ boolean block) throws IOException {
+
+ byte[] first = new byte[1];
+ int read = processor.read(block, first, 0, 1);
+ if (read == 1) {
+ return new WsFrame(first[0], processor);
+ } else if (read == 0) {
+ return null;
+ } else if (read == -1) {
+ throw new EOFException(sm.getString("frame.readEos"));
+ } else {
+ throw new IOException(
+ sm.getString("frame.readFailed", Integer.valueOf(read)));
+ }
+ }
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsInputStream.java Thu Mar 15 23:39:48 2012
@@ -17,6 +17,7 @@
package org.apache.catalina.websocket;
import java.io.IOException;
+import java.io.InputStream;
import org.apache.coyote.http11.upgrade.UpgradeProcessor;
import org.apache.tomcat.util.res.StringManager;
@@ -27,7 +28,7 @@ import org.apache.tomcat.util.res.String
* makes the number of bytes declared in the payload length available for
* reading even if more bytes are available from the socket.
*/
-public class WsInputStream extends java.io.InputStream {
+public class WsInputStream extends InputStream {
private static final StringManager sm =
StringManager.getManager(Constants.Package);
@@ -43,55 +44,42 @@ public class WsInputStream extends java.
private String error = null;
- public WsInputStream(UpgradeProcessor<?> processor, WsOutbound outbound)
- throws IOException {
+ public WsInputStream(UpgradeProcessor<?> processor, WsOutbound outbound) {
this.processor = processor;
this.outbound = outbound;
- processFrame();
}
- public WsFrame getFrame() {
+ /**
+ * Process the next WebSocket frame.
+ *
+ * @param block Should this method block until a frame is presented if no
+ * data is currently available to process. Note that is a
+ * single byte is available, this method will block until the
+ * complete frame (excluding payload for non-control frames) is
+ * available.
+ *
+ * @return The next frame to be processed or <code>null</code> if block is
+ * <code>false</code> and there is no data to be processed.
+ *
+ * @throws IOException If a problem occurs reading the data for the frame.
+ */
+ public WsFrame nextFrame(boolean block) throws IOException {
+ frame = WsFrame.nextFrame(processor, block);
+ if (frame != null) {
+ readThisFragment = 0;
+ remaining = frame.getPayLoadLength();
+ }
return frame;
}
- private void processFrame() throws IOException {
- frame = new WsFrame(processor);
- readThisFragment = 0;
- remaining = frame.getPayLoadLength();
- }
-
-
// ----------------------------------------------------- InputStream methods
@Override
public int read() throws IOException {
- if (error != null) {
- throw new IOException(error);
- }
- while (remaining == 0 && !getFrame().getFin()) {
- // Need more data - process next frame
- processFrame();
- while (frame.isControl()) {
- if (getFrame().getOpCode() == Constants.OPCODE_PING) {
- outbound.pong(frame.getPayLoad());
- } else if (getFrame().getOpCode() == Constants.OPCODE_PONG) {
- // NO-OP. Swallow it.
- } else if (getFrame().getOpCode() == Constants.OPCODE_CLOSE) {
- outbound.close(frame);
- } else{
- throw new IOException(sm.getString("is.unknownOpCode",
- Byte.valueOf(getFrame().getOpCode())));
- }
- processFrame();
- }
- if (getFrame().getOpCode() != Constants.OPCODE_CONTINUATION) {
- error = sm.getString("is.notContinutation",
- Byte.valueOf(getFrame().getOpCode()));
- throw new IOException(error);
- }
- }
+
+ makePayloadDataAvailable();
if (remaining == 0) {
return -1;
@@ -111,31 +99,8 @@ public class WsInputStream extends java.
@Override
public int read(byte b[], int off, int len) throws IOException {
- if (error != null) {
- throw new IOException(error);
- }
- while (remaining == 0 && !getFrame().getFin()) {
- // Need more data - process next frame
- processFrame();
- while (frame.isControl()) {
- if (getFrame().getOpCode() == Constants.OPCODE_PING) {
- outbound.pong(frame.getPayLoad());
- } else if (getFrame().getOpCode() == Constants.OPCODE_PONG) {
- // NO-OP. Swallow it.
- } else if (getFrame().getOpCode() == Constants.OPCODE_CLOSE) {
- outbound.close(frame);
- } else{
- throw new IOException(sm.getString("is.unknownOpCode",
- Byte.valueOf(getFrame().getOpCode())));
- }
- processFrame();
- }
- if (getFrame().getOpCode() != Constants.OPCODE_CONTINUATION) {
- error = sm.getString("is.notContinutation",
- Byte.valueOf(getFrame().getOpCode()));
- throw new IOException(error);
- }
- }
+
+ makePayloadDataAvailable();
if (remaining == 0) {
return -1;
@@ -144,7 +109,7 @@ public class WsInputStream extends java.
if (len > remaining) {
len = (int) remaining;
}
- int result = processor.read(b, off, len);
+ int result = processor.read(true, b, off, len);
if(result == -1) {
return -1;
}
@@ -158,4 +123,35 @@ public class WsInputStream extends java.
return result;
}
+
+ /*
+ * Ensures that there is payload data ready to read.
+ */
+ private void makePayloadDataAvailable() throws IOException {
+ if (error != null) {
+ throw new IOException(error);
+ }
+ while (remaining == 0 && !frame.getFin()) {
+ // Need more data - process next frame
+ nextFrame(true);
+ while (frame.isControl()) {
+ if (frame.getOpCode() == Constants.OPCODE_PING) {
+ outbound.pong(frame.getPayLoad());
+ } else if (frame.getOpCode() == Constants.OPCODE_PONG) {
+ // NO-OP. Swallow it.
+ } else if (frame.getOpCode() == Constants.OPCODE_CLOSE) {
+ outbound.close(frame);
+ } else{
+ throw new IOException(sm.getString("is.unknownOpCode",
+ Byte.valueOf(frame.getOpCode())));
+ }
+ nextFrame(true);
+ }
+ if (frame.getOpCode() != Constants.OPCODE_CONTINUATION) {
+ error = sm.getString("is.notContinuation",
+ Byte.valueOf(frame.getOpCode()));
+ throw new IOException(error);
+ }
+ }
+ }
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/catalina/websocket/WsOutbound.java Thu Mar 15 23:39:48 2012
@@ -27,13 +27,16 @@ import org.apache.tomcat.util.buf.B2CCon
import org.apache.tomcat.util.res.StringManager;
/**
- * Provides the means to write WebSocket messages to the client.
+ * Provides the means to write WebSocket messages to the client. All methods
+ * that write to the client (or update a buffer that is later written to the
+ * client) are synchronized to prevent multiple threads trying to write to the
+ * client at the same time.
*/
public class WsOutbound {
private static final StringManager sm =
StringManager.getManager(Constants.Package);
- private static final int DEFAULT_BUFFER_SIZE = 8192;
+ public static final int DEFAULT_BUFFER_SIZE = 8192;
private UpgradeOutbound upgradeOutbound;
private ByteBuffer bb;
@@ -44,10 +47,15 @@ public class WsOutbound {
public WsOutbound(UpgradeOutbound upgradeOutbound) {
+ this(upgradeOutbound, DEFAULT_BUFFER_SIZE, DEFAULT_BUFFER_SIZE);
+ }
+
+
+ public WsOutbound(UpgradeOutbound upgradeOutbound, int byteBufferSize,
+ int charBufferSize) {
this.upgradeOutbound = upgradeOutbound;
- // TODO: Make buffer size configurable
- this.bb = ByteBuffer.allocate(DEFAULT_BUFFER_SIZE);
- this.cb = CharBuffer.allocate(DEFAULT_BUFFER_SIZE);
+ this.bb = ByteBuffer.allocate(byteBufferSize);
+ this.cb = CharBuffer.allocate(charBufferSize);
}
@@ -63,7 +71,7 @@ public class WsOutbound {
* @throws IOException If a flush is required and an error occurs writing
* the WebSocket frame to the client
*/
- public void writeBinaryData(int b) throws IOException {
+ public synchronized void writeBinaryData(int b) throws IOException {
if (closed) {
throw new IOException(sm.getString("outbound.closed"));
}
@@ -88,12 +96,12 @@ public class WsOutbound {
* message started. If the buffer for textual data is full, the buffer will
* be flushed and a new textual continuation fragment started.
*
- * @param b The character to send to the client.
+ * @param c The character to send to the client.
*
* @throws IOException If a flush is required and an error occurs writing
* the WebSocket frame to the client
*/
- public void writeTextData(char c) throws IOException {
+ public synchronized void writeTextData(char c) throws IOException {
if (closed) {
throw new IOException(sm.getString("outbound.closed"));
}
@@ -122,7 +130,9 @@ public class WsOutbound {
*
* @throws IOException If an error occurs writing to the client
*/
- public void writeBinaryMessage(ByteBuffer msgBb) throws IOException {
+ public synchronized void writeBinaryMessage(ByteBuffer msgBb)
+ throws IOException {
+
if (closed) {
throw new IOException(sm.getString("outbound.closed"));
}
@@ -141,11 +151,13 @@ public class WsOutbound {
* a WebSocket text message as a single frame with the provided buffer as
* the payload of the message.
*
- * @param msgBb The buffer containing the payload
+ * @param msgCb The buffer containing the payload
*
* @throws IOException If an error occurs writing to the client
*/
- public void writeTextMessage(CharBuffer msgCb) throws IOException {
+ public synchronized void writeTextMessage(CharBuffer msgCb)
+ throws IOException {
+
if (closed) {
throw new IOException(sm.getString("outbound.closed"));
}
@@ -164,7 +176,7 @@ public class WsOutbound {
*
* @throws IOException If an error occurs writing to the client
*/
- public void flush() throws IOException {
+ public synchronized void flush() throws IOException {
if (closed) {
throw new IOException(sm.getString("outbound.closed"));
}
@@ -209,7 +221,7 @@ public class WsOutbound {
close(status, frame.getPayLoad());
} else {
// Invalid close code
- close(1002, null);
+ close(Constants.STATUS_PROTOCOL_ERROR, null);
}
} else {
// No status
@@ -220,9 +232,15 @@ public class WsOutbound {
private boolean validateCloseStatus(int status) {
- if (status == 1000 || status == 1001 || status == 1002 ||
- status == 1003 || status == 1007 || status == 1008 ||
- status == 1009 || status == 1010 || status == 1011 ||
+ if (status == Constants.STATUS_CLOSE_NORMAL ||
+ status == Constants.STATUS_SHUTDOWN ||
+ status == Constants.STATUS_PROTOCOL_ERROR ||
+ status == Constants.STATUS_UNEXPECTED_DATA_TYPE ||
+ status == Constants.STATUS_BAD_DATA ||
+ status == Constants.STATUS_POLICY_VIOLATION ||
+ status == Constants.STATUS_MESSAGE_TOO_LARGE ||
+ status == Constants.STATUS_REQUIRED_EXTENSION ||
+ status == Constants.STATUS_UNEXPECTED_CONDITION ||
(status > 2999 && status < 5000)) {
// Other 1xxx reserved / not permitted
// 2xxx reserved
@@ -245,9 +263,9 @@ public class WsOutbound {
*
* @throws IOException If an error occurs writing to the client
*/
- public void close(int status, ByteBuffer data) throws IOException {
- // TODO Think about threading requirements for writing. This is not
- // currently thread safe and writing almost certainly needs to be.
+ public synchronized void close(int status, ByteBuffer data)
+ throws IOException {
+
if (closed) {
return;
}
@@ -282,9 +300,8 @@ public class WsOutbound {
*
* @throws IOException If an error occurs writing to the client
*/
- public void pong(ByteBuffer data) throws IOException {
- // TODO Think about threading requirements for writing. This is not
- // currently thread safe and writing almost certainly needs to be.
+ public synchronized void pong(ByteBuffer data) throws IOException {
+
if (closed) {
throw new IOException(sm.getString("outbound.closed"));
}
@@ -315,10 +332,6 @@ public class WsOutbound {
private void doWriteBytes(ByteBuffer buffer, boolean finalFragment)
throws IOException {
- if (closed) {
- throw new IOException("Closed");
- }
-
// Work out the first byte
int first = 0x00;
if (finalFragment) {
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/AbstractProtocol.java Thu Mar 15 23:39:48 2012
@@ -563,7 +563,18 @@ public abstract class AbstractProtocol i
if (state != SocketState.CLOSED && processor.isAsync()) {
state = processor.asyncPostProcess();
}
- } while (state == SocketState.ASYNC_END);
+
+ if (state == SocketState.UPGRADING) {
+ // Get the UpgradeInbound handler
+ UpgradeInbound inbound = processor.getUpgradeInbound();
+ // Release the Http11 processor to be re-used
+ release(socket, processor, false, false);
+ // Create the light-weight upgrade processor
+ processor = createUpgradeProcessor(socket, inbound);
+ inbound.onUpgradeComplete();
+ }
+ } while (state == SocketState.ASYNC_END ||
+ state == SocketState.UPGRADING);
if (state == SocketState.LONG) {
// In the middle of processing a request/response. Keep the
@@ -581,16 +592,7 @@ public abstract class AbstractProtocol i
release(socket, processor, false, false);
} else if (state == SocketState.UPGRADED) {
// Need to keep the connection associated with the processor
- upgradePoll(socket, processor);
- } else if (state == SocketState.UPGRADING) {
- // Get the UpgradeInbound handler
- UpgradeInbound inbound = processor.getUpgradeInbound();
- // Release the Http11 processor to be re-used
- release(socket, processor, false, false);
- // Create the light-weight upgrade processor
- processor = createUpgradeProcessor(socket, inbound);
- // Need to keep the connection associated with the processor
- upgradePoll(socket, processor);
+ longPoll(socket, processor);
} else {
// Connection closed. OK to recycle the processor.
if (!(processor instanceof UpgradeProcessor)) {
@@ -629,8 +631,6 @@ public abstract class AbstractProtocol i
Processor<S> processor);
protected abstract void longPoll(SocketWrapper<S> socket,
Processor<S> processor);
- protected abstract void upgradePoll(SocketWrapper<S> socket,
- Processor<S> processor);
protected abstract void release(SocketWrapper<S> socket,
Processor<S> processor, boolean socketClosing,
boolean addToPoller);
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/ajp/AbstractAjpProtocol.java Thu Mar 15 23:39:48 2012
@@ -91,12 +91,6 @@ public abstract class AbstractAjpProtoco
}
@Override
- protected void upgradePoll(SocketWrapper<S> socket,
- Processor<S> processor) {
- // TODO Should never happen. ISE?
- }
-
- @Override
protected P createUpgradeProcessor(SocketWrapper<S> socket,
UpgradeInbound inbound) {
// TODO should fail - throw IOE
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11AprProtocol.java Thu Mar 15 23:39:48 2012
@@ -238,22 +238,20 @@ public class Http11AprProtocol extends A
connections.put(socket.getSocket(), processor);
if (processor.isAsync()) {
+ // Async
socket.setAsync(true);
} else if (processor.isComet() && proto.endpoint.isRunning()) {
+ // Comet
((AprEndpoint) proto.endpoint).getCometPoller().add(
socket.getSocket().longValue(), false);
+ } else {
+ // Upgraded
+ ((AprEndpoint) proto.endpoint).getPoller().add(
+ socket.getSocket().longValue(), false);
}
}
@Override
- protected void upgradePoll(SocketWrapper<Long> socket,
- Processor<Long> processor) {
- connections.put(socket.getSocket(), processor);
- ((AprEndpoint) proto.endpoint).getPoller().add(
- socket.getSocket().longValue(), false);
- }
-
- @Override
protected Http11AprProcessor createProcessor() {
Http11AprProcessor processor = new Http11AprProcessor(
proto.getMaxHttpHeaderSize(), (AprEndpoint)proto.endpoint,
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11NioProtocol.java Thu Mar 15 23:39:48 2012
@@ -17,7 +17,6 @@
package org.apache.coyote.http11;
import java.io.IOException;
-import java.nio.channels.SelectionKey;
import java.nio.channels.SocketChannel;
import java.util.Iterator;
@@ -31,7 +30,6 @@ import org.apache.tomcat.util.net.Abstra
import org.apache.tomcat.util.net.NioChannel;
import org.apache.tomcat.util.net.NioEndpoint;
import org.apache.tomcat.util.net.NioEndpoint.Handler;
-import org.apache.tomcat.util.net.NioEndpoint.KeyAttachment;
import org.apache.tomcat.util.net.SSLImplementation;
import org.apache.tomcat.util.net.SecureNioChannel;
import org.apache.tomcat.util.net.SocketWrapper;
@@ -251,6 +249,7 @@ public class Http11NioProtocol extends A
} else {
// Either:
// - this is comet request
+ // - this is an upgraded connection
// - the request line/headers have not been completely
// read
socket.getSocket().getPoller().add(socket.getSocket());
@@ -287,17 +286,5 @@ public class Http11NioProtocol extends A
return new UpgradeNioProcessor(socket, inbound,
((Http11NioProtocol) getProtocol()).getEndpoint().getSelectorPool());
}
-
- @Override
- protected void upgradePoll(SocketWrapper<NioChannel> socket,
- Processor<NioChannel> processor) {
- connections.put(socket.getSocket(), processor);
-
- SelectionKey key = socket.getSocket().getIOChannel().keyFor(
- socket.getSocket().getPoller().getSelector());
- key.interestOps(SelectionKey.OP_READ);
- ((KeyAttachment) socket).interestOps(
- SelectionKey.OP_READ);
- }
}
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/Http11Protocol.java Thu Mar 15 23:39:48 2012
@@ -191,11 +191,5 @@ public class Http11Protocol extends Abst
throws IOException {
return new UpgradeBioProcessor(socket, inbound);
}
-
- @Override
- protected void upgradePoll(SocketWrapper<Socket> socket,
- Processor<Socket> processor) {
- connections.put(socket.getSocket(), processor);
- }
}
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/LocalStrings.properties
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/LocalStrings.properties?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/LocalStrings.properties (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/LocalStrings.properties Thu Mar 15 23:39:48 2012
@@ -13,4 +13,7 @@
# See the License for the specific language governing permissions and
# limitations under the License.
+apr.error=Unexpected error [{0}] reading data from the APR/native socket.
+
nio.eof.error=Unexpected EOF read on the socket
+
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeAprProcessor.java Thu Mar 15 23:39:48 2012
@@ -19,14 +19,9 @@ package org.apache.coyote.http11.upgrade
import java.io.IOException;
import org.apache.tomcat.jni.Socket;
+import org.apache.tomcat.jni.Status;
import org.apache.tomcat.util.net.SocketWrapper;
-/**
- * Implementation note: The need to extend Http11Processor could probably be
- * removed if the Processor interface was expanded to cover all of the methods
- * required by the AbstractProtocol. That would simplify the code and further
- * reduce the size of instances of this class.
- */
public class UpgradeAprProcessor extends UpgradeProcessor<Long> {
private final long socket;
@@ -67,13 +62,35 @@ public class UpgradeAprProcessor extends
@Override
public int read() throws IOException {
byte[] bytes = new byte[1];
- Socket.recv(socket, bytes, 0, 1);
- return bytes[0];
+ int result = Socket.recv(socket, bytes, 0, 1);
+ if (result == -1) {
+ return -1;
+ } else {
+ return bytes[0] & 0xFF;
+ }
}
@Override
- public int read(byte[] bytes, int off, int len) throws IOException {
- return Socket.recv(socket, bytes, off, len);
+ public int read(boolean block, byte[] bytes, int off, int len)
+ throws IOException {
+ if (!block) {
+ Socket.optSet(socket, Socket.APR_SO_NONBLOCK, -1);
+ }
+ try {
+ int result = Socket.recv(socket, bytes, off, len);
+ if (result > 0) {
+ return result;
+ } else if (-result == Status.EAGAIN) {
+ return 0;
+ } else {
+ throw new IOException(sm.getString("apr.error",
+ Integer.valueOf(-result)));
+ }
+ } finally {
+ if (!block) {
+ Socket.optSet(socket, Socket.APR_SO_NONBLOCK, 0);
+ }
+ }
}
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeBioProcessor.java Thu Mar 15 23:39:48 2012
@@ -23,12 +23,6 @@ import java.net.Socket;
import org.apache.tomcat.util.net.SocketWrapper;
-/**
- * Implementation note: The need to extend Http11Processor could probably be
- * removed if the Processor interface was expanded to cover all of the methods
- * required by the AbstractProtocol. That would simplify the code and further
- * reduce the size of instances of this class.
- */
public class UpgradeBioProcessor extends UpgradeProcessor<Socket> {
private final InputStream inputStream;
@@ -74,7 +68,10 @@ public class UpgradeBioProcessor extends
@Override
- public int read(byte[] bytes, int off, int len) throws IOException {
+ public int read(boolean block, byte[] bytes, int off, int len)
+ throws IOException {
+ // The BIO endpoint always uses blocking IO so the block parameter is
+ // ignored and a blocking read is performed.
return inputStream.read(bytes, off, len);
}
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeInbound.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeInbound.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeInbound.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeInbound.java Thu Mar 15 23:39:48 2012
@@ -28,6 +28,8 @@ public interface UpgradeInbound {
void setUpgradeProcessor(UpgradeProcessor<?> processor);
+ void onUpgradeComplete();
+
SocketState onData() throws IOException;
void setUpgradeOutbound(UpgradeOutbound upgradeOutbound);
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeNioProcessor.java Thu Mar 15 23:39:48 2012
@@ -25,16 +25,12 @@ import org.apache.tomcat.util.net.NioEnd
import org.apache.tomcat.util.net.NioSelectorPool;
import org.apache.tomcat.util.net.SocketWrapper;
-/**
- * Implementation note: The need to extend Http11Processor could probably be
- * removed if the Processor interface was expanded to cover all of the methods
- * required by the AbstractProtocol. That would simplify the code and further
- * reduce the size of instances of this class.
- */
public class UpgradeNioProcessor extends UpgradeProcessor<NioChannel> {
private final NioChannel nioChannel;
private final NioSelectorPool pool;
+ private final int maxRead;
+ private final int maxWrite;
public UpgradeNioProcessor(SocketWrapper<NioChannel> wrapper,
UpgradeInbound upgradeInbound, NioSelectorPool pool) {
@@ -42,6 +38,8 @@ public class UpgradeNioProcessor extends
this.nioChannel = wrapper.getSocket();
this.pool = pool;
+ this.maxRead = nioChannel.getBufHandler().getReadBuffer().capacity();
+ this.maxWrite = nioChannel.getBufHandler().getWriteBuffer().capacity();
}
@@ -82,7 +80,11 @@ public class UpgradeNioProcessor extends
@Override
public void write(byte[]b, int off, int len) throws IOException {
- writeToSocket(b, off, len);
+ int written = 0;
+ while (len - written > maxWrite) {
+ written += writeToSocket(b, off + written, maxWrite);
+ }
+ writeToSocket(b, off + written, len - written);
}
/*
@@ -91,13 +93,22 @@ public class UpgradeNioProcessor extends
@Override
public int read() throws IOException {
byte[] bytes = new byte[1];
- readSocket(true, bytes, 0, 1);
- return bytes[0];
+ int result = readSocket(true, bytes, 0, 1);
+ if (result == -1) {
+ return -1;
+ } else {
+ return bytes[0] & 0xFF;
+ }
}
@Override
- public int read(byte[] bytes, int off, int len) throws IOException {
- return readSocket(true, bytes, off, len);
+ public int read(boolean block, byte[] bytes, int off, int len)
+ throws IOException {
+ if (len > maxRead) {
+ return readSocket(block, bytes, off, maxRead);
+ } else {
+ return readSocket(block, bytes, off, len);
+ }
}
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeOutbound.java Thu Mar 15 23:39:48 2012
@@ -22,8 +22,6 @@ import java.io.OutputStream;
/**
* Allows data to be written to the upgraded connection.
- *
- * TODO: Override more methods for efficiency.
*/
public class UpgradeOutbound extends OutputStream {
Modified: tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java (original)
+++ tomcat/tc7.0.x/trunk/java/org/apache/coyote/http11/upgrade/UpgradeProcessor.java Thu Mar 15 23:39:48 2012
@@ -46,8 +46,32 @@ public abstract class UpgradeProcessor<S
public abstract void write(byte[] b, int off, int len) throws IOException;
// Input methods
+ /**
+ * This is always a blocking read of a single byte.
+ *
+ * @return The next byte or -1 if the end of the input is reached.
+ *
+ * @throws IOException If a problem occurs trying to read from the input
+ */
public abstract int read() throws IOException;
- public abstract int read(byte[] bytes, int off, int len) throws IOException;
+
+ /**
+ * Read up to len bytes from the input in either blocking or non-blocking
+ * mode (where non-blocking is supported). If the input does not support
+ * non-blocking reads, a blcoking read will be performed.
+ *
+ * @param block
+ * @param bytes
+ * @param off
+ * @param len
+ * @return The number of bytes read or -1 if the end of the input is
+ * reached. Non-blocking reads may return zero if no data is
+ * available. Blocking reads never return zero.
+ *
+ * @throws IOException If a problem occurs trying to read from the input
+ */
+ public abstract int read(boolean block, byte[] bytes, int off, int len)
+ throws IOException;
@Override
public final UpgradeInbound getUpgradeInbound() {
Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/EchoMessage.java Thu Mar 15 23:39:48 2012
@@ -20,6 +20,8 @@ import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.CharBuffer;
+import javax.servlet.ServletException;
+
import org.apache.catalina.websocket.MessageInbound;
import org.apache.catalina.websocket.StreamInbound;
import org.apache.catalina.websocket.WebSocketServlet;
@@ -28,14 +30,41 @@ import org.apache.catalina.websocket.Web
public class EchoMessage extends WebSocketServlet {
private static final long serialVersionUID = 1L;
+ private volatile int byteBufSize;
+ private volatile int charBufSize;
+
+ @Override
+ public void init() throws ServletException {
+ super.init();
+ byteBufSize = getInitParameterIntValue("byteBufferMaxSize", 2097152);
+ charBufSize = getInitParameterIntValue("charBufferMaxSize", 2097152);
+ }
+
+ public int getInitParameterIntValue(String name, int defaultValue) {
+ String val = this.getInitParameter(name);
+ int result = defaultValue;
+ try {
+ result = Integer.parseInt(val);
+ }catch (Exception x) {
+ }
+ return result;
+ }
+
+
@Override
protected StreamInbound createWebSocketInbound(String subProtocol) {
- return new EchoMessageInbound();
+ return new EchoMessageInbound(byteBufSize,charBufSize);
}
private static final class EchoMessageInbound extends MessageInbound {
+ public EchoMessageInbound(int byteBufferMaxSize, int charBufferMaxSize) {
+ super();
+ setByteBufferMaxSize(byteBufferMaxSize);
+ setCharBufferMaxSize(charBufferMaxSize);
+ }
+
@Override
protected void onBinaryMessage(ByteBuffer message) throws IOException {
getWsOutbound().writeBinaryMessage(message);
Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java?rev=1301270&r1=1297716&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java Thu Mar 15 23:39:48 2012
@@ -1,21 +1,21 @@
-/*
- * 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 websocket.snake;
-
-public enum Direction {
- NONE, NORTH, SOUTH, EAST, WEST
-}
+/*
+ * 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 websocket.snake;
+
+public enum Direction {
+ NONE, NORTH, SOUTH, EAST, WEST
+}
Propchange: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Direction.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java?rev=1301270&r1=1297716&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java Thu Mar 15 23:39:48 2012
@@ -1,65 +1,65 @@
-/*
- * 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 websocket.snake;
-
-public class Location {
-
- public int x;
- public int y;
-
- public Location(int x, int y) {
- this.x = x;
- this.y = y;
- }
-
- public Location getAdjacentLocation(Direction direction) {
- switch (direction) {
- case NORTH:
- return new Location(x, y - SnakeWebSocketServlet.GRID_SIZE);
- case SOUTH:
- return new Location(x, y + SnakeWebSocketServlet.GRID_SIZE);
- case EAST:
- return new Location(x + SnakeWebSocketServlet.GRID_SIZE, y);
- case WEST:
- return new Location(x - SnakeWebSocketServlet.GRID_SIZE, y);
- case NONE:
- // fall through
- default:
- return this;
- }
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Location location = (Location) o;
-
- if (x != location.x) return false;
- if (y != location.y) return false;
-
- return true;
- }
-
- @Override
- public int hashCode() {
- int result = x;
- result = 31 * result + y;
- return result;
- }
-}
+/*
+ * 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 websocket.snake;
+
+public class Location {
+
+ public int x;
+ public int y;
+
+ public Location(int x, int y) {
+ this.x = x;
+ this.y = y;
+ }
+
+ public Location getAdjacentLocation(Direction direction) {
+ switch (direction) {
+ case NORTH:
+ return new Location(x, y - SnakeWebSocketServlet.GRID_SIZE);
+ case SOUTH:
+ return new Location(x, y + SnakeWebSocketServlet.GRID_SIZE);
+ case EAST:
+ return new Location(x + SnakeWebSocketServlet.GRID_SIZE, y);
+ case WEST:
+ return new Location(x - SnakeWebSocketServlet.GRID_SIZE, y);
+ case NONE:
+ // fall through
+ default:
+ return this;
+ }
+ }
+
+ @Override
+ public boolean equals(Object o) {
+ if (this == o) return true;
+ if (o == null || getClass() != o.getClass()) return false;
+
+ Location location = (Location) o;
+
+ if (x != location.x) return false;
+ if (y != location.y) return false;
+
+ return true;
+ }
+
+ @Override
+ public int hashCode() {
+ int result = x;
+ result = 31 * result + y;
+ return result;
+ }
+}
Propchange: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Location.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java?rev=1301270&r1=1297716&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java Thu Mar 15 23:39:48 2012
@@ -1,142 +1,134 @@
-/*
- * 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 websocket.snake;
-
-import java.io.IOException;
-import java.nio.CharBuffer;
-import java.util.ArrayDeque;
-import java.util.Collection;
-import java.util.Deque;
-import java.util.Iterator;
-
-import org.apache.catalina.websocket.WsOutbound;
-
-public class Snake {
-
- private static final int DEFAULT_LENGTH = 6;
-
- private final int id;
- private final WsOutbound outbound;
-
- private Direction direction;
- private Deque<Location> locations = new ArrayDeque<Location>();
- private String hexColor;
-
- public Snake(int id, WsOutbound outbound) {
- this.id = id;
- this.outbound = outbound;
- this.hexColor = SnakeWebSocketServlet.getRandomHexColor();
- resetState();
- }
-
- private void resetState() {
- this.direction = Direction.NONE;
- this.locations.clear();
- Location startLocation = SnakeWebSocketServlet.getRandomLocation();
- for (int i = 0; i < DEFAULT_LENGTH; i++) {
- locations.add(startLocation);
- }
- }
-
- private void kill() {
- resetState();
- try {
- CharBuffer response = CharBuffer.wrap("{'type': 'dead'}");
- outbound.writeTextMessage(response);
- } catch (IOException ioe) {
- // Ignore
- }
- }
-
- private void reward() {
- grow();
- try {
- CharBuffer response = CharBuffer.wrap("{'type': 'kill'}");
- outbound.writeTextMessage(response);
- } catch (IOException ioe) {
- // Ignore
- }
- }
-
- public synchronized void update(Collection<Snake> snakes) {
- Location firstLocation = locations.getFirst();
- Location nextLocation = firstLocation.getAdjacentLocation(direction);
- if (nextLocation.x >= SnakeWebSocketServlet.PLAYFIELD_WIDTH) {
- nextLocation.x = 0;
- }
- if (nextLocation.y >= SnakeWebSocketServlet.PLAYFIELD_HEIGHT) {
- nextLocation.y = 0;
- }
- if (nextLocation.x < 0) {
- nextLocation.x = SnakeWebSocketServlet.PLAYFIELD_WIDTH;
- }
- if (nextLocation.y < 0) {
- nextLocation.y = SnakeWebSocketServlet.PLAYFIELD_HEIGHT;
- }
- locations.addFirst(nextLocation);
- locations.removeLast();
-
- for (Snake snake : snakes) {
- if (snake.getId() != getId() &&
- colliding(snake.getHeadLocation())) {
- snake.kill();
- reward();
- }
- }
- }
-
- private void grow() {
- Location lastLocation = locations.getLast();
- Location newLocation = new Location(lastLocation.x, lastLocation.y);
- locations.add(newLocation);
- }
-
- private boolean colliding(Location location) {
- return direction != Direction.NONE && locations.contains(location);
- }
-
- public void setDirection(Direction direction) {
- this.direction = direction;
- }
-
- public synchronized String getLocationsJson() {
- StringBuilder sb = new StringBuilder();
- for (Iterator<Location> iterator = locations.iterator();
- iterator.hasNext();) {
- Location location = iterator.next();
- sb.append(String.format("{x: %d, y: %d}",
- Integer.valueOf(location.x), Integer.valueOf(location.y)));
- if (iterator.hasNext()) {
- sb.append(',');
- }
- }
- return String.format("{'id':%d,'body':[%s]}",
- Integer.valueOf(id), sb.toString());
- }
-
- public int getId() {
- return id;
- }
-
- public String getHexColor() {
- return hexColor;
- }
-
- public synchronized Location getHeadLocation() {
- return locations.getFirst();
- }
-}
+/*
+ * 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 websocket.snake;
+
+import java.io.IOException;
+import java.nio.CharBuffer;
+import java.util.ArrayDeque;
+import java.util.Collection;
+import java.util.Deque;
+
+import org.apache.catalina.websocket.WsOutbound;
+
+public class Snake {
+
+ private static final int DEFAULT_LENGTH = 5;
+
+ private final int id;
+ private final WsOutbound outbound;
+
+ private Direction direction;
+ private int length = DEFAULT_LENGTH;
+ private Location head;
+ private Deque<Location> tail = new ArrayDeque<Location>();
+ private String hexColor;
+
+ public Snake(int id, WsOutbound outbound) {
+ this.id = id;
+ this.outbound = outbound;
+ this.hexColor = SnakeWebSocketServlet.getRandomHexColor();
+ resetState();
+ }
+
+ private void resetState() {
+ this.direction = Direction.NONE;
+ this.head = SnakeWebSocketServlet.getRandomLocation();
+ this.tail.clear();
+ this.length = DEFAULT_LENGTH;
+ }
+
+ private synchronized void kill() {
+ resetState();
+ try {
+ CharBuffer response = CharBuffer.wrap("{'type': 'dead'}");
+ outbound.writeTextMessage(response);
+ } catch (IOException ioe) {
+ // Ignore
+ }
+ }
+
+ private synchronized void reward() {
+ length++;
+ try {
+ CharBuffer response = CharBuffer.wrap("{'type': 'kill'}");
+ outbound.writeTextMessage(response);
+ } catch (IOException ioe) {
+ // Ignore
+ }
+ }
+
+ public synchronized void update(Collection<Snake> snakes) {
+ Location nextLocation = head.getAdjacentLocation(direction);
+ if (nextLocation.x >= SnakeWebSocketServlet.PLAYFIELD_WIDTH) {
+ nextLocation.x = 0;
+ }
+ if (nextLocation.y >= SnakeWebSocketServlet.PLAYFIELD_HEIGHT) {
+ nextLocation.y = 0;
+ }
+ if (nextLocation.x < 0) {
+ nextLocation.x = SnakeWebSocketServlet.PLAYFIELD_WIDTH;
+ }
+ if (nextLocation.y < 0) {
+ nextLocation.y = SnakeWebSocketServlet.PLAYFIELD_HEIGHT;
+ }
+ if (direction != Direction.NONE) {
+ tail.addFirst(head);
+ if (tail.size() > length) {
+ tail.removeLast();
+ }
+ head = nextLocation;
+ }
+
+ for (Snake snake : snakes) {
+ if (snake.getTail().contains(head)) {
+ kill();
+ if (id != snake.id) {
+ snake.reward();
+ }
+ }
+ }
+ }
+
+ public synchronized Collection<Location> getTail() {
+ return tail;
+ }
+
+ public synchronized void setDirection(Direction direction) {
+ this.direction = direction;
+ }
+
+ public synchronized String getLocationsJson() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(String.format("{x: %d, y: %d}",
+ Integer.valueOf(head.x), Integer.valueOf(head.y)));
+ for (Location location : tail) {
+ sb.append(',');
+ sb.append(String.format("{x: %d, y: %d}",
+ Integer.valueOf(location.x), Integer.valueOf(location.y)));
+ }
+ return String.format("{'id':%d,'body':[%s]}",
+ Integer.valueOf(id), sb.toString());
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public String getHexColor() {
+ return hexColor;
+ }
+}
Propchange: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/Snake.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java?rev=1301270&r1=1297716&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java Thu Mar 15 23:39:48 2012
@@ -101,6 +101,7 @@ public class SnakeWebSocketServlet exten
CharBuffer response = CharBuffer.wrap(message);
connection.getWsOutbound().writeTextMessage(response);
} catch (IOException ignore) {
+ // Ignore
}
}
}
@@ -196,13 +197,13 @@ public class SnakeWebSocketServlet exten
@Override
protected void onTextMessage(CharBuffer charBuffer) throws IOException {
String message = charBuffer.toString();
- if ("left".equals(message)) {
+ if ("west".equals(message)) {
snake.setDirection(Direction.WEST);
- } else if ("up".equals(message)) {
+ } else if ("north".equals(message)) {
snake.setDirection(Direction.NORTH);
- } else if ("right".equals(message)) {
+ } else if ("east".equals(message)) {
snake.setDirection(Direction.EAST);
- } else if ("down".equals(message)) {
+ } else if ("south".equals(message)) {
snake.setDirection(Direction.SOUTH);
}
}
Propchange: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/classes/websocket/snake/SnakeWebSocketServlet.java
------------------------------------------------------------------------------
svn:eol-style = native
Modified: tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/WEB-INF/web.xml Thu Mar 15 23:39:48 2012
@@ -359,10 +359,31 @@
<servlet>
<servlet-name>wsEchoMessage</servlet-name>
<servlet-class>websocket.EchoMessage</servlet-class>
+ <!-- Uncomment the following block to increase the default maximum
+ WebSocket buffer size from 2MB to 20MB which is required for the
+ Autobahn test suite to pass fully. -->
+ <!--
+ <init-param>
+ <param-name>byteBufferMaxSize</param-name>
+ <param-value>20971520</param-value>
+ </init-param>
+ <init-param>
+ <param-name>charBufferMaxSize</param-name>
+ <param-value>20971520</param-value>
+ </init-param>
+ -->
</servlet>
<servlet-mapping>
<servlet-name>wsEchoMessage</servlet-name>
<url-pattern>/websocket/echoMessage</url-pattern>
</servlet-mapping>
+ <servlet>
+ <servlet-name>wsSnake</servlet-name>
+ <servlet-class>websocket.snake.SnakeWebSocketServlet</servlet-class>
+ </servlet>
+ <servlet-mapping>
+ <servlet-name>wsSnake</servlet-name>
+ <url-pattern>/websocket/snake</url-pattern>
+ </servlet-mapping>
</web-app>
Modified: tomcat/tc7.0.x/trunk/webapps/examples/websocket/echo.html
URL: http://svn.apache.org/viewvc/tomcat/tc7.0.x/trunk/webapps/examples/websocket/echo.html?rev=1301270&r1=1301269&r2=1301270&view=diff
==============================================================================
--- tomcat/tc7.0.x/trunk/webapps/examples/websocket/echo.html (original)
+++ tomcat/tc7.0.x/trunk/webapps/examples/websocket/echo.html Thu Mar 15 23:39:48 2012
@@ -17,38 +17,140 @@
<!DOCTYPE html>
<html>
<head>
-<title>Apache Tomcat WebSocket Examples: Echo</title>
-<script type="text/javascript">
-function echo(target) {
- if ("WebSocket" in window) {
- // TODO: Can we use relative URLs?
- var ws = new WebSocket(target);
- ws.onopen = function() {
- ws.send("Connection opened");
- alert("WebSocket connection opened.");
- ws.send("Here is a message!");
- }
- ws.onmessage = function(event) {
- alert("Received: " + event.data);
- }
- ws.onclose = function() {
- alert("WebSocket connection closed.");
- }
- // TODO: Extend with a text box for users to enter data
- } else {
- alert("WebSocket is not supported by this browser.");
- }
-}
-</script>
+ <title>Apache Tomcat WebSocket Examples: Echo</title>
+ <style type="text/css">
+ #connect-container {
+ float: left;
+ width: 400px
+ }
+
+ #connect-container div {
+ padding: 5px;
+ }
+
+ #console-container {
+ float: left;
+ padding-left: 20px;
+ width: 400px;
+ }
+
+ #console {
+ border: 1px solid #CCCCCC;
+ border-right-color: #999999;
+ border-bottom-color: #999999;
+ height: 170px;
+ overflow-y: scroll;
+ padding: 5px;
+ width: 100%;
+ }
+
+ #console p {
+ padding: 0;
+ margin: 0;
+ }
+ </style>
+ <script type="text/javascript">
+ var ws = null;
+
+ function setConnected(connected) {
+ document.getElementById('connect').disabled = connected;
+ document.getElementById('disconnect').disabled = !connected;
+ document.getElementById('echo').disabled = !connected;
+ }
+
+ function connect() {
+ var target = document.getElementById('target').value;
+ if (target == '') {
+ alert('Please select server side connection implementation.');
+ return;
+ }
+ if ('WebSocket' in window) {
+ ws = new WebSocket(target);
+ } else if ('MozWebSocket' in window) {
+ ws = new MozWebSocket(target);
+ } else {
+ alert('WebSocket is not supported by this browser.');
+ return;
+ }
+ ws.onopen = function () {
+ setConnected(true);
+ log('Info: WebSocket connection opened.');
+ };
+ ws.onmessage = function (event) {
+ log('Received: ' + event.data);
+ };
+ ws.onclose = function () {
+ setConnected(false);
+ log('Info: WebSocket connection closed.');
+ };
+ }
+
+ function disconnect() {
+ if (ws != null) {
+ ws.close();
+ ws = null;
+ }
+ setConnected(false);
+ }
+
+ function echo() {
+ if (ws != null) {
+ var message = document.getElementById('message').value;
+ log('Sent: ' + message);
+ ws.send(message);
+ } else {
+ alert('WebSocket connection not established, please connect.');
+ }
+ }
+
+ function updateTarget(target) {
+ document.getElementById('target').value = 'ws://' + window.location.host + target;
+ }
+
+ function log(message) {
+ var console = document.getElementById('console');
+ var p = document.createElement('p');
+ p.style.wordWrap = 'break-word';
+ p.innerHTML = message;
+ console.appendChild(p);
+ while (console.childNodes.length > 25) {
+ console.removeChild(console.firstChild);
+ }
+ console.scrollTop = console.scrollHeight;
+ }
+ </script>
</head>
<body>
-<div id="echoStreamTest">
-<a href="javascript:echo('ws://localhost:8080/examples/websocket/echoStream')">
-Start echo example using streams on the server side</a>
-</div>
-<div id="echoStreamMessage">
-<a href="javascript:echo('ws://localhost:8080/examples/websocket/echoMessage')">
-Start echo example using messages on the server side</a>
+<noscript><h1>Seems your browser doesn't support Javascript! Websockets rely on Javascript being enabled. Please enable
+ Javascript and reload this page!</h1></noscript>
+<div>
+ <div id="connect-container">
+ <div>
+ <span>Connect using:</span>
+ <!-- echo example using streams on the server side -->
+ <input id="radio1" type="radio" name="group1" value="/examples/websocket/echoStream"
+ onclick="updateTarget(this.value);"> <label for="radio1">streams</label>
+ <!-- echo example using messages on the server side -->
+ <input id="radio2" type="radio" name="group1" value="/examples/websocket/echoMessage"
+ onclick="updateTarget(this.value);"> <label for="radio2">messages</label>
+ </div>
+ <div>
+ <input id="target" type="text" size="40" style="width: 350px"/>
+ </div>
+ <div>
+ <button id="connect" onclick="connect();">Connect</button>
+ <button id="disconnect" disabled="disabled" onclick="disconnect();">Disconnect</button>
+ </div>
+ <div>
+ <textarea id="message" style="width: 350px">Here is a message!</textarea>
+ </div>
+ <div>
+ <button id="echo" onclick="echo();" disabled="disabled">Echo message</button>
+ </div>
+ </div>
+ <div id="console-container">
+ <div id="console"></div>
+ </div>
</div>
</body>
</html>
\ No newline at end of file
---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tomcat.apache.org
For additional commands, e-mail: dev-help@tomcat.apache.org