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?
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
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)
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With