Clean up the new WebSocket package
This commit is contained in:
parent
4759a791b3
commit
fafeae7aa3
@ -76,7 +76,7 @@ public class AutobahnServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
if (this.handshaker == null) {
|
if (this.handshaker == null) {
|
||||||
wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
|
wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
|
||||||
} else {
|
} else {
|
||||||
this.handshaker.performOpeningHandshake(ctx.getChannel(), req);
|
this.handshaker.handshake(ctx.getChannel(), req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,7 +85,7 @@ public class AutobahnServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
.format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass().getSimpleName()));
|
.format("Channel %s received %s", ctx.getChannel().getId(), frame.getClass().getSimpleName()));
|
||||||
|
|
||||||
if (frame instanceof CloseWebSocketFrame) {
|
if (frame instanceof CloseWebSocketFrame) {
|
||||||
this.handshaker.performClosingHandshake(ctx.getChannel(), (CloseWebSocketFrame) frame);
|
this.handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame);
|
||||||
} else if (frame instanceof PingWebSocketFrame) {
|
} else if (frame instanceof PingWebSocketFrame) {
|
||||||
ctx.getChannel().write(
|
ctx.getChannel().write(
|
||||||
new PongWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
|
new PongWebSocketFrame(frame.isFinalFragment(), frame.getRsv(), frame.getBinaryData()));
|
||||||
|
@ -100,7 +100,7 @@ public class WebSocketClient {
|
|||||||
|
|
||||||
Channel ch = future.getChannel();
|
Channel ch = future.getChannel();
|
||||||
|
|
||||||
handshaker.performOpeningHandshake(ch);
|
handshaker.handshake(ch);
|
||||||
|
|
||||||
Thread.sleep(1000);
|
Thread.sleep(1000);
|
||||||
|
|
||||||
|
@ -52,8 +52,8 @@ public class WebSocketClientHandler extends SimpleChannelUpstreamHandler {
|
|||||||
@Override
|
@Override
|
||||||
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
|
public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
|
||||||
Channel ch = ctx.getChannel();
|
Channel ch = ctx.getChannel();
|
||||||
if (!handshaker.isOpeningHandshakeCompleted()) {
|
if (!handshaker.isHandshakeComplete()) {
|
||||||
handshaker.performClosingHandshake(ch, (HttpResponse) e.getMessage());
|
handshaker.finishHandshake(ch, (HttpResponse) e.getMessage());
|
||||||
System.out.println("WebSocket Client connected!");
|
System.out.println("WebSocket Client connected!");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ public class WebSocketServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
if (this.handshaker == null) {
|
if (this.handshaker == null) {
|
||||||
wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
|
wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
|
||||||
} else {
|
} else {
|
||||||
this.handshaker.performOpeningHandshake(ctx.getChannel(), req);
|
this.handshaker.handshake(ctx.getChannel(), req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ public class WebSocketServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
|
|
||||||
// Check for closing frame
|
// Check for closing frame
|
||||||
if (frame instanceof CloseWebSocketFrame) {
|
if (frame instanceof CloseWebSocketFrame) {
|
||||||
this.handshaker.performClosingHandshake(ctx.getChannel(), (CloseWebSocketFrame) frame);
|
this.handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame);
|
||||||
return;
|
return;
|
||||||
} else if (frame instanceof PingWebSocketFrame) {
|
} else if (frame instanceof PingWebSocketFrame) {
|
||||||
ctx.getChannel().write(new PongWebSocketFrame(frame.getBinaryData()));
|
ctx.getChannel().write(new PongWebSocketFrame(frame.getBinaryData()));
|
||||||
|
@ -96,7 +96,7 @@ public class WebSocketSslServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
if (this.handshaker == null) {
|
if (this.handshaker == null) {
|
||||||
wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
|
wsFactory.sendUnsupportedWebSocketVersionResponse(ctx.getChannel());
|
||||||
} else {
|
} else {
|
||||||
this.handshaker.performOpeningHandshake(ctx.getChannel(), req);
|
this.handshaker.handshake(ctx.getChannel(), req);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +104,7 @@ public class WebSocketSslServerHandler extends SimpleChannelUpstreamHandler {
|
|||||||
|
|
||||||
// Check for closing frame
|
// Check for closing frame
|
||||||
if (frame instanceof CloseWebSocketFrame) {
|
if (frame instanceof CloseWebSocketFrame) {
|
||||||
this.handshaker.performClosingHandshake(ctx.getChannel(), (CloseWebSocketFrame) frame);
|
this.handshaker.close(ctx.getChannel(), (CloseWebSocketFrame) frame);
|
||||||
return;
|
return;
|
||||||
} else if (frame instanceof PingWebSocketFrame) {
|
} else if (frame instanceof PingWebSocketFrame) {
|
||||||
ctx.getChannel().write(new PongWebSocketFrame(frame.getBinaryData()));
|
ctx.getChannel().write(new PongWebSocketFrame(frame.getBinaryData()));
|
||||||
|
@ -27,7 +27,7 @@ public class BinaryWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty binary frame.
|
* Creates a new empty binary frame.
|
||||||
*/
|
*/
|
||||||
public BinaryWebSocketFrame() {
|
public BinaryWebSocketFrame() {
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +37,7 @@ public class BinaryWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public BinaryWebSocketFrame(ChannelBuffer binaryData) {
|
public BinaryWebSocketFrame(ChannelBuffer binaryData) {
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,9 +51,9 @@ public class BinaryWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public BinaryWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
public BinaryWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
||||||
this.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -26,7 +26,7 @@ public class CloseWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty close frame.
|
* Creates a new empty close frame.
|
||||||
*/
|
*/
|
||||||
public CloseWebSocketFrame() {
|
public CloseWebSocketFrame() {
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,8 +38,8 @@ 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.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -31,7 +31,7 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty continuation frame.
|
* Creates a new empty continuation frame.
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame() {
|
public ContinuationWebSocketFrame() {
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -41,7 +41,7 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame(ChannelBuffer binaryData) {
|
public ContinuationWebSocketFrame(ChannelBuffer binaryData) {
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -55,9 +55,9 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
||||||
this.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -73,9 +73,9 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* Aggregated text set by decoder on the final continuation frame of a fragmented text message
|
* Aggregated text set by decoder on the final continuation frame of a fragmented text message
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData, String aggregatedText) {
|
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData, String aggregatedText) {
|
||||||
this.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
this.aggregatedText = aggregatedText;
|
this.aggregatedText = aggregatedText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -90,19 +90,19 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
* text content of the frame.
|
* text content of the frame.
|
||||||
*/
|
*/
|
||||||
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, String text) {
|
public ContinuationWebSocketFrame(boolean finalFragment, int rsv, String text) {
|
||||||
this.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
this.setText(text);
|
setText(text);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the text data in this frame
|
* Returns the text data in this frame
|
||||||
*/
|
*/
|
||||||
public String getText() {
|
public String getText() {
|
||||||
if (this.getBinaryData() == null) {
|
if (getBinaryData() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.getBinaryData().toString(CharsetUtil.UTF_8);
|
return getBinaryData().toString(CharsetUtil.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,9 +113,9 @@ public class ContinuationWebSocketFrame extends WebSocketFrame {
|
|||||||
*/
|
*/
|
||||||
public void setText(String text) {
|
public void setText(String text) {
|
||||||
if (text == null || text.isEmpty()) {
|
if (text == null || text.isEmpty()) {
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
} else {
|
} else {
|
||||||
this.setBinaryData(ChannelBuffers.copiedBuffer(text, CharsetUtil.UTF_8));
|
setBinaryData(ChannelBuffers.copiedBuffer(text, CharsetUtil.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ public class PingWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty ping frame.
|
* Creates a new empty ping frame.
|
||||||
*/
|
*/
|
||||||
public PingWebSocketFrame() {
|
public PingWebSocketFrame() {
|
||||||
this.setFinalFragment(true);
|
setFinalFragment(true);
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -38,7 +38,7 @@ public class PingWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public PingWebSocketFrame(ChannelBuffer binaryData) {
|
public PingWebSocketFrame(ChannelBuffer binaryData) {
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -52,9 +52,9 @@ public class PingWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public PingWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
public PingWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
||||||
this.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -27,7 +27,7 @@ public class PongWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty pong frame.
|
* Creates a new empty pong frame.
|
||||||
*/
|
*/
|
||||||
public PongWebSocketFrame() {
|
public PongWebSocketFrame() {
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -37,7 +37,7 @@ public class PongWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public PongWebSocketFrame(ChannelBuffer binaryData) {
|
public PongWebSocketFrame(ChannelBuffer binaryData) {
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -51,9 +51,9 @@ public class PongWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame.
|
* the content of the frame.
|
||||||
*/
|
*/
|
||||||
public PongWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
public PongWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
||||||
this.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -28,7 +28,7 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* Creates a new empty text frame.
|
* Creates a new empty text frame.
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame() {
|
public TextWebSocketFrame() {
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -39,9 +39,9 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
*/
|
*/
|
||||||
public TextWebSocketFrame(String text) {
|
public TextWebSocketFrame(String text) {
|
||||||
if (text == null || text.isEmpty()) {
|
if (text == null || text.isEmpty()) {
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
} else {
|
} else {
|
||||||
this.setBinaryData(ChannelBuffers.copiedBuffer(text, CharsetUtil.UTF_8));
|
setBinaryData(ChannelBuffers.copiedBuffer(text, CharsetUtil.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -52,7 +52,7 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame. Must be UTF-8 encoded
|
* the content of the frame. Must be UTF-8 encoded
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame(ChannelBuffer binaryData) {
|
public TextWebSocketFrame(ChannelBuffer binaryData) {
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -66,12 +66,12 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* String to put in the frame
|
* String to put in the frame
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame(boolean finalFragment, int rsv, String text) {
|
public TextWebSocketFrame(boolean finalFragment, int rsv, String text) {
|
||||||
this.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
if (text == null || text.isEmpty()) {
|
if (text == null || text.isEmpty()) {
|
||||||
this.setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
setBinaryData(ChannelBuffers.EMPTY_BUFFER);
|
||||||
} else {
|
} else {
|
||||||
this.setBinaryData(ChannelBuffers.copiedBuffer(text, CharsetUtil.UTF_8));
|
setBinaryData(ChannelBuffers.copiedBuffer(text, CharsetUtil.UTF_8));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,19 +86,19 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
* the content of the frame. Must be UTF-8 encoded
|
* the content of the frame. Must be UTF-8 encoded
|
||||||
*/
|
*/
|
||||||
public TextWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
public TextWebSocketFrame(boolean finalFragment, int rsv, ChannelBuffer binaryData) {
|
||||||
this.setFinalFragment(finalFragment);
|
setFinalFragment(finalFragment);
|
||||||
this.setRsv(rsv);
|
setRsv(rsv);
|
||||||
this.setBinaryData(binaryData);
|
setBinaryData(binaryData);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the text data in this frame
|
* Returns the text data in this frame
|
||||||
*/
|
*/
|
||||||
public String getText() {
|
public String getText() {
|
||||||
if (this.getBinaryData() == null) {
|
if (getBinaryData() == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return this.getBinaryData().toString(CharsetUtil.UTF_8);
|
return getBinaryData().toString(CharsetUtil.UTF_8);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -111,7 +111,7 @@ public class TextWebSocketFrame extends WebSocketFrame {
|
|||||||
if (text == null) {
|
if (text == null) {
|
||||||
throw new NullPointerException("text");
|
throw new NullPointerException("text");
|
||||||
}
|
}
|
||||||
this.setBinaryData(ChannelBuffers.copiedBuffer(text, CharsetUtil.UTF_8));
|
setBinaryData(ChannelBuffers.copiedBuffer(text, CharsetUtil.UTF_8));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -60,7 +60,7 @@ final class UTF8Output {
|
|||||||
public void write(int b) {
|
public void write(int b) {
|
||||||
byte type = TYPES[b & 0xFF];
|
byte type = TYPES[b & 0xFF];
|
||||||
|
|
||||||
codep = (state != UTF8_ACCEPT) ? (b & 0x3f) | (codep << 6) : (0xff >> type) & b;
|
codep = state != UTF8_ACCEPT ? b & 0x3f | codep << 6 : 0xff >> type & b;
|
||||||
|
|
||||||
state = STATES[state + type];
|
state = STATES[state + type];
|
||||||
|
|
||||||
|
@ -33,7 +33,7 @@ import org.jboss.netty.handler.codec.replay.VoidEnum;
|
|||||||
*/
|
*/
|
||||||
public class WebSocket00FrameDecoder extends ReplayingDecoder<VoidEnum> {
|
public class WebSocket00FrameDecoder extends ReplayingDecoder<VoidEnum> {
|
||||||
|
|
||||||
public static final int DEFAULT_MAX_FRAME_SIZE = 16384;
|
private static final int DEFAULT_MAX_FRAME_SIZE = 16384;
|
||||||
|
|
||||||
private final int maxFrameSize;
|
private final int maxFrameSize;
|
||||||
private boolean receivedClosingHandshake;
|
private boolean receivedClosingHandshake;
|
||||||
@ -70,7 +70,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<VoidEnum> {
|
|||||||
return decodeBinaryFrame(type, buffer);
|
return decodeBinaryFrame(type, buffer);
|
||||||
} else {
|
} else {
|
||||||
// Decode a 0xff terminated UTF-8 string
|
// Decode a 0xff terminated UTF-8 string
|
||||||
return decodeTextFrame(type, buffer);
|
return decodeTextFrame(buffer);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -92,7 +92,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<VoidEnum> {
|
|||||||
}
|
}
|
||||||
} while ((b & 0x80) == 0x80);
|
} while ((b & 0x80) == 0x80);
|
||||||
|
|
||||||
if (type == ((byte) 0xFF) && frameSize == 0) {
|
if (type == (byte) 0xFF && frameSize == 0) {
|
||||||
receivedClosingHandshake = true;
|
receivedClosingHandshake = true;
|
||||||
return new CloseWebSocketFrame();
|
return new CloseWebSocketFrame();
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ public class WebSocket00FrameDecoder extends ReplayingDecoder<VoidEnum> {
|
|||||||
return new BinaryWebSocketFrame(buffer.readBytes((int) frameSize));
|
return new BinaryWebSocketFrame(buffer.readBytes((int) frameSize));
|
||||||
}
|
}
|
||||||
|
|
||||||
private WebSocketFrame decodeTextFrame(byte type, ChannelBuffer buffer) throws TooLongFrameException {
|
private WebSocketFrame decodeTextFrame(ChannelBuffer buffer) throws TooLongFrameException {
|
||||||
int ridx = buffer.readerIndex();
|
int ridx = buffer.readerIndex();
|
||||||
int rbytes = actualReadableBytes();
|
int rbytes = actualReadableBytes();
|
||||||
int delimPos = buffer.indexOf(ridx, ridx + rbytes, (byte) 0xFF);
|
int delimPos = buffer.indexOf(ridx, ridx + rbytes, (byte) 0xFF);
|
||||||
|
@ -75,8 +75,8 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
private int framePayloadBytesRead;
|
private int framePayloadBytesRead;
|
||||||
private ChannelBuffer maskingKey;
|
private ChannelBuffer maskingKey;
|
||||||
|
|
||||||
private boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
private boolean maskedPayload;
|
private final boolean maskedPayload;
|
||||||
private boolean receivedClosingHandshake;
|
private boolean receivedClosingHandshake;
|
||||||
|
|
||||||
public enum State {
|
public enum State {
|
||||||
@ -129,12 +129,12 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
boolean frameMasked = (b & 0x80) != 0;
|
boolean frameMasked = (b & 0x80) != 0;
|
||||||
int framePayloadLen1 = b & 0x7F;
|
int framePayloadLen1 = b & 0x7F;
|
||||||
|
|
||||||
if (frameRsv != 0 && !this.allowExtensions) {
|
if (frameRsv != 0 && !allowExtensions) {
|
||||||
protocolViolation(channel, "RSV != 0 and no extension negotiated, RSV:" + frameRsv);
|
protocolViolation(channel, "RSV != 0 and no extension negotiated, RSV:" + frameRsv);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.maskedPayload && !frameMasked) {
|
if (maskedPayload && !frameMasked) {
|
||||||
protocolViolation(channel, "unmasked client to server frame");
|
protocolViolation(channel, "unmasked client to server frame");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -211,7 +211,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
|
|
||||||
checkpoint(State.MASKING_KEY);
|
checkpoint(State.MASKING_KEY);
|
||||||
case MASKING_KEY:
|
case MASKING_KEY:
|
||||||
if (this.maskedPayload) {
|
if (maskedPayload) {
|
||||||
maskingKey = buffer.readBytes(4);
|
maskingKey = buffer.readBytes(4);
|
||||||
}
|
}
|
||||||
checkpoint(State.PAYLOAD);
|
checkpoint(State.PAYLOAD);
|
||||||
@ -236,7 +236,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
framePayload = channel.getConfig().getBufferFactory().getBuffer(toFrameLength(framePayloadLength));
|
framePayload = channel.getConfig().getBufferFactory().getBuffer(toFrameLength(framePayloadLength));
|
||||||
}
|
}
|
||||||
framePayload.writeBytes(payloadBuffer);
|
framePayload.writeBytes(payloadBuffer);
|
||||||
framePayloadBytesRead = framePayloadBytesRead + rbytes;
|
framePayloadBytesRead += rbytes;
|
||||||
|
|
||||||
// Return null to wait for more bytes to arrive
|
// Return null to wait for more bytes to arrive
|
||||||
return null;
|
return null;
|
||||||
@ -258,7 +258,7 @@ public class WebSocket08FrameDecoder extends ReplayingDecoder<WebSocket08FrameDe
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Unmask data if needed
|
// Unmask data if needed
|
||||||
if (this.maskedPayload) {
|
if (maskedPayload) {
|
||||||
unmask(framePayload);
|
unmask(framePayload);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -269,7 +269,7 @@ 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) {
|
||||||
this.receivedClosingHandshake = true;
|
receivedClosingHandshake = true;
|
||||||
return new CloseWebSocketFrame(frameFinalFlag, frameRsv);
|
return new CloseWebSocketFrame(frameFinalFlag, frameRsv);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -66,7 +66,7 @@ public class WebSocket08FrameEncoder extends OneToOneEncoder {
|
|||||||
private static final byte OPCODE_PING = 0x9;
|
private static final byte OPCODE_PING = 0x9;
|
||||||
private static final byte OPCODE_PONG = 0xA;
|
private static final byte OPCODE_PONG = 0xA;
|
||||||
|
|
||||||
private boolean maskPayload;
|
private final boolean maskPayload;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor
|
* Constructor
|
||||||
@ -82,7 +82,7 @@ public class WebSocket08FrameEncoder extends OneToOneEncoder {
|
|||||||
@Override
|
@Override
|
||||||
protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
|
protected Object encode(ChannelHandlerContext ctx, Channel channel, Object msg) throws Exception {
|
||||||
|
|
||||||
byte[] mask = null;
|
byte[] mask;
|
||||||
|
|
||||||
if (msg instanceof WebSocketFrame) {
|
if (msg instanceof WebSocketFrame) {
|
||||||
WebSocketFrame frame = (WebSocketFrame) msg;
|
WebSocketFrame frame = (WebSocketFrame) msg;
|
||||||
@ -118,7 +118,7 @@ public class WebSocket08FrameEncoder extends OneToOneEncoder {
|
|||||||
if (frame.isFinalFragment()) {
|
if (frame.isFinalFragment()) {
|
||||||
b0 |= 1 << 7;
|
b0 |= 1 << 7;
|
||||||
}
|
}
|
||||||
b0 |= (frame.getRsv() % 8) << 4;
|
b0 |= frame.getRsv() % 8 << 4;
|
||||||
b0 |= opcode % 128;
|
b0 |= opcode % 128;
|
||||||
|
|
||||||
ChannelBuffer header;
|
ChannelBuffer header;
|
||||||
@ -129,27 +129,27 @@ public class WebSocket08FrameEncoder extends OneToOneEncoder {
|
|||||||
+ length);
|
+ length);
|
||||||
}
|
}
|
||||||
|
|
||||||
int maskLength = this.maskPayload ? 4 : 0;
|
int maskLength = maskPayload ? 4 : 0;
|
||||||
if (length <= 125) {
|
if (length <= 125) {
|
||||||
header = ChannelBuffers.buffer(2 + maskLength);
|
header = ChannelBuffers.buffer(2 + maskLength);
|
||||||
header.writeByte(b0);
|
header.writeByte(b0);
|
||||||
byte b = (byte) (this.maskPayload ? (0x80 | (byte) length) : (byte) length);
|
byte b = (byte) (maskPayload ? 0x80 | (byte) length : (byte) length);
|
||||||
header.writeByte(b);
|
header.writeByte(b);
|
||||||
} else if (length <= 0xFFFF) {
|
} else if (length <= 0xFFFF) {
|
||||||
header = ChannelBuffers.buffer(4 + maskLength);
|
header = ChannelBuffers.buffer(4 + maskLength);
|
||||||
header.writeByte(b0);
|
header.writeByte(b0);
|
||||||
header.writeByte(this.maskPayload ? 0xFE : 126);
|
header.writeByte(maskPayload ? 0xFE : 126);
|
||||||
header.writeByte((length >>> 8) & 0xFF);
|
header.writeByte(length >>> 8 & 0xFF);
|
||||||
header.writeByte(length & 0xFF);
|
header.writeByte(length & 0xFF);
|
||||||
} else {
|
} else {
|
||||||
header = ChannelBuffers.buffer(10 + maskLength);
|
header = ChannelBuffers.buffer(10 + maskLength);
|
||||||
header.writeByte(b0);
|
header.writeByte(b0);
|
||||||
header.writeByte(this.maskPayload ? 0xFF : 127);
|
header.writeByte(maskPayload ? 0xFF : 127);
|
||||||
header.writeLong(length);
|
header.writeLong(length);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Write payload
|
// Write payload
|
||||||
if (this.maskPayload) {
|
if (maskPayload) {
|
||||||
Integer random = (int) (Math.random() * Integer.MAX_VALUE);
|
Integer random = (int) (Math.random() * Integer.MAX_VALUE);
|
||||||
mask = ByteBuffer.allocate(4).putInt(random).array();
|
mask = ByteBuffer.allocate(4).putInt(random).array();
|
||||||
header.writeBytes(mask);
|
header.writeBytes(mask);
|
||||||
|
@ -23,6 +23,7 @@ import java.util.Map;
|
|||||||
import org.jboss.netty.buffer.ChannelBuffer;
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
import org.jboss.netty.buffer.ChannelBuffers;
|
import org.jboss.netty.buffer.ChannelBuffers;
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
import org.jboss.netty.handler.codec.base64.Base64;
|
import org.jboss.netty.handler.codec.base64.Base64;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||||
import org.jboss.netty.util.CharsetUtil;
|
import org.jboss.netty.util.CharsetUtil;
|
||||||
@ -32,48 +33,44 @@ import org.jboss.netty.util.CharsetUtil;
|
|||||||
*/
|
*/
|
||||||
public abstract class WebSocketClientHandshaker {
|
public abstract class WebSocketClientHandshaker {
|
||||||
|
|
||||||
private URI webSocketURL;
|
private final URI webSocketUrl;
|
||||||
|
|
||||||
private WebSocketVersion version = WebSocketVersion.UNKNOWN;
|
private final WebSocketVersion version;
|
||||||
|
|
||||||
private boolean openingHandshakeCompleted;
|
private boolean handshakeComplete;
|
||||||
|
|
||||||
private String subProtocolRequest;
|
private final String expectedSubprotocol;
|
||||||
|
|
||||||
private String subProtocolResponse;
|
private String actualSubprotocol;
|
||||||
|
|
||||||
protected Map<String, String> customHeaders;
|
protected final Map<String, String> customHeaders;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Base constructor
|
* Base constructor
|
||||||
*
|
*
|
||||||
* @param webSocketURL
|
* @param webSocketUrl
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param version
|
* @param version
|
||||||
* Version of web socket specification to use to connect to the server
|
* Version of web socket specification to use to connect to the server
|
||||||
* @param subProtocol
|
* @param subprotocol
|
||||||
* Sub protocol request sent to the server.
|
* Sub protocol request sent to the server.
|
||||||
* @param customHeaders
|
* @param customHeaders
|
||||||
* Map of custom headers to add to the client request
|
* Map of custom headers to add to the client request
|
||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker(URI webSocketURL, WebSocketVersion version, String subProtocol,
|
public WebSocketClientHandshaker(URI webSocketUrl, WebSocketVersion version, String subprotocol,
|
||||||
Map<String, String> customHeaders) {
|
Map<String, String> customHeaders) {
|
||||||
this.webSocketURL = webSocketURL;
|
this.webSocketUrl = webSocketUrl;
|
||||||
this.version = version;
|
this.version = version;
|
||||||
this.subProtocolRequest = subProtocol;
|
expectedSubprotocol = subprotocol;
|
||||||
this.customHeaders = customHeaders;
|
this.customHeaders = customHeaders;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the URI to the web socket. e.g. "ws://myhost.com/path"
|
* Returns the URI to the web socket. e.g. "ws://myhost.com/path"
|
||||||
*/
|
*/
|
||||||
public URI getWebSocketURL() {
|
public URI getWebSocketUrl() {
|
||||||
return webSocketURL;
|
return webSocketUrl;
|
||||||
}
|
|
||||||
|
|
||||||
protected void setWebSocketURL(URI webSocketURL) {
|
|
||||||
this.webSocketURL = webSocketURL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -83,133 +80,50 @@ public abstract class WebSocketClientHandshaker {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setVersion(WebSocketVersion version) {
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Flag to indicate if the opening handshake is complete
|
* Flag to indicate if the opening handshake is complete
|
||||||
*/
|
*/
|
||||||
public boolean isOpeningHandshakeCompleted() {
|
public boolean isHandshakeComplete() {
|
||||||
return openingHandshakeCompleted;
|
return handshakeComplete;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setOpenningHandshakeCompleted(boolean openningHandshakeCompleted) {
|
protected void setHandshakeComplete() {
|
||||||
this.openingHandshakeCompleted = openningHandshakeCompleted;
|
handshakeComplete = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the sub protocol request sent to the server as specified in the constructor
|
* Returns the sub protocol request sent to the server as specified in the constructor
|
||||||
*/
|
*/
|
||||||
public String getSubProtocolRequest() {
|
public String getExpectedSubprotocol() {
|
||||||
return subProtocolRequest;
|
return expectedSubprotocol;
|
||||||
}
|
|
||||||
|
|
||||||
protected void setSubProtocolRequest(String subProtocolRequest) {
|
|
||||||
this.subProtocolRequest = subProtocolRequest;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the sub protocol response and sent by the server. Only available after end of handshake.
|
* Returns the sub protocol response and sent by the server. Only available after end of handshake.
|
||||||
*/
|
*/
|
||||||
public String getSubProtocolResponse() {
|
public String getActualSubprotocol() {
|
||||||
return subProtocolResponse;
|
return actualSubprotocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void setSubProtocolResponse(String subProtocolResponse) {
|
protected void setActualSubprotocol(String actualSubprotocol) {
|
||||||
this.subProtocolResponse = subProtocolResponse;
|
this.actualSubprotocol = actualSubprotocol;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the opening handshake
|
* Begins the opening handshake
|
||||||
*
|
*
|
||||||
* @param channel
|
* @param channel
|
||||||
* Channel
|
* Channel
|
||||||
*/
|
*/
|
||||||
public abstract void performOpeningHandshake(Channel channel);
|
public abstract ChannelFuture handshake(Channel channel);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the closing handshake
|
* Validates and finishes the opening handshake initiated by {@link #handshake}}.
|
||||||
*
|
*
|
||||||
* @param channel
|
* @param channel
|
||||||
* Channel
|
* Channel
|
||||||
* @param response
|
* @param response
|
||||||
* HTTP response containing the closing handshake details
|
* HTTP response containing the closing handshake details
|
||||||
*/
|
*/
|
||||||
public abstract void performClosingHandshake(Channel channel, HttpResponse response)
|
public abstract void finishHandshake(Channel channel, HttpResponse response);
|
||||||
throws WebSocketHandshakeException;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs an MD5 hash
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* Data to hash
|
|
||||||
* @return Hashed data
|
|
||||||
*/
|
|
||||||
protected byte[] md5(byte[] bytes) {
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
|
||||||
return md.digest(bytes);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new InternalError("MD5 not supported on this platform");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs an SHA-1 hash
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* Data to hash
|
|
||||||
* @return Hashed data
|
|
||||||
*/
|
|
||||||
protected byte[] sha1(byte[] bytes) {
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
|
||||||
return md.digest(bytes);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new InternalError("SHA-1 not supported on this platform");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base 64 encoding
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* Bytes to encode
|
|
||||||
* @return encoded string
|
|
||||||
*/
|
|
||||||
protected String base64Encode(byte[] bytes) {
|
|
||||||
ChannelBuffer hashed = ChannelBuffers.wrappedBuffer(bytes);
|
|
||||||
return Base64.encode(hashed).toString(CharsetUtil.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Creates some random bytes
|
|
||||||
*
|
|
||||||
* @param size
|
|
||||||
* Number of random bytes to create
|
|
||||||
* @return random bytes
|
|
||||||
*/
|
|
||||||
protected byte[] createRandomBytes(int size) {
|
|
||||||
byte[] bytes = new byte[size];
|
|
||||||
|
|
||||||
for (int i = 0; i < size; i++) {
|
|
||||||
bytes[i] = (byte) createRandomNumber(0, 255);
|
|
||||||
}
|
|
||||||
|
|
||||||
return bytes;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generates a random number
|
|
||||||
*
|
|
||||||
* @param min
|
|
||||||
* Minimum value
|
|
||||||
* @param max
|
|
||||||
* Maximum value
|
|
||||||
* @return Random number
|
|
||||||
*/
|
|
||||||
protected int createRandomNumber(int min, int max) {
|
|
||||||
return (int) (Math.random() * max + min);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import java.util.Map;
|
|||||||
|
|
||||||
import org.jboss.netty.buffer.ChannelBuffers;
|
import org.jboss.netty.buffer.ChannelBuffers;
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
|
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
@ -55,14 +56,15 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param version
|
* @param version
|
||||||
* Version of web socket specification to use to connect to the server
|
* Version of web socket specification to use to connect to the server
|
||||||
* @param subProtocol
|
* @param subprotocol
|
||||||
* Sub protocol request sent to the server.
|
* Sub protocol request sent to the server.
|
||||||
* @param customHeaders
|
* @param customHeaders
|
||||||
* Map of custom headers to add to the client request
|
* Map of custom headers to add to the client request
|
||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subProtocol,
|
public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
||||||
Map<String, String> customHeaders) {
|
Map<String, String> customHeaders) {
|
||||||
super(webSocketURL, version, subProtocol, customHeaders);
|
super(webSocketURL, version, subprotocol, customHeaders);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -86,16 +88,16 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
* Channel into which we can write our request
|
* Channel into which we can write our request
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performOpeningHandshake(Channel channel) {
|
public ChannelFuture handshake(Channel channel) {
|
||||||
// Make keys
|
// Make keys
|
||||||
int spaces1 = createRandomNumber(1, 12);
|
int spaces1 = WebSocketUtil.randomNumber(1, 12);
|
||||||
int spaces2 = createRandomNumber(1, 12);
|
int spaces2 = WebSocketUtil.randomNumber(1, 12);
|
||||||
|
|
||||||
int max1 = Integer.MAX_VALUE / spaces1;
|
int max1 = Integer.MAX_VALUE / spaces1;
|
||||||
int max2 = Integer.MAX_VALUE / spaces2;
|
int max2 = Integer.MAX_VALUE / spaces2;
|
||||||
|
|
||||||
int number1 = createRandomNumber(0, max1);
|
int number1 = WebSocketUtil.randomNumber(0, max1);
|
||||||
int number2 = createRandomNumber(0, max2);
|
int number2 = WebSocketUtil.randomNumber(0, max2);
|
||||||
|
|
||||||
int product1 = number1 * spaces1;
|
int product1 = number1 * spaces1;
|
||||||
int product2 = number2 * spaces2;
|
int product2 = number2 * spaces2;
|
||||||
@ -109,7 +111,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
key1 = insertSpaces(key1, spaces1);
|
key1 = insertSpaces(key1, spaces1);
|
||||||
key2 = insertSpaces(key2, spaces2);
|
key2 = insertSpaces(key2, spaces2);
|
||||||
|
|
||||||
byte[] key3 = createRandomBytes(8);
|
byte[] key3 = WebSocketUtil.randomBytes(8);
|
||||||
|
|
||||||
ByteBuffer buffer = ByteBuffer.allocate(4);
|
ByteBuffer buffer = ByteBuffer.allocate(4);
|
||||||
buffer.putInt(number1);
|
buffer.putInt(number1);
|
||||||
@ -122,10 +124,10 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
System.arraycopy(number1Array, 0, challenge, 0, 4);
|
System.arraycopy(number1Array, 0, challenge, 0, 4);
|
||||||
System.arraycopy(number2Array, 0, challenge, 4, 4);
|
System.arraycopy(number2Array, 0, challenge, 4, 4);
|
||||||
System.arraycopy(key3, 0, challenge, 8, 8);
|
System.arraycopy(key3, 0, challenge, 8, 8);
|
||||||
this.expectedChallengeResponseBytes = md5(challenge);
|
expectedChallengeResponseBytes = WebSocketUtil.md5(challenge);
|
||||||
|
|
||||||
// Get path
|
// Get path
|
||||||
URI wsURL = this.getWebSocketURL();
|
URI wsURL = getWebSocketUrl();
|
||||||
String path = wsURL.getPath();
|
String path = wsURL.getPath();
|
||||||
if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) {
|
if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) {
|
||||||
path = wsURL.getPath() + "?" + wsURL.getQuery();
|
path = wsURL.getPath() + "?" + wsURL.getQuery();
|
||||||
@ -139,19 +141,23 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost());
|
request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost());
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1);
|
request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1);
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2);
|
request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2);
|
||||||
if (this.getSubProtocolRequest() != null && !this.getSubProtocolRequest().equals("")) {
|
if (getExpectedSubprotocol() != null && !getExpectedSubprotocol().equals("")) {
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, this.getSubProtocolRequest());
|
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, getExpectedSubprotocol());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (customHeaders != null) {
|
if (customHeaders != null) {
|
||||||
for (String header : customHeaders.keySet()) {
|
for (String header : customHeaders.keySet()) {
|
||||||
request.addHeader(header, customHeaders.get(header));
|
request.addHeader(header, customHeaders.get(header));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
request.setContent(ChannelBuffers.copiedBuffer(key3));
|
request.setContent(ChannelBuffers.copiedBuffer(key3));
|
||||||
|
|
||||||
channel.write(request);
|
ChannelFuture future = channel.write(request);
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket00FrameEncoder());
|
channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket00FrameEncoder());
|
||||||
|
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -177,7 +183,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
* @throws WebSocketHandshakeException
|
* @throws WebSocketHandshakeException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException {
|
public void finishHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException {
|
||||||
final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake");
|
final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake");
|
||||||
|
|
||||||
if (!response.getStatus().equals(status)) {
|
if (!response.getStatus().equals(status)) {
|
||||||
@ -202,28 +208,28 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String protocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
String protocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
this.setSubProtocolResponse(protocol);
|
setActualSubprotocol(protocol);
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", new WebSocket00FrameDecoder());
|
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", new WebSocket00FrameDecoder());
|
||||||
|
|
||||||
this.setOpenningHandshakeCompleted(true);
|
setHandshakeComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
private String insertRandomCharacters(String key) {
|
private String insertRandomCharacters(String key) {
|
||||||
int count = createRandomNumber(1, 12);
|
int count = WebSocketUtil.randomNumber(1, 12);
|
||||||
|
|
||||||
char[] randomChars = new char[count];
|
char[] randomChars = new char[count];
|
||||||
int randCount = 0;
|
int randCount = 0;
|
||||||
while (randCount < count) {
|
while (randCount < count) {
|
||||||
int rand = (int) (Math.random() * 0x7e + 0x21);
|
int rand = (int) (Math.random() * 0x7e + 0x21);
|
||||||
if (((0x21 < rand) && (rand < 0x2f)) || ((0x3a < rand) && (rand < 0x7e))) {
|
if (0x21 < rand && rand < 0x2f || 0x3a < rand && rand < 0x7e) {
|
||||||
randomChars[randCount] = (char) rand;
|
randomChars[randCount] = (char) rand;
|
||||||
randCount += 1;
|
randCount += 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < count; i++) {
|
for (int i = 0; i < count; i++) {
|
||||||
int split = createRandomNumber(0, key.length());
|
int split = WebSocketUtil.randomNumber(0, key.length());
|
||||||
String part1 = key.substring(0, split);
|
String part1 = key.substring(0, split);
|
||||||
String part2 = key.substring(split);
|
String part2 = key.substring(split);
|
||||||
key = part1 + randomChars[i] + part2;
|
key = part1 + randomChars[i] + part2;
|
||||||
@ -234,7 +240,7 @@ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker {
|
|||||||
|
|
||||||
private String insertSpaces(String key, int spaces) {
|
private String insertSpaces(String key, int spaces) {
|
||||||
for (int i = 0; i < spaces; i++) {
|
for (int i = 0; i < spaces; i++) {
|
||||||
int split = createRandomNumber(1, key.length() - 1);
|
int split = WebSocketUtil.randomNumber(1, key.length() - 1);
|
||||||
String part1 = key.substring(0, split);
|
String part1 = key.substring(0, split);
|
||||||
String part2 = key.substring(split);
|
String part2 = key.substring(split);
|
||||||
key = part1 + " " + part2;
|
key = part1 + " " + part2;
|
||||||
|
@ -19,6 +19,7 @@ import java.net.URI;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
|
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
@ -50,7 +51,7 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
|
|
||||||
private static final String protocol = null;
|
private static final String protocol = null;
|
||||||
|
|
||||||
private boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location and version to initiate
|
* Constructor specifying the destination web socket location and version to initiate
|
||||||
@ -60,16 +61,16 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param version
|
* @param version
|
||||||
* Version of web socket specification to use to connect to the server
|
* Version of web socket specification to use to connect to the server
|
||||||
* @param subProtocol
|
* @param subprotocol
|
||||||
* Sub protocol request sent to the server.
|
* Sub protocol request sent to the server.
|
||||||
* @param allowExtensions
|
* @param allowExtensions
|
||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* Allow extensions to be used in the reserved bits of the web socket frame
|
||||||
* @param customHeaders
|
* @param customHeaders
|
||||||
* Map of custom headers to add to the client request
|
* Map of custom headers to add to the client request
|
||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subProtocol,
|
public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
||||||
boolean allowExtensions, Map<String, String> customHeaders) {
|
boolean allowExtensions, Map<String, String> customHeaders) {
|
||||||
super(webSocketURL, version, subProtocol, customHeaders);
|
super(webSocketURL, version, subprotocol, customHeaders);
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,25 +95,25 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
* Channel into which we can write our request
|
* Channel into which we can write our request
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performOpeningHandshake(Channel channel) {
|
public ChannelFuture handshake(Channel channel) {
|
||||||
// Get path
|
// Get path
|
||||||
URI wsURL = this.getWebSocketURL();
|
URI wsURL = getWebSocketUrl();
|
||||||
String path = wsURL.getPath();
|
String path = wsURL.getPath();
|
||||||
if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) {
|
if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) {
|
||||||
path = wsURL.getPath() + "?" + wsURL.getQuery();
|
path = wsURL.getPath() + "?" + wsURL.getQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get 16 bit nonce and base 64 encode it
|
// Get 16 bit nonce and base 64 encode it
|
||||||
byte[] nonce = createRandomBytes(16);
|
byte[] nonce = WebSocketUtil.randomBytes(16);
|
||||||
String key = base64Encode(nonce);
|
String key = WebSocketUtil.base64(nonce);
|
||||||
|
|
||||||
String acceptSeed = key + MAGIC_GUID;
|
String acceptSeed = key + MAGIC_GUID;
|
||||||
byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
|
byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
|
||||||
this.expectedChallengeResponseString = base64Encode(sha1);
|
expectedChallengeResponseString = WebSocketUtil.base64(sha1);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("WS Version 08 Client Handshake key: %s. Expected response: %s.", key,
|
logger.debug(String.format("WS Version 08 Client Handshake key: %s. Expected response: %s.", key,
|
||||||
this.expectedChallengeResponseString));
|
expectedChallengeResponseString));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format request
|
// Format request
|
||||||
@ -126,15 +127,18 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol);
|
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol);
|
||||||
}
|
}
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "8");
|
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "8");
|
||||||
|
|
||||||
if (customHeaders != null) {
|
if (customHeaders != null) {
|
||||||
for (String header : customHeaders.keySet()) {
|
for (String header : customHeaders.keySet()) {
|
||||||
request.addHeader(header, customHeaders.get(header));
|
request.addHeader(header, customHeaders.get(header));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.write(request);
|
ChannelFuture future = channel.write(request);
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket08FrameEncoder(true));
|
channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket08FrameEncoder(true));
|
||||||
|
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,8 +161,8 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
* @throws WebSocketHandshakeException
|
* @throws WebSocketHandshakeException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException {
|
public void finishHandshake(Channel channel, HttpResponse response) {
|
||||||
final HttpResponseStatus status = new HttpResponseStatus(101, "Switching Protocols");
|
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||||
|
|
||||||
if (!response.getStatus().equals(status)) {
|
if (!response.getStatus().equals(status)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
||||||
@ -177,15 +181,14 @@ public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT);
|
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT);
|
||||||
if (accept == null || !accept.equals(this.expectedChallengeResponseString)) {
|
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
|
||||||
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
||||||
this.expectedChallengeResponseString));
|
expectedChallengeResponseString));
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
||||||
new WebSocket08FrameDecoder(false, this.allowExtensions));
|
new WebSocket08FrameDecoder(false, allowExtensions));
|
||||||
|
|
||||||
this.setOpenningHandshakeCompleted(true);
|
setHandshakeComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -19,6 +19,7 @@ import java.net.URI;
|
|||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
|
import org.jboss.netty.handler.codec.http.DefaultHttpRequest;
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
@ -35,9 +36,9 @@ import org.jboss.netty.util.CharsetUtil;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Performs client side opening and closing handshakes for <a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a>.
|
* Performs client side opening and closing handshakes for web socket specification version <a
|
||||||
* This was originally <a href="http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17"
|
* href="http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17" >draft-ietf-hybi-thewebsocketprotocol-
|
||||||
* >draft-ietf-hybi-thewebsocketprotocol- 17</a>
|
* 17</a>
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
||||||
@ -50,7 +51,7 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
|||||||
|
|
||||||
private static final String protocol = null;
|
private static final String protocol = null;
|
||||||
|
|
||||||
private boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location and version to initiate
|
* Constructor specifying the destination web socket location and version to initiate
|
||||||
@ -60,16 +61,16 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
|||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param version
|
* @param version
|
||||||
* Version of web socket specification to use to connect to the server
|
* Version of web socket specification to use to connect to the server
|
||||||
* @param subProtocol
|
* @param subprotocol
|
||||||
* Sub protocol request sent to the server.
|
* Sub protocol request sent to the server.
|
||||||
* @param allowExtensions
|
* @param allowExtensions
|
||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* Allow extensions to be used in the reserved bits of the web socket frame
|
||||||
* @param customHeaders
|
* @param customHeaders
|
||||||
* Map of custom headers to add to the client request
|
* Map of custom headers to add to the client request
|
||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subProtocol,
|
public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
||||||
boolean allowExtensions, Map<String, String> customHeaders) {
|
boolean allowExtensions, Map<String, String> customHeaders) {
|
||||||
super(webSocketURL, version, subProtocol, customHeaders);
|
super(webSocketURL, version, subprotocol, customHeaders);
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -94,25 +95,25 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
|||||||
* Channel into which we can write our request
|
* Channel into which we can write our request
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performOpeningHandshake(Channel channel) {
|
public ChannelFuture handshake(Channel channel) {
|
||||||
// Get path
|
// Get path
|
||||||
URI wsURL = this.getWebSocketURL();
|
URI wsURL = getWebSocketUrl();
|
||||||
String path = wsURL.getPath();
|
String path = wsURL.getPath();
|
||||||
if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) {
|
if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) {
|
||||||
path = wsURL.getPath() + "?" + wsURL.getQuery();
|
path = wsURL.getPath() + "?" + wsURL.getQuery();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get 16 bit nonce and base 64 encode it
|
// Get 16 bit nonce and base 64 encode it
|
||||||
byte[] nonce = createRandomBytes(16);
|
byte[] nonce = WebSocketUtil.randomBytes(16);
|
||||||
String key = base64Encode(nonce);
|
String key = WebSocketUtil.base64(nonce);
|
||||||
|
|
||||||
String acceptSeed = key + MAGIC_GUID;
|
String acceptSeed = key + MAGIC_GUID;
|
||||||
byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
|
byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
|
||||||
this.expectedChallengeResponseString = base64Encode(sha1);
|
expectedChallengeResponseString = WebSocketUtil.base64(sha1);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("WS Version 13 Client Handshake key: %s. Expected response: %s.", key,
|
logger.debug(String.format("WS Version 13 Client Handshake key: %s. Expected response: %s.", key,
|
||||||
this.expectedChallengeResponseString));
|
expectedChallengeResponseString));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Format request
|
// Format request
|
||||||
@ -126,15 +127,18 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
|||||||
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol);
|
request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol);
|
||||||
}
|
}
|
||||||
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "13");
|
request.addHeader(Names.SEC_WEBSOCKET_VERSION, "13");
|
||||||
|
|
||||||
if (customHeaders != null) {
|
if (customHeaders != null) {
|
||||||
for (String header : customHeaders.keySet()) {
|
for (String header : customHeaders.keySet()) {
|
||||||
request.addHeader(header, customHeaders.get(header));
|
request.addHeader(header, customHeaders.get(header));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.write(request);
|
ChannelFuture future = channel.write(request);
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket13FrameEncoder(true));
|
channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket13FrameEncoder(true));
|
||||||
|
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,8 +161,8 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
|||||||
* @throws WebSocketHandshakeException
|
* @throws WebSocketHandshakeException
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException {
|
public void finishHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException {
|
||||||
final HttpResponseStatus status = new HttpResponseStatus(101, "Switching Protocols");
|
final HttpResponseStatus status = HttpResponseStatus.SWITCHING_PROTOCOLS;
|
||||||
|
|
||||||
if (!response.getStatus().equals(status)) {
|
if (!response.getStatus().equals(status)) {
|
||||||
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus());
|
||||||
@ -177,15 +181,14 @@ public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT);
|
String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT);
|
||||||
if (accept == null || !accept.equals(this.expectedChallengeResponseString)) {
|
if (accept == null || !accept.equals(expectedChallengeResponseString)) {
|
||||||
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept,
|
||||||
this.expectedChallengeResponseString));
|
expectedChallengeResponseString));
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder",
|
||||||
new WebSocket13FrameDecoder(false, this.allowExtensions));
|
new WebSocket13FrameDecoder(false, allowExtensions));
|
||||||
|
|
||||||
this.setOpenningHandshakeCompleted(true);
|
setHandshakeComplete();
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -31,27 +31,27 @@ public class WebSocketClientHandshakerFactory {
|
|||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param version
|
* @param version
|
||||||
* Version of web socket specification to use to connect to the server
|
* Version of web socket specification to use to connect to the server
|
||||||
* @param subProtocol
|
* @param subprotocol
|
||||||
* Sub protocol request sent to the server. Null if no sub-protocol support is required.
|
* Sub protocol request sent to the server. Null if no sub-protocol support is required.
|
||||||
* @param allowExtensions
|
* @param allowExtensions
|
||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* Allow extensions to be used in the reserved bits of the web socket frame
|
||||||
* @param customHeaders
|
* @param customHeaders
|
||||||
* custom HTTP headers
|
* Custom HTTP headers to send during the handshake
|
||||||
* @throws WebSocketHandshakeException
|
* @throws WebSocketHandshakeException
|
||||||
*/
|
*/
|
||||||
public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketVersion version, String subProtocol,
|
public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketVersion version, String subprotocol,
|
||||||
boolean allowExtensions, Map<String, String> customHeaders) throws WebSocketHandshakeException {
|
boolean allowExtensions, Map<String, String> customHeaders) throws WebSocketHandshakeException {
|
||||||
if (version == WebSocketVersion.V13) {
|
if (version == WebSocketVersion.V13) {
|
||||||
return new WebSocketClientHandshaker13(webSocketURL, version, subProtocol, allowExtensions, customHeaders);
|
return new WebSocketClientHandshaker13(webSocketURL, version, subprotocol, allowExtensions, customHeaders);
|
||||||
}
|
}
|
||||||
if (version == WebSocketVersion.V08) {
|
if (version == WebSocketVersion.V08) {
|
||||||
return new WebSocketClientHandshaker08(webSocketURL, version, subProtocol, allowExtensions, customHeaders);
|
return new WebSocketClientHandshaker08(webSocketURL, version, subprotocol, allowExtensions, customHeaders);
|
||||||
}
|
}
|
||||||
if (version == WebSocketVersion.V00) {
|
if (version == WebSocketVersion.V00) {
|
||||||
return new WebSocketClientHandshaker00(webSocketURL, version, subProtocol, customHeaders);
|
return new WebSocketClientHandshaker00(webSocketURL, version, subprotocol, customHeaders);
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new WebSocketHandshakeException("Protocol " + version.toString() + " not supported.");
|
throw new WebSocketHandshakeException("Protocol version " + version.toString() + " not supported.");
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ package org.jboss.netty.handler.codec.http.websocketx;
|
|||||||
/**
|
/**
|
||||||
* Exception during handshaking process
|
* Exception during handshaking process
|
||||||
*/
|
*/
|
||||||
public class WebSocketHandshakeException extends Exception {
|
public class WebSocketHandshakeException extends RuntimeException {
|
||||||
|
|
||||||
private static final long serialVersionUID = 1L;
|
private static final long serialVersionUID = 1L;
|
||||||
|
|
||||||
|
@ -17,65 +17,70 @@ package org.jboss.netty.handler.codec.http.websocketx;
|
|||||||
|
|
||||||
import java.security.MessageDigest;
|
import java.security.MessageDigest;
|
||||||
import java.security.NoSuchAlgorithmException;
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.util.LinkedHashSet;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
import org.jboss.netty.buffer.ChannelBuffer;
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
import org.jboss.netty.buffer.ChannelBuffers;
|
import org.jboss.netty.buffer.ChannelBuffers;
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
import org.jboss.netty.handler.codec.base64.Base64;
|
import org.jboss.netty.handler.codec.base64.Base64;
|
||||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
import org.jboss.netty.util.CharsetUtil;
|
import org.jboss.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for server side web socket opening and closing handshakes
|
||||||
|
*/
|
||||||
public abstract class WebSocketServerHandshaker {
|
public abstract class WebSocketServerHandshaker {
|
||||||
|
|
||||||
private String webSocketURL;
|
private final String webSocketUrl;
|
||||||
|
|
||||||
private String subProtocols;
|
private final String[] subprotocols;
|
||||||
|
|
||||||
private String[] subProtocolsArray;
|
private final WebSocketVersion version;
|
||||||
|
|
||||||
private WebSocketVersion version = WebSocketVersion.UNKNOWN;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location
|
* Constructor specifying the destination web socket location
|
||||||
*
|
*
|
||||||
* @param webSocketURL
|
* @param version
|
||||||
|
* the protocol version
|
||||||
|
* @param webSocketUrl
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param subProtocols
|
* @param subprotocols
|
||||||
* CSV of supported protocols. Null if sub protocols not supported.
|
* CSV of supported protocols. Null if sub protocols not supported.
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshaker(String webSocketURL, String subProtocols) {
|
protected WebSocketServerHandshaker(
|
||||||
this.webSocketURL = webSocketURL;
|
WebSocketVersion version, String webSocketUrl, String subprotocols) {
|
||||||
this.subProtocols = subProtocols;
|
this.version = version;
|
||||||
|
this.webSocketUrl = webSocketUrl;
|
||||||
if (this.subProtocols != null) {
|
if (subprotocols != null) {
|
||||||
this.subProtocolsArray = subProtocols.split(",");
|
String[] subprotocolArray = subprotocols.split(",");
|
||||||
for (int i = 0; i < this.subProtocolsArray.length; i++) {
|
for (int i = 0; i < subprotocolArray.length; i++) {
|
||||||
this.subProtocolsArray[i] = this.subProtocolsArray[i].trim();
|
subprotocolArray[i] = subprotocolArray[i].trim();
|
||||||
}
|
}
|
||||||
|
this.subprotocols = subprotocolArray;
|
||||||
|
} else {
|
||||||
|
this.subprotocols = new String[0];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the URL of the web socket
|
* Returns the URL of the web socket
|
||||||
*/
|
*/
|
||||||
public String getWebSocketURL() {
|
public String getWebSocketUrl() {
|
||||||
return webSocketURL;
|
return webSocketUrl;
|
||||||
}
|
|
||||||
|
|
||||||
public void setWebSocketURL(String webSocketURL) {
|
|
||||||
this.webSocketURL = webSocketURL;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the CSV of supported sub protocols
|
* Returns the CSV of supported sub protocols
|
||||||
*/
|
*/
|
||||||
public String getSubProtocols() {
|
public Set<String> getSubprotocols() {
|
||||||
return subProtocols;
|
Set<String> ret = new LinkedHashSet<String>();
|
||||||
|
for (String p: this.subprotocols) {
|
||||||
|
ret.add(p);
|
||||||
}
|
}
|
||||||
|
return ret;
|
||||||
public void setSubProtocols(String subProtocols) {
|
|
||||||
this.subProtocols = subProtocols;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -85,10 +90,6 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
return version;
|
return version;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setVersion(WebSocketVersion version) {
|
|
||||||
this.version = version;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the opening handshake
|
* Performs the opening handshake
|
||||||
*
|
*
|
||||||
@ -96,9 +97,8 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* Channel
|
* Channel
|
||||||
* @param req
|
* @param req
|
||||||
* HTTP Request
|
* HTTP Request
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
*/
|
||||||
public abstract void performOpeningHandshake(Channel channel, HttpRequest req);
|
public abstract ChannelFuture handshake(Channel channel, HttpRequest req);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Performs the closing handshake
|
* Performs the closing handshake
|
||||||
@ -108,71 +108,27 @@ public abstract class WebSocketServerHandshaker {
|
|||||||
* @param frame
|
* @param frame
|
||||||
* Closing Frame that was received
|
* Closing Frame that was received
|
||||||
*/
|
*/
|
||||||
public abstract void performClosingHandshake(Channel channel, CloseWebSocketFrame frame);
|
public abstract ChannelFuture close(Channel channel, CloseWebSocketFrame frame);
|
||||||
|
|
||||||
/**
|
|
||||||
* Performs an MD5 hash
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* Data to hash
|
|
||||||
* @return Hashed data
|
|
||||||
*/
|
|
||||||
protected byte[] md5(byte[] bytes) {
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("MD5");
|
|
||||||
return md.digest(bytes);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new InternalError("MD5 not supported on this platform");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* SHA-1 hashing. Instance this we think it is not thread safe
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* byte to hash
|
|
||||||
* @return hashed
|
|
||||||
*/
|
|
||||||
protected byte[] sha1(byte[] bytes) {
|
|
||||||
try {
|
|
||||||
MessageDigest md = MessageDigest.getInstance("SHA1");
|
|
||||||
return md.digest(bytes);
|
|
||||||
} catch (NoSuchAlgorithmException e) {
|
|
||||||
throw new InternalError("SHA-1 not supported on this platform");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Base 64 encoding
|
|
||||||
*
|
|
||||||
* @param bytes
|
|
||||||
* Bytes to encode
|
|
||||||
* @return encoded string
|
|
||||||
*/
|
|
||||||
protected String base64Encode(byte[] bytes) {
|
|
||||||
ChannelBuffer hashed = ChannelBuffers.wrappedBuffer(bytes);
|
|
||||||
return Base64.encode(hashed).toString(CharsetUtil.UTF_8);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Selects the first matching supported sub protocol
|
* Selects the first matching supported sub protocol
|
||||||
*
|
*
|
||||||
* @param requestedSubProtocol
|
* @param requestedSubprotocols
|
||||||
* CSV of protocols to be supported. e.g. "chat, superchat"
|
* CSV of protocols to be supported. e.g. "chat, superchat"
|
||||||
* @return First matching supported sub protocol. Null if not found.
|
* @return First matching supported sub protocol. Null if not found.
|
||||||
*/
|
*/
|
||||||
protected String selectSubProtocol(String requestedSubProtocol) {
|
protected String selectSubprotocol(String requestedSubprotocols) {
|
||||||
if (requestedSubProtocol == null || this.subProtocolsArray == null) {
|
if (requestedSubprotocols == null || subprotocols.length == 0) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
String[] requesteSubProtocolsArray = requestedSubProtocol.split(",");
|
String[] requesteSubprotocolArray = requestedSubprotocols.split(",");
|
||||||
for (int i = 0; i < requesteSubProtocolsArray.length; i++) {
|
for (String p: requesteSubprotocolArray) {
|
||||||
String requesteSubProtocol = requesteSubProtocolsArray[i].trim();
|
String requestedSubprotocol = p.trim();
|
||||||
|
|
||||||
for (String supportedSubProtocol : this.subProtocolsArray) {
|
for (String supportedSubprotocol: subprotocols) {
|
||||||
if (requesteSubProtocol.equals(supportedSubProtocol)) {
|
if (requestedSubprotocol.equals(supportedSubprotocol)) {
|
||||||
return requesteSubProtocol;
|
return requestedSubprotocol;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,34 +15,24 @@
|
|||||||
*/
|
*/
|
||||||
package org.jboss.netty.handler.codec.http.websocketx;
|
package org.jboss.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.CONNECTION;
|
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.ORIGIN;
|
import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_KEY1;
|
import static org.jboss.netty.handler.codec.http.HttpVersion.*;
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_KEY2;
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_LOCATION;
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_ORIGIN;
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.SEC_WEBSOCKET_PROTOCOL;
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_LOCATION;
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_ORIGIN;
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.WEBSOCKET_PROTOCOL;
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET;
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import org.jboss.netty.buffer.ChannelBuffer;
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
import org.jboss.netty.buffer.ChannelBuffers;
|
import org.jboss.netty.buffer.ChannelBuffers;
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
import org.jboss.netty.channel.ChannelPipeline;
|
import org.jboss.netty.channel.ChannelPipeline;
|
||||||
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
|
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
|
||||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
|
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
|
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Values;
|
|
||||||
import org.jboss.netty.logging.InternalLogger;
|
import org.jboss.netty.logging.InternalLogger;
|
||||||
import org.jboss.netty.logging.InternalLoggerFactory;
|
import org.jboss.netty.logging.InternalLoggerFactory;
|
||||||
|
|
||||||
@ -66,11 +56,11 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param subProtocols
|
* @param subprotocols
|
||||||
* CSV of supported protocols
|
* CSV of supported protocols
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshaker00(String webSocketURL, String subProtocols) {
|
public WebSocketServerHandshaker00(String webSocketURL, String subprotocols) {
|
||||||
super(webSocketURL, subProtocols);
|
super(WebSocketVersion.V00, webSocketURL, subprotocols);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -91,6 +81,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
* Connection: Upgrade
|
* Connection: Upgrade
|
||||||
* Host: example.com
|
* Host: example.com
|
||||||
* Origin: http://example.com
|
* Origin: http://example.com
|
||||||
|
* Sec-WebSocket-Protocol: chat, sample
|
||||||
* Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5
|
* Sec-WebSocket-Key1: 4 @1 46546xW%0l 1 5
|
||||||
* Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
|
* Sec-WebSocket-Key2: 12998 5 Y3 1 .P00
|
||||||
*
|
*
|
||||||
@ -116,20 +107,18 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
* Channel
|
* Channel
|
||||||
* @param req
|
* @param req
|
||||||
* HTTP request
|
* HTTP request
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performOpeningHandshake(Channel channel, HttpRequest req) {
|
public ChannelFuture handshake(Channel channel, HttpRequest req) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s web socket spec version 00 handshake", channel.getId()));
|
logger.debug(String.format("Channel %s WS Version 00 server handshake", channel.getId()));
|
||||||
}
|
}
|
||||||
this.setVersion(WebSocketVersion.V00);
|
|
||||||
|
|
||||||
// Serve the WebSocket handshake request.
|
// Serve the WebSocket handshake request.
|
||||||
if (!Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION))
|
if (!Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION))
|
||||||
|| !WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) {
|
|| !WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) {
|
||||||
return;
|
throw new WebSocketHandshakeException("not a WebSocket handshake request: missing upgrade");
|
||||||
}
|
}
|
||||||
|
|
||||||
// Hixie 75 does not contain these headers while Hixie 76 does
|
// Hixie 75 does not contain these headers while Hixie 76 does
|
||||||
@ -145,10 +134,10 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
if (isHixie76) {
|
if (isHixie76) {
|
||||||
// New handshake method with a challenge:
|
// New handshake method with a challenge:
|
||||||
res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
|
res.addHeader(SEC_WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
|
||||||
res.addHeader(SEC_WEBSOCKET_LOCATION, this.getWebSocketURL());
|
res.addHeader(SEC_WEBSOCKET_LOCATION, getWebSocketUrl());
|
||||||
String protocol = req.getHeader(SEC_WEBSOCKET_PROTOCOL);
|
String protocol = req.getHeader(SEC_WEBSOCKET_PROTOCOL);
|
||||||
if (protocol != null) {
|
if (protocol != null) {
|
||||||
res.addHeader(SEC_WEBSOCKET_PROTOCOL, selectSubProtocol(protocol));
|
res.addHeader(SEC_WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the answer of the challenge.
|
// Calculate the answer of the challenge.
|
||||||
@ -161,15 +150,15 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
input.writeInt(a);
|
input.writeInt(a);
|
||||||
input.writeInt(b);
|
input.writeInt(b);
|
||||||
input.writeLong(c);
|
input.writeLong(c);
|
||||||
ChannelBuffer output = ChannelBuffers.wrappedBuffer(this.md5(input.array()));
|
ChannelBuffer output = ChannelBuffers.wrappedBuffer(WebSocketUtil.md5(input.array()));
|
||||||
res.setContent(output);
|
res.setContent(output);
|
||||||
} else {
|
} else {
|
||||||
// Old Hixie 75 handshake method with no challenge:
|
// Old Hixie 75 handshake method with no challenge:
|
||||||
res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
|
res.addHeader(WEBSOCKET_ORIGIN, req.getHeader(ORIGIN));
|
||||||
res.addHeader(WEBSOCKET_LOCATION, this.getWebSocketURL());
|
res.addHeader(WEBSOCKET_LOCATION, getWebSocketUrl());
|
||||||
String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
|
String protocol = req.getHeader(WEBSOCKET_PROTOCOL);
|
||||||
if (protocol != null) {
|
if (protocol != null) {
|
||||||
res.addHeader(WEBSOCKET_PROTOCOL, selectSubProtocol(protocol));
|
res.addHeader(WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,9 +167,11 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
p.remove(HttpChunkAggregator.class);
|
p.remove(HttpChunkAggregator.class);
|
||||||
p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket00FrameDecoder());
|
p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket00FrameDecoder());
|
||||||
|
|
||||||
channel.write(res);
|
ChannelFuture future = channel.write(res);
|
||||||
|
|
||||||
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket00FrameEncoder());
|
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket00FrameEncoder());
|
||||||
|
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -192,7 +183,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker {
|
|||||||
* Web Socket frame that was received
|
* Web Socket frame that was received
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performClosingHandshake(Channel channel, CloseWebSocketFrame frame) {
|
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame) {
|
||||||
channel.write(frame);
|
return channel.write(frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,10 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.jboss.netty.handler.codec.http.websocketx;
|
package org.jboss.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET;
|
import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.*;
|
||||||
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
import static org.jboss.netty.handler.codec.http.HttpVersion.*;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
import org.jboss.netty.channel.ChannelFuture;
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
@ -26,12 +24,12 @@ import org.jboss.netty.channel.ChannelFutureListener;
|
|||||||
import org.jboss.netty.channel.ChannelPipeline;
|
import org.jboss.netty.channel.ChannelPipeline;
|
||||||
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
|
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
|
||||||
|
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
||||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
|
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
|
import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
import org.jboss.netty.handler.codec.http.HttpResponseStatus;
|
||||||
import org.jboss.netty.handler.codec.http.HttpHeaders.Names;
|
|
||||||
import org.jboss.netty.logging.InternalLogger;
|
import org.jboss.netty.logging.InternalLogger;
|
||||||
import org.jboss.netty.logging.InternalLoggerFactory;
|
import org.jboss.netty.logging.InternalLoggerFactory;
|
||||||
import org.jboss.netty.util.CharsetUtil;
|
import org.jboss.netty.util.CharsetUtil;
|
||||||
@ -49,7 +47,7 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
|||||||
|
|
||||||
public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||||
|
|
||||||
private boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location
|
* Constructor specifying the destination web socket location
|
||||||
@ -57,13 +55,13 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
|||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param subProtocols
|
* @param subprotocols
|
||||||
* CSV of supported protocols
|
* CSV of supported protocols
|
||||||
* @param allowExtensions
|
* @param allowExtensions
|
||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* Allow extensions to be used in the reserved bits of the web socket frame
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshaker08(String webSocketURL, String subProtocols, boolean allowExtensions) {
|
public WebSocketServerHandshaker08(String webSocketURL, String subprotocols, boolean allowExtensions) {
|
||||||
super(webSocketURL, subProtocols);
|
super(WebSocketVersion.V08, webSocketURL, subprotocols);
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,48 +103,46 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
|||||||
* Channel
|
* Channel
|
||||||
* @param req
|
* @param req
|
||||||
* HTTP request
|
* HTTP request
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performOpeningHandshake(Channel channel, HttpRequest req) {
|
public ChannelFuture handshake(Channel channel, HttpRequest req) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s WS version 08 handshake", channel.getId()));
|
logger.debug(String.format("Channel %s WS Version 8 server handshake", channel.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101, "Switching Protocols"));
|
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||||
this.setVersion(WebSocketVersion.V08);
|
|
||||||
|
|
||||||
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY);
|
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
res.setStatus(HttpResponseStatus.BAD_REQUEST);
|
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
String acceptSeed = key + WEBSOCKET_08_ACCEPT_GUID;
|
String acceptSeed = key + WEBSOCKET_08_ACCEPT_GUID;
|
||||||
byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
|
byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
|
||||||
String accept = base64Encode(sha1);
|
String accept = WebSocketUtil.base64(sha1);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("WS Version 08 Server Handshake key: %s. Response: %s.", key, accept));
|
logger.debug(String.format("WS Version 8 Server Handshake key: %s. Response: %s.", key, accept));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setStatus(new HttpResponseStatus(101, "Switching Protocols"));
|
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||||
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||||
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||||
String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
if (protocol != null) {
|
if (protocol != null) {
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, this.selectSubProtocol(protocol));
|
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.write(res);
|
ChannelFuture future = channel.write(res);
|
||||||
|
|
||||||
// Upgrade the connection and send the handshake response.
|
// Upgrade the connection and send the handshake response.
|
||||||
ChannelPipeline p = channel.getPipeline();
|
ChannelPipeline p = channel.getPipeline();
|
||||||
p.remove(HttpChunkAggregator.class);
|
p.remove(HttpChunkAggregator.class);
|
||||||
p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket08FrameDecoder(true, this.allowExtensions));
|
p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket08FrameDecoder(true, allowExtensions));
|
||||||
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket08FrameEncoder(false));
|
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket08FrameEncoder(false));
|
||||||
|
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,9 +154,10 @@ public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker {
|
|||||||
* Web Socket frame that was received
|
* Web Socket frame that was received
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performClosingHandshake(Channel channel, CloseWebSocketFrame frame) {
|
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame) {
|
||||||
ChannelFuture f = channel.write(frame);
|
ChannelFuture f = channel.write(frame);
|
||||||
f.addListener(ChannelFutureListener.CLOSE);
|
f.addListener(ChannelFutureListener.CLOSE);
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -18,14 +18,12 @@ package org.jboss.netty.handler.codec.http.websocketx;
|
|||||||
import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET;
|
import static org.jboss.netty.handler.codec.http.HttpHeaders.Values.WEBSOCKET;
|
||||||
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
import static org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1;
|
||||||
|
|
||||||
import java.security.NoSuchAlgorithmException;
|
|
||||||
|
|
||||||
import org.jboss.netty.channel.Channel;
|
import org.jboss.netty.channel.Channel;
|
||||||
import org.jboss.netty.channel.ChannelFuture;
|
import org.jboss.netty.channel.ChannelFuture;
|
||||||
import org.jboss.netty.channel.ChannelFutureListener;
|
import org.jboss.netty.channel.ChannelFutureListener;
|
||||||
import org.jboss.netty.channel.ChannelPipeline;
|
import org.jboss.netty.channel.ChannelPipeline;
|
||||||
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
|
||||||
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
|
import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
|
||||||
|
import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
|
||||||
import org.jboss.netty.handler.codec.http.HttpRequest;
|
import org.jboss.netty.handler.codec.http.HttpRequest;
|
||||||
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
|
import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
|
||||||
import org.jboss.netty.handler.codec.http.HttpResponse;
|
import org.jboss.netty.handler.codec.http.HttpResponse;
|
||||||
@ -38,9 +36,10 @@ import org.jboss.netty.util.CharsetUtil;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Performs server side opening and closing handshakes for <a href="http://tools.ietf.org/html/rfc6455">RFC 6455</a>.
|
* Performs server side opening and closing handshakes for <a href="http://tools.ietf.org/html/rfc6455 ">RFC 6455</a>
|
||||||
* This was originally <a href="http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17"
|
* (originally web socket specification version <a
|
||||||
* >draft-ietf-hybi-thewebsocketprotocol- 17</a>
|
* href="http://tools.ietf.org/html/draft-ietf-hybi-thewebsocketprotocol-17" >draft-ietf-hybi-thewebsocketprotocol-
|
||||||
|
* 17</a>).
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
||||||
@ -49,7 +48,7 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
|||||||
|
|
||||||
public static final String WEBSOCKET_13_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
public static final String WEBSOCKET_13_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11";
|
||||||
|
|
||||||
private boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location
|
* Constructor specifying the destination web socket location
|
||||||
@ -57,13 +56,13 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
|||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param subProtocols
|
* @param subprotocols
|
||||||
* CSV of supported protocols
|
* CSV of supported protocols
|
||||||
* @param allowExtensions
|
* @param allowExtensions
|
||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* Allow extensions to be used in the reserved bits of the web socket frame
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshaker13(String webSocketURL, String subProtocols, boolean allowExtensions) {
|
public WebSocketServerHandshaker13(String webSocketURL, String subprotocols, boolean allowExtensions) {
|
||||||
super(webSocketURL, subProtocols);
|
super(WebSocketVersion.V13, webSocketURL, subprotocols);
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -105,48 +104,46 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
|||||||
* Channel
|
* Channel
|
||||||
* @param req
|
* @param req
|
||||||
* HTTP request
|
* HTTP request
|
||||||
* @throws NoSuchAlgorithmException
|
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performOpeningHandshake(Channel channel, HttpRequest req) {
|
public ChannelFuture handshake(Channel channel, HttpRequest req) {
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("Channel %s WS version 13 handshake", channel.getId()));
|
logger.debug(String.format("Channel %s WS Version 13 server handshake", channel.getId()));
|
||||||
}
|
}
|
||||||
|
|
||||||
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101, "Switching Protocols"));
|
HttpResponse res = new DefaultHttpResponse(HTTP_1_1, HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||||
this.setVersion(WebSocketVersion.V13);
|
|
||||||
|
|
||||||
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY);
|
String key = req.getHeader(Names.SEC_WEBSOCKET_KEY);
|
||||||
if (key == null) {
|
if (key == null) {
|
||||||
res.setStatus(HttpResponseStatus.BAD_REQUEST);
|
throw new WebSocketHandshakeException("not a WebSocket request: missing key");
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
String acceptSeed = key + WEBSOCKET_13_ACCEPT_GUID;
|
String acceptSeed = key + WEBSOCKET_13_ACCEPT_GUID;
|
||||||
byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
|
byte[] sha1 = WebSocketUtil.sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII));
|
||||||
String accept = base64Encode(sha1);
|
String accept = WebSocketUtil.base64(sha1);
|
||||||
|
|
||||||
if (logger.isDebugEnabled()) {
|
if (logger.isDebugEnabled()) {
|
||||||
logger.debug(String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept));
|
logger.debug(String.format("WS Version 13 Server Handshake key: %s. Response: %s.", key, accept));
|
||||||
}
|
}
|
||||||
|
|
||||||
res.setStatus(new HttpResponseStatus(101, "Switching Protocols"));
|
res.setStatus(HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||||
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase());
|
||||||
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
res.addHeader(Names.CONNECTION, Names.UPGRADE);
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept);
|
||||||
String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL);
|
||||||
if (protocol != null) {
|
if (protocol != null) {
|
||||||
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, this.selectSubProtocol(protocol));
|
res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, selectSubprotocol(protocol));
|
||||||
}
|
}
|
||||||
|
|
||||||
channel.write(res);
|
ChannelFuture future = channel.write(res);
|
||||||
|
|
||||||
// Upgrade the connection and send the handshake response.
|
// Upgrade the connection and send the handshake response.
|
||||||
ChannelPipeline p = channel.getPipeline();
|
ChannelPipeline p = channel.getPipeline();
|
||||||
p.remove(HttpChunkAggregator.class);
|
p.remove(HttpChunkAggregator.class);
|
||||||
p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket13FrameDecoder(true, this.allowExtensions));
|
p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket13FrameDecoder(true, allowExtensions));
|
||||||
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false));
|
p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false));
|
||||||
|
|
||||||
|
return future;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -158,9 +155,10 @@ public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker {
|
|||||||
* Web Socket frame that was received
|
* Web Socket frame that was received
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
public void performClosingHandshake(Channel channel, CloseWebSocketFrame frame) {
|
public ChannelFuture close(Channel channel, CloseWebSocketFrame frame) {
|
||||||
ChannelFuture f = channel.write(frame);
|
ChannelFuture f = channel.write(frame);
|
||||||
f.addListener(ChannelFutureListener.CLOSE);
|
f.addListener(ChannelFutureListener.CLOSE);
|
||||||
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -30,9 +30,9 @@ public class WebSocketServerHandshakerFactory {
|
|||||||
|
|
||||||
private final String webSocketURL;
|
private final String webSocketURL;
|
||||||
|
|
||||||
private final String subProtocols;
|
private final String subprotocols;
|
||||||
|
|
||||||
private boolean allowExtensions;
|
private final boolean allowExtensions;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor specifying the destination web socket location
|
* Constructor specifying the destination web socket location
|
||||||
@ -40,14 +40,14 @@ public class WebSocketServerHandshakerFactory {
|
|||||||
* @param webSocketURL
|
* @param webSocketURL
|
||||||
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
* URL for web socket communications. e.g "ws://myhost.com/mypath". Subsequent web socket frames will be
|
||||||
* sent to this URL.
|
* sent to this URL.
|
||||||
* @param subProtocols
|
* @param subprotocols
|
||||||
* CSV of supported protocols. Null if sub protocols not supported.
|
* CSV of supported protocols. Null if sub protocols not supported.
|
||||||
* @param allowExtensions
|
* @param allowExtensions
|
||||||
* Allow extensions to be used in the reserved bits of the web socket frame
|
* Allow extensions to be used in the reserved bits of the web socket frame
|
||||||
*/
|
*/
|
||||||
public WebSocketServerHandshakerFactory(String webSocketURL, String subProtocols, boolean allowExtensions) {
|
public WebSocketServerHandshakerFactory(String webSocketURL, String subprotocols, boolean allowExtensions) {
|
||||||
this.webSocketURL = webSocketURL;
|
this.webSocketURL = webSocketURL;
|
||||||
this.subProtocols = subProtocols;
|
this.subprotocols = subprotocols;
|
||||||
this.allowExtensions = allowExtensions;
|
this.allowExtensions = allowExtensions;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,19 +62,17 @@ public class WebSocketServerHandshakerFactory {
|
|||||||
String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION);
|
String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION);
|
||||||
if (version != null) {
|
if (version != null) {
|
||||||
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
|
if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) {
|
||||||
// Version 13 of the wire protocol - assume version 17 of the
|
// Version 13 of the wire protocol - RFC 6455 (version 17 of the draft hybi specification).
|
||||||
// specification.
|
return new WebSocketServerHandshaker13(webSocketURL, subprotocols, allowExtensions);
|
||||||
return new WebSocketServerHandshaker13(webSocketURL, subProtocols, this.allowExtensions);
|
|
||||||
} else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) {
|
} else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) {
|
||||||
// Version 8 of the wire protocol - assume version 10 of the
|
// Version 8 of the wire protocol - version 10 of the draft hybi specification.
|
||||||
// specification.
|
return new WebSocketServerHandshaker08(webSocketURL, subprotocols, allowExtensions);
|
||||||
return new WebSocketServerHandshaker08(webSocketURL, subProtocols, this.allowExtensions);
|
|
||||||
} else {
|
} else {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Assume version 00 where version header was not specified
|
// Assume version 00 where version header was not specified
|
||||||
return new WebSocketServerHandshaker00(webSocketURL, subProtocols);
|
return new WebSocketServerHandshaker00(webSocketURL, subprotocols);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -85,8 +83,9 @@ public class WebSocketServerHandshakerFactory {
|
|||||||
* Channel
|
* Channel
|
||||||
*/
|
*/
|
||||||
public void sendUnsupportedWebSocketVersionResponse(Channel channel) {
|
public void sendUnsupportedWebSocketVersionResponse(Channel channel) {
|
||||||
HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, new HttpResponseStatus(101,
|
HttpResponse res = new DefaultHttpResponse(
|
||||||
"Switching Protocols"));
|
HttpVersion.HTTP_1_1,
|
||||||
|
HttpResponseStatus.SWITCHING_PROTOCOLS);
|
||||||
res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED);
|
res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED);
|
||||||
res.setHeader(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
|
res.setHeader(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue());
|
||||||
channel.write(res);
|
channel.write(res);
|
||||||
|
@ -0,0 +1,109 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2011 The Netty Project
|
||||||
|
*
|
||||||
|
* The Netty Project 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 org.jboss.netty.handler.codec.http.websocketx;
|
||||||
|
|
||||||
|
import java.security.MessageDigest;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffer;
|
||||||
|
import org.jboss.netty.buffer.ChannelBuffers;
|
||||||
|
import org.jboss.netty.handler.codec.base64.Base64;
|
||||||
|
import org.jboss.netty.util.CharsetUtil;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* TODO Document me.
|
||||||
|
*/
|
||||||
|
final class WebSocketUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an MD5 hash
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* Data to hash
|
||||||
|
* @return Hashed data
|
||||||
|
*/
|
||||||
|
static byte[] md5(byte[] bytes) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("MD5");
|
||||||
|
return md.digest(bytes);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new InternalError("MD5 not supported on this platform");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs an SHA-1 hash
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* Data to hash
|
||||||
|
* @return Hashed data
|
||||||
|
*/
|
||||||
|
static byte[] sha1(byte[] bytes) {
|
||||||
|
try {
|
||||||
|
MessageDigest md = MessageDigest.getInstance("SHA1");
|
||||||
|
return md.digest(bytes);
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new InternalError("SHA-1 not supported on this platform");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base 64 encoding
|
||||||
|
*
|
||||||
|
* @param bytes
|
||||||
|
* Bytes to encode
|
||||||
|
* @return encoded string
|
||||||
|
*/
|
||||||
|
static String base64(byte[] bytes) {
|
||||||
|
ChannelBuffer hashed = ChannelBuffers.wrappedBuffer(bytes);
|
||||||
|
return Base64.encode(hashed).toString(CharsetUtil.UTF_8);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates some random bytes
|
||||||
|
*
|
||||||
|
* @param size
|
||||||
|
* Number of random bytes to create
|
||||||
|
* @return random bytes
|
||||||
|
*/
|
||||||
|
static byte[] randomBytes(int size) {
|
||||||
|
byte[] bytes = new byte[size];
|
||||||
|
|
||||||
|
for (int i = 0; i < size; i++) {
|
||||||
|
bytes[i] = (byte) randomNumber(0, 255);
|
||||||
|
}
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generates a random number
|
||||||
|
*
|
||||||
|
* @param min
|
||||||
|
* Minimum value
|
||||||
|
* @param max
|
||||||
|
* Maximum value
|
||||||
|
* @return Random number
|
||||||
|
*/
|
||||||
|
static int randomNumber(int min, int max) {
|
||||||
|
return (int) (Math.random() * max + min);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private WebSocketUtil() {
|
||||||
|
// Unused
|
||||||
|
}
|
||||||
|
}
|
@ -17,7 +17,11 @@ package org.jboss.netty.handler.codec.http.websocketx;
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* <p>
|
* <p>
|
||||||
* Version of the web socket wire protocol.
|
* Versions of the web socket specification.
|
||||||
|
* </p>
|
||||||
|
* <p>
|
||||||
|
* A specification is tied to one wire protocol version but a protocol version may have use by more than 1 version of
|
||||||
|
* the specification.
|
||||||
* </p>
|
* </p>
|
||||||
*/
|
*/
|
||||||
public enum WebSocketVersion {
|
public enum WebSocketVersion {
|
||||||
@ -42,6 +46,9 @@ public enum WebSocketVersion {
|
|||||||
*/
|
*/
|
||||||
V13;
|
V13;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return Value for HTTP Header 'Sec-WebSocket-Version'
|
||||||
|
*/
|
||||||
public String toHttpHeaderValue() {
|
public String toHttpHeaderValue() {
|
||||||
if (this == V00) {
|
if (this == V00) {
|
||||||
return "0";
|
return "0";
|
||||||
@ -50,6 +57,6 @@ public enum WebSocketVersion {
|
|||||||
} else if (this == V13) {
|
} else if (this == V13) {
|
||||||
return "13";
|
return "13";
|
||||||
}
|
}
|
||||||
throw new IllegalArgumentException(this.toString() + " cannot be converted to a HttpHeaderValue.");
|
throw new IllegalStateException("Unknown web socket version: " + this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -27,11 +27,6 @@
|
|||||||
* </ul>
|
* </ul>
|
||||||
* </p>
|
* </p>
|
||||||
* <p>
|
* <p>
|
||||||
* In the future, as the specification develops, more versions will be supported.
|
|
||||||
* This contrasts the <tt>org.jboss.netty.handler.codec.http.websocket</tt> package which only
|
|
||||||
* supports draft-ietf-hybi-thewebsocketprotocol-00.
|
|
||||||
* </p>
|
|
||||||
* <p>
|
|
||||||
* For the detailed instruction on adding add Web Socket support to your HTTP
|
* For the detailed instruction on adding add Web Socket support to your HTTP
|
||||||
* server, take a look into the <tt>WebSocketServerX</tt> example located in the
|
* server, take a look into the <tt>WebSocketServerX</tt> example located in the
|
||||||
* {@code org.jboss.netty.example.http.websocket} package.
|
* {@code org.jboss.netty.example.http.websocket} package.
|
||||||
|
Loading…
x
Reference in New Issue
Block a user