Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Forwarding HTTP/REST Request to another REST server in Spray

I've a bunch of existing REST services (#1 and #2 below) that are running on different endpoints that are used internally only. Now I want to expose some of these REST APIs (API-1 and API-2) externally using Spray because this external endpoint will also provide some additional APIs (API-3, API-4).

Is there a simple/recommended way to forward the external REST requests to my new endpoint to existing REST endpoints?

enter image description here

like image 461
Soumya Simanta Avatar asked Jan 17 '15 00:01

Soumya Simanta


2 Answers

It sounds like what you want is the proposed proxyTo directive:

path("foo") {
  get {
    proxyTo("http://oldapi.example.com")
  }
}

(or, more likely, proxyToUnmatchedPath). There is an issue open for it:

https://github.com/spray/spray/issues/145

Looks like somebody has been working on this; here is a commit in a Spray fork:

https://github.com/bthuillier/spray/commit/d31fc1b5e1415e1b908fe7d1f01f364a727e2593

But the commit appears not yet to be in the master Spray repo. You could ask about its status on the issue page.

Also, here is a blog post from CakeSolutions about how you can do the proxying manually:

http://www.cakesolutions.net/teamblogs/http-proxy-with-spray

A comment on that page points out that Spray has an undocumented thing called ProxySettings, and points to the following tests for it:

https://github.com/spray/spray/blob/master/spray-can-tests/src/test/scala/spray/can/client/ProxySpec.scala

UPDATE; Soumya has asked the Spray team about this on the spray-user Google Group:

https://groups.google.com/forum/#!topic/spray-user/MlUn-y4X8RE

like image 82
AmigoNico Avatar answered Oct 21 '22 11:10

AmigoNico


I was able to proxy a single service with the help of the CakeSolution blog. In the following example, the proxy is running on http://localhost:20000 and the actual REST endpoint is running at http://localhost:7001.

Not sure how proxy multiple services using this approach.

I like @cmbaxter's solution of using Nginx as the proxy but I'm still curious if there is a way to extend the following approach to do it in Spray.

import akka.actor.{ActorRef, Props}
import akka.io.IO
import akka.util.Timeout
import spray.can.Http
import spray.can.Http.ClientConnectionType
import spray.http.HttpResponse
import spray.routing.{RequestContext, HttpServiceActor, Route}


import scala.concurrent.duration._
import akka.pattern.ask


object ProxyRESTService {

   def main(args: Array[String]) {

   //create an actor system
   implicit val actorSystem = akka.actor.ActorSystem("proxy-actor-system")
   implicit val timeout: Timeout = Timeout(5 seconds)
   implicit val dis = actorSystem.dispatcher

   //host on which proxy is running
   val proxyHost = "localhost"
   //port on which proxy is listening
   val proxyPort = 20000

  //host where REST service is running
  val restServiceHost = "localhost"
  //port where REST service is running
  val restServicePort = 7001

  val setup = Http.HostConnectorSetup(
   proxyHost,
   proxyPort,
   connectionType = ClientConnectionType.Proxied(restServiceHost,   restServicePort)
)

IO(Http)(actorSystem).ask(setup).map {
  case Http.HostConnectorInfo(connector, _) =>
    val service = actorSystem.actorOf(Props(new ProxyService(connector)))
    IO(Http) ! Http.Bind(service, proxyHost, port = proxyPort)
}
}

}

.

class ProxyService(connector: ActorRef) extends HttpServiceActor  {
  implicit val timeout: Timeout = Timeout(5 seconds)
  implicit def executionContext = actorRefFactory.dispatcher
  val route: Route = (ctx: RequestContext) => ctx.complete(connector.ask(ctx.request).mapTo[HttpResponse])
  def receive: Receive = runRoute(route)
}
like image 29
Soumya Simanta Avatar answered Oct 21 '22 11:10

Soumya Simanta