diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrame.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrame.java index 1f8ed46028..8ed9acb20b 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrame.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/CloseWebSocketFrame.java @@ -15,7 +15,9 @@ */ package io.netty.handler.codec.http.websocketx; +import io.netty.buffer.ChannelBuffer; import io.netty.buffer.ChannelBuffers; +import io.netty.util.CharsetUtil; /** * Web Socket Frame for closing the connection @@ -30,7 +32,20 @@ public class CloseWebSocketFrame extends WebSocketFrame { } /** - * Creates a new close frame + * Creates a new empty close frame with closing status code and reason text + * + * @param statusCode + * Integer status code as per RFC 6455. For + * example, 1000 indicates normal closure. + * @param reasonText + * Reason text. Set to null if no text. + */ + public CloseWebSocketFrame(int statusCode, String reasonText) { + this(true, 0, statusCode, reasonText); + } + + /** + * Creates a new close frame with no losing status code and no reason text * * @param finalFragment * flag indicating if this frame is the final fragment @@ -38,8 +53,93 @@ public class CloseWebSocketFrame extends WebSocketFrame { * reserved bits used for protocol extensions */ public CloseWebSocketFrame(boolean finalFragment, int rsv) { + this(finalFragment, rsv, null); + } + + /** + * Creates a new close frame with closing status code and reason text + * + * @param finalFragment + * flag indicating if this frame is the final fragment + * @param rsv + * reserved bits used for protocol extensions + * @param statusCode + * Integer status code as per RFC 6455. For + * example, 1000 indicates normal closure. + * @param reasonText + * Reason text. Set to null if no text. + */ + public CloseWebSocketFrame(boolean finalFragment, int rsv, int statusCode, String reasonText) { setFinalFragment(finalFragment); setRsv(rsv); + + byte[] reasonBytes = new byte[0]; + if (reasonText != null) { + reasonBytes = reasonText.getBytes(CharsetUtil.UTF_8); + } + + ChannelBuffer binaryData = ChannelBuffers.buffer(2 + reasonBytes.length); + binaryData.writeShort(statusCode); + if (reasonBytes.length > 0) { + binaryData.writeBytes(reasonBytes); + } + + binaryData.readerIndex(0); + setBinaryData(binaryData); + } + + /** + * Creates a new close frame + * + * @param finalFragment + * flag indicating if this frame is the final fragment + * @param rsv + * reserved bits used for protocol extensions + * @param binaryData + * the content of the frame. Must be 2 byte integer followed by optional UTF-8 encoded string. + */ + public CloseWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) { + setFinalFragment(finalFragment); + setRsv(rsv); + if (binaryData == null) { + setBinaryData(ChannelBuffers.EMPTY_BUFFER); + } else { + setBinaryData(binaryData); + } + } + + /** + * Returns the closing status code as per RFC 6455. If + * a status code is set, -1 is returned. + */ + public int getStatusCode() { + ChannelBuffer binaryData = this.getBinaryData(); + if (binaryData == null || binaryData.capacity() == 0) { + return -1; + } + + binaryData.readerIndex(0); + int statusCode = binaryData.readShort(); + binaryData.readerIndex(0); + + return statusCode; + } + + /** + * Returns the reason text as per RFC 6455 If a reason + * text is not supplied, an empty string is returned. + */ + public String getReasonText() { + ChannelBuffer binaryData = this.getBinaryData(); + if (binaryData == null || binaryData.capacity() <= 2) { + return ""; + } + + binaryData.readerIndex(2); + String reasonText = binaryData.toString(CharsetUtil.UTF_8); + binaryData.readerIndex(0); + + return reasonText; } @Override diff --git a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java index 5bd7ea0038..b590c11d8a 100644 --- a/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java +++ b/codec-http/src/main/java/io/netty/handler/codec/http/websocketx/WebSocket08FrameDecoder.java @@ -284,8 +284,9 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder= 0 && statusCode <= 999) || (statusCode >= 1004 && statusCode <= 1006) + || (statusCode >= 1012 && statusCode <= 2999)) { + protocolViolation(channel, "Invalid close frame status code: " + statusCode); + } + + // May have UTF-8 message + if (buffer.readableBytes() > 0) { + byte[] b = new byte[buffer.readableBytes()]; + buffer.readBytes(b); + try { + new UTF8Output(b); + } catch (UTF8Exception ex) { + protocolViolation(channel, "Invalid close frame reason text. Invalid UTF-8 bytes"); + } + } + + // Restore reader index + buffer.readerIndex(idx); + } } diff --git a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java index 0729d0b5ad..d04cb842c4 100644 --- a/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java +++ b/example/src/main/java/io/netty/example/http/websocketx/autobahn/AutobahnServerHandler.java @@ -82,11 +82,10 @@ public class AutobahnServerHandler extends SimpleChannelUpstreamHandler { private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) { if (logger.isDebugEnabled()) { - logger.debug(String - .format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass().getSimpleName())); + logger.debug(String.format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass() + .getSimpleName())); } - if (frame instanceof CloseWebSocketFrame) { this.handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame); } else if (frame instanceof PingWebSocketFrame) {