Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gregorydickson/1d3f6881bc00dfd95c3ef45870eb6ace to your computer and use it in GitHub Desktop.
Save gregorydickson/1d3f6881bc00dfd95c3ef45870eb6ace to your computer and use it in GitHub Desktop.
Just enough Groovy code to implement a GET'able HTTP server with regex based path routing.
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}"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment