可插拔的客户端传输 / HTTP(S) 代理支持

Pluggable Client Transports / HTTP(S) proxy Support

The client side infrastructure has support to plug different transport mechanisms underneath (the API may still change in the future). A client side transport is represented by an instance of akka.http.scaladsl.ClientTransportakka.http.javadsl.ClientTransport:

客户端基础设施支持在下面插入的不同传输机制(该 API 在未来仍有可能改变)。客户端传输由 akka.http.scaladsl.ClientTransportakka.http.javadsl.ClientTransport 的实例表示。

Scala
@ApiMayChange
trait ClientTransport {
  def connectTo(host: String, port: Int, settings: ClientConnectionSettings)(implicit system: ActorSystem): Flow[ByteString, ByteString, Future[OutgoingConnection]]
}
Java
@ApiMayChange
abstract class ClientTransport {
  def connectTo(host: String, port: Int, settings: ClientConnectionSettings, system: ActorSystem): Flow[ByteString, ByteString, CompletionStage[OutgoingConnection]]
}

A transport implementation defines how the client infrastructure should communicate with a given host.

传输实现定义了客户端基础设施应该如何与指定主机通信。

Note

In our model, SSL/TLS runs on top of the client transport, even if you could theoretically see it as part of the transport layer itself.

在我们的模型中,SSL/TLS 运行在客户端传输之上,即使理论上你可以将它看作为传输层本身的一部分。

Configuring Client Transports

配置客户端传输

A ClientTransportClientTransport can be configured in the ClientConnectionSettingsClientConnectionSettings. Right now, this is not possible through config files but only by code. First, use ClientConnectionSettings.withTransport to configure a transport, then use ConnectionPoolSettings.withConnectionSettings. ClientConnectionSettingsClientConnectionSettings can be passed to all client-side entry points in HttpHttpHttpHttp.

可以在 ClientConnectionSettingsClientConnectionSettings 中配置 ClientTransportClientTransport 。现在,不能通过配置文件而只能通过代码实现。首先,使用 ClientConnectionSettings.withTransport 来配置一个传输,然后使用 ConnectionPoolSettings.withConnectionSettingsClientConnectionSettingsClientConnectionSettings 可以被传递到 HttpHttpHttpHttp 中的所有客户端入口点。

Predefined Transports

预定义传输

TCP

The default transport is ClientTransport.TCP which simply opens a TCP connection to the target host.

默认传输是 ClientTransport.TCP,它简单打开到目标的 TCP 连接。

HTTP(S) Proxy

HTTP(S) 代理

A transport that connects to target servers via an HTTP(S) proxy. An HTTP(S) proxy uses the HTTP CONNECT method (as specified in RFC 7231 Section 4.3.6) to create tunnels to target servers. The proxy itself should transparently forward data to the target servers so that end-to-end encryption should still work (if TLS breaks, then the proxy might be fussing with your data).

一个传输通过 HTTP(S) 代理连接到目标服务器。HTTP(S) 代理使用 HTTP CONNECT 方法 (在 RFC 7231,4.3.6 节 中指定)创建到目标服务器的隧道。 代理本身应该透明的转发数据到目标服务器,因此端到端的加密应该仍然工作(如果 TLS 中断,那么代理可能干扰你的数据)。

This approach is commonly used to securely proxy requests to HTTPS endpoints. In theory it could also be used to proxy requests targeting HTTP endpoints, but we have not yet found a proxy that in fact allows this.

这个方法常见用于安全的代理请求到 HTTPS 端点。理论上它也可用于代理针对 HTTP 端点的请求,但我们还没有找到一个(这样的)代理,实际上允许这样做。

Instantiate the HTTP(S) proxy transport using ClientTransport.httpsProxy(proxyAddress).

使用 ClientTransport.httpsProxy(proxyAddress) 实例化 HTTP(S) 代理传输。

The proxy transport can also be setup using ClientTransport.httpsProxy() or ClientTransport.httpsProxy(basicHttpCredentials) In order to defined the transport as such, you will need to set the proxy host / port in your conf file like the following.

可以使用 ClientTransport.httpsProxy()ClientTransport.httpsProxy(basicHttpCredentials) 来设置代理传输。 为了像这样定义代理,你将需要在 配置 文件中设置代理主机/端口,就像下面:

