diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/package-info.java b/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/package-info.java index f2041cab7c..ae6ea1d20a 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/package-info.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/autobahn/package-info.java @@ -36,7 +36,7 @@ * *

08. Go to test suite directory: cd testsuite/websockets * - *

09. Edit fuzzing_clinet_spec.json and set the version to 10 or 17. + *

09. Edit fuzzing_clinet_spec.json and set the hybi specification version to 10 or 17 (RFC 6455). * * { * "options": {"failByDrop": false}, diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/client/App.java b/src/main/java/org/jboss/netty/example/http/websocketx/client/App.java index c5171aedc0..785054850a 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/client/App.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/client/App.java @@ -18,6 +18,7 @@ package org.jboss.netty.example.http.websocketx.client; import java.net.URI; import java.util.ArrayList; +import java.util.HashMap; import java.util.logging.ConsoleHandler; import java.util.logging.Level; import java.util.logging.Logger; @@ -28,7 +29,7 @@ import org.jboss.netty.handler.codec.http.websocketx.PingWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.PongWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.TextWebSocketFrame; import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame; -import org.jboss.netty.handler.codec.http.websocketx.WebSocketSpecificationVersion; +import org.jboss.netty.handler.codec.http.websocketx.WebSocketVersion; /** * A HTTP client demo app @@ -55,31 +56,34 @@ public class App { MyCallbackHandler callbackHandler = new MyCallbackHandler(); WebSocketClientFactory factory = new WebSocketClientFactory(); - // Connect with spec version 17. You can change it to V10 or V00. - // If you change it to V00, ping is not supported and remember to change HttpResponseDecoder to - // WebSocketHttpResponseDecoder in the pipeline. - WebSocketClient client = factory.newClient(new URI("ws://localhost:8080/websocket"), - WebSocketSpecificationVersion.V17, callbackHandler); + HashMap customHeaders = new HashMap(); + customHeaders.put("MyHeader", "MyValue"); + + // Connect with V13 (RFC 6455). You can change it to V08 or V00. + // If you change it to V00, ping is not supported and remember to change + // HttpResponseDecoder to WebSocketHttpResponseDecoder in the pipeline. + WebSocketClient client = factory.newClient(new URI("ws://localhost:8080/websocket"), WebSocketVersion.V13, + callbackHandler, customHeaders); // Connect - System.out.println("WebSocket Client connecting"); - client.connect().awaitUninterruptibly(); + System.out.println("WebSocket Client connecting"); + client.connect().awaitUninterruptibly(); Thread.sleep(200); // Send 10 messages and wait for responses - System.out.println("WebSocket Client sending message"); + System.out.println("WebSocket Client sending message"); for (int i = 0; i < 10; i++) { client.send(new TextWebSocketFrame("Message #" + i)); } Thread.sleep(1000); // Ping - System.out.println("WebSocket Client sending ping"); + System.out.println("WebSocket Client sending ping"); client.send(new PingWebSocketFrame(ChannelBuffers.copiedBuffer(new byte[] { 1, 2, 3, 4, 5, 6 }))); Thread.sleep(1000); // Close - System.out.println("WebSocket Client sending close"); + System.out.println("WebSocket Client sending close"); client.send(new CloseWebSocketFrame()); Thread.sleep(1000); diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClient.java b/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClient.java index 79db6c5262..2ebfd91e38 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClient.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClient.java @@ -29,26 +29,26 @@ import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame; */ public interface WebSocketClient { - /** - * Connect to server Host and port is setup by the factory. - * - * @return Connect future. Fires when connected. - */ - ChannelFuture connect(); + /** + * Connect to server Host and port is setup by the factory. + * + * @return Connect future. Fires when connected. + */ + ChannelFuture connect(); - /** - * Disconnect from the server - * - * @return Disconnect future. Fires when disconnected. - */ - ChannelFuture disconnect(); + /** + * Disconnect from the server + * + * @return Disconnect future. Fires when disconnected. + */ + ChannelFuture disconnect(); - /** - * Send data to server - * - * @param frame - * Data for sending - * @return Write future. Will fire when the data is sent. - */ - ChannelFuture send(WebSocketFrame frame); + /** + * Send data to server + * + * @param frame + * Data for sending + * @return Write future. Will fire when the data is sent. + */ + ChannelFuture send(WebSocketFrame frame); } diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClientFactory.java b/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClientFactory.java index 112c10d40a..73184e3566 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClientFactory.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClientFactory.java @@ -29,60 +29,65 @@ import org.jboss.netty.channel.Channels; import org.jboss.netty.channel.socket.nio.NioClientSocketChannelFactory; import org.jboss.netty.handler.codec.http.HttpRequestEncoder; import org.jboss.netty.handler.codec.http.HttpResponseDecoder; -import org.jboss.netty.handler.codec.http.websocketx.WebSocketSpecificationVersion; +import org.jboss.netty.handler.codec.http.websocketx.WebSocketVersion; import java.net.URI; +import java.util.Map; import java.util.concurrent.Executors; /** * Copied from https://github.com/cgbystrom/netty-tools * - * A factory for creating WebSocket clients. The entry point for creating and connecting a client. Can and should be - * used to create multiple instances. + * A factory for creating WebSocket clients. The entry point for creating and + * connecting a client. Can and should be used to create multiple instances. */ public class WebSocketClientFactory { - private final NioClientSocketChannelFactory socketChannelFactory = new NioClientSocketChannelFactory( - Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); + private final NioClientSocketChannelFactory socketChannelFactory = new NioClientSocketChannelFactory( + Executors.newCachedThreadPool(), Executors.newCachedThreadPool()); - /** - * Create a new WebSocket client - * - * @param url - * URL to connect to. - * @param version - * Web Socket version to support - * @param callback - * Callback interface to receive events - * @return A WebSocket client. Call {@link WebSocketClient#connect()} to connect. - */ - public WebSocketClient newClient(final URI url, - final WebSocketSpecificationVersion version, - final WebSocketCallback callback) { - ClientBootstrap bootstrap = new ClientBootstrap(socketChannelFactory); + /** + * Create a new WebSocket client + * + * @param url + * URL to connect to. + * @param version + * Web Socket version to support + * @param callback + * Callback interface to receive events + * @param customHeaders + * Map of custom headers to add to the client request + * @return A WebSocket client. Call {@link WebSocketClient#connect()} to + * connect. + */ + public WebSocketClient newClient(final URI url, final WebSocketVersion version, final WebSocketCallback callback, + final Map customHeaders) { + ClientBootstrap bootstrap = new ClientBootstrap(socketChannelFactory); - String protocol = url.getScheme(); - if (!protocol.equals("ws") && !protocol.equals("wss")) { - throw new IllegalArgumentException("Unsupported protocol: " + protocol); - } + String protocol = url.getScheme(); + if (!protocol.equals("ws") && !protocol.equals("wss")) { + throw new IllegalArgumentException("Unsupported protocol: " + protocol); + } - final WebSocketClientHandler clientHandler = new WebSocketClientHandler(bootstrap, url, version, callback); + final WebSocketClientHandler clientHandler = new WebSocketClientHandler(bootstrap, url, version, callback, + customHeaders); - bootstrap.setPipelineFactory(new ChannelPipelineFactory() { + bootstrap.setPipelineFactory(new ChannelPipelineFactory() { - public ChannelPipeline getPipeline() throws Exception { - ChannelPipeline pipeline = Channels.pipeline(); - - // If you wish to support HyBi V00, you need to use WebSocketHttpResponseDecoder instead for - // HttpResponseDecoder. - pipeline.addLast("decoder", new HttpResponseDecoder()); - - pipeline.addLast("encoder", new HttpRequestEncoder()); - pipeline.addLast("ws-handler", clientHandler); - return pipeline; - } - }); + public ChannelPipeline getPipeline() throws Exception { + ChannelPipeline pipeline = Channels.pipeline(); - return clientHandler; - } + // If you wish to support HyBi V00, you need to use + // WebSocketHttpResponseDecoder instead for + // HttpResponseDecoder. + pipeline.addLast("decoder", new HttpResponseDecoder()); + + pipeline.addLast("encoder", new HttpRequestEncoder()); + pipeline.addLast("ws-handler", clientHandler); + return pipeline; + } + }); + + return clientHandler; + } } diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClientHandler.java b/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClientHandler.java index 5c12363abd..61ba90d7c7 100644 --- a/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClientHandler.java +++ b/src/main/java/org/jboss/netty/example/http/websocketx/client/WebSocketClientHandler.java @@ -24,6 +24,7 @@ package org.jboss.netty.example.http.websocketx.client; import java.net.InetSocketAddress; import java.net.URI; +import java.util.Map; import org.jboss.netty.bootstrap.ClientBootstrap; import org.jboss.netty.channel.Channel; @@ -37,85 +38,91 @@ import org.jboss.netty.handler.codec.http.HttpResponse; import org.jboss.netty.handler.codec.http.websocketx.WebSocketClientHandshaker; import org.jboss.netty.handler.codec.http.websocketx.WebSocketClientHandshakerFactory; import org.jboss.netty.handler.codec.http.websocketx.WebSocketFrame; -import org.jboss.netty.handler.codec.http.websocketx.WebSocketSpecificationVersion; +import org.jboss.netty.handler.codec.http.websocketx.WebSocketVersion; import org.jboss.netty.util.CharsetUtil; /** * Copied from https://github.com/cgbystrom/netty-tools * - * Handles socket communication for a connected WebSocket client Not intended for end-users. Please use - * {@link WebSocketClient} or {@link WebSocketCallback} for controlling your client. + * Handles socket communication for a connected WebSocket client Not intended + * for end-users. Please use {@link WebSocketClient} or + * {@link WebSocketCallback} for controlling your client. */ public class WebSocketClientHandler extends SimpleChannelUpstreamHandler implements WebSocketClient { - private final ClientBootstrap bootstrap; - private URI url; - private final WebSocketCallback callback; - private Channel channel; - private WebSocketClientHandshaker handshaker = null; - private final WebSocketSpecificationVersion version; + private final ClientBootstrap bootstrap; + private URI url; + private final WebSocketCallback callback; + private Channel channel; + private WebSocketClientHandshaker handshaker = null; + private final WebSocketVersion version; + private Map customHeaders = null; - public WebSocketClientHandler(ClientBootstrap bootstrap, URI url, WebSocketSpecificationVersion version, WebSocketCallback callback) { - this.bootstrap = bootstrap; - this.url = url; - this.version = version; - this.callback = callback; - } + public WebSocketClientHandler(ClientBootstrap bootstrap, URI url, WebSocketVersion version, + WebSocketCallback callback, Map customHeaders) { + this.bootstrap = bootstrap; + this.url = url; + this.version = version; + this.callback = callback; + this.customHeaders = customHeaders; + } - @Override - public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - channel = e.getChannel(); - this.handshaker = new WebSocketClientHandshakerFactory().newHandshaker(url, version, null, false); - handshaker.performOpeningHandshake(channel); - } + @Override + public void channelConnected(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + channel = e.getChannel(); + this.handshaker = new WebSocketClientHandshakerFactory() + .newHandshaker(url, version, null, false, customHeaders); + handshaker.performOpeningHandshake(channel); + } - @Override - public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { - callback.onDisconnect(this); - } + @Override + public void channelClosed(ChannelHandlerContext ctx, ChannelStateEvent e) throws Exception { + callback.onDisconnect(this); + } - @Override - public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { - if (!handshaker.isOpeningHandshakeCompleted()) { - handshaker.performClosingHandshake(ctx.getChannel(), (HttpResponse) e.getMessage()); - callback.onConnect(this); - return; - } + @Override + public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception { + if (!handshaker.isOpeningHandshakeCompleted()) { + handshaker.performClosingHandshake(ctx.getChannel(), (HttpResponse) e.getMessage()); + callback.onConnect(this); + return; + } - if (e.getMessage() instanceof HttpResponse) { - HttpResponse response = (HttpResponse) e.getMessage(); - throw new WebSocketException("Unexpected HttpResponse (status=" + response.getStatus() + ", content=" - + response.getContent().toString(CharsetUtil.UTF_8) + ")"); - } + if (e.getMessage() instanceof HttpResponse) { + HttpResponse response = (HttpResponse) e.getMessage(); + throw new WebSocketException("Unexpected HttpResponse (status=" + response.getStatus() + ", content=" + + response.getContent().toString(CharsetUtil.UTF_8) + ")"); + } - WebSocketFrame frame = (WebSocketFrame) e.getMessage(); - callback.onMessage(this, frame); - } + WebSocketFrame frame = (WebSocketFrame) e.getMessage(); + callback.onMessage(this, frame); + } - @Override - public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { - final Throwable t = e.getCause(); - callback.onError(t); - e.getChannel().close(); - } + @Override + public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception { + final Throwable t = e.getCause(); + callback.onError(t); + e.getChannel().close(); + } - public ChannelFuture connect() { - return bootstrap.connect(new InetSocketAddress(url.getHost(), url.getPort())); - } + public ChannelFuture connect() { + return bootstrap.connect(new InetSocketAddress(url.getHost(), url.getPort())); + } - public ChannelFuture disconnect() { - return channel.close(); - } + public ChannelFuture disconnect() { + return channel.close(); + } - public ChannelFuture send(WebSocketFrame frame) { - return channel.write(frame); - } + public ChannelFuture send(WebSocketFrame frame) { + return channel.write(frame); + } - public URI getUrl() { - return url; - } + public URI getUrl() { + return url; + } + + public void setUrl(URI url) { + this.url = url; + } - public void setUrl(URI url) { - this.url = url; - } } \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java index 792d5c6357..5c235787c0 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker.java @@ -18,6 +18,7 @@ package org.jboss.netty.handler.codec.http.websocketx; import java.net.URI; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; +import java.util.Map; import org.jboss.netty.buffer.ChannelBuffer; import org.jboss.netty.buffer.ChannelBuffers; @@ -31,174 +32,188 @@ import org.jboss.netty.util.CharsetUtil; */ public abstract class WebSocketClientHandshaker { - private URI webSocketURL; + private URI webSocketURL; - private WebSocketSpecificationVersion version = WebSocketSpecificationVersion.UNKNOWN; + private WebSocketVersion version = WebSocketVersion.UNKNOWN; - private boolean openingHandshakeCompleted = false; + private boolean openingHandshakeCompleted = false; - private String subProtocolRequest = null; + private String subProtocolRequest = null; - private String subProtocolResponse = null; + private String subProtocolResponse = null; - /** - * - * @param webSocketURL - * @param version - * @param subProtocol - */ - public WebSocketClientHandshaker(URI webSocketURL, WebSocketSpecificationVersion version, String subProtocol) { - this.webSocketURL = webSocketURL; - this.version = version; - this.subProtocolRequest = subProtocol; - } + protected Map customHeaders = null; - /** - * Returns the URI to the web socket. e.g. "ws://myhost.com/path" - */ - public URI getWebSocketURL() { - return webSocketURL; - } + /** + * Base constructor + * + * @param webSocketURL + * URL for web socket communications. e.g + * "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param version + * Version of web socket specification to use to connect to the + * server + * @param subProtocol + * Sub protocol request sent to the server. + * @param customHeaders + * Map of custom headers to add to the client request + */ + public WebSocketClientHandshaker(URI webSocketURL, WebSocketVersion version, String subProtocol, + Map customHeaders) { + this.webSocketURL = webSocketURL; + this.version = version; + this.subProtocolRequest = subProtocol; + this.customHeaders = customHeaders; + } - protected void setWebSocketURL(URI webSocketURL) { - this.webSocketURL = webSocketURL; - } + /** + * Returns the URI to the web socket. e.g. "ws://myhost.com/path" + */ + public URI getWebSocketURL() { + return webSocketURL; + } - /** - * Version of the web socket specification that is being used - */ - public WebSocketSpecificationVersion getVersion() { - return version; - } + protected void setWebSocketURL(URI webSocketURL) { + this.webSocketURL = webSocketURL; + } - protected void setVersion(WebSocketSpecificationVersion version) { - this.version = version; - } + /** + * Version of the web socket specification that is being used + */ + public WebSocketVersion getVersion() { + return version; + } - /** - * Flag to indicate if the opening handshake is complete - */ - public boolean isOpeningHandshakeCompleted() { - return openingHandshakeCompleted; - } + protected void setVersion(WebSocketVersion version) { + this.version = version; + } - protected void setOpenningHandshakeCompleted(boolean openningHandshakeCompleted) { - this.openingHandshakeCompleted = openningHandshakeCompleted; - } + /** + * Flag to indicate if the opening handshake is complete + */ + public boolean isOpeningHandshakeCompleted() { + return openingHandshakeCompleted; + } - /** - * Returns the sub protocol request sent to the server as specified in the - * constructor - */ - public String getSubProtocolRequest() { - return subProtocolRequest; - } + protected void setOpenningHandshakeCompleted(boolean openningHandshakeCompleted) { + this.openingHandshakeCompleted = openningHandshakeCompleted; + } - protected void setSubProtocolRequest(String subProtocolRequest) { - this.subProtocolRequest = subProtocolRequest; - } + /** + * Returns the sub protocol request sent to the server as specified in the + * constructor + */ + public String getSubProtocolRequest() { + return subProtocolRequest; + } - /** - * Returns the sub protocol response and sent by the server. Only available - * after end of handshake. - */ - public String getSubProtocolResponse() { - return subProtocolResponse; - } + protected void setSubProtocolRequest(String subProtocolRequest) { + this.subProtocolRequest = subProtocolRequest; + } - protected void setSubProtocolResponse(String subProtocolResponse) { - this.subProtocolResponse = subProtocolResponse; - } + /** + * Returns the sub protocol response and sent by the server. Only available + * after end of handshake. + */ + public String getSubProtocolResponse() { + return subProtocolResponse; + } - /** - * Performs the opening handshake - * - * @param channel - * Channel - */ - public abstract void performOpeningHandshake(Channel channel); + protected void setSubProtocolResponse(String subProtocolResponse) { + this.subProtocolResponse = subProtocolResponse; + } - /** - * Performs the closing handshake - * - * @param channel - * Channel - * @param response - * HTTP response containing the closing handshake details - */ - public abstract void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException; + /** + * Performs the opening handshake + * + * @param channel + * Channel + */ + public abstract void performOpeningHandshake(Channel channel); - /** - * 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 the closing handshake + * + * @param channel + * Channel + * @param response + * HTTP response containing the closing handshake details + */ + public abstract void performClosingHandshake(Channel channel, HttpResponse response) + throws WebSocketHandshakeException; - /** - * 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"); - } - } + /** + * 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"); + } + } - /** - * 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); - } + /** + * 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"); + } + } - /** - * 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]; + /** + * 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); + } - for (int i = 0; i < size; i++) { - bytes[i] = (byte) createRandomNumber(0, 255); - } + /** + * 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]; - return bytes; - } + for (int i = 0; i < size; i++) { + bytes[i] = (byte) createRandomNumber(0, 255); + } - /** - * 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); - } + 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); + } } \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java index 127ad2edc5..f3f21df57e 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker00.java @@ -18,6 +18,7 @@ package org.jboss.netty.handler.codec.http.websocketx; import java.net.URI; import java.nio.ByteBuffer; import java.util.Arrays; +import java.util.Map; import org.jboss.netty.buffer.ChannelBuffers; import org.jboss.netty.channel.Channel; @@ -45,196 +46,206 @@ import org.jboss.netty.handler.codec.http.HttpVersion; */ public class WebSocketClientHandshaker00 extends WebSocketClientHandshaker { - private byte[] expectedChallengeResponseBytes = null; + private byte[] expectedChallengeResponseBytes = null; - /** - * Constructor specifying the destination web socket location and version to - * initiate - * - * @param webSocketURL - * URL for web socket communications. e.g - * "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the - * server - * @param subProtocol - * Sub protocol request sent to the server. - */ - public WebSocketClientHandshaker00(URI webSocketURL, WebSocketSpecificationVersion version, String subProtocol) { - super(webSocketURL, version, subProtocol); - } + /** + * Constructor specifying the destination web socket location and version to + * initiate + * + * @param webSocketURL + * URL for web socket communications. e.g + * "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param version + * Version of web socket specification to use to connect to the + * server + * @param subProtocol + * Sub protocol request sent to the server. + * @param customHeaders + * Map of custom headers to add to the client request + */ + public WebSocketClientHandshaker00(URI webSocketURL, WebSocketVersion version, String subProtocol, + Map customHeaders) { + super(webSocketURL, version, subProtocol, customHeaders); + } - /** - *

- * Sends the opening request to the server: - *

- * - *
-     * GET /demo HTTP/1.1
-     * Upgrade: WebSocket
-     * Connection: Upgrade
-     * Host: example.com
-     * Origin: http://example.com
-     * Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
-     * Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
-     * 
-     * ^n:ds[4U
-     * 
- * - * @param channel - * Channel into which we can write our request - */ - @Override - public void performOpeningHandshake(Channel channel) { - // Make keys - int spaces1 = createRandomNumber(1, 12); - int spaces2 = createRandomNumber(1, 12); + /** + *

+ * Sends the opening request to the server: + *

+ * + *
+	 * GET /demo HTTP/1.1
+	 * Upgrade: WebSocket
+	 * Connection: Upgrade
+	 * Host: example.com
+	 * Origin: http://example.com
+	 * Sec-WebSocket-Key1: 4 @1  46546xW%0l 1 5
+	 * Sec-WebSocket-Key2: 12998 5 Y3 1  .P00
+	 * 
+	 * ^n:ds[4U
+	 * 
+ * + * @param channel + * Channel into which we can write our request + */ + @Override + public void performOpeningHandshake(Channel channel) { + // Make keys + int spaces1 = createRandomNumber(1, 12); + int spaces2 = createRandomNumber(1, 12); - int max1 = Integer.MAX_VALUE / spaces1; - int max2 = Integer.MAX_VALUE / spaces2; + int max1 = Integer.MAX_VALUE / spaces1; + int max2 = Integer.MAX_VALUE / spaces2; - int number1 = createRandomNumber(0, max1); - int number2 = createRandomNumber(0, max2); + int number1 = createRandomNumber(0, max1); + int number2 = createRandomNumber(0, max2); - int product1 = number1 * spaces1; - int product2 = number2 * spaces2; + int product1 = number1 * spaces1; + int product2 = number2 * spaces2; - String key1 = Integer.toString(product1); - String key2 = Integer.toString(product2); + String key1 = Integer.toString(product1); + String key2 = Integer.toString(product2); - key1 = insertRandomCharacters(key1); - key2 = insertRandomCharacters(key2); + key1 = insertRandomCharacters(key1); + key2 = insertRandomCharacters(key2); - key1 = insertSpaces(key1, spaces1); - key2 = insertSpaces(key2, spaces2); + key1 = insertSpaces(key1, spaces1); + key2 = insertSpaces(key2, spaces2); - byte[] key3 = createRandomBytes(8); + byte[] key3 = createRandomBytes(8); - ByteBuffer buffer = ByteBuffer.allocate(4); - buffer.putInt(number1); - byte[] number1Array = buffer.array(); - buffer = ByteBuffer.allocate(4); - buffer.putInt(number2); - byte[] number2Array = buffer.array(); + ByteBuffer buffer = ByteBuffer.allocate(4); + buffer.putInt(number1); + byte[] number1Array = buffer.array(); + buffer = ByteBuffer.allocate(4); + buffer.putInt(number2); + byte[] number2Array = buffer.array(); - byte[] challenge = new byte[16]; - System.arraycopy(number1Array, 0, challenge, 0, 4); - System.arraycopy(number2Array, 0, challenge, 4, 4); - System.arraycopy(key3, 0, challenge, 8, 8); - this.expectedChallengeResponseBytes = md5(challenge); + byte[] challenge = new byte[16]; + System.arraycopy(number1Array, 0, challenge, 0, 4); + System.arraycopy(number2Array, 0, challenge, 4, 4); + System.arraycopy(key3, 0, challenge, 8, 8); + this.expectedChallengeResponseBytes = md5(challenge); - // Get path - URI wsURL = this.getWebSocketURL(); - String path = wsURL.getPath(); - if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) { - path = wsURL.getPath() + "?" + wsURL.getQuery(); - } + // Get path + URI wsURL = this.getWebSocketURL(); + String path = wsURL.getPath(); + if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) { + path = wsURL.getPath() + "?" + wsURL.getQuery(); + } - // Format request - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); - request.addHeader(Names.UPGRADE, Values.WEBSOCKET); - request.addHeader(Names.CONNECTION, Values.UPGRADE); - request.addHeader(Names.HOST, wsURL.getHost()); - request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost()); - request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1); - request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2); - if (this.getSubProtocolRequest() != null && !this.getSubProtocolRequest().equals("")) { - request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, this.getSubProtocolRequest()); - } - request.setContent(ChannelBuffers.copiedBuffer(key3)); + // Format request + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); + request.addHeader(Names.UPGRADE, Values.WEBSOCKET); + request.addHeader(Names.CONNECTION, Values.UPGRADE); + request.addHeader(Names.HOST, wsURL.getHost()); + request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost()); + request.addHeader(Names.SEC_WEBSOCKET_KEY1, key1); + request.addHeader(Names.SEC_WEBSOCKET_KEY2, key2); + if (this.getSubProtocolRequest() != null && !this.getSubProtocolRequest().equals("")) { + request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, this.getSubProtocolRequest()); + } + if (customHeaders != null) { + for (String header : customHeaders.keySet()) { + request.addHeader(header, customHeaders.get(header)); + } + } + request.setContent(ChannelBuffers.copiedBuffer(key3)); - channel.write(request); + channel.write(request); - channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket00FrameEncoder()); - } + channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket00FrameEncoder()); + } - /** - *

- * Process server response: - *

- * - *
-     * HTTP/1.1 101 WebSocket Protocol Handshake
-     * Upgrade: WebSocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Location: ws://example.com/demo
-     * Sec-WebSocket-Protocol: sample
-     * 
-     * 8jKS'y:G*Co,Wxa-
-     * 
- * - * @param channel - * Channel - * @param response - * HTTP response returned from the server for the request sent by - * beginOpeningHandshake00(). - * @throws WebSocketHandshakeException - */ - @Override - public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException { - final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake"); + /** + *

+ * Process server response: + *

+ * + *
+	 * HTTP/1.1 101 WebSocket Protocol Handshake
+	 * Upgrade: WebSocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Origin: http://example.com
+	 * Sec-WebSocket-Location: ws://example.com/demo
+	 * Sec-WebSocket-Protocol: sample
+	 * 
+	 * 8jKS'y:G*Co,Wxa-
+	 * 
+ * + * @param channel + * Channel + * @param response + * HTTP response returned from the server for the request sent by + * beginOpeningHandshake00(). + * @throws WebSocketHandshakeException + */ + @Override + public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException { + final HttpResponseStatus status = new HttpResponseStatus(101, "WebSocket Protocol Handshake"); - if (!response.getStatus().equals(status)) { - throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); - } + if (!response.getStatus().equals(status)) { + throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); + } - String upgrade = response.getHeader(Names.UPGRADE); - if (upgrade == null || !upgrade.equals(Values.WEBSOCKET)) { - throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + response.getHeader(Names.UPGRADE)); - } + String upgrade = response.getHeader(Names.UPGRADE); + if (upgrade == null || !upgrade.equals(Values.WEBSOCKET)) { + throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + + response.getHeader(Names.UPGRADE)); + } - String connection = response.getHeader(Names.CONNECTION); - if (connection == null || !connection.equals(Values.UPGRADE)) { - throw new WebSocketHandshakeException("Invalid handshake response connection: " + response.getHeader(Names.CONNECTION)); - } + String connection = response.getHeader(Names.CONNECTION); + if (connection == null || !connection.equals(Values.UPGRADE)) { + throw new WebSocketHandshakeException("Invalid handshake response connection: " + + response.getHeader(Names.CONNECTION)); + } - byte[] challenge = response.getContent().array(); - if (!Arrays.equals(challenge, expectedChallengeResponseBytes)) { - throw new WebSocketHandshakeException("Invalid challenge"); - } + byte[] challenge = response.getContent().array(); + if (!Arrays.equals(challenge, expectedChallengeResponseBytes)) { + throw new WebSocketHandshakeException("Invalid challenge"); + } - String protocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); - this.setSubProtocolResponse(protocol); + String protocol = response.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + this.setSubProtocolResponse(protocol); - channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", new WebSocket00FrameDecoder()); + channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", new WebSocket00FrameDecoder()); - this.setOpenningHandshakeCompleted(true); - } + this.setOpenningHandshakeCompleted(true); + } - private String insertRandomCharacters(String key) { - int count = createRandomNumber(1, 12); + private String insertRandomCharacters(String key) { + int count = createRandomNumber(1, 12); - char[] randomChars = new char[count]; - int randCount = 0; - while (randCount < count) { - int rand = (int) (Math.random() * 0x7e + 0x21); - if (((0x21 < rand) && (rand < 0x2f)) || ((0x3a < rand) && (rand < 0x7e))) { - randomChars[randCount] = (char) rand; - randCount += 1; - } - } + char[] randomChars = new char[count]; + int randCount = 0; + while (randCount < count) { + int rand = (int) (Math.random() * 0x7e + 0x21); + if (((0x21 < rand) && (rand < 0x2f)) || ((0x3a < rand) && (rand < 0x7e))) { + randomChars[randCount] = (char) rand; + randCount += 1; + } + } - for (int i = 0; i < count; i++) { - int split = createRandomNumber(0, key.length()); - String part1 = key.substring(0, split); - String part2 = key.substring(split); - key = part1 + randomChars[i] + part2; - } + for (int i = 0; i < count; i++) { + int split = createRandomNumber(0, key.length()); + String part1 = key.substring(0, split); + String part2 = key.substring(split); + key = part1 + randomChars[i] + part2; + } - return key; - } + return key; + } - private String insertSpaces(String key, int spaces) { - for (int i = 0; i < spaces; i++) { - int split = createRandomNumber(1, key.length() - 1); - String part1 = key.substring(0, split); - String part2 = key.substring(split); - key = part1 + " " + part2; - } + private String insertSpaces(String key, int spaces) { + for (int i = 0; i < spaces; i++) { + int split = createRandomNumber(1, key.length() - 1); + String part1 = key.substring(0, split); + String part2 = key.substring(split); + key = part1 + " " + part2; + } - return key; - } + return key; + } } \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java new file mode 100644 index 0000000000..62bfbc7fef --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker08.java @@ -0,0 +1,197 @@ +/* + * 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.net.URI; +import java.util.Map; + +import org.jboss.netty.channel.Channel; +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.Values; +import org.jboss.netty.handler.codec.http.HttpMethod; +import org.jboss.netty.handler.codec.http.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpRequestEncoder; +import org.jboss.netty.handler.codec.http.HttpResponse; +import org.jboss.netty.handler.codec.http.HttpResponseDecoder; +import org.jboss.netty.handler.codec.http.HttpResponseStatus; +import org.jboss.netty.handler.codec.http.HttpVersion; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; +import org.jboss.netty.util.CharsetUtil; + +/** + *

+ * Performs client side opening and closing handshakes for web socket + * specification version draft-ietf-hybi-thewebsocketprotocol- 10 + *

+ */ +public class WebSocketClientHandshaker08 extends WebSocketClientHandshaker { + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker08.class); + + public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private String expectedChallengeResponseString = null; + + private static final String protocol = null; + + private boolean allowExtensions = false; + + /** + * Constructor specifying the destination web socket location and version to + * initiate + * + * @param webSocketURL + * URL for web socket communications. e.g + * "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param version + * Version of web socket specification to use to connect to the + * server + * @param subProtocol + * Sub protocol request sent to the server. + * @param allowExtensions + * Allow extensions to be used in the reserved bits of the web + * socket frame + * @param customHeaders + * Map of custom headers to add to the client request + */ + public WebSocketClientHandshaker08(URI webSocketURL, WebSocketVersion version, String subProtocol, + boolean allowExtensions, Map customHeaders) { + super(webSocketURL, version, subProtocol, customHeaders); + this.allowExtensions = allowExtensions; + } + + /** + * /** + *

+ * Sends the opening request to the server: + *

+ * + *
+	 * GET /chat HTTP/1.1
+	 * Host: server.example.com
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+	 * Sec-WebSocket-Origin: http://example.com
+	 * Sec-WebSocket-Protocol: chat, superchat
+	 * Sec-WebSocket-Version: 8
+	 * 
+ * + * @param channel + * Channel into which we can write our request + */ + @Override + public void performOpeningHandshake(Channel channel) { + // Get path + URI wsURL = this.getWebSocketURL(); + String path = wsURL.getPath(); + if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) { + path = wsURL.getPath() + "?" + wsURL.getQuery(); + } + + // Get 16 bit nonce and base 64 encode it + byte[] nonce = createRandomBytes(16); + String key = base64Encode(nonce); + + String acceptSeed = key + MAGIC_GUID; + byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); + this.expectedChallengeResponseString = base64Encode(sha1); + + if (logger.isDebugEnabled()) { + logger.debug(String.format("WS Version 08 Client Handshake key: %s. Expected response: %s.", key, + this.expectedChallengeResponseString)); + } + + // Format request + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); + request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); + request.addHeader(Names.CONNECTION, Values.UPGRADE); + request.addHeader(Names.SEC_WEBSOCKET_KEY, key); + request.addHeader(Names.HOST, wsURL.getHost()); + request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost()); + if (protocol != null && !protocol.equals("")) { + request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol); + } + request.addHeader(Names.SEC_WEBSOCKET_VERSION, "8"); + if (customHeaders != null) { + for (String header : customHeaders.keySet()) { + request.addHeader(header, customHeaders.get(header)); + } + } + + channel.write(request); + + channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket08FrameEncoder(true)); + } + + /** + *

+ * Process server response: + *

+ * + *
+	 * HTTP/1.1 101 Switching Protocols
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+	 * Sec-WebSocket-Protocol: chat
+	 * 
+ * + * @param channel + * Channel + * @param response + * HTTP response returned from the server for the request sent by + * beginOpeningHandshake00(). + * @throws WebSocketHandshakeException + */ + @Override + public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException { + final HttpResponseStatus status = new HttpResponseStatus(101, "Switching Protocols"); + + if (!response.getStatus().equals(status)) { + throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); + } + + String upgrade = response.getHeader(Names.UPGRADE); + if (upgrade == null || !upgrade.equals(Values.WEBSOCKET.toLowerCase())) { + throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + + response.getHeader(Names.UPGRADE)); + } + + String connection = response.getHeader(Names.CONNECTION); + if (connection == null || !connection.equals(Values.UPGRADE)) { + throw new WebSocketHandshakeException("Invalid handshake response connection: " + + response.getHeader(Names.CONNECTION)); + } + + String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); + if (accept == null || !accept.equals(this.expectedChallengeResponseString)) { + throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, + this.expectedChallengeResponseString)); + } + + channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", + new WebSocket08FrameDecoder(false, this.allowExtensions)); + + this.setOpenningHandshakeCompleted(true); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker10.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker10.java deleted file mode 100644 index 879aede004..0000000000 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker10.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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.net.URI; - -import org.jboss.netty.channel.Channel; -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.Values; -import org.jboss.netty.handler.codec.http.HttpMethod; -import org.jboss.netty.handler.codec.http.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpRequestEncoder; -import org.jboss.netty.handler.codec.http.HttpResponse; -import org.jboss.netty.handler.codec.http.HttpResponseDecoder; -import org.jboss.netty.handler.codec.http.HttpResponseStatus; -import org.jboss.netty.handler.codec.http.HttpVersion; -import org.jboss.netty.logging.InternalLogger; -import org.jboss.netty.logging.InternalLoggerFactory; -import org.jboss.netty.util.CharsetUtil; - -/** - *

- * Performs client side opening and closing handshakes for web socket - * specification version draft-ietf-hybi-thewebsocketprotocol- 10 - *

- */ -public class WebSocketClientHandshaker10 extends WebSocketClientHandshaker { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker10.class); - - public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - private String expectedChallengeResponseString = null; - - private static final String protocol = null; - - private boolean allowExtensions = false; - - /** - * Constructor specifying the destination web socket location and version to - * initiate - * - * @param webSocketURL - * URL for web socket communications. e.g - * "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the - * server - * @param subProtocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web - * socket frame - */ - public WebSocketClientHandshaker10(URI webSocketURL, WebSocketSpecificationVersion version, String subProtocol, boolean allowExtensions) { - super(webSocketURL, version, subProtocol); - this.allowExtensions = allowExtensions; - } - - /** - * /** - *

- * Sends the opening request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 8
-     * 
- * - * @param channel - * Channel into which we can write our request - */ - @Override - public void performOpeningHandshake(Channel channel) { - // Get path - URI wsURL = this.getWebSocketURL(); - String path = wsURL.getPath(); - if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) { - path = wsURL.getPath() + "?" + wsURL.getQuery(); - } - - // Get 16 bit nonce and base 64 encode it - byte[] nonce = createRandomBytes(16); - String key = base64Encode(nonce); - - String acceptSeed = key + MAGIC_GUID; - byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - this.expectedChallengeResponseString = base64Encode(sha1); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("HyBi10 Client Handshake key: %s. Expected response: %s.", key, this.expectedChallengeResponseString)); - } - - // Format request - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); - request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); - request.addHeader(Names.CONNECTION, Values.UPGRADE); - request.addHeader(Names.SEC_WEBSOCKET_KEY, key); - request.addHeader(Names.HOST, wsURL.getHost()); - request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost()); - if (protocol != null && !protocol.equals("")) { - request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol); - } - request.addHeader(Names.SEC_WEBSOCKET_VERSION, "8"); - - channel.write(request); - - channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket08FrameEncoder(true)); - } - - /** - *

- * Process server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- * - * @param channel - * Channel - * @param response - * HTTP response returned from the server for the request sent by - * beginOpeningHandshake00(). - * @throws WebSocketHandshakeException - */ - @Override - public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException { - final HttpResponseStatus status = new HttpResponseStatus(101, "Switching Protocols"); - - if (!response.getStatus().equals(status)) { - throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); - } - - String upgrade = response.getHeader(Names.UPGRADE); - if (upgrade == null || !upgrade.equals(Values.WEBSOCKET.toLowerCase())) { - throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + response.getHeader(Names.UPGRADE)); - } - - String connection = response.getHeader(Names.CONNECTION); - if (connection == null || !connection.equals(Values.UPGRADE)) { - throw new WebSocketHandshakeException("Invalid handshake response connection: " + response.getHeader(Names.CONNECTION)); - } - - String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); - if (accept == null || !accept.equals(this.expectedChallengeResponseString)) { - throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, this.expectedChallengeResponseString)); - } - - channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", new WebSocket08FrameDecoder(false, this.allowExtensions)); - - this.setOpenningHandshakeCompleted(true); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java new file mode 100644 index 0000000000..d7dc30267f --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker13.java @@ -0,0 +1,197 @@ +/* + * 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.net.URI; +import java.util.Map; + +import org.jboss.netty.channel.Channel; +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.Values; +import org.jboss.netty.handler.codec.http.HttpMethod; +import org.jboss.netty.handler.codec.http.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpRequestEncoder; +import org.jboss.netty.handler.codec.http.HttpResponse; +import org.jboss.netty.handler.codec.http.HttpResponseDecoder; +import org.jboss.netty.handler.codec.http.HttpResponseStatus; +import org.jboss.netty.handler.codec.http.HttpVersion; +import org.jboss.netty.logging.InternalLogger; +import org.jboss.netty.logging.InternalLoggerFactory; +import org.jboss.netty.util.CharsetUtil; + +/** + *

