Vert.x Http Proxy

Vert.x Http Proxy 是一个基于 Vert.x 的反向代理服务器,其目的是实现可重用的反向代理逻辑, 这样,使用者就可以关注更高层面的问题了

警告
这个模块处于 Tech Preview 阶段,这意味着在不同版本之间 API 可能会不太一样

使用 Vert.x Http Proxy

使用 Vert.x Http Proxy 之前,请把以下的依赖放到您的构建描述文件的 依赖 部分中

  • Maven (在您的 pom.xml 文件中):

<dependency>
 <groupId>io.vertx</groupId>
 <artifactId>vertx-http-proxy</artifactId>
 <version>4.4.0</version>
</dependency>
  • Gradle (在您的 build.gradle 文件中):

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

反向代理服务器

为了使用 Vert.x Http Proxy 实现一个反向代理,您需要了解这些名词:

  1. Proxy Server 处理用户代理请求并将它们转发到 origin server

  2. Origin Server 处理来自 proxy server 的请求并做出相应响应

您可以创建一个 proxy server,监听 8080 端口并实现反向代理逻辑

HttpClient proxyClient = vertx.createHttpClient();

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

HttpServer proxyServer = vertx.createHttpServer();

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

所有的(user-agent)用户代理请求都方便地转发到 origin server

源服务器(Origin Server)路由

您可以创建一个代理,将所有流量转发到单个服务器,如前所述。

您可以设置源选择器(Origin Selector)以将流量路由到指定的服务器。

proxy.originSelector(request -> Future.succeededFuture(resolveOriginAddress(request)));

您可以设置一个函数来创建对源服务器的客户端请求,以实现最大的灵活性。

proxy.originRequestProvider((request, client) -> client.request(resolveOriginOptions(request)));

头部转发

端到端的头部会被代理转发,而逐跳(hop-by-hop)头部会被忽略。

请求校验

作为一种透明代理,请求中的请求权限信息 (HTTP/1.1 协议的 {@code Host} 标头,HTTP/2 协议的 {@code :authority} 伪标头),会被代理保留。

您也可以重写请求权限信息。

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

当请求权限信息被重写时,在对源服务器的请求上会设置 {@code x-forwarded-host} 头部,其值为 原始的权限信息。

警告
更改请求权限信息可能会产生不良的副作用,并可能影响代理的 Web 服务器,因为其可能 依赖原始的请求权限信息来处理 Cookie、URL 重定向等操作。

WebSockets

该 HTTP 代理默认支持 WebSocket 协议。

WebSocket 的握手请求会被转发到源服务器 (包含 connection 头部) ,并且用户端 和源服务器间会发生握手。

您可以通过 setSupportWebSocket 方法来设置其是否支持 WebSocket 协议。

代理缓存

大部分情况下,代理不会缓存响应内容,并且会忽视大部分的缓存指令,您也可以使用缓存选项来开启缓存。

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

代理拦截

拦截是使用新功能扩展代理的强大方法。

您可以实现 handleProxyRequest 对代理请求执行的任何操作

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

    filter(proxyRequest.headers());

    // 继续拦截链
    return context.sendRequest();
  }
});

代理响应也是如此

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

    filter(proxyResponse.headers());

    //  继续拦截链
    return context.sendResponse();
  }
});

Body 过滤

您可以简单地用一个新的 Body 替换原来的 Body 来过滤body

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

    // 创建一个Body
    Body filteredBody = filter(proxyResponse.getBody());

    // 然后使用它
    proxyResponse.setBody(filteredBody);

    // 继续拦截链
    return context.sendResponse();
  }
});

拦截控制

sendRequestsendResponse 继续当前拦截链,然后将结果发到(origin server)源服务器或者(user-agent)用户代理。

您可以更改控制器, 例如, 您可以立即向用户代理(user-agent)发送响应,而无需请求源服务器(origin server)

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

    ProxyRequest proxyRequest = context.request();

    // 释放资源
    proxyRequest.release();

    // 创建一个响应并设置参数
    ProxyResponse proxyResponse = proxyRequest.response()
      .setStatusCode(200)
      .putHeader("content-type", "text/plain")
      .setBody(Body.body(Buffer.buffer("Hello World")));

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