A Reactive HTTP Reverse Proxy in Play!

By on November 6th, 2014 in Product and Technology, Uncategorized
TrueAccord Blog

At TrueAccord, we use Play to develop our backend. Given our development environment, we need to have part of our URL space routed to a different server written in Python. We initially thought of setting up a lightweight HTTP server like nginx that would act as a reverse proxy for both of our development servers, which is a reasonable solution. However, we also wanted to avoid having yet another moving part in our development environment and were curious if we could write something quick in Scala that could achieve this.

As it turns out, writing this little reverse proxy in Scala/Play is relatively straightforward. It’s also pretty impressive that with so few lines of code we get a reactive proxy server that streams the content continuously to the end client while chunks of it are still arriving from the upstream server. A more traditional (and time-intensive) implementation would have buffered the entire upstream response until it was complete and only then sent it to the client..

So, without further ado, here is the code:

In line 14, proxyRequest.stream returns a Future[(WSResponseHeaders, Enumerator[Array[Byte]])]. This means that at some point in the future, our closure at line 15 will get called and will be supplied two things: the headers returned from the upsteam server (WSResponseHeaders) and an Enumerator[Array[Byte]], which is a producer of arrays of bytes. Each array of byte that it will produce is a part of the response body from the upstream server. Conveniently, Play provides a Result constructor that takes producers like this and turns them into responses that can be served to the end client.

flattenMultiMap is a little helper function that converts the query string parameters from the collection type they are given by Play requests to the format expected by WS.url.

Pretty cool, eh?

5 thoughts on “A Reactive HTTP Reverse Proxy in Play!

  1. I digged into our git log to find this. Here we go:

    private def flattenMultiMap(headers: Map[String, Seq[String]]): Seq[(String, String)] = for {
    (name, values) <- headers.toSeq
    value <- values
    } yield (name, value)

Leave a Reply

Your email address will not be published. Required fields are marked *