+ * Performs client side opening and closing handshakes for RFC 6455. This was originally + * draft-ietf-hybi-thewebsocketprotocol- 17 + *

+ */ +public class WebSocketClientHandshaker13 extends WebSocketClientHandshaker { + + private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker13.class); + + public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private String expectedChallengeResponseString = null; + + private static final String protocol = null; + + private boolean allowExtensions = false; + + /** + * Constructor specifying the destination web socket location and version to + * initiate + * + * @param webSocketURL + * URL for web socket communications. e.g + * "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param version + * Version of web socket specification to use to connect to the + * server + * @param subProtocol + * Sub protocol request sent to the server. + * @param allowExtensions + * Allow extensions to be used in the reserved bits of the web + * socket frame + * @param customHeaders + * Map of custom headers to add to the client request + */ + public WebSocketClientHandshaker13(URI webSocketURL, WebSocketVersion version, String subProtocol, + boolean allowExtensions, Map customHeaders) { + super(webSocketURL, version, subProtocol, customHeaders); + this.allowExtensions = allowExtensions; + } + + /** + * /** + *

+ * Sends the opening request to the server: + *

+ * + *
+	 * GET /chat HTTP/1.1
+	 * Host: server.example.com
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+	 * Sec-WebSocket-Origin: http://example.com
+	 * Sec-WebSocket-Protocol: chat, superchat
+	 * Sec-WebSocket-Version: 13
+	 * 
+ * + * @param channel + * Channel into which we can write our request + */ + @Override + public void performOpeningHandshake(Channel channel) { + // Get path + URI wsURL = this.getWebSocketURL(); + String path = wsURL.getPath(); + if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) { + path = wsURL.getPath() + "?" + wsURL.getQuery(); + } + + // Get 16 bit nonce and base 64 encode it + byte[] nonce = createRandomBytes(16); + String key = base64Encode(nonce); + + String acceptSeed = key + MAGIC_GUID; + byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); + this.expectedChallengeResponseString = base64Encode(sha1); + + if (logger.isDebugEnabled()) { + logger.debug(String.format("WS Version 13 Client Handshake key: %s. Expected response: %s.", key, + this.expectedChallengeResponseString)); + } + + // Format request + HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); + request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); + request.addHeader(Names.CONNECTION, Values.UPGRADE); + request.addHeader(Names.SEC_WEBSOCKET_KEY, key); + request.addHeader(Names.HOST, wsURL.getHost()); + request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost()); + if (protocol != null && !protocol.equals("")) { + request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol); + } + request.addHeader(Names.SEC_WEBSOCKET_VERSION, "13"); + if (customHeaders != null) { + for (String header : customHeaders.keySet()) { + request.addHeader(header, customHeaders.get(header)); + } + } + + channel.write(request); + + channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket13FrameEncoder(true)); + } + + /** + *

+ * Process server response: + *

+ * + *
+	 * HTTP/1.1 101 Switching Protocols
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+	 * Sec-WebSocket-Protocol: chat
+	 * 
+ * + * @param channel + * Channel + * @param response + * HTTP response returned from the server for the request sent by + * beginOpeningHandshake00(). + * @throws WebSocketHandshakeException + */ + @Override + public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException { + final HttpResponseStatus status = new HttpResponseStatus(101, "Switching Protocols"); + + if (!response.getStatus().equals(status)) { + throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); + } + + String upgrade = response.getHeader(Names.UPGRADE); + if (upgrade == null || !upgrade.equals(Values.WEBSOCKET.toLowerCase())) { + throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + + response.getHeader(Names.UPGRADE)); + } + + String connection = response.getHeader(Names.CONNECTION); + if (connection == null || !connection.equals(Values.UPGRADE)) { + throw new WebSocketHandshakeException("Invalid handshake response connection: " + + response.getHeader(Names.CONNECTION)); + } + + String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); + if (accept == null || !accept.equals(this.expectedChallengeResponseString)) { + throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, + this.expectedChallengeResponseString)); + } + + channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", + new WebSocket13FrameDecoder(false, this.allowExtensions)); + + this.setOpenningHandshakeCompleted(true); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker17.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker17.java deleted file mode 100644 index 83c4aeee2d..0000000000 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshaker17.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * 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.net.URI; - -import org.jboss.netty.channel.Channel; -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.Values; -import org.jboss.netty.handler.codec.http.HttpMethod; -import org.jboss.netty.handler.codec.http.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpRequestEncoder; -import org.jboss.netty.handler.codec.http.HttpResponse; -import org.jboss.netty.handler.codec.http.HttpResponseDecoder; -import org.jboss.netty.handler.codec.http.HttpResponseStatus; -import org.jboss.netty.handler.codec.http.HttpVersion; -import org.jboss.netty.logging.InternalLogger; -import org.jboss.netty.logging.InternalLoggerFactory; -import org.jboss.netty.util.CharsetUtil; - -/** - *