akka.http.client.proxy {
 https {
   host = ""
   port = 443
 }
}

If host is left as "" and you attempt to setup a httpsProxy transport, an exception will be thrown.

如果主机被保留为 "" 并且尝试设置一个 httpsProxy 代理,将抛出一个异常。

Use HTTP(S) proxy with Http().singleRequestHttp.get(...).singleRequest

Http().singleRequestHttp.get(...).singleRequest 一起使用 HTTP(S) 代理

To make use of an HTTP proxy when using the singleRequest API you simply need to configure the proxy and pass the appropriate settings object when calling the single request method.

要在使用 singleRequest API 时使用 HTTP 代理,你只需要配置代理并在调用单请求方法时传递合适的设置对象。

Scala
import java.net.InetSocketAddress

import akka.actor.ActorSystem
import akka.stream.ActorMaterializer
import akka.http.scaladsl.{ ClientTransport, Http }

implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()

val proxyHost = "localhost"
val proxyPort = 8888

val httpsProxyTransport = ClientTransport.httpsProxy(InetSocketAddress.createUnresolved(proxyHost, proxyPort))

val settings = ConnectionPoolSettings(system)
  .withConnectionSettings(ClientConnectionSettings(system)
    .withTransport(httpsProxyTransport))
Http().singleRequest(HttpRequest(uri = "https://google.com"), settings = settings)
Java

