diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java index b5efcc9de2e..ebe48263a3e 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocket.java @@ -109,7 +109,23 @@ public interface WebSocket String getProtocol(); void sendMessage(String data) throws IOException; void sendMessage(byte[] data, int offset, int length) throws IOException; + + /** + * @deprecated Use {@link #close()} + */ void disconnect(); + + /** + * Close the connection with normal close code. + */ + void close(); + + /** Close the connection with specific closeCode and message. + * @param closeCode The close code to send, or -1 for no close code + * @param message The message to send or null for no message + */ + void close(int closeCode,String message); + boolean isOpen(); /** @@ -154,12 +170,6 @@ public interface WebSocket */ public interface FrameConnection extends Connection { - /** Close the connection with specific closeCode and message. - * @param closeCode - * @param message - */ - void close(int closeCode,String message); - /** * @return The opcode of a binary message */ diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java index d0607bd26d4..3e44be14601 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD00.java @@ -296,6 +296,12 @@ public class WebSocketConnectionD00 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ public void disconnect() + { + close(); + } + + /* ------------------------------------------------------------ */ + public void close() { try { diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java index 7b0e583c1eb..cd324a44098 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD06.java @@ -508,6 +508,12 @@ public class WebSocketConnectionD06 extends AbstractConnection implements WebSoc /* ------------------------------------------------------------ */ public void disconnect() + { + close(); + } + + /* ------------------------------------------------------------ */ + public void close() { close(CLOSE_NORMAL,null); } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD08.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD08.java index 18e2dbd7081..782a1a23661 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD08.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD08.java @@ -585,9 +585,15 @@ public class WebSocketConnectionD08 extends AbstractConnection implements WebSoc { return opcode==OP_PONG; } - + /* ------------------------------------------------------------ */ public void disconnect() + { + close(); + } + + /* ------------------------------------------------------------ */ + public void close() { close(CLOSE_NORMAL,null); } diff --git a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java index 4abcc463343..2cd3a220e07 100644 --- a/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java +++ b/jetty-websocket/src/main/java/org/eclipse/jetty/websocket/WebSocketConnectionD13.java @@ -382,16 +382,18 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { if (!closed_out) { - // Close code 1005 (CLOSE No Code) is never to be sent as a status over - // a Close control frame. - if ( (code<=0) || (code == WebSocketConnectionD13.CLOSE_NO_CODE) ) - { + // Close code 1005/1006 are never to be sent as a status over + // a Close control frame. Code<-1 also means no node. + + if (code<0 || (code == WebSocketConnectionD13.CLOSE_NO_CODE) || code==WebSocketConnectionD13.CLOSE_NO_CLOSE) + code=-1; + else if (code==0) code=WebSocketConnectionD13.CLOSE_NORMAL; - } + byte[] bytes = ("xx"+(message==null?"":message)).getBytes(StringUtil.__ISO_8859_1); bytes[0]=(byte)(code/0x100); bytes[1]=(byte)(code%0x100); - _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,bytes.length); + _outbound.addFrame((byte)FLAG_FIN,WebSocketConnectionD13.OP_CLOSE,bytes,0,code>0?bytes.length:0); _outbound.flush(); } } @@ -607,6 +609,12 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc { close(CLOSE_NORMAL,null); } + + /* ------------------------------------------------------------ */ + public void close() + { + close(CLOSE_NORMAL,null); + } /* ------------------------------------------------------------ */ public void setAllowFrameFragmentation(boolean allowFragmentation) @@ -767,7 +775,7 @@ public class WebSocketConnectionD13 extends AbstractConnection implements WebSoc ( code > 1010 && code <= 2999 ) || code >= 5000 ) { - errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Invalid close control status code " + code); + errorClose(WebSocketConnectionD13.CLOSE_PROTOCOL,"Invalid close code " + code); return; } diff --git a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java index 62849cd16eb..3e147381c5a 100644 --- a/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java +++ b/jetty-websocket/src/test/java/org/eclipse/jetty/websocket/WebSocketMessageD13Test.java @@ -908,52 +908,233 @@ public class WebSocketMessageD13Test assertEquals(WebSocketConnectionD13.CLOSE_MESSAGE_TOO_LARGE,code); lookFor("Message size > 15",input); } + + @Test - public void testCloseCode() throws Exception + public void testCloseIn() throws Exception { - Socket socket = new Socket("localhost", __connector.getLocalPort()); - OutputStream output = socket.getOutputStream(); - output.write( - ("GET /chat HTTP/1.1\r\n"+ - "Host: server.example.com\r\n"+ - "Upgrade: websocket\r\n"+ - "Connection: Upgrade\r\n"+ - "Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==\r\n"+ - "Sec-WebSocket-Origin: http://example.com\r\n"+ - "Sec-WebSocket-Protocol: chat\r\n" + - "Sec-WebSocket-Version: "+WebSocketConnectionD13.VERSION+"\r\n"+ - "\r\n").getBytes("ISO-8859-1")); - output.flush(); + int[][] tests = + { + {-1,0,-1}, + {-1,0,-1}, + {1000,2,1000}, + {1000,2+4,1000}, + {1005,2+23,1002}, + {1005,2+23,1002}, + {1006,2+23,1002}, + {1006,2+23,1002}, + {4000,2,4000}, + {4000,2+4,4000}, + {9000,2+23,1002}, + {9000,2+23,1002} + }; - socket.setSoTimeout(100000); - InputStream input = socket.getInputStream(); + String[] mesg = + { + "", + "", + "", + "mesg", + "", + "mesg", + "", + "mesg", + "", + "mesg", + "", + "mesg" + }; + + String[] resp = + { + "", + "", + "", + "mesg", + "Invalid close code 1005", + "Invalid close code 1005", + "Invalid close code 1006", + "Invalid close code 1006", + "", + "mesg", + "Invalid close code 9000", + "Invalid close code 9000" + }; - lookFor("HTTP/1.1 101 Switching Protocols\r\n",input); - skipTo("Sec-WebSocket-Accept: ",input); - lookFor("s3pPLMBiTxaQ9kYGzzhZRbK+xOo=",input); - skipTo("\r\n\r\n",input); + for (int t=0;t0) + { + output.write(code/0x100); + output.write(code%0x100); + output.write(m.getBytes()); + } + output.flush(); + + __serverWebSocket.awaitDisconnected(1000); + + byte[] buf = new byte[128]; + int len = input.read(buf); + + assertEquals(tst,2+tests[t][1],len); + assertEquals(tst,(byte)0x88,buf[0]); + + if (len>=4) + { + code=(0xff&buf[2])*0x100+(0xff&buf[3]); + assertEquals(tst,tests[t][2],code); + + if (len>4) + { + m = new String(buf,4,len-4,"UTF-8"); + assertEquals(tst,resp[t],m); + } + } + else + assertEquals(tst,tests[t][2],-1); + + + len = input.read(buf); + assertEquals(tst,-1,len); + } } + + + + @Test + public void testCloseOut() throws Exception + { + int[][] tests = + { + {-1,0,-1}, + {-1,0,-1}, + {0,2,1000}, + {0,2+4,1000}, + {1000,2,1000}, + {1000,2+4,1000}, + {1005,0,-1}, + {1005,0,-1}, + {1006,0,-1}, + {1006,0,-1}, + {9000,2,9000}, + {9000,2+4,9000} + }; + + String[] mesg = + { + null, + "Not Sent", + null, + "mesg", + null, + "mesg", + null, + "mesg", + null, + "mesg", + null, + "mesg" + }; + + for (int t=0;t=4) + { + int code=(0xff&buf[2])*0x100+(0xff&buf[3]); + assertEquals(tst,tests[t][2],code); + + if (len>4) + { + String m = new String(buf,4,len-4,"UTF-8"); + assertEquals(tst,mesg[t],m); + } + } + else + assertEquals(tst,tests[t][2],-1); + + output.write(0x88); + output.write(0x80); + output.write(0x00); + output.write(0x00); + output.write(0x00); + output.write(0x00); + output.flush(); + + len = input.read(buf); + assertEquals(tst,-1,len); + } + } + @Test public void testNotUTF8() throws Exception