- * Performs client side opening and closing handshakes for web socket - * specification version draft-ietf-hybi-thewebsocketprotocol- 17 - *

- */ -public class WebSocketClientHandshaker17 extends WebSocketClientHandshaker { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketClientHandshaker17.class); - - public static final String MAGIC_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - private String expectedChallengeResponseString = null; - - private static final String protocol = null; - - private boolean allowExtensions = false; - - /** - * Constructor specifying the destination web socket location and version to - * initiate - * - * @param webSocketURL - * URL for web socket communications. e.g - * "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the - * server - * @param subProtocol - * Sub protocol request sent to the server. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web - * socket frame - */ - public WebSocketClientHandshaker17(URI webSocketURL, WebSocketSpecificationVersion version, String subProtocol, boolean allowExtensions) { - super(webSocketURL, version, subProtocol); - this.allowExtensions = allowExtensions; - } - - /** - * /** - *

- * Sends the opening request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 13
-     * 
- * - * @param channel - * Channel into which we can write our request - */ - @Override - public void performOpeningHandshake(Channel channel) { - // Get path - URI wsURL = this.getWebSocketURL(); - String path = wsURL.getPath(); - if (wsURL.getQuery() != null && wsURL.getQuery().length() > 0) { - path = wsURL.getPath() + "?" + wsURL.getQuery(); - } - - // Get 16 bit nonce and base 64 encode it - byte[] nonce = createRandomBytes(16); - String key = base64Encode(nonce); - - String acceptSeed = key + MAGIC_GUID; - byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - this.expectedChallengeResponseString = base64Encode(sha1); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("HyBi17 Client Handshake key: %s. Expected response: %s.", key, this.expectedChallengeResponseString)); - } - - // Format request - HttpRequest request = new DefaultHttpRequest(HttpVersion.HTTP_1_1, HttpMethod.GET, path); - request.addHeader(Names.UPGRADE, Values.WEBSOCKET.toLowerCase()); - request.addHeader(Names.CONNECTION, Values.UPGRADE); - request.addHeader(Names.SEC_WEBSOCKET_KEY, key); - request.addHeader(Names.HOST, wsURL.getHost()); - request.addHeader(Names.ORIGIN, "http://" + wsURL.getHost()); - if (protocol != null && !protocol.equals("")) { - request.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, protocol); - } - request.addHeader(Names.SEC_WEBSOCKET_VERSION, "13"); - - channel.write(request); - - channel.getPipeline().replace(HttpRequestEncoder.class, "ws-encoder", new WebSocket13FrameEncoder(true)); - } - - /** - *

- * Process server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- * - * @param channel - * Channel - * @param response - * HTTP response returned from the server for the request sent by - * beginOpeningHandshake00(). - * @throws WebSocketHandshakeException - */ - @Override - public void performClosingHandshake(Channel channel, HttpResponse response) throws WebSocketHandshakeException { - final HttpResponseStatus status = new HttpResponseStatus(101, "Switching Protocols"); - - if (!response.getStatus().equals(status)) { - throw new WebSocketHandshakeException("Invalid handshake response status: " + response.getStatus()); - } - - String upgrade = response.getHeader(Names.UPGRADE); - if (upgrade == null || !upgrade.equals(Values.WEBSOCKET.toLowerCase())) { - throw new WebSocketHandshakeException("Invalid handshake response upgrade: " + response.getHeader(Names.UPGRADE)); - } - - String connection = response.getHeader(Names.CONNECTION); - if (connection == null || !connection.equals(Values.UPGRADE)) { - throw new WebSocketHandshakeException("Invalid handshake response connection: " + response.getHeader(Names.CONNECTION)); - } - - String accept = response.getHeader(Names.SEC_WEBSOCKET_ACCEPT); - if (accept == null || !accept.equals(this.expectedChallengeResponseString)) { - throw new WebSocketHandshakeException(String.format("Invalid challenge. Actual: %s. Expected: %s", accept, this.expectedChallengeResponseString)); - } - - channel.getPipeline().replace(HttpResponseDecoder.class, "ws-decoder", new WebSocket13FrameDecoder(false, this.allowExtensions)); - - this.setOpenningHandshakeCompleted(true); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java index 31bea2973c..6ec12a3393 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketClientHandshakerFactory.java @@ -16,42 +16,46 @@ package org.jboss.netty.handler.codec.http.websocketx; import java.net.URI; +import java.util.Map; /** * Instances the appropriate handshake class to use for clients */ public class WebSocketClientHandshakerFactory { - /** - * Instances a new handshaker - * - * @param webSocketURL - * URL for web socket communications. e.g - * "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param version - * Version of web socket specification to use to connect to the - * server - * @param subProtocol - * Sub protocol request sent to the server. Null if no - * sub-protocol support is required. - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web - * socket frame - * @throws WebSocketHandshakeException - */ - public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketSpecificationVersion version, String subProtocol, boolean allowExtensions) throws WebSocketHandshakeException { - if (version == WebSocketSpecificationVersion.V17) { - return new WebSocketClientHandshaker17(webSocketURL, version, subProtocol, allowExtensions); - } - if (version == WebSocketSpecificationVersion.V10) { - return new WebSocketClientHandshaker10(webSocketURL, version, subProtocol, allowExtensions); - } - if (version == WebSocketSpecificationVersion.V00) { - return new WebSocketClientHandshaker00(webSocketURL, version, subProtocol); - } + /** + * Instances a new handshaker + * + * @param webSocketURL + * URL for web socket communications. e.g + * "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param version + * Version of web socket specification to use to connect to the + * server + * @param subProtocol + * Sub protocol request sent to the server. Null if no + * sub-protocol support is required. + * @param allowExtensions + * Allow extensions to be used in the reserved bits of the web + * socket frame + * @param customHeaders + * custom HTTP headers + * @throws WebSocketHandshakeException + */ + public WebSocketClientHandshaker newHandshaker(URI webSocketURL, WebSocketVersion version, String subProtocol, + boolean allowExtensions, Map customHeaders) throws WebSocketHandshakeException { + if (version == WebSocketVersion.V13) { + return new WebSocketClientHandshaker13(webSocketURL, version, subProtocol, allowExtensions, customHeaders); + } + if (version == WebSocketVersion.V08) { + return new WebSocketClientHandshaker08(webSocketURL, version, subProtocol, allowExtensions, customHeaders); + } + if (version == WebSocketVersion.V00) { + return new WebSocketClientHandshaker00(webSocketURL, version, subProtocol, customHeaders); + } - throw new WebSocketHandshakeException("Protocol version " + version.toString() + " not supported."); + throw new WebSocketHandshakeException("Protocol " + version.toString() + " not supported."); - } + } } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java index 4871416fc0..f0cd27eaa0 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker.java @@ -33,7 +33,7 @@ public abstract class WebSocketServerHandshaker { private String[] subProtocolsArray = null; - private WebSocketSpecificationVersion version = WebSocketSpecificationVersion.UNKNOWN; + private WebSocketVersion version = WebSocketVersion.UNKNOWN; /** * Constructor specifying the destination web socket location @@ -83,11 +83,11 @@ public abstract class WebSocketServerHandshaker { /** * Returns the version of the specification being supported */ - public WebSocketSpecificationVersion getVersion() { + public WebSocketVersion getVersion() { return version; } - public void setVersion(WebSocketSpecificationVersion version) { + public void setVersion(WebSocketVersion version) { this.version = version; } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java index cb902dbd84..2a8fde99db 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker00.java @@ -129,7 +129,7 @@ public class WebSocketServerHandshaker00 extends WebSocketServerHandshaker { if (logger.isDebugEnabled()) { logger.debug(String.format("Channel %s web socket spec version 00 handshake", channel.getId())); } - this.setVersion(WebSocketSpecificationVersion.V00); + this.setVersion(WebSocketVersion.V00); // Serve the WebSocket handshake request. if (!Values.UPGRADE.equalsIgnoreCase(req.getHeader(CONNECTION)) || !WEBSOCKET.equalsIgnoreCase(req.getHeader(Names.UPGRADE))) { diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java new file mode 100644 index 0000000000..4acab1b4bd --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker08.java @@ -0,0 +1,180 @@ +/* + * 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 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.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelFutureListener; +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.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpRequestDecoder; +import org.jboss.netty.handler.codec.http.HttpResponse; +import org.jboss.netty.handler.codec.http.HttpResponseEncoder; +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.InternalLoggerFactory; +import org.jboss.netty.util.CharsetUtil; + +/** + *

+ * Performs server side opening and closing handshakes for web socket + * specification version draft-ietf-hybi-thewebsocketprotocol- 10 + *

+ */ +public class WebSocketServerHandshaker08 extends WebSocketServerHandshaker { + + private static final InternalLogger logger = InternalLoggerFactory + .getInstance(WebSocketServerHandshaker08.class); + + public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private boolean allowExtensions = false; + + /** + * Constructor specifying the destination web socket location + * + * @param webSocketURL + * URL for web socket communications. e.g + * "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param subProtocols + * CSV of supported protocols + * @param allowExtensions + * Allow extensions to be used in the reserved bits of the web + * socket frame + */ + public WebSocketServerHandshaker08(String webSocketURL, + String subProtocols, boolean allowExtensions) { + super(webSocketURL, subProtocols); + this.allowExtensions = allowExtensions; + } + + /** + *

+ * Handle the web socket handshake for the web socket specification HyBi + * version 8 to 10. Version 8, 9 and 10 share the same wire protocol. + *

+ * + *

+ * Browser request to the server: + *

+ * + *
+	 * GET /chat HTTP/1.1
+	 * Host: server.example.com
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+	 * Sec-WebSocket-Origin: http://example.com
+	 * Sec-WebSocket-Protocol: chat, superchat
+	 * Sec-WebSocket-Version: 8
+	 * 
+ * + *

+ * Server response: + *

+ * + *
+	 * HTTP/1.1 101 Switching Protocols
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+	 * Sec-WebSocket-Protocol: chat
+	 * 
+ * + * @param channel + * Channel + * @param req + * HTTP request + * @throws NoSuchAlgorithmException + */ + @Override + public void performOpeningHandshake(Channel channel, HttpRequest req) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format( + "Channel %s WS version 08 handshake", + channel.getId())); + } + + HttpResponse res = new DefaultHttpResponse(HTTP_1_1, + new HttpResponseStatus(101, "Switching Protocols")); + this.setVersion(WebSocketVersion.V08); + + String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); + if (key == null) { + res.setStatus(HttpResponseStatus.BAD_REQUEST); + return; + } + String acceptSeed = key + WEBSOCKET_08_ACCEPT_GUID; + byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); + String accept = base64Encode(sha1); + + if (logger.isDebugEnabled()) { + logger.debug(String.format( + "WS Version 08 Server Handshake key: %s. Response: %s.", + key, accept)); + } + + res.setStatus(new HttpResponseStatus(101, "Switching Protocols")); + res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); + res.addHeader(Names.CONNECTION, Names.UPGRADE); + res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); + String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + if (protocol != null) { + res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, + this.selectSubProtocol(protocol)); + } + + channel.write(res); + + // Upgrade the connection and send the handshake response. + ChannelPipeline p = channel.getPipeline(); + p.remove(HttpChunkAggregator.class); + p.replace(HttpRequestDecoder.class, "wsdecoder", + new WebSocket08FrameDecoder(true, this.allowExtensions)); + p.replace(HttpResponseEncoder.class, "wsencoder", + new WebSocket08FrameEncoder(false)); + + } + + /** + * Echo back the closing frame and close the connection + * + * @param channel + * Channel + * @param frame + * Web Socket frame that was received + */ + @Override + public void performClosingHandshake(Channel channel, + CloseWebSocketFrame frame) { + ChannelFuture f = channel.write(frame); + f.addListener(ChannelFutureListener.CLOSE); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker10.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker10.java deleted file mode 100644 index 3ca89d686e..0000000000 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker10.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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 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.channel.Channel; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; -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.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpRequestDecoder; -import org.jboss.netty.handler.codec.http.HttpResponse; -import org.jboss.netty.handler.codec.http.HttpResponseEncoder; -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.InternalLoggerFactory; -import org.jboss.netty.util.CharsetUtil; - -/** - *

