|
// Simple/raw translation of the [bad] Java Netty WS example to Scala. Nothing interesting...just first phase. |
|
|
|
import java.net.InetSocketAddress; |
|
import java.util.concurrent.Executors; |
|
import org.jboss.netty.bootstrap.ServerBootstrap; |
|
import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory; |
|
|
|
import org.jboss.netty.handler.codec.http.HttpHeaders._ |
|
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.HttpResponseStatus._ |
|
import org.jboss.netty.handler.codec.http.HttpVersion._ |
|
import org.jboss.netty.buffer.{ChannelBuffer,ChannelBuffers} |
|
import org.jboss.netty.channel.{ChannelFuture,ChannelFutureListener,ChannelHandlerContext,ChannelPipeline,ExceptionEvent,MessageEvent,SimpleChannelUpstreamHandler} |
|
|
|
import org.jboss.netty.channel.Channels._ |
|
import org.jboss.netty.channel.{ChannelPipeline,ChannelPipelineFactory} |
|
import org.jboss.netty.handler.codec.http.{HttpChunkAggregator,HttpRequestDecoder,HttpResponseEncoder} |
|
|
|
import org.jboss.netty.handler.codec.http.{DefaultHttpResponse,HttpHeaders,HttpRequest,HttpResponse,HttpResponseStatus} |
|
import org.jboss.netty.handler.codec.http.HttpHeaders.{Names,Values} |
|
import org.jboss.netty.handler.codec.http.websocket.{DefaultWebSocketFrame,WebSocketFrame,WebSocketFrameDecoder,WebSocketFrameEncoder} |
|
|
|
import org.jboss.netty.util.CharsetUtil; |
|
|
|
case class UnknownRequestType(msg: String) extends Exception(msg) |
|
|
|
object WebSocketServerHandler { |
|
val WS_PATH = "/ws" |
|
} |
|
|
|
class WebSocketServerHandler extends SimpleChannelUpstreamHandler { |
|
@throws(classOf[Exception]) |
|
override def messageReceived(ctx: ChannelHandlerContext, e: MessageEvent) { |
|
val msg = e.getMessage() |
|
msg match { |
|
case _: HttpRequest => handleHttpRequest(ctx, msg.asInstanceOf[HttpRequest]) |
|
case _: WebSocketFrame => handleWebSocketFrame(ctx, msg.asInstanceOf[WebSocketFrame]) |
|
case _ => throw new UnknownRequestType("Unknown request message type: " + msg.getClass.getName) |
|
} |
|
} |
|
|
|
private[this] def handleHttpRequest(ctx: ChannelHandlerContext, req: HttpRequest) { |
|
req.getUri match { |
|
case WebSocketServerHandler.WS_PATH => sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, FORBIDDEN)) |
|
case _ => sendHttpResponse(ctx, req, new DefaultHttpResponse(HTTP_1_1, NOT_FOUND)) |
|
} |
|
} |
|
|
|
/* |
|
* This is the meat of the server event processing |
|
* Here we square the number and return it |
|
*/ |
|
private[this] def handleWebSocketFrame(ctx: ChannelHandlerContext, frame: WebSocketFrame) { |
|
System.out.println(frame.getTextData) |
|
try { |
|
val number = Integer.parseInt(frame.getTextData) |
|
ctx.getChannel().write(new DefaultWebSocketFrame((number*number).toString)) |
|
} catch { |
|
case e: NumberFormatException => |
|
ctx.getChannel().write(new DefaultWebSocketFrame("ERROR")) |
|
} |
|
} |
|
|
|
private[this] def sendHttpResponse(ctx: ChannelHandlerContext, req: HttpRequest, res: HttpResponse) { |
|
// Generate an error page if response status code is not OK (200). |
|
res.getStatus.getCode match { |
|
case 200 => |
|
res.setContent(ChannelBuffers.copiedBuffer(res.getStatus.toString, CharsetUtil.UTF_8)) |
|
setContentLength(res, res.getContent.readableBytes) |
|
case _ => |
|
if (!isKeepAlive(req)) { |
|
val f = ctx.getChannel.write(res) |
|
f.addListener(ChannelFutureListener.CLOSE) |
|
} |
|
} |
|
} |
|
|
|
@throws(classOf[Exception]) |
|
override def exceptionCaught(ctx: ChannelHandlerContext, e: ExceptionEvent) { |
|
e.getCause.printStackTrace |
|
e.getChannel.close |
|
} |
|
|
|
private[this] def getWebSocketLocation(req: HttpRequest) = "ws://" + req.getHeader(HttpHeaders.Names.HOST) + WebSocketServerHandler.WS_PATH |
|
} |
|
|
|
class WebSocketServerPipelineFactory extends ChannelPipelineFactory { |
|
@throws(classOf[Exception]) |
|
def getPipeline(): ChannelPipeline = { |
|
val pl = pipeline() |
|
pl.addLast("decoder", new HttpRequestDecoder) |
|
pl.addLast("aggregator", new HttpChunkAggregator(65536)) |
|
pl.addLast("encoder", new HttpResponseEncoder) |
|
pl.addLast("handler", new WebSocketServerHandler) |
|
pl |
|
} |
|
} |
|
|
|
/** |
|
* Representing an HTTP server that will serve WebSocket requests on path /ws |
|
*/ |
|
object WebSocketRunner { |
|
val PORT: Int = 8080 |
|
|
|
def main(args: Array[String]) { |
|
// Configure the server. |
|
val bootstrap = new ServerBootstrap( |
|
new NioServerSocketChannelFactory(Executors.newCachedThreadPool(), Executors.newCachedThreadPool())) |
|
|
|
// Set up the event pipeline factory. |
|
bootstrap.setPipelineFactory(new WebSocketServerPipelineFactory()) |
|
|
|
// Bind and start to accept incoming connections |
|
bootstrap.bind(new InetSocketAddress(PORT)) |
|
println("WebSocket Server Started") |
|
} |
|
} |