Vert.x HTTP 代理

Vert.x HTTP 代理是一个基于 Vert.x 的反向代理,旨在实现可重用的反向代理逻辑,以专注于更高级的关注点。

此模块具有技术预览状态,这意味着 API 可能会在版本之间发生变化。

使用 Vert.x HTTP 代理

要使用 Vert.x HTTP 代理,请将以下依赖项添加到构建描述符的 dependencies 部分

  • Maven(在您的 pom.xml 中)

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

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

反向代理服务器

为了使用 Vert.x HTTP 代理实现反向代理,您需要以下内容

  1. 处理用户代理请求并将其转发到源服务器代理服务器

  2. 处理来自代理服务器请求并相应响应的源服务器

您可以创建一个监听端口8080并实现反向代理逻辑的代理服务器

HttpClient proxyClient = vertx.createHttpClient();

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

HttpServer proxyServer = vertx.createHttpServer();

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

所有用户代理请求都会方便地转发到源服务器

源服务器路由

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

您可以设置一个源选择器来将流量路由到给定服务器

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

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

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

请求头转发

端到端请求头由代理转发,逐跳请求头被忽略。

请求权威

作为透明代理,请求权威(HTTP/1.1 的 Host 请求头,HTTP/2 的 :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);
  }
});

当请求权威被覆盖时,源服务器请求上的 x-forwarded-host 请求头会设置为原始权威值。

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

WebSocket

代理默认支持 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());

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

代理响应也如此

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 替换为新的来过滤正文

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();
  }
});

拦截控制

sendRequestsendResponse 继续当前的拦截链,然后将结果发送到源服务器或用户代理。

您可以更改控制,例如,您可以立即向用户代理发送响应,而无需向源服务器发出请求

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);
  }
});

可定制的拦截器

您可以使用 ProxyInterceptor.builder,它有助于实现一个修改请求/响应头和正文的拦截器

  • 请求路径

  • 查询参数

  • 请求和响应头

  • 正文转换

此类拦截器通过 ProxyInterceptorBuilder 创建和配置。

请求头拦截

您可以应用拦截器,通过常用操作更改请求和响应中的请求头

proxy.addInterceptor(
  ProxyInterceptor.builder().filteringResponseHeaders(shouldRemove).build());

请求头修改方法可以多次调用,操作将按照配置顺序应用。

请查看 ProxyInterceptorBuilder 获取可用方法的详细信息。

查询参数拦截

您可以应用拦截器来更新或移除查询参数

proxy.addInterceptor(
  ProxyInterceptor.builder().settingQueryParam(key, value).build());

查询参数修改方法可以多次调用,操作将按照配置顺序应用。

您也可以参考 ProxyInterceptorBuilder 获取更多信息。

正文拦截器

您可以使用 BodyTransformer 来创建正文转换。

一组预定义转换有助于创建转换器。

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

正文转换器随后通过构建器转换为代理拦截器

proxy.addInterceptor(
  ProxyInterceptor
    .builder()
    .transformingResponseBody(transformer)
    .build()
);

BodyTransformers 为常见数据类型(如 JsonObject)提供转换。

proxy.addInterceptor(
  ProxyInterceptor
    .builder()
    .transformingResponseBody(
      BodyTransformers.jsonObject(
        jsonObject -> removeSomeFields(jsonObject)
      )
    ).build());

BodyTransformers 中提供的大多数转换是同步的并缓冲字节。默认最大字节量为 256K 字节,您可以提供不同的量

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

请查看 BodyTransformers 以获取其他支持的转换。

您也可以实现 BodyTransformer 契约,以最好地适应您的需求。

拦截与 WebSocket 升级

默认情况下,拦截器在 WebSocket 升级期间不会被调用。

要在 WebSocket 握手期间使拦截器可用,请使用 addInterceptor

ProxyInterceptor interceptor = ProxyInterceptor.builder()
  .addingPathPrefix("/api")
  .build();
proxy.addInterceptor(interceptor, true);