- * Performs server side opening and closing handshakes for web socket - * specification version draft-ietf-hybi-thewebsocketprotocol- 10 - *

- */ -public class WebSocketServerHandshaker10 extends WebSocketServerHandshaker { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketServerHandshaker10.class); - - public static final String WEBSOCKET_08_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - private boolean allowExtensions = false; - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g - * "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param subProtocols - * CSV of supported protocols - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web - * socket frame - */ - public WebSocketServerHandshaker10(String webSocketURL, String subProtocols, boolean allowExtensions) { - super(webSocketURL, subProtocols); - this.allowExtensions = allowExtensions; - } - - /** - *

- * Handle the web socket handshake for the web socket specification HyBi - * version 8 to 10. Version 8, 9 and 10 share the same wire protocol. - *

- * - *

- * Browser request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 8
-     * 
- * - *

- * Server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- * - * @param channel - * Channel - * @param req - * HTTP request - * @throws NoSuchAlgorithmException - */ - @Override - public void performOpeningHandshake(Channel channel, HttpRequest req) { - - if (logger.isDebugEnabled()) { - logger.debug(String.format("Channel %s web socket spec version 10 handshake", channel.getId())); - } - - HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101, "Switching Protocols")); - this.setVersion(WebSocketSpecificationVersion.V10); - - String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); - if (key == null) { - res.setStatus(HttpResponseStatus.BAD_REQUEST); - return; - } - String acceptSeed = key + WEBSOCKET_08_ACCEPT_GUID; - byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - String accept = base64Encode(sha1); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("HyBi10 Server Handshake key: %s. Response: %s.", key, accept)); - } - - res.setStatus(new HttpResponseStatus(101, "Switching Protocols")); - res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); - res.addHeader(Names.CONNECTION, Names.UPGRADE); - res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); - String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); - if (protocol != null) { - res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, this.selectSubProtocol(protocol)); - } - - channel.write(res); - - // Upgrade the connection and send the handshake response. - ChannelPipeline p = channel.getPipeline(); - p.remove(HttpChunkAggregator.class); - p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket08FrameDecoder(true, this.allowExtensions)); - p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket08FrameEncoder(false)); - - } - - /** - * Echo back the closing frame and close the connection - * - * @param channel - * Channel - * @param frame - * Web Socket frame that was received - */ - @Override - public void performClosingHandshake(Channel channel, CloseWebSocketFrame frame) { - ChannelFuture f = channel.write(frame); - f.addListener(ChannelFutureListener.CLOSE); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java new file mode 100644 index 0000000000..9928743e55 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker13.java @@ -0,0 +1,180 @@ +/* + * 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 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.channel.Channel; +import org.jboss.netty.channel.ChannelFuture; +import org.jboss.netty.channel.ChannelFutureListener; +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.HttpRequest; +import org.jboss.netty.handler.codec.http.HttpRequestDecoder; +import org.jboss.netty.handler.codec.http.HttpResponse; +import org.jboss.netty.handler.codec.http.HttpResponseEncoder; +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.InternalLoggerFactory; +import org.jboss.netty.util.CharsetUtil; + +/** + *

