Skip to content

Instantly share code, notes, and snippets.

@itzg
Created October 21, 2013 20:07

Revisions

  1. itzg created this gist Oct 21, 2013.
    103 changes: 103 additions & 0 deletions SmallNettyServer.groovy
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,103 @@
    import org.jboss.netty.bootstrap.ServerBootstrap
    import org.jboss.netty.buffer.ChannelBuffers
    import org.jboss.netty.channel.ChannelHandlerContext
    import org.jboss.netty.channel.ChannelPipeline
    import org.jboss.netty.channel.ChannelPipelineFactory
    import org.jboss.netty.channel.Channels
    import org.jboss.netty.channel.MessageEvent
    import org.jboss.netty.channel.SimpleChannelUpstreamHandler
    import org.jboss.netty.channel.socket.nio.NioServerSocketChannelFactory
    import org.jboss.netty.handler.codec.http.DefaultHttpResponse
    import org.jboss.netty.handler.codec.http.HttpHeaders
    import org.jboss.netty.handler.codec.http.HttpMethod
    import org.jboss.netty.handler.codec.http.HttpRequest
    import org.jboss.netty.handler.codec.http.HttpResponse
    import org.jboss.netty.handler.codec.http.HttpResponseStatus
    import org.jboss.netty.handler.codec.http.HttpServerCodec
    import org.jboss.netty.handler.codec.http.HttpVersion
    import org.slf4j.Logger
    import org.slf4j.LoggerFactory


    class SmallServer {

    protected static Logger logger = LoggerFactory.getLogger(SmallServer)


    protected final routes = [
    '/content.json': { req ->
    '{"field":"value"}'
    },
    '/': {
    """<body>
    Success. Here's <a href='/missing'>a link to missing content</a>
    </body>
    """
    }
    ]


    protected static HttpResponse createBadResponse(HttpResponseStatus status) {
    def response = new DefaultHttpResponse(HttpVersion.HTTP_1_0, status)
    response.addHeader(HttpHeaders.Names.CONTENT_LENGTH, 0)

    return response
    }

    int startServer() {

    final def us = this

    ServerBootstrap serverBootstrap = new ServerBootstrap(new NioServerSocketChannelFactory())
    serverBootstrap.pipelineFactory = new ChannelPipelineFactory() {
    ChannelPipeline getPipeline() throws Exception {
    Channels.pipeline(new HttpServerCodec(), new SimpleChannelUpstreamHandler() {
    void messageReceived(ChannelHandlerContext ctx, MessageEvent evt) throws Exception {
    if (evt.message instanceof HttpRequest && evt.message.method == HttpMethod.GET) {
    HttpRequest req = evt.message

    def routeHandlerEntry = us.routes.find { it.key.isCase(req.uri) }
    if (routeHandlerEntry) {
    SmallServer.logger.info("Routing ${req.uri}")

    def response = new DefaultHttpResponse(HttpVersion.HTTP_1_0, HttpResponseStatus.OK)
    try {
    def stringContent = routeHandlerEntry.value.call(req)
    def content = ChannelBuffers.copiedBuffer(stringContent, Charset.forName('UTF-8'))
    response.content = content
    response.addHeader(HttpHeaders.Names.CONTENT_LENGTH, content.readableBytes())
    if (stringContent[0] in ['<']) {
    response.addHeader(HttpHeaders.Names.CONTENT_TYPE, 'text/html')
    }
    else if (stringContent[0] in ['[','{']) {
    response.addHeader(HttpHeaders.Names.CONTENT_TYPE, 'application/json')
    }
    evt.channel.write(response)
    }
    catch (Exception e) {
    SmallServer.logger.error("Handler ${req.uri} failed", e)
    evt.channel.write(createBadResponse(HttpResponseStatus.INTERNAL_SERVER_ERROR))
    }
    }
    else {
    SmallServer.logger.warn("Not found: ${req.uri}")
    evt.channel.write(createBadResponse(HttpResponseStatus.NOT_FOUND))
    }

    }
    else {
    SmallServer.logger.warn("Bad request: ${req}")
    evt.channel.write(createBadResponse(HttpResponseStatus.BAD_REQUEST))
    }
    }
    })
    }
    }

    def serverPort = serverBootstrap.bind(new InetSocketAddress(0)).localAddress.port
    println "Server is ready at http://localhost:${serverPort}"
    }

    }