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;
|
||||
|
||||
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 <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
|
||||
* 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 <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);
|
||||
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
|
||||
|
@ -284,8 +284,9 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
||||
} else if (frameOpcode == OPCODE_PONG) {
|
||||
return new PongWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||
} else if (frameOpcode == OPCODE_CLOSE) {
|
||||
checkCloseFrameBody(channel, framePayload);
|
||||
receivedClosingHandshake = true;
|
||||
return new CloseWebSocketFrame(frameFinalFlag, frameRsv);
|
||||
return new CloseWebSocketFrame(frameFinalFlag, frameRsv, framePayload);
|
||||
}
|
||||
|
||||
// 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");
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
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) {
|
||||
|
Loading…
Reference in New Issue
Block a user