+ * Performs server side opening and closing handshakes for RFC 6455. This was originally + * draft-ietf-hybi-thewebsocketprotocol- 17 + *

+ */ +public class WebSocketServerHandshaker13 extends WebSocketServerHandshaker { + + private static final InternalLogger logger = InternalLoggerFactory + .getInstance(WebSocketServerHandshaker13.class); + + public static final String WEBSOCKET_17_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; + + private boolean allowExtensions = false; + + /** + * Constructor specifying the destination web socket location + * + * @param webSocketURL + * URL for web socket communications. e.g + * "ws://myhost.com/mypath". Subsequent web socket frames will be + * sent to this URL. + * @param subProtocols + * CSV of supported protocols + * @param allowExtensions + * Allow extensions to be used in the reserved bits of the web + * socket frame + */ + public WebSocketServerHandshaker13(String webSocketURL, + String subProtocols, boolean allowExtensions) { + super(webSocketURL, subProtocols); + this.allowExtensions = allowExtensions; + } + + /** + *

+ * Handle the web socket handshake for the web socket specification HyBi + * versions 13-17. Versions 13-17 share the same wire protocol. + *

+ * + *

+ * Browser request to the server: + *

+ * + *
+	 * GET /chat HTTP/1.1
+	 * Host: server.example.com
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
+	 * Sec-WebSocket-Origin: http://example.com
+	 * Sec-WebSocket-Protocol: chat, superchat
+	 * Sec-WebSocket-Version: 13
+	 * 
+ * + *

+ * Server response: + *

+ * + *
+	 * HTTP/1.1 101 Switching Protocols
+	 * Upgrade: websocket
+	 * Connection: Upgrade
+	 * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
+	 * Sec-WebSocket-Protocol: chat
+	 * 
+ * + * @param channel + * Channel + * @param req + * HTTP request + * @throws NoSuchAlgorithmException + */ + @Override + public void performOpeningHandshake(Channel channel, HttpRequest req) { + + if (logger.isDebugEnabled()) { + logger.debug(String.format( + "Channel %s WS version 13 handshake", + channel.getId())); + } + + HttpResponse res = new DefaultHttpResponse(HTTP_1_1, + new HttpResponseStatus(101, "Switching Protocols")); + this.setVersion(WebSocketVersion.V13); + + String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); + if (key == null) { + res.setStatus(HttpResponseStatus.BAD_REQUEST); + return; + } + String acceptSeed = key + WEBSOCKET_17_ACCEPT_GUID; + byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); + String accept = base64Encode(sha1); + + if (logger.isDebugEnabled()) { + logger.debug(String.format( + "WS Version 13 Server Handshake key: %s. Response: %s.", key, + accept)); + } + + res.setStatus(new HttpResponseStatus(101, "Switching Protocols")); + res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); + res.addHeader(Names.CONNECTION, Names.UPGRADE); + res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); + String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); + if (protocol != null) { + res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, + this.selectSubProtocol(protocol)); + } + + channel.write(res); + + // Upgrade the connection and send the handshake response. + ChannelPipeline p = channel.getPipeline(); + p.remove(HttpChunkAggregator.class); + p.replace(HttpRequestDecoder.class, "wsdecoder", + new WebSocket13FrameDecoder(true, this.allowExtensions)); + p.replace(HttpResponseEncoder.class, "wsencoder", + new WebSocket13FrameEncoder(false)); + + } + + /** + * Echo back the closing frame and close the connection + * + * @param channel + * Channel + * @param frame + * Web Socket frame that was received + */ + @Override + public void performClosingHandshake(Channel channel, + CloseWebSocketFrame frame) { + ChannelFuture f = channel.write(frame); + f.addListener(ChannelFutureListener.CLOSE); + } + +} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker17.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker17.java deleted file mode 100644 index aec5088797..0000000000 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshaker17.java +++ /dev/null @@ -1,169 +0,0 @@ -/* - * 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 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.channel.Channel; -import org.jboss.netty.channel.ChannelFuture; -import org.jboss.netty.channel.ChannelFutureListener; -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.HttpRequest; -import org.jboss.netty.handler.codec.http.HttpRequestDecoder; -import org.jboss.netty.handler.codec.http.HttpResponse; -import org.jboss.netty.handler.codec.http.HttpResponseEncoder; -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.InternalLoggerFactory; -import org.jboss.netty.util.CharsetUtil; - -/** - *

- * Performs server side opening and closing handshakes for web socket - * specification version draft-ietf-hybi-thewebsocketprotocol- 17 - *

- */ -public class WebSocketServerHandshaker17 extends WebSocketServerHandshaker { - - private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketServerHandshaker17.class); - - public static final String WEBSOCKET_17_ACCEPT_GUID = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"; - - private boolean allowExtensions = false; - - /** - * Constructor specifying the destination web socket location - * - * @param webSocketURL - * URL for web socket communications. e.g - * "ws://myhost.com/mypath". Subsequent web socket frames will be - * sent to this URL. - * @param subProtocols - * CSV of supported protocols - * @param allowExtensions - * Allow extensions to be used in the reserved bits of the web - * socket frame - */ - public WebSocketServerHandshaker17(String webSocketURL, String subProtocols, boolean allowExtensions) { - super(webSocketURL, subProtocols); - this.allowExtensions = allowExtensions; - } - - /** - *

- * Handle the web socket handshake for the web socket specification HyBi - * versions 13-17. Versions 13-17 share the same wire protocol. - *

- * - *

- * Browser request to the server: - *

- * - *
-     * GET /chat HTTP/1.1
-     * Host: server.example.com
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ==
-     * Sec-WebSocket-Origin: http://example.com
-     * Sec-WebSocket-Protocol: chat, superchat
-     * Sec-WebSocket-Version: 13
-     * 
- * - *

