Introduction to OpenIG (Part 3: Concepts)

The previous posts exposed you to OpenIG: use cases and an initial configuration example. Before going further, I would like to introduce the underlying concepts that you should understand.

HTTP Exchanges Requests and Responses

OpenIG is a specialized HTTP reverse-proxy: it only deals with HTTP messages. The purpose of OpenIG is to give a complete control over the messages flowing through itself (incoming requests and outgoing responses).

In order to ease handling of the whole message processing (request and response), OpenIG uses the concept of Exchange: it's a simple way to link a Request and a Response together. It also contains a Session instance that can be used to store session-scoped properties. The Exchange itself is a Map so it's a natural container for request-scoped properties.

When a request comes through OpenIG, an Exchange object is created and populated with an initial Request and a Session instance.

A Request is the OpenIG model for an incoming HTTP message, it captures both the message's entity and all of its headers. It also has dedicated accessors for message's target URI, cookies and form/query parameters.

A Response is the complementary model object for outgoing HTTP messages. In addition to the entity and headers accessors, Response provides setter for the HTTP status code (20x -> ok, 30x -> redirect, 50x -> server error, ...).

Exchange processing

Now we have a model of the message's content, what can we do to apply transformations to it?

OpenIG offers a simple, but powerful, API to process exchanges:

  • Handler that are responsible to produce a Response object into the Exchange
  • Filter that can intercept the flowing Exchange (incoming and outgoing)

Handler

What do the doc says about Handler.handle() ?

Called to request the handler respond to the request.

A handler that doesn't hand-off an exchange to another handler downstream is responsible for creating the response in the exchange object.

Hmmm, an example would help, right ?

OpenIG offers a rich set of Handlers, the most significative one would be the ClientHandler. This is a highly used component usually ending Exchange's processing that simply forward the Request to the required URI and wraps the returned HTTP message into the Exchange's Response.

In other words, it acts as a client to the protected resource (hence the name).

Filter

Again, what do the doc says about Filter.filter() ?

Filters the request and/or response of an exchange.

Initially, exchange.request contains the request to be filtered. To pass the request to the next filter or handler in the chain, the filter calls next.handle(exchange). After this call, exchange.response contains the response that can be filtered.

This method may elect not to pass the request to the next filter or handler, and instead handle the request itself. It can achieve this by merely avoiding a call to next.handle(exchange) and creating its own response object the exchange. The filter is also at liberty to replace a response with another of its own after the call to next.handle(exchange).

This is easier to understand I think, everyone is used to interceptors nowadays...

The traditional example of a Filter is the CaptureFilter: this filter simply prints the content of the incoming request, then call the next handler in chain and finally prints the outgoing response's content.

Filters are contained inside a special Handler called a Chain. The chain is responsible to sequentially invoke each of the filter declared in its configuration, before handing the flow to its terminal Handler.

All together: a Chain example

If you have your OpenIG up and running, please shut it down and replace its configuration file (config.json) with the following content:

Compared to previous, this configuration will enhance the response message with an additional HTTP header named X-Hello.

The message flow is depicted in the following diagram:

Exchange Flow

A Filter can intercept both the Request and the Reponse flows. In this case, our HeaderFilter is configured to only act on the response flow (because the outgoing message is a handy way to observe a filter in action from an outside perspective).

Wrap up

OpenIG provides a low-level HTTP model API that let you alter HTTP messages in many ways. The message processing is handled through some kind of pipeline composed of handlers and filters.

All the processing logic you want to apply to your messages finally depends on the way you compose your handlers and filters together.

Next

In the next post we'll see how to debug your configurations.

Introduction to OpenIG (Part 2: First contact…)

What do I need to start ?

First, you need to install OpenIG in the root context of a web container (Tomcat used for the example):

  1. Download OpenIG
  2. Grab a fresh Apache Tomcat instance (Tomcat 7.0 or 8.0) and unzip it
  3. Remove all war files from the webapps/ directory
  4. Move OpenIG-3.0.0.war to that directory and rename it to ROOT.war (OpenIG needs to be installed in the root context: /)
  5. Start Tomcat (bin/catalina.sh run from within the tomcat directory).

    Don't worry if you see some warnings about Offending classes during startup: that's because OpenIG web-app provides its own EL implementation (useful when the web container doesn't provide one). This pollutes the startup logs but does not prevent OpenIG to run properly.

If you're curious, you can go to http://localhost:8080 (or whatever port number you're using), you should see the OpenIG's welcome page (admire the ASCII art).

OpenIG 3.0.0 - Welcome Page

If you don't see anything, or an error, make sure that you don't have a previous OpenIG configuration file in $HOME/.openig/config/config.json ($APP_DATAForgeRockOpenIGconfigconfig.json for our windows users).

You might wonder why OpenIG requires to be installed in the root context. The reason is simple: most of the time, when you want to access a web site, you just wanna type a domain name in your browser navbar (like http://app.example.com).

That gives you a very easy to use and to remember entry point URL as well as the flexibility of mapping incoming requests to any internal URL (really routing requests in fact).

Your first configuration

What would you think of an OpenIG Hello World example? After all that reading, I'm sure you'll be happy to get your hands dirty :)

So, finally some code (let's face it; it's JSON configuration really).

Place that configuration file inside the $HOME/.openig/config/routes/ directory. It will be auto-(re)loaded by OpenIG.

It's quite self-descriptive: if the incoming request's path matches ^/hello (starts with /hello), it is dispatched to this route (a JSON file placed in the routes/ directory is called a route). This route is configured with a single StaticResponseHandler that simply return a Hello World message (we'll see what a Handler is in an upcoming post) to the user-agent.

OpenIG Hello World

At first look, that doesn't seem very fancy, but you've been exposed to multiple core concepts of OpenIG (they'll be covered in more details in subsequent posts):

  • The OpenIG configuration file format (expressed as JSON)
  • The expression language (${} for expressing conditions based the value of some properties)
  • The Exchange HTTP model (exchange.request.uri.path in the expression)
  • The route concept (a simple way to isolate processing chains in different files and to ease setup with auto-(re)loading)

In the next post, we'll have a look at the concepts providing the backbone of OpenIG (Exchange, Handler and Filter).

See you soon.