Some Notes

First, I realized I stole the naming scheme for this blog series from my friend Allison who wrote an awesome thing called Zero To Grails! Sorry Allison! You should check out her awesome work at More than a pipeline and gr8 ladies.

Second, I want to point out the excellent reference project for Ratpack called example-books. This is what I reference when I need to figure out how to do something or what has changed in a Ratpack release. It is updated for every Ratpack release.

Lastly, I’ve been considering switching the examples in this blog series from Groovy to Java. One advantage of Ratpack is the Groovy DSL and how simple it is to create a quick application with it, but I do think it hides what is really happening in Ratpack a bit. I’ve decided to stay with Groovy, but to post the equivalent Java once I’m done with the main series.

You can find all the code for this post on github with the tag ‘config-and-testing’

Configuration

Previously, the Reverse Proxy was hard coded to proxy all requests to cellarhq. That isn’t very useful, it would be better to based on an external config file. Ratpack’s configuration is excellent. It supports yaml, properties files, system properties, and environment variables to name a few, and more formats can be supported easily.

1
2
3
4
5
6
serverConfig { d -> d
  .props(Resources.asByteSource(Resources.getResource('application.properties')))
  .env()
  .sysProps()
  .require('/proxyConfig', ProxyConfig)
}

Loading configuration from a file and then layering on environment variables and system properties happens in the ratpack block of ratpack.groovy. The ServerConfig is available globally to anything that has access to the registry. The configuration specific to the reverse proxy is stored in a plain old groovy object, and is also added to the registry.

1
2
3
4
5
6
7
package reverseproxy.config

class ProxyConfig {
  String forwardToHost = 'InvalidHost'
  Integer forwardToPort = 80
  String forwardToScheme = 'http'
}

Finally, the ProxyConfig instance can be accessed from the registry and then used in handlers.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
all { HttpClient httpClient, ProxyConfig proxyConfig ->
  URI requestURI = new URI(request.rawUri)
  URI proxyUri = new URI(
          proxyConfig.forwardToScheme,
          requestURI.userInfo,
          proxyConfig.forwardToHost,
          proxyConfig.forwardToPort,
          requestURI.path,
          requestURI.query,
          requestURI.fragment)

  httpClient.requestStream(proxyUri) { RequestSpec spec ->
    spec.headers.copy(request.headers)
  }.then { StreamedResponse responseStream ->
    responseStream.forwardTo(response)
  }
}

Testing

So far the reverse proxy is very basic and doesn’t do much. Before additionally functionality is layered on, it is important to have a functional test to ensure everything keeps working. Testing is an important part of Ratpack and I previously wrote about how Ratpack can actually improve the testing of Grails and other frameworks.

A functional test of the reverse proxy requires a proxy target and it is best to avoid external dependencies in tests. Ratpack provides a fantastic feature to overcome this using an EmbeddedApp in the test itself.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
class ReverseProxyBasicTest extends Specification {
  @Shared
  ApplicationUnderTest aut = new GroovyRatpackMainApplicationUnderTest()

  TestHttpClient client = aut.httpClient

  @Shared
  EmbeddedApp proxiedHost = GroovyEmbeddedApp.of {
    handlers {
      all {
        render "rendered ${request.rawUri}"
      }
    }
  }

  def setupSpec() {
    System.setProperty('ratpack.proxyConfig.forwardToHost', proxiedHost.address.host)
    System.setProperty('ratpack.proxyConfig.forwardToPort', Integer.toString(proxiedHost.address.port))
    System.setProperty('ratpack.proxyConfig.forwardToScheme', proxiedHost.address.scheme)
  }

  def "get request to ratpack is proxied to the embedded app"() {
    expect:
    client.getText(url) == "rendered /${url}"

    where:
    url << ["", "api", "about"]
  }
}

This tests starts the Reverse Proxy application, that’s the application under test, creates a test http client to call the reverse proxy and then creates an embedded app that is separate from the application under test in order to proxy requests to that embedded application. The embedded app is just rendering text back to the proxy. The setupSpec() is setting several system properties. Those system properties are being used by the ConfigData instance in the Ratpack application to set configuration for the embedded application. There’s no need for a test configuration file because of this. Each test can easily specify the configuration it needs and no more.