- * Server response: - *

- * - *
-     * HTTP/1.1 101 Switching Protocols
-     * Upgrade: websocket
-     * Connection: Upgrade
-     * Sec-WebSocket-Accept: s3pPLMBiTxaQ9kYGzzhZRbK+xOo=
-     * Sec-WebSocket-Protocol: chat
-     * 
- * - * @param channel - * Channel - * @param req - * HTTP request - * @throws NoSuchAlgorithmException - */ - @Override - public void performOpeningHandshake(Channel channel, HttpRequest req) { - - if (logger.isDebugEnabled()) { - logger.debug(String.format("Channel %s web socket spec version 17 handshake", channel.getId())); - } - - HttpResponse res = new DefaultHttpResponse(HTTP_1_1, new HttpResponseStatus(101, "Switching Protocols")); - this.setVersion(WebSocketSpecificationVersion.V17); - - String key = req.getHeader(Names.SEC_WEBSOCKET_KEY); - if (key == null) { - res.setStatus(HttpResponseStatus.BAD_REQUEST); - return; - } - String acceptSeed = key + WEBSOCKET_17_ACCEPT_GUID; - byte[] sha1 = sha1(acceptSeed.getBytes(CharsetUtil.US_ASCII)); - String accept = base64Encode(sha1); - - if (logger.isDebugEnabled()) { - logger.debug(String.format("HyBi17 Server Handshake key: %s. Response: %s.", key, accept)); - } - - res.setStatus(new HttpResponseStatus(101, "Switching Protocols")); - res.addHeader(Names.UPGRADE, WEBSOCKET.toLowerCase()); - res.addHeader(Names.CONNECTION, Names.UPGRADE); - res.addHeader(Names.SEC_WEBSOCKET_ACCEPT, accept); - String protocol = req.getHeader(Names.SEC_WEBSOCKET_PROTOCOL); - if (protocol != null) { - res.addHeader(Names.SEC_WEBSOCKET_PROTOCOL, this.selectSubProtocol(protocol)); - } - - channel.write(res); - - // Upgrade the connection and send the handshake response. - ChannelPipeline p = channel.getPipeline(); - p.remove(HttpChunkAggregator.class); - p.replace(HttpRequestDecoder.class, "wsdecoder", new WebSocket13FrameDecoder(true, this.allowExtensions)); - p.replace(HttpResponseEncoder.class, "wsencoder", new WebSocket13FrameEncoder(false)); - - } - - /** - * Echo back the closing frame and close the connection - * - * @param channel - * Channel - * @param frame - * Web Socket frame that was received - */ - @Override - public void performClosingHandshake(Channel channel, CloseWebSocketFrame frame) { - ChannelFuture f = channel.write(frame); - f.addListener(ChannelFutureListener.CLOSE); - } - -} \ No newline at end of file diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java index a64ff99f75..e46cde796a 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketServerHandshakerFactory.java @@ -64,14 +64,14 @@ public class WebSocketServerHandshakerFactory { String version = req.getHeader(Names.SEC_WEBSOCKET_VERSION); if (version != null) { - if (version.equals("13")) { + if (version.equals(WebSocketVersion.V13.toHttpHeaderValue())) { // Version 13 of the wire protocol - assume version 17 of the // specification. - return new WebSocketServerHandshaker17(webSocketURL, subProtocols, this.allowExtensions); - } else if (version.equals("8")) { + return new WebSocketServerHandshaker13(webSocketURL, subProtocols, this.allowExtensions); + } else if (version.equals(WebSocketVersion.V08.toHttpHeaderValue())) { // Version 8 of the wire protocol - assume version 10 of the // specification. - return new WebSocketServerHandshaker10(webSocketURL, subProtocols, this.allowExtensions); + return new WebSocketServerHandshaker08(webSocketURL, subProtocols, this.allowExtensions); } else { return null; } @@ -91,7 +91,7 @@ public class WebSocketServerHandshakerFactory { HttpResponse res = new DefaultHttpResponse(HttpVersion.HTTP_1_1, new HttpResponseStatus(101, "Switching Protocols")); res.setStatus(HttpResponseStatus.UPGRADE_REQUIRED); - res.setHeader(Names.SEC_WEBSOCKET_VERSION, "13"); + res.setHeader(Names.SEC_WEBSOCKET_VERSION, WebSocketVersion.V13.toHttpHeaderValue()); channel.write(res); } diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketSpecificationVersion.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketSpecificationVersion.java deleted file mode 100644 index 9e2b3d19b8..0000000000 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketSpecificationVersion.java +++ /dev/null @@ -1,51 +0,0 @@ -/* - * 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; - -/** - *

- * Versions of the web socket specification. - *

- *

- * A specification is tied to one wire protocol version but a protocol version - * may have use by more than 1 version of the specification. - *

- */ -public enum WebSocketSpecificationVersion { - UNKNOWN, - - /** - * draft-ietf-hybi-thewebsocketprotocol- 00. - */ - V00, - - /** - * draft-ietf-hybi-thewebsocketprotocol- 10 - */ - V10, - - /** - * draft-ietf-hybi-thewebsocketprotocol- 17 - */ - V17 - -} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketVersion.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketVersion.java new file mode 100644 index 0000000000..d0d4c7e506 --- /dev/null +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/WebSocketVersion.java @@ -0,0 +1,58 @@ +/* + * 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; + +/** + *

+ * Version of the web socket wire protocol. + *

+ */ +public enum WebSocketVersion { + UNKNOWN, + + /** + * draft-ietf-hybi-thewebsocketprotocol- 00. + */ + V00, + + /** + * draft-ietf-hybi-thewebsocketprotocol- 10 + */ + V08, + + /** + * RFC 6455. This was + * originally draft-ietf-hybi-thewebsocketprotocol- 17 + */ + V13; + + public String toHttpHeaderValue() { + if (this == V00) { + return "0"; + } else if (this == V08) { + return "8"; + } else if (this == V13) { + return "13"; + } + throw new IllegalArgumentException(this.toString() + " cannot be converted to a string."); + } +} diff --git a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/package-info.java b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/package-info.java index 90e1af55ea..a550146468 100644 --- a/src/main/java/org/jboss/netty/handler/codec/http/websocketx/package-info.java +++ b/src/main/java/org/jboss/netty/handler/codec/http/websocketx/package-info.java @@ -23,7 +23,7 @@ * *

*