Skip to content

Instantly share code, notes, and snippets.

@stonegao
Forked from soheilhy/HttpServer.scala
Created June 14, 2012 02:53
Show Gist options
  • Star 8 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stonegao/2927803 to your computer and use it in GitHub Desktop.
Save stonegao/2927803 to your computer and use it in GitHub Desktop.
Routing based on HTTP method in Finagle
import com.twitter.finagle.http.path._
import com.twitter.finagle.http.service.RoutingService
import com.twitter.finagle.http.{Request, Response, RichHttp, Http}
import com.twitter.finagle.{Service, SimpleFilter}
import org.jboss.netty.handler.codec.http._
import org.jboss.netty.handler.codec.http.HttpResponseStatus._
import org.jboss.netty.handler.codec.http.HttpVersion.HTTP_1_1
import org.jboss.netty.buffer.ChannelBuffers.copiedBuffer
import org.jboss.netty.util.CharsetUtil.UTF_8
import com.twitter.util.Future
import java.net.InetSocketAddress
import com.twitter.finagle.builder.{Server, ServerBuilder}
import com.twitter.finagle.http.Method
/**
* This example demonstrates a sophisticated HTTP server that handles exceptions
* and performs authorization via a shared secret. The exception handling and
* authorization code are written as Filters, thus isolating these aspects from
* the main service (here called "Respond") for better code organization.
*/
object HttpServer {
/**
* A simple Filter that catches exceptions and converts them to appropriate
* HTTP responses.
*/
class HandleExceptions extends SimpleFilter[HttpRequest, HttpResponse] {
def apply(request: HttpRequest, service: Service[HttpRequest, HttpResponse]) = {
// `handle` asynchronously handles exceptions.
service(request) handle {
case error =>
val statusCode = error match {
case _: IllegalArgumentException =>
FORBIDDEN
case _ =>
INTERNAL_SERVER_ERROR
}
val errorResponse = new DefaultHttpResponse(HTTP_1_1, statusCode)
errorResponse.setContent(copiedBuffer(error.getStackTraceString, UTF_8))
errorResponse
}
}
}
/**
* A simple Filter that checks that the request is valid by inspecting the
* "Authorization" header.
*/
class Authorize extends SimpleFilter[HttpRequest, HttpResponse] {
def apply(request: HttpRequest, continue: Service[HttpRequest, HttpResponse]) = {
if ("open sesame" == request.getHeader("Authorization")) {
continue(request)
} else {
Future.exception(new IllegalArgumentException("You don't know the secret"))
}
}
}
/**
* The service itself. Simply echos back "hello world"
*/
class Respond extends Service[HttpRequest, HttpResponse] {
def apply(request: HttpRequest) = {
val response = new DefaultHttpResponse(HTTP_1_1, OK)
response.setContent(copiedBuffer("hello world", UTF_8))
Future.value(response)
}
}
class ShowService extends Service[Request, Response] {
def apply(request: Request) = {
val response = new DefaultHttpResponse(HTTP_1_1, OK)
response.setContent(copiedBuffer("show service world", UTF_8))
Future.value(Response(response))
}
}
class SearchService(val i: Int) extends Service[Request, Response] {
def apply(request: Request) = {
val response = new DefaultHttpResponse(HTTP_1_1, OK)
response.setContent(copiedBuffer("search service world", UTF_8))
Future.value(Response(response))
}
}
val myShowService = new ShowService
def main(args: Array[String]) {
val handleExceptions = new HandleExceptions
val authorize = new Authorize
val respond = new Respond
val routingService =
MyRoutingService.byRequest { request =>
(request.method, Path(request.path)) match {
case Method.Get -> Root / "api1" / Integer(userId) => myShowService
case Method.Post -> Root / "api2" / Integer(userId) => new SearchService(userId)
}
}
// compose the Filters and Service together:
val myService: Service[HttpRequest, HttpResponse] = handleExceptions andThen respond
val server: Server = ServerBuilder()
.codec(RichHttp[Request](Http()))
.bindTo(new InetSocketAddress(8080))
.name("httpserver")
.build(routingService)
}
}
object MyRoutingService {
def byRequest[REQUEST](routes: PartialFunction[Request, Service[REQUEST, Response]]) =
new RoutingService(
new PartialFunction[Request, Service[REQUEST, Response]] {
def apply(request: Request) = routes(request)
def isDefinedAt(request: Request) = routes.isDefinedAt(request)
})
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment