sebi sebi - 2 months ago 35
Scala Question

Migrating routes from spray.io to akka http

I am working on migrating an application from Spray.io to Akka-http. The application is micro-service based, having many small libraries that we build on top of. Here's an example of composing directives and routes in one specific micro-service :

val routes =
(decompressRequest & compressResponseIfRequested) {
metricsRoute ~
healthStatusRoute ~
apiRoutes // only these are my app's routes
}


All of the above, except
apiRoutes
are defined in internal libraries.

I would like to start using Akka-http in this micro-service (migrate
apiRoutes
to Akka-http) without changing any of the libraries that i am currently using, because that would force all other developers to change their code at the same time.

Is this possible? Is there a way for Akka-http to make use of Spray.io directives/routes?

As far as i can tell, the migration guide has no such information.

Answer

This isn't difficult to do. As you know, a Route in Spray is RequestContext ⇒ Unit and a Route in Akka is RequestContext ⇒ Future[RouteResult] (with their respective versions of RequestContext, of course). What I did was create a wrapper that can wrap a Spray route to provide this conversion through a Spray service actor. Routes are individually wrapped then folded with ~ as necessary for the complete top level Akka route. Conversion over time is then a process of removing wrappers one-by-one until all the routes have been converted.

Since you are converting to Akka, start with a Akka HTTP socket handler and the result of your fold. Now you are going to add wrapped Spray routes to that fold.

For it's part, unconverted Spray directives want to respond to an Actor, so your wrapper creates a service actor for the route being wrapped. The presentation of this actor to Akka is simple: A function that takes a RequestContext and returns a Future[RouteResult] is the same as an ask() on the service actor with a message that returns a RouteResult (conveniently wrapped in a Future by the ActorSystem). Excluding the Actor, this wrapper is about ten lines of code.

The service actor itself extends Spray HttpServiceActor and accepts one message, which is an Akka RequestContext. This message is mechanically transformed into a Spray RequestContext, dealing with whatever usage patterns are required. The last line of that message handler is self ! ctx, sending the translated context to the Spray HttpServiceActor handler in the superclass.

The result translation back to Akka is through withRouteResponseMapped() on the RequestContext that was sent to Spray. The function that you pass does the reverse, mapping Spray constructs back to Akka and returning a RouteResult. This is pretty straightforward if you are just doing HttpEntity.Strict return values.

I wish I could post the code here, but it was written for a client and they have unknown (but apparently strict) constraints on IP sharing.