= Vert.x Http Proxy
:toc: left

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`):

[source,xml,subs="+attributes"]
----
<dependency>
  <groupId>io.vertx</groupId>
  <artifactId>vertx-http-proxy</artifactId>
  <version>5.0.2</version>
</dependency>
----

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

[source,groovy,subs="+attributes"]
----
dependencies {
  compile 'io.vertx:vertx-http-proxy:5.0.2'
}
----

== 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

[source,java]
----
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:

[source,java]
----
proxy.origin(OriginRequestProvider.selector(proxyContext -> resolveOriginAddress(proxyContext)));
----

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

[source,java]
----
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 https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/X-Forwarded-Host[`x-forwarded-*`] or https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Forwarded[`forwarded`] headers on the proxied request.
For example, it might use the values to compute a created HTTP resource https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Location[location].

Nevertheless, you can override the request authority:

[source,java]
----
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 `link:../../apidocs/io/vertx/httpproxy/ProxyOptions.html#setSupportWebSocket-boolean-[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.

[source,java]
----
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 `link:../../apidocs/io/vertx/httpproxy/ProxyInterceptor.html#handleProxyRequest-io.vertx.httpproxy.ProxyContext-[handleProxyRequest]` to perform any operation on the proxy request

[source,java]
----
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

[source,java]
----
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 `link:../../apidocs/io/vertx/httpproxy/Body.html[Body]` with a new one

[source,java]
----
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

`link:../../apidocs/io/vertx/httpproxy/ProxyContext.html#sendRequest--[sendRequest]` and `link:../../apidocs/io/vertx/httpproxy/ProxyContext.html#sendResponse--[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

[source,java]
----
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);
  }
});
----

==== Customizable interceptor

You can use `link:../../apidocs/io/vertx/httpproxy/ProxyInterceptor.html#builder--[ProxyInterceptor.builder]` that facilitates the implementation of an interceptor
that modifies the request/response heads and bodies:

- request path
- query params
- request and response headers
- body transformation

Such interceptor is created and configured with a `link:../../apidocs/io/vertx/httpproxy/ProxyInterceptorBuilder.html[ProxyInterceptorBuilder]`.

===== Headers interception

You can apply the interceptor to change headers from the request and response with common operations:

[source,java]
----
proxy.addInterceptor(
  ProxyInterceptor.builder().filteringResponseHeaders(shouldRemove).build());
----

Headers modifying methods can be invoked several times, operations are applied in the order of configuration.

Check out `link:../../apidocs/io/vertx/httpproxy/ProxyInterceptorBuilder.html[ProxyInterceptorBuilder]` for details about the available methods.

===== Query params interception

You can apply the interceptor to update or remove query parameters:

[source,java]
----
proxy.addInterceptor(
  ProxyInterceptor.builder().settingQueryParam(key, value).build());
----

Query params modifying methods can be invoked several times, operations are applied in the order of configuration.

You can also refer to `link:../../apidocs/io/vertx/httpproxy/ProxyInterceptorBuilder.html[ProxyInterceptorBuilder]` for more information.

==== Body interceptor

You can use a `link:../../apidocs/io/vertx/httpproxy/BodyTransformer.html[BodyTransformer]` to create body transformations.

A set of predefined transformations facilitates the creation of a transformer.

[source,java]
----
BodyTransformer transformer = BodyTransformers.transform(
  MediaType.APPLICATION_JSON,
  MediaType.APPLICATION_OCTET_STREAM,
  buffer -> {
    // Apply some transformation
    return buffer;
  }
);
----

A body transformer is then turned into a proxy interceptor with the builder:

[source,java]
----
proxy.addInterceptor(
  ProxyInterceptor
    .builder()
    .transformingResponseBody(transformer)
    .build()
);
----

`link:../../apidocs/io/vertx/httpproxy/BodyTransformers.html[BodyTransformers]` provides transformation for common data types, like `link:../../apidocs/io/vertx/core/json/JsonObject.html[JsonObject]`:

[source,java]
----
proxy.addInterceptor(
  ProxyInterceptor
    .builder()
    .transformingResponseBody(
      BodyTransformers.jsonObject(
        jsonObject -> removeSomeFields(jsonObject)
      )
    ).build());
----

Most transformations provided in `link:../../apidocs/io/vertx/httpproxy/BodyTransformers.html[BodyTransformers]` are synchronous and buffer bytes. The default
maximum amount of bytes is 256K bytes, you can provide a different amount:

[source,java]
----
proxy.addInterceptor(
  ProxyInterceptor
    .builder()
    .transformingResponseBody(
      BodyTransformers.jsonObject(
        // Maximum amount of buffered bytes
        128 * 1024,
        jsonObject -> removeSomeFields(jsonObject)
      )
    ).build());
----

Please check the `link:../../apidocs/io/vertx/httpproxy/BodyTransformers.html[BodyTransformers]` for other supported transformations.

NOTE: you can also implement `link:../../apidocs/io/vertx/httpproxy/BodyTransformer.html[BodyTransformer]` contract to best adapt it to your needs.

==== Interception and WebSocket upgrades

By default, interceptors are not invoked during WebSocket upgrades.

To make an interceptor available during the WebSocket handshake, use `link:../../apidocs/io/vertx/httpproxy/HttpProxy.html#addInterceptor-io.vertx.httpproxy.ProxyInterceptor-boolean-[addInterceptor]`:

[source,java]
----
ProxyInterceptor interceptor = ProxyInterceptor.builder()
  .addingPathPrefix("/api")
  .build();
proxy.addInterceptor(interceptor, true);
----