forked from netty/netty
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request netty#53 from veebs/WebSocketSSL
Added WebSocket Server with SSL example app
- Loading branch information
Showing
6 changed files
with
501 additions
and
0 deletions.
There are no files selected for viewing
74 changes: 74 additions & 0 deletions
74
src/main/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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: | ||
* | ||
* <ul> | ||
* <li>Safari 5+ (draft-ietf-hybi-thewebsocketprotocol-00) | ||
* <li> | ||
* <li>Chrome 6-13 (draft-ietf-hybi-thewebsocketprotocol-00) | ||
* <li> | ||
* <li>Chrome 14+ (draft-ietf-hybi-thewebsocketprotocol-10) | ||
* <li> | ||
* <li>Firefox 7+ (draft-ietf-hybi-thewebsocketprotocol-10) | ||
* <li> | ||
* </ul> | ||
* | ||
* @author <a href="http://www.jboss.org/netty/">The Netty Project</a> | ||
* @author <a href="http://gleamynode.net/">Trustin Lee</a> | ||
* @author <a href="http://www.veebsbraindump.com/">Vibul Imtarnasan</a> | ||
* | ||
* @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/"); | ||
} | ||
} |
152 changes: 152 additions & 0 deletions
152
...ain/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerHandler.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <a href="http://www.jboss.org/netty/">The Netty Project</a> | ||
* @author <a href="http://gleamynode.net/">Trustin Lee</a> | ||
* @author <a href="http://www.veebsbraindump.com/">Vibul Imtarnasan</a> | ||
* | ||
* @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; | ||
} | ||
} |
73 changes: 73 additions & 0 deletions
73
...n/java/org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerIndexPage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <a href="http://www.jboss.org/netty/">The Netty Project</a> | ||
* @author <a href="http://gleamynode.net/">Trustin Lee</a> | ||
* @author <a href="http://www.veebsbraindump.com/">Vibul Imtarnasan</a> | ||
* | ||
* @version $Rev$, $Date$ | ||
*/ | ||
public class WebSocketSslServerIndexPage { | ||
|
||
private static final String NEWLINE = "\r\n"; | ||
|
||
public static ChannelBuffer getContent(String webSocketLocation) { | ||
return ChannelBuffers.copiedBuffer( | ||
"<html><head><title>Web Socket Test</title></head>" + NEWLINE + | ||
"<body>" + NEWLINE + | ||
"<script type=\"text/javascript\">" + NEWLINE + | ||
"var socket;" + NEWLINE + | ||
"if (!window.WebSocket) {" + NEWLINE + | ||
" window.WebSocket = window.MozWebSocket;" + NEWLINE + | ||
"}" + NEWLINE + | ||
"if (window.WebSocket) {" + NEWLINE + | ||
" socket = new WebSocket(\"" + webSocketLocation + "\");" + NEWLINE + | ||
" socket.onmessage = function(event) { var ta = document.getElementById('responseText'); ta.value = ta.value + '\\n' + event.data };" + NEWLINE + | ||
" socket.onopen = function(event) { var ta = document.getElementById('responseText'); ta.value = \"Web Socket opened!\"; };" + NEWLINE + | ||
" socket.onclose = function(event) { var ta = document.getElementById('responseText'); ta.value = ta.value + \"Web Socket closed\"; };" + NEWLINE + | ||
"} else {" + NEWLINE + | ||
" alert(\"Your browser does not support Web Socket.\");" + NEWLINE + | ||
"}" + NEWLINE + | ||
"" + NEWLINE + | ||
"function send(message) {" + NEWLINE + | ||
" if (!window.WebSocket) { return; }" + NEWLINE + | ||
" if (socket.readyState == WebSocket.OPEN) {" + NEWLINE + | ||
" socket.send(message);" + NEWLINE + | ||
" } else {" + NEWLINE + | ||
" alert(\"The socket is not open.\");" + NEWLINE + | ||
" }" + NEWLINE + | ||
"}" + NEWLINE + | ||
"</script>" + NEWLINE + | ||
"<form onsubmit=\"return false;\">" + NEWLINE + | ||
"<input type=\"text\" name=\"message\" value=\"Hello, World!\"/>" + | ||
"<input type=\"button\" value=\"Send Web Socket Data\" onclick=\"send(this.form.message.value)\" />" + NEWLINE + | ||
"<h3>Output</h3>" + NEWLINE + | ||
"<textarea id=\"responseText\" style=\"width: 500px; height:300px;\"></textarea>" + NEWLINE + | ||
"</form>" + NEWLINE + | ||
"</body>" + NEWLINE + | ||
"</html>" + NEWLINE, | ||
CharsetUtil.US_ASCII); | ||
} | ||
} |
52 changes: 52 additions & 0 deletions
52
.../org/jboss/netty/example/http/websocketx/sslserver/WebSocketSslServerPipelineFactory.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 <a href="http://www.jboss.org/netty/">The Netty Project</a> | ||
* @author <a href="http://gleamynode.net/">Trustin Lee</a> | ||
* @author <a href="http://www.veebsbraindump.com/">Vibul Imtarnasan</a> | ||
* | ||
* @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; | ||
} | ||
} |
Oops, something went wrong.