Issue #250. Implement web socket close frame status code and reason text.
This commit is contained in:
parent
33c085b9b9
commit
91dc8efd4b
@ -15,7 +15,9 @@
|
|||||||
*/
|
*/
|
||||||
package io.netty.handler.codec.http.websocketx;
|
package io.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
|
import io.netty.buffer.ChannelBuffer;
|
||||||
import io.netty.buffer.ChannelBuffers;
|
import io.netty.buffer.ChannelBuffers;
|
||||||
|
import io.netty.util.CharsetUtil;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Web Socket Frame for closing the connection
|
* 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 <a href="http://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. For
|
||||||
|
* example, <tt>1000</tt> 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
|
* @param finalFragment
|
||||||
* flag indicating if this frame is the final fragment
|
* flag indicating if this frame is the final fragment
|
||||||
@ -38,8 +53,93 @@ public class CloseWebSocketFrame extends WebSocketFrame {
|
|||||||
* reserved bits used for protocol extensions
|
* reserved bits used for protocol extensions
|
||||||
*/
|
*/
|
||||||
public CloseWebSocketFrame(boolean finalFragment, int rsv) {
|
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 <a href="http://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. For
|
||||||
|
* example, <tt>1000</tt> 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);
|
setFinalFragment(finalFragment);
|
||||||
setRsv(rsv);
|
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 <a href="http://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a>. 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 <a href="http://tools.ietf.org/html/rfc6455#section-7.4">RFC 6455</a> 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
|
@Override
|
||||||
|
@ -284,8 +284,9 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
} else if (frameOpcode == OPCODE_PONG) {
|
} else if (frameOpcode == OPCODE_PONG) {
|
||||||
return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||||
} else if (frameOpcode == OPCODE_CLOSE) {
|
} else if (frameOpcode == OPCODE_CLOSE) {
|
||||||
|
checkCloseFrameBody(channel, framePayload);
|
||||||
receivedClosingHandshake = true;
|
receivedClosingHandshake = true;
|
||||||
return new CloseWebSocketFrame(frameFinalFlag, frameRsv);
|
return new CloseWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Processing for possible fragmented messages for text and binary
|
// Processing for possible fragmented messages for text and binary
|
||||||
@ -391,4 +392,38 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
protocolViolation(channel, "invalid UTF-8 bytes");
|
protocolViolation(channel, "invalid UTF-8 bytes");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void checkCloseFrameBody(Channel channel, ChannelBuffer buffer) throws CorruptedFrameException {
|
||||||
|
if (buffer == null || buffer.capacity() == 0) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (buffer.capacity() == 1) {
|
||||||
|
protocolViolation(channel, "Invalid close frame body");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save reader index
|
||||||
|
int idx = buffer.readerIndex();
|
||||||
|
buffer.readerIndex(0);
|
||||||
|
|
||||||
|
// Must have 2 byte integer within the valid range
|
||||||
|
int statusCode = buffer.readShort();
|
||||||
|
if ((statusCode >= 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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,11 +82,10 @@ public class AutobahnServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
|
|
||||||
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
|
private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String
|
logger.debug(String.format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass()
|
||||||
.format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass().getSimpleName()));
|
.getSimpleName()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (frame instanceof CloseWebSocketFrame) {
|
if (frame instanceof CloseWebSocketFrame) {
|
||||||
this.handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame);
|
this.handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame);
|
||||||
} else if (frame instanceof PingWebSocketFrame) {
|
} else if (frame instanceof PingWebSocketFrame) {
|
||||||
|
Loading…
Reference in New Issue
Block a user