-
-
Save stonegao/2927803 to your computer and use it in GitHub Desktop.
Routing based on HTTP method in Finagle
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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