Search code examples
scalajvmfinagle

How to use a Filtered Service with RoutingService in Finagle


Fair warning, I'm new to Scala, so I could be missing something simple here.

I have this code, it works great

import java.net.InetSocketAddress

import com.twitter.finagle.builder.Server
import com.twitter.finagle.builder.ServerBuilder
import com.twitter.finagle.http.Http
import com.twitter.finagle.http.Request
import com.twitter.finagle.http.RichHttp
import java.util.logging.Logger
import com.typesafe.config.ConfigFactory
import org.jboss.netty.buffer.ChannelBuffers.copiedBuffer
import org.jboss.netty.util.CharsetUtil.UTF_8
import com.twitter.finagle.Service
import com.twitter.finagle.http.Response
import com.twitter.util.Future
import com.twitter.finagle.http.service.RoutingService
import org.jboss.netty.handler.codec.http.HttpResponse
import org.jboss.netty.handler.codec.http.HttpRequest
import com.twitter.finagle.SimpleFilter

object TestServer extends App {

    val myService = new 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("Hello World", UTF_8))
                                response
                    }
                }
            }

            val routingService = RoutingService.byPath {
                case "/" => myService
            }

    val server: Server = ServerBuilder()
            .codec(RichHttp[Request](Http()))
            .bindTo(new InetSocketAddress(8080))
            .name("httpserver")
            .build(routingService)

    println("Server is up.")

    class Authorize extends SimpleFilter[HttpRequest, HttpResponse] {
        def apply(request: HttpRequest, continue: Service[HttpRequest, HttpResponse]) = {
            println("filtered")
            continue(request)
    }
}

}

But I want to add that Authorize filter to my service. If I add this:

val authorize = new Authorize
val aService: Service[HttpRequest, HttpResponse] = authorize andThen myService

I get a compilation error:

[error]  found   : com.twitter.finagle.Service[com.twitter.finagle.http.Request,com.twitter.finagle.http.Response]
[error]  required: com.twitter.finagle.Service[org.jboss.netty.handler.codec.http.HttpRequest,org.jboss.netty.handler.codec.http.HttpResponse]

If I change the service to be type Service[HttpRequest, HttpResponse], I can't use it the routingService.

[error]  found   : com.twitter.finagle.Service[org.jboss.netty.handler.codec.http.HttpRequest,org.jboss.netty.handler.codec.http.HttpResponse]
[error]  required: com.twitter.finagle.Service[?,com.twitter.finagle.http.Response]

Due to these type errors, I am having a hard time getting the routing service to work with filters.

Thanks!

EDIT:

I just wanted to add an example of the code working using @Septem's answer below, use this for the filter:

class Authorize extends Filter[Request, Response, Request, Response] {
        def apply(request: Request, continue: Service[Request, Response]) = {
            println("filtered")
            continue(request)
        }
    }

I'm stilla little hazy on finagle and scala- but I guess what was happening here is that I was using the SimpleFilter which already assumed a service of a certain type,


Solution

  • why do you use different request and response types in your service and filter?

    your service is using request and response from finagle:

    val myService = new Service[Request, Response] 
    

    but your filter is using request and response from netty:

    class Authorize extends SimpleFilter[HttpRequest, HttpResponse]
    

    I think this is the reason for the compliation error, you have to choose one that satisfies your need, and stick to it.

    --edit--

    then I think you should implement your filter base on Filter instead of SimpleFilter

    abstract class Filter[-ReqIn, +RepOut, +ReqOut, -RepIn] extends ((ReqIn, Service[ReqOut, RepIn]) => Future[RepOut])
    

    the abstract class Filter allow you to transform request/response types

    check out the doc for details