Finally the test makes an http request to the Reverse Proxy and verifies the result is as expected. This is a simple but powerful test and each new feature added to the reverse proxy will begin with a test.

Adding a second handler

It would be nice to be able to view the configuration for the reverse proxy via a simple html page rendered by the application itself. It’s an easy first feature to add, and demonstrates adding a path specific handler.

First though, a test.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class ConfigPageTest extends GebReportingSpec {
  @Shared
  ApplicationUnderTest aut = new GroovyRatpackMainApplicationUnderTest()

  def setupSpec() {
    System.setProperty('ratpack.proxyConfig.forwardToHost', 'testhost')
    System.setProperty('ratpack.proxyConfig.forwardToPort', '123')
    System.setProperty('ratpack.proxyConfig.forwardToScheme', 'https')
  }

  def setup() {
    browser.baseUrl = aut.address.toString() + 'reverseProxyAdmin'
  }

  def "admin page displays config"() {
    when:
    to ConfigPage

    then:
    host == "Proxied Host: testhost"
    port == "Proxied Port: 123"
    scheme == "Proxied Scheme: https"
  }
}

This test uses Geb to functionally test a page through firefox. Geb is beyond the scope of this article, but it is an easy (and default) way to test html generated by Ratpack. Here, we verify that there is a page that responds to /reverseProxyAdmin and has 3 <li> elements which include the configuration data.

The handler and template to generate this are simple. You will need to add the Handlebars module to the application. Modules are the method Ratpack uses to add functionality not included in Ratpack core. Most functionality is in a module, because Ratpack tries to be as unopinionated as possible. Simple add module HandlebarsModule to the bindings block of ratpack.groovy and add the build dependency compile ratpack.dependency('handlebars') to build.gradle.

Create a handlebars directory inside of the ratpack source directory then create a reverseProxyAdmin.html.hbs file. Remove the spaces between the braces, I couldn’t get octopress to render them correctly without it.

1
2
3
4
5
6
7
8
9
10
11
12
13
<html>
  <head>
      <title>Reverse Proxy Config</title>
  </head>
  <body>
    <h1>Config Data</h1>
    <ul>
      <li>Proxied Host: { { config.forwardToHost } }</li>
      <li>Proxied Port: { { config.forwardToPort } }</li>
      <li>Proxied Scheme: { { config.forwardToScheme } }</li>
    </ul>
  </body>
</html>

Finally the new handler must be added to handlers block of ratpack.groovy.

1
2
3
get('reverseProxyAdmin') { ProxyConfig proxyConfig ->
    render handlebarsTemplate('reverseProxyAdmin.html', config: proxyConfig)
}

The handler itself is very simple. It just renders a handlebars template and passes a map containing the configuration to the template. Using get with a path instead of all means that this handler will be invoked only when there is an HTTP GET request to /reverseProxyAdmin. Technically this means the application can’t reverse proxy anything to that path, but I think we can live with that for now. Try adding this handler below the original handler and then start the application and navigate to the page.

It didn’t work did it? That’s because handlers are executed in order and only 1 handler may render a response. So the all handler intercepted the request and proxied it. Most likely you received a 404 from the proxied host. To fix this, just reverse the order of the handlers and try again. This is an important thing to remember when building Ratpack applications! It is quite different than MVC frameworks like grails or rails which have a routing definition which says which controller to call for which URLs.

Conclusion

We layered some additional functionality into the application and added tests. It’s almost a real application now! In the next series we will start to move code out of the Ratpack.groovy file and see how a larger application can be structured. In doing so, we’ll add a few new features to the application and explore logging and more handler composition with Ratpack.

Introduction

There is a lot of interest in Ratpack after the gr8conf conference season and a question I heard multiple times was “What’s the use case for Ratpack?”. It’s a great question. The main advantages of Ratpack to me are:

  • Designed from the ground up to be modern Java. Reading the source is a great way to see how Java should be written in 2015.
  • Asynchronous and non blocking at the core. Ratpack has a pool of worker threads to execute blocking code separate from the threads that handle requests. Understanding your application to the point of knowing what code is blocking on IO or computation is critical for writing performant applications. Using higher abstraction frameworks makes it easy to forget what is really happening inside your application. You can see how core this is to the Ratpack API by looking at the Session interface or Request.getBody method which returns a promise.
  • The handler chain is an interesting and powerful way to compose functionality.

I have code in 3 production Ratpack applications. One of them is a traditional web application serving some REST web services and some HTML. The other two are micro-services built on Ratpack and Cassandra. Both of these are easy to implement in Ratpack.

A lot of frameworks these days have a sort of ‘canonical’ use case that many people build as a type of hello-world introduction to the framework. Rails and Grails had the blog example, Javascript MVC frameworks like Angular often use the to-do app example and I’ve seen elixir use a real time chat application. Naturally Ratpack can build any of these applications, but I wanted to write a series of blog posts explaining how to build a Ratpack app from the ground up that would show off some of the unique features of Ratpack. I decided on a reverse proxy with features like traffic logging and authentication. It will show off the non-blocking, streaming, configuration and composable handler chain that makes Ratpack really interesting to me. Naturally after I decided this, I found that Rus Hart, Ratpack core team member, had the idea first. Never the less this is part 1 of N in building a Reverse Proxy with Ratpack.

What is a reverse proxy?

A reverse proxy takes a request from a client and then makes a request to one or more server to retrieve the requested resource and then returns that resource to the original client. This is what nginx does if you’re familiar. Read wikipedia for a better explanation.

Getting Started

I’m assuming you have Java8 installed and are familiar with java, groovy and gradle. Having GVM and lazybones installed is recommended.

1
lazybones create ratpack ratpack-reverse-proxy --with-git

Lazybones is the easiest way to bootstrap an empty, working Ratpack application. If you don’t have lazybones installed and don’t want to, then create the following folder structure. You can also clone the github repo for this project. Checkout the tag ‘POC’ for the code related to this post.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
.
├── README.md
├── build.gradle
├── gradle
│   └── wrapper
│       ├── gradle-wrapper.jar
│       └── gradle-wrapper.properties
├── gradlew
├── gradlew.bat
└── src
    ├── main
    │   └── groovy
    ├── ratpack
    │   ├── public
    │   │   ├── images
    │   │   │   └── favicon.ico
    │   │   ├── index.html
    │   │   ├── lib
    │   │   ├── scripts
    │   │   └── styles
    │   ├── ratpack.groovy
    │   └── templates
    │       └── index.gtpl
    └── test
        └── groovy

Run gradle run to see the skelaton start up. This will prove everything is installed correctly. Browse to http://localhost:5050/ and you should see the page redirect to index.html. Try http://localhost:5050/about and see what happens. Remember what happens, it will be relevant.

Proof of Concept

Now lets make this a proof of concept for a reverse proxy. Just for fun, this will reverse proxy cellarhq.com. Replace the contents of ratpack.groovy with the following:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import ratpack.http.client.HttpClient
import ratpack.http.client.RequestSpec
import ratpack.http.client.StreamedResponse

import static ratpack.groovy.Groovy.ratpack

ratpack {
  handlers {
    all { HttpClient httpClient ->
      URI proxyUri = new URI(request.rawUri)
      proxyUri.host = 'www.cellarhq.com'
      proxyUri.scheme = 'https'
      httpClient.requestStream(proxyUri) { RequestSpec spec ->
        spec.headers.copy(request.headers)
      }.then { StreamedResponse responseStream ->
        responseStream.send(response)
      }
    }
  }
}

Run this with gradle run again and browse to http://localhost:5050/ again and you’ll see the CellarHQ home page. Click around, the links actually work as well. All the requests to localhost are being proxied to cellarhq by our little app. Try http://localhost:5050/about specifically and notice you get an actual page this time. The original application was only handling requests to the root. This application is handling requests for any path.

Some core concepts

There isn’t a lot of code but it is a nice introduction to some core Ratpack concepts. The handler chain is the most important of these. All requests are processed by the handler chain. Each handler is simply a function which either responds to the request, or does some work and delegates to another handler. Our handler chain is a single function which is passed to the all method. This indicates that every request will be processed by this handler. In the hello-world example generated by lazybones the get method was used, which processes only HTTP GET requests for /.

The closure passed to the all method, our one and only handler, takes an HttpClient as parameter. However, an HttpClient is never explicitly created in our example. This is because the instance of HttpClient is created and managed by Ratpack itself. It is stored the Registry which is a store of objects by type. Ratpack ships with support for Guice and Spring when you need to add a DI library on top of the registry. Ratpack takes care of passing objects from the registry to Handlers.

Finally let’s examine the meat of handler for a moment.

1
2
3
4
5
httpClient.requestStream(proxyUri) { RequestSpec spec ->
    spec.headers.copy(request.headers)
}.then { StreamedResponse responseStream ->
    responseStream.send(response)
}

Here you can start to see the asynchronous API that Ratpack is built on. The HttpClient is non blocking already, and the requestStream method returns a Promise. Ratpack has its own promise api that is very easy to use. In this case we’re streaming the response to the client upon completion of the promise. That’s the then method. It’s important to know that the HttpClient is already non-blocking so we don’t have to do anything to account for blocking code in this handler chain. If the handler needed perform database or file IO, then we would explicitly call out the blocking code and allow Ratpack to schedule that work on a different thread.

Conclusion of Part 1

This was just a proof of concept and example of building a very simple, not fully functional reverse proxy with Ratpack. It introduced some core concepts but left a lot unexplained. In the next post we will turn the proof of concept into a ‘real’ application and cover configuration, logging, and look at how a Ratpack application is structured for a non trivial (though still simple) example.

If you’re interested in seeing a medium sized application built on Ratpack, feel free to look at CellarHQ which is a beer inventory management system for beer nerds like myself. NB: At the time of this writing, the CellarHQ code is slightly behind the current Ratpack version and doesn’t include all the pre 1.0 API changes.

Most modern web applications call an external service via http. Applications built on the increasingly popular micro service architecture use many external services. In a unit test it’s easy to mock the http client. In integration or functional testing it is both more difficult to inject a mock, and less useful because typically in these tests you don’t want to mock the actual HTTP call.

Ratpack has a method for embedding a tiny web service directly into your test. In a few lines of code you can write a real web service to return real data to your test. The best part is the embedded app starts up blazingly fast so you don’t pay a sigificant penalty for using it.

You can use this feature even if the application you’re testing isn’t build on Ratpack. if you’re using a java or groovy test framework and JDK8, you can take advantage of Ratpack and it is a great way to introduce ratpack to your organization.

Here’s an example testing a Grails3 application that is calling the Github api to render a list of pull request titles.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class GithubController {

    String githubApiURL = grailsApplication.config.getProperty('githubApi')

    def index() {
        String githubApiURL = grailsApplication.config.getProperty('githubApi')
        def restClient = new RESTClient(githubApiURL)
        def grailsPullRequestURL = "repos/grails/grails-core/pulls"

        try {
            def response = restClient.get(path: grailsPullRequestURL, contentType: 'application/json')

            if (response.status > 200) {
                render status: 500
            } else {
                render response.data*.title as JSON
            }
        } catch (all) {
            render status: 500
        }

    }
}

And here’s a spock integration test that calls http://localhost:8080/github/. Then the embdedded ratpack app stubs out the github response and allows the test to assert on the result. There’s also an example test for ensuring errors from github would be appropriately handled.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
@Integration
@Rollback
class GithubControllerSpec extends Specification {
    RESTClient restClient
    def grailsApplication

    def setup() {
        restClient = new RESTClient("http://localhost:8080/")

    }

    void "list some pr titles correctly"() {
        given:
        EmbeddedApp github = GroovyEmbeddedApp.of {
            handlers {
                all {
                    render '[{"title": "pr1"}, {"title": "pr2"}, {"title": "pr3"}]'
                }
            }
        }

        grailsApplication.config.githubApi = "http://${github.address.host}:${github.address.port}"

        when:
        def response = restClient.get(path: "github/", contentType: 'application/json', requestContentType: 'application/json')

        then:
        response.status == 200
        response.data == ['pr1', 'pr2', 'pr3']
    }

    void "handle 500 errors from github gracefully"() {
        given:
        EmbeddedApp github = GroovyEmbeddedApp.of {
            handlers {
                all {
                    getResponse().status(500)
                    render '[]'
                }
            }
        }

        grailsApplication.config.githubApi = "http://${github.address.host}:${github.address.port}"

        when:
        def response
        try {
            restClient.get(path: "github/", contentType: 'application/json', requestContentType: 'application/json')
        } catch (all) {
            response = all.response
        }

        then:

        response.status == 500
    }
}