diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServer.java b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServer.java
new file mode 100644
index 000000000000..3fe4a60c5ea7
--- /dev/null
+++ b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServer.java
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * Red Hat 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.example.http.websocketx.sslserver;
+
+import java.net.InetSocketAddress;
+import java.util.concurrent.Executors;
+import java.util.logging.ConsoleHandler;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import org.jboss.netty.bootstrap.ServerBootstrap;
+import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory;
+
+/**
+ * A HTTP server which serves Web Socket requests at:
+ *
+ * https://localhost:8081/websocket
+ *
+ * Open your browser at https://localhost:8081/, then the demo page will be
+ * loaded and a Web Socket connection will be made automatically.
+ *
+ * This server illustrates support for the different web socket specification
+ * versions and will work with:
+ *
+ *
+ * - Safari 5+ (draft-ietf-hybi-thewebsocketprotocol-00)
+ *
-
+ *
- Chrome 6-13 (draft-ietf-hybi-thewebsocketprotocol-00)
+ *
-
+ *
- Chrome 14+ (draft-ietf-hybi-thewebsocketprotocol-10)
+ *
-
+ *
- Firefox 7+ (draft-ietf-hybi-thewebsocketprotocol-10)
+ *
-
+ *
+ *
+ * @author The Netty Project
+ * @author Trustin Lee
+ * @author Vibul Imtarnasan
+ *
+ * @version $Rev$, $Date$
+ */
+public class WebSocketSslServer {
+ public static void main(String[] args) {
+ ConsoleHandler ch = new ConsoleHandler();
+ ch.setLevel(Level.FINE);
+ Logger.getLogger("").addHandler(ch);
+ Logger.getLogger("").setLevel(Level.FINE);
+
+ // Configure the server.
+ ServerBootstrap bootstrap = new ServerBootstrap(new NioServerSocketChannelFactory(
+ Executors.newCachedThreadPool(), Executors.newCachedThreadPool()));
+
+ // Set up the event pipeline factory.
+ bootstrap.setPipelineFactory(new WebSocketSslServerPipelineFactory());
+
+ // Bind and start to accept incoming connections.
+ bootstrap.bind(new InetSocketAddress(8081));
+
+ System.out.println("Web Socket Server started on 8081. Open your browser and navigate to https://localhost:8081/");
+ }
+}
diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java
new file mode 100644
index 000000000000..9b3875b1da98
--- /dev/null
+++ b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * Red Hat 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.example.http.websocketx.sslserver;
+
+import static org.jboss.netty.handler.codec.http.HttpHeaders.*;
+import static org.jboss.netty.handler.codec.http.HttpHeaders.Names.*;
+import static org.jboss.netty.handler.codec.http.HttpMethod.*;
+import static org.jboss.netty.handler.codec.http.HttpResponseStatus.*;
+import static org.jboss.netty.handler.codec.http.HttpVersion.*;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.channel.ChannelFuture;
+import org.jboss.netty.channel.ChannelFutureListener;
+import org.jboss.netty.channel.ChannelHandlerContext;
+import org.jboss.netty.channel.ExceptionEvent;
+import org.jboss.netty.channel.MessageEvent;
+import org.jboss.netty.channel.SimpleChannelUpstreamHandler;
+import org.jboss.netty.handler.codec.http.DefaultHttpResponse;
+import org.jboss.netty.handler.codec.http.HttpHeaders;
+import org.jboss.netty.handler.codec.http.HttpRequest;
+import org.jboss.netty.handler.codec.http.HttpResponse;
+import org.jboss.netty.handler.codec.http.websocketx.CloseWebSocketFrame;
+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.WebSocketServerHandshaker;
+import org.jboss.netty.handler.codec.http.websocketx.WebSocketServerHandshakerFactory;
+import org.jboss.netty.logging.InternalLogger;
+import org.jboss.netty.logging.InternalLoggerFactory;
+import org.jboss.netty.util.CharsetUtil;
+
+/**
+ * Handles handshakes and messages
+ *
+ * @author The Netty Project
+ * @author Trustin Lee
+ * @author Vibul Imtarnasan
+ *
+ * @version $Rev$, $Date$
+ */
+public class WebSocketSslServerHandler extends SimpleChannelUpstreamHandler {
+ private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketSslServerHandler.class);
+
+ private static final String WEBSOCKET_PATH = "/websocket";
+
+ private WebSocketServerHandshaker handshaker = null;
+
+ @Override
+ public void messageReceived(ChannelHandlerContext ctx, MessageEvent e) throws Exception {
+ Object msg = e.getMessage();
+ if (msg instanceof HttpRequest) {
+ handleHttpRequest(ctx, (HttpRequest) msg);
+ } else if (msg instanceof WebSocketFrame) {
+ handleWebSocketFrame(ctx, (WebSocketFrame) msg);
+ }
+ }
+
+ private void handleHttpRequest(ChannelHandlerContext ctx, HttpRequest req) throws Exception {
+ // Allow only GET methods.
+ if (req.getMethod() != GET) {
+ sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN));
+ return;
+ }
+
+ // Send the demo page and favicon.ico
+ if (req.getUri().equals("/")) {
+ HttpResponse res = new DefaultHttpResponse(HTTP_1_1, OK);
+
+ ChannelBuffer content = WebSocketSslServerIndexPage.getContent(getWebSocketLocation(req));
+
+ res.setHeader(CONTENT_TYPE, "text/html; charset=UTF-8");
+ setContentLength(res, content.readableBytes());
+
+ res.setContent(content);
+ sendHttpResponse(ctx, req, res);
+ return;
+ } else if (req.getUri().equals("/favicon.ico")) {
+ HttpResponse res = new DefaultHttpResponse(HTTP_1_1, NOT_FOUND);
+ sendHttpResponse(ctx, req, res);
+ return;
+ }
+
+ // Handshake
+ WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory(
+ this.getWebSocketLocation(req), null, false);
+ this.handshaker = wsFactory.newHandshaker(ctx, req);
+ if (this.handshaker == null) {
+ wsFactory.sendUnsupportedWebSocketVersionResponse(ctx);
+ } else {
+ this.handshaker.executeOpeningHandshake(ctx, req);
+ }
+ }
+
+ private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) {
+
+ // Check for closing frame
+ if (frame instanceof CloseWebSocketFrame) {
+ this.handshaker.executeClosingHandshake(ctx, (CloseWebSocketFrame) frame);
+ return;
+ } else if (frame instanceof PingWebSocketFrame) {
+ ctx.getChannel().write(new PongWebSocketFrame(frame.getBinaryData()));
+ return;
+ } else if (!(frame instanceof TextWebSocketFrame)) {
+ throw new UnsupportedOperationException(String.format("%s frame types not supported", frame.getClass()
+ .getName()));
+ }
+
+ // Send the uppercase string back.
+ String request = ((TextWebSocketFrame) frame).getText();
+ logger.debug(String.format("Channel %s received %s", ctx.getChannel().getId(), request));
+ ctx.getChannel().write(new TextWebSocketFrame(request.toUpperCase()));
+ }
+
+ private void sendHttpResponse(ChannelHandlerContext ctx, HttpRequest req, HttpResponse res) {
+ // Generate an error page if response status code is not OK (200).
+ if (res.getStatus().getCode() != 200) {
+ res.setContent(ChannelBuffers.copiedBuffer(res.getStatus().toString(), CharsetUtil.UTF_8));
+ setContentLength(res, res.getContent().readableBytes());
+ }
+
+ // Send the response and close the connection if necessary.
+ ChannelFuture f = ctx.getChannel().write(res);
+ if (!isKeepAlive(req) || res.getStatus().getCode() != 200) {
+ f.addListener(ChannelFutureListener.CLOSE);
+ }
+ }
+
+ @Override
+ public void exceptionCaught(ChannelHandlerContext ctx, ExceptionEvent e) throws Exception {
+ e.getCause().printStackTrace();
+ e.getChannel().close();
+ }
+
+ private String getWebSocketLocation(HttpRequest req) {
+ return "wss://" + req.getHeader(HttpHeaders.Names.HOST) + WEBSOCKET_PATH;
+ }
+}
diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerIndexPage.java b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerIndexPage.java
new file mode 100644
index 000000000000..8fe45996f9f2
--- /dev/null
+++ b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerIndexPage.java
@@ -0,0 +1,73 @@
+/*
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * Red Hat 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.example.http.websocketx.sslserver;
+
+import org.jboss.netty.buffer.ChannelBuffer;
+import org.jboss.netty.buffer.ChannelBuffers;
+import org.jboss.netty.util.CharsetUtil;
+
+
+/**
+ * Generates the demo HTML page which is served at http://localhost:8080/
+ *
+ * @author The Netty Project
+ * @author Trustin Lee
+ * @author Vibul Imtarnasan
+ *
+ * @version $Rev$, $Date$
+ */
+public class WebSocketSslServerIndexPage {
+
+ private static final String NEWLINE = "\r\n";
+
+ public static ChannelBuffer getContent(String webSocketLocation) {
+ return ChannelBuffers.copiedBuffer(
+ "Web Socket Test" + NEWLINE +
+ "" + NEWLINE +
+ "" + NEWLINE +
+ "" + NEWLINE +
+ "" + NEWLINE +
+ "" + NEWLINE,
+ CharsetUtil.US_ASCII);
+ }
+}
diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerPipelineFactory.java b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerPipelineFactory.java
new file mode 100644
index 000000000000..6b3e2bf9ed48
--- /dev/null
+++ b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerPipelineFactory.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2010 Red Hat, Inc.
+ *
+ * Red Hat 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.example.http.websocketx.sslserver;
+
+import static org.jboss.netty.channel.Channels.*;
+
+import javax.net.ssl.SSLEngine;
+
+import org.jboss.netty.channel.ChannelPipeline;
+import org.jboss.netty.channel.ChannelPipelineFactory;
+import org.jboss.netty.handler.codec.http.HttpChunkAggregator;
+import org.jboss.netty.handler.codec.http.HttpRequestDecoder;
+import org.jboss.netty.handler.codec.http.HttpResponseEncoder;
+import org.jboss.netty.handler.ssl.SslHandler;
+
+/**
+ * @author The Netty Project
+ * @author Trustin Lee
+ * @author Vibul Imtarnasan
+ *
+ * @version $Rev$, $Date$
+ */
+public class WebSocketSslServerPipelineFactory implements ChannelPipelineFactory {
+ @Override
+ public ChannelPipeline getPipeline() throws Exception {
+ // Create a default pipeline implementation.
+ ChannelPipeline pipeline = pipeline();
+
+ SSLEngine engine = WebSocketSslServerSslContext.getInstance().getServerContext().createSSLEngine();
+ engine.setUseClientMode(false);
+ pipeline.addLast("ssl", new SslHandler(engine));
+
+ pipeline.addLast("decoder", new HttpRequestDecoder());
+ pipeline.addLast("aggregator", new HttpChunkAggregator(65536));
+ pipeline.addLast("encoder", new HttpResponseEncoder());
+ pipeline.addLast("handler", new WebSocketSslServerHandler());
+ return pipeline;
+ }
+}
diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerSslContext.java b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerSslContext.java
new file mode 100644
index 000000000000..6b668e60dc67
--- /dev/null
+++ b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerSslContext.java
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2009 Red Hat, Inc.
+ *
+ * Red Hat 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.example.http.websocketx.sslserver;
+
+import java.io.FileInputStream;
+import java.security.KeyStore;
+import java.security.Security;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+
+import org.jboss.netty.logging.InternalLogger;
+import org.jboss.netty.logging.InternalLoggerFactory;
+
+/**
+ * Creates a {@link SSLContext} for just server certificates.
+ *
+ * @author The Netty Project
+ * @author Trustin Lee
+ * @author Vibul Imtarnasan
+ *
+ * @version $Rev$, $Date$
+ */
+public class WebSocketSslServerSslContext {
+
+ private static final InternalLogger logger = InternalLoggerFactory.getInstance(WebSocketSslServerSslContext.class);
+ private static final String PROTOCOL = "TLS";
+ private SSLContext _serverContext;
+
+ /**
+ * Returns the singleton instance for this class
+ */
+ public static WebSocketSslServerSslContext getInstance() {
+ return SingletonHolder.INSTANCE;
+ }
+
+ /**
+ * SingletonHolder is loaded on the first execution of
+ * Singleton.getInstance() or the first access to SingletonHolder.INSTANCE,
+ * not before.
+ *
+ * See http://en.wikipedia.org/wiki/Singleton_pattern
+ */
+ private static class SingletonHolder {
+
+ public static final WebSocketSslServerSslContext INSTANCE = new WebSocketSslServerSslContext();
+ }
+
+ /**
+ * Constructor for singleton
+ */
+ private WebSocketSslServerSslContext() {
+ try {
+ // Key store (Server side certificate)
+ String algorithm = Security.getProperty("ssl.KeyManagerFactory.algorithm");
+ if (algorithm == null) {
+ algorithm = "SunX509";
+ }
+
+ SSLContext serverContext = null;
+ try {
+ String keyStoreFilePath = System.getProperty("keystore.file.path");
+ String keyStoreFilePassword = System.getProperty("keystore.file.password");
+
+ KeyStore ks = KeyStore.getInstance("JKS");
+ FileInputStream fin = new FileInputStream(keyStoreFilePath);
+ ks.load(fin, keyStoreFilePassword.toCharArray());
+
+ // Set up key manager factory to use our key store
+ // Assume key password is the same as the key store file
+ // password
+ KeyManagerFactory kmf = KeyManagerFactory.getInstance(algorithm);
+ kmf.init(ks, keyStoreFilePassword.toCharArray());
+
+ // Initialise the SSLContext to work with our key managers.
+ serverContext = SSLContext.getInstance(PROTOCOL);
+ serverContext.init(kmf.getKeyManagers(), null, null);
+ } catch (Exception e) {
+ throw new Error("Failed to initialize the server-side SSLContext", e);
+ }
+ _serverContext = serverContext;
+
+ return;
+ } catch (Exception ex) {
+ logger.error("Error initializing SslContextManager. " + ex.getMessage(), ex);
+ System.exit(1);
+
+ }
+ }
+
+ /**
+ * Returns the server context with server side key store
+ */
+ public SSLContext getServerContext() {
+ return _serverContext;
+ }
+
+}
diff --git a/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/package-info.java b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/package-info.java
new file mode 100644
index 000000000000..3e91cb30a873
--- /dev/null
+++ b/src/main/java/org/jboss/netty/example/http/websocketx/sslserver/package-info.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright 2009 Red Hat, Inc.
+ *
+ * Red Hat 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.
+ */
+
+/**
+ * This package contains an example web socket web server with server SSL.
+ *
To run this example, follow the steps below:
+ *
+ * - Step 1. Generate Your Key
+ * -
+ *
keytool -genkey -keystore mySrvKeystore -keyalg RSA
.
+ * Make sure that you set the key password to be the same the key file password.
+ *
+ * - Step 2. Specify your key store file and password as system properties
+ * -
+ *
-Dkeystore.file.path=<path to mySrvKeystore> -Dkeystore.file.password=<password>
+ *
+ * - Step 3. Run WebSocketSslServer as a Java application
+ * -
+ * Once started, you can test the web server against your browser by navigating to https://localhost:8081/
+ *
+ *
+ * To find out more about setting up key stores, refer to this
+ * giude.
+ */
+package org.jboss.netty.example.http.websocketx.sslserver;
+