final ActorSystem system = ActorSystem.create(); ClientTransport proxy = ClientTransport.httpsProxy(InetSocketAddress.createUnresolved("192.168.2.5", 8080)); ConnectionPoolSettings poolSettingsWithHttpsProxy = ConnectionPoolSettings.create(system) .withConnectionSettings(ClientConnectionSettings.create(system).withTransport(proxy)); final CompletionStage<HttpResponse> responseFuture = Http.get(system) .singleRequest( HttpRequest.create("https://github.com"), Http.get(system).defaultClientHttpsContext(), poolSettingsWithHttpsProxy, // <- pass in the custom settings here system.log());

Use HTTP(S) proxy that requires authentication

使用需要身份验证的 HTTP(S) 代理

In order to use a HTTP(S) proxy that requires authentication, you need to provide HttpCredentialsHttpCredentials that will be used when making the CONNECT request to the proxy:

为了使用需要身份验证的 HTTP(S) 代理,你需要提供在向代理发起 CONNECT 请求时将使用的 HttpCredentialsHttpCredentials

Scala
import akka.http.scaladsl.model.headers

val proxyAddress = InetSocketAddress.createUnresolved(proxyHost, proxyPort)
val auth = headers.BasicHttpCredentials("proxy-user", "secret-proxy-pass-dont-tell-anyone")

val httpsProxyTransport = ClientTransport.httpsProxy(proxyAddress, auth)

val settings = ConnectionPoolSettings(system)
  .withConnectionSettings(ClientConnectionSettings(system)
    .withTransport(httpsProxyTransport))
Http().singleRequest(HttpRequest(uri = "http://akka.io"), settings = settings)
Java
InetSocketAddress proxyAddress =
  InetSocketAddress.createUnresolved("192.168.2.5", 8080);
HttpCredentials credentials =
  HttpCredentials.createBasicHttpCredentials("proxy-user", "secret-proxy-pass-dont-tell-anyone");

ClientTransport proxy = ClientTransport.httpsProxy(proxyAddress, credentials); // include credentials
ConnectionPoolSettings poolSettingsWithHttpsProxy = ConnectionPoolSettings.create(system)
    .withConnectionSettings(ClientConnectionSettings.create(system).withTransport(proxy));

final CompletionStage<HttpResponse> responseFuture =
    Http.get(system)
        .singleRequest(
              HttpRequest.create("https://github.com"),
              Http.get(system).defaultClientHttpsContext(),
              poolSettingsWithHttpsProxy, // <- pass in the custom settings here
              system.log());

Use HTTP(S) proxy with Http().singleWebSocketRequestHttp.get(…).singleWebSocketRequest

Http().singleWebSocketRequestHttp.get(…).singleWebSocketRequest 一起使用 HTTP(S) 代理

Making use of an HTTP proxy when using the singleWebSocketRequest is done like using singleRequest, except you set ClientConnectionSettings instead of ConnectionPoolSettings:

当使用 singleWebSocketRequest 时使用 HTTP 代理类似于使用 singleRequest,除了设置 ClientConnectionSettings 来替代 ConnectionPoolSettings

Scala
import java.net.InetSocketAddress

import akka.actor.ActorSystem
import akka.NotUsed
import akka.stream.ActorMaterializer
import akka.http.scaladsl.{ ClientTransport, Http }
import akka.http.scaladsl.settings.ClientConnectionSettings
import akka.http.scaladsl.model.ws._
import akka.stream.scaladsl._

implicit val system = ActorSystem()
implicit val materializer = ActorMaterializer()

val flow: Flow[Message, Message, NotUsed] =
  Flow.fromSinkAndSource(
    Sink.foreach(println),
    Source.single(TextMessage("hello world!")))

val proxyHost = "localhost"
val proxyPort = 8888

val httpsProxyTransport = ClientTransport.httpsProxy(InetSocketAddress.createUnresolved(proxyHost, proxyPort))

val settings = ClientConnectionSettings(system).withTransport(httpsProxyTransport)
Http().singleWebSocketRequest(WebSocketRequest(uri = "wss://example.com:8080/some/path"), clientFlow = flow, settings = settings)
Java

final ActorSystem system = ActorSystem.create(); final Materializer materializer = ActorMaterializer.create(system); final Flow<Message, Message, NotUsed> flow = Flow.fromSinkAndSource( Sink.foreach(System.out::println), Source.single(TextMessage.create("hello world"))); ClientTransport proxy = ClientTransport.httpsProxy(InetSocketAddress.createUnresolved("192.168.2.5", 8080)); ClientConnectionSettings clientSettingsWithHttpsProxy = ClientConnectionSettings.create(system) .withTransport(proxy); Http.get(system) .singleWebSocketRequest( WebSocketRequest.create("wss://example.com:8080/some/path"), flow, Http.get(system).defaultClientHttpsContext(), null, clientSettingsWithHttpsProxy, // <- pass in the custom settings here system.log(), materializer);

Use HTTP(S) proxy that requires authentication for Web Sockets

为 Web Sockets 使用需要身份验证的 HTTP(S) 代理

Here is an example for Web Socket:

这是一个 Web Socket 的例子:

Scala
import akka.http.scaladsl.model.headers

val proxyAddress = InetSocketAddress.createUnresolved(proxyHost, proxyPort)
val auth = headers.BasicHttpCredentials("proxy-user", "secret-proxy-pass-dont-tell-anyone")

val httpsProxyTransport = ClientTransport.httpsProxy(proxyAddress, auth)

val settings = ClientConnectionSettings(system).withTransport(httpsProxyTransport)
Http().singleWebSocketRequest(WebSocketRequest(uri = "wss://example.com:8080/some/path"), clientFlow = flow, settings = settings)
Java
InetSocketAddress proxyAddress =
  InetSocketAddress.createUnresolved("192.168.2.5", 8080);
HttpCredentials credentials =
  HttpCredentials.createBasicHttpCredentials("proxy-user", "secret-proxy-pass-dont-tell-anyone");

ClientTransport proxy = ClientTransport.httpsProxy(proxyAddress, credentials); // include credentials
ClientConnectionSettings clientSettingsWithHttpsProxy = ClientConnectionSettings.create(system)
  .withTransport(proxy);

Http.get(system)
  .singleWebSocketRequest(
    WebSocketRequest.create("wss://example.com:8080/some/path"),
    flow,
    Http.get(system).defaultClientHttpsContext(),
    null,
    clientSettingsWithHttpsProxy, // <- pass in the custom settings here
    system.log(),
    materializer);

Implementing Custom Transports

实现自定义传输

Implement ClientTransport.connectTo to implement a custom client transport.

实现 ClientTransport.connectTo 来实现一个自定义客户端传输。

Here are some ideas for custom (or future predefined) transports:

  • SSH tunnel transport: connects to the target host through an SSH tunnel
  • Per-host configurable transport: allows choosing transports per target host

这是自定义(或者未来预制)传输的一些语音:

  • SSH 隧道传输:通过 SSH 隧道连接到目标主机
  • 每主机可配置传输:允许选择每个目标主机的传输
在此文档中发现错误?该页面的源代码可以在 这里 找到。欢迎随时编辑并提交 Pull Request。