Skip to content

Instantly share code, notes, and snippets.

@soheilhy
Created June 14, 2012 02:45
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 4 You must be signed in to fork a gist
  • Save soheilhy/2927765 to your computer and use it in GitHub Desktop.
Save soheilhy/2927765 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[Request, Response] {
def apply(request: Request, service: Service[Request, Response]) = {
// `handle` asynchronously handles exceptions.
service(request) handle {
case error =>
error.printStackTrace()
val statusCode = error match {
case _: IllegalArgumentException =>
FORBIDDEN
case _ =>
INTERNAL_SERVER_ERROR
}
val errorResponse = Response(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[Request, Response] {
def apply(request: Request, continue: Service[Request, Response]) = {
if ("open sesame" == request.getHeader("Authorization")) {
continue(request)
} else {
Future.exception(new IllegalArgumentException("You don't know the secret"))
}
}
}
object ShowService extends Service[Request, Response] {
def apply(request: Request) = {
val response = Response()
response.setContent(copiedBuffer("show service world", UTF_8))
Future.value(response)
}
}
class SearchService(val i: Int) extends Service[Request, Response] {
def apply(request: Request) = {
// If you throw an exception here, the filter will catch it.
// throw new RuntimeException("s")
Future {
val response = Response()
response.setContent(copiedBuffer("search service world", UTF_8))
response
}
}
}
def main(args: Array[String]) {
val handleExceptions = new HandleExceptions
val routingService =
MyRoutingService.byRequest { request =>
(request.method, Path(request.path)) match {
case Method.Get -> Root / "api1" / Integer(userId) => ShowService
case Method.Post -> Root / "api2" / Integer(userId) => new SearchService(userId)
}
}
// compose the Filters and Service together:
val debuggingService: Service[Request, Response] =
handleExceptions andThen routingService
val server: Server = ServerBuilder()
.codec(RichHttp[Request](Http()))
.bindTo(new InetSocketAddress(8080))
.name("httpserver")
.build(debuggingService)
}
}
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