# Vert.x Http Proxy

Vert.x Http Proxy is a reverse proxy based on Vert.x, it aims to implement reusable reverse proxy logic to focus on higher concerns.

Warning | This module has Tech Preview status, this means the API can change between versions.

## Using Vert.x Http Proxy

To use Vert.x Http Proxy, add the following dependency to the dependencies section of your build descriptor:

- Maven (in your `pom.xml`):

    <dependency>
     <groupId>io.vertx</groupId>
     <artifactId>vertx-http-proxy</artifactId>
     <version>4.5.17</version>
    </dependency>

- Gradle (in your `build.gradle` file):

    dependencies {
     compile 'io.vertx:vertx-http-proxy:4.5.17'
    }

## Reverse proxy server

In order to accomplish a reverse proxy with Vert.x Http Proxy you need the following:

1. Proxy Server that handles user-agent requests and forward them to the origin server

2. Origin Server that handles requests from the proxy server and respond accordingly

You can create a proxy server that listens to port `8080` and implement reverse proxy logic

    HttpClient proxyClient = vertx.createHttpClient();

    HttpProxy proxy = HttpProxy.reverseProxy(proxyClient);
    proxy.origin(7070, "origin");

    HttpServer proxyServer = vertx.createHttpServer();

    proxyServer.requestHandler(proxy).listen(8080);

All user-agent requests are forwarded to the origin server conveniently.

### Origin server routing

You can create a proxy that forwards all the traffic to a single server like seen before.

You can set an origin selector to route the traffic to a given server:

    proxy.origin(OriginRequestProvider.selector(proxyContext -> resolveOriginAddress(proxyContext)));

You can set a function to create the client request to the origin server for ultimate flexibility:

    proxy.origin((proxyContext) -> proxyContext.client().request(resolveOriginOptions(proxyContext)));

### Headers forwarding

End-to-end headers are forwarded by the proxy, hop-by-hop headers are ignored.

#### Request authority

By default, the proxy request authority (`Host` header for HTTP/1.1, `:authority` pseudo header for HTTP/2) is set by the HTTP client according to the origin server address.

Caution | The origin server may need you to set the `x-forwarded-*` or `forwarded` headers on the proxied request. For example, it might use the values to compute a created HTTP resource location.

Nevertheless, you can override the request authority:

    proxy.addInterceptor(new ProxyInterceptor() {
      @Override
      public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
        ProxyRequest proxyRequest = context.request();
        proxyRequest.setAuthority(HostAndPort.create("example.com", 80));
        return ProxyInterceptor.super.handleProxyRequest(context);
      }
    });

When the request authority is overridden, the `x-forwarded-host` header is automatically set on the request to the origin server with the original authority value.

### WebSockets

The proxy supports WebSocket by default.

WebSocket handshake requests are forwarded to the origin server (including the `connection` header) and the handshake happens between the user agent and the origin server.

You can configure WebSocket support with `setSupportWebSocket`.

### Proxy caching

By default the proxy does not cache response and ignores most cache directives, you can enable caching by setting the cache options.

    HttpProxy proxy = HttpProxy.reverseProxy(new ProxyOptions().setCacheOptions(new CacheOptions()), proxyClient);

### Proxy interception

Interception is a powerful way to extend the proxy withg new features.

You can implement `handleProxyRequest` to perform any operation on the proxy request

    proxy.addInterceptor(new ProxyInterceptor() {
      @Override
      public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {
        ProxyRequest proxyRequest = context.request();

        filter(proxyRequest.headers());

        // Continue the interception chain
        return context.sendRequest();
      }
    });

Likewise with the proxy response

    proxy.addInterceptor(new ProxyInterceptor() {
      @Override
      public Future<Void> handleProxyResponse(ProxyContext context) {
        ProxyResponse proxyResponse = context.response();

        filter(proxyResponse.headers());

        // Continue the interception chain
        return context.sendResponse();
      }
    });

#### Body filtering

You can filter body by simply replacing the original `Body` with a new one

    proxy.addInterceptor(new ProxyInterceptor() {
      @Override
      public Future<Void> handleProxyResponse(ProxyContext context) {
        ProxyResponse proxyResponse = context.response();

        // Create a filtered body
        Body filteredBody = filter(proxyResponse.getBody());

        // And then let the response use it
        proxyResponse.setBody(filteredBody);

        // Continue the interception chain
        return context.sendResponse();
      }
    });

#### Interception control

`sendRequest` and `sendResponse` continue the current interception chain and then send the result to the origin server or the user-agent.

You can change the control, e.g you can send a response immediately to the user-agent without even requesting the origin server

    proxy.addInterceptor(new ProxyInterceptor() {
      @Override
      public Future<ProxyResponse> handleProxyRequest(ProxyContext context) {

        ProxyRequest proxyRequest = context.request();

        // Release the underlying resources
        proxyRequest.release();

        // Create a response and populate it
        ProxyResponse proxyResponse = proxyRequest.response()
          .setStatusCode(200)
          .putHeader("content-type", "text/plain")
          .setBody(Body.body(Buffer.buffer("Hello World")));

        return Future.succeededFuture(proxyResponse);
      }
    });