В http клиент может построить туннель, используя метод CONNECT для подключения промежуточного прокси и, наконец, подключиться к исходному серверу (RF C 7230).
Я читаю исходный код jdk11u6 PlainTunnelingConnection (пакет jdk.internal. net .http). Я думаю, этот класс может быть применен java для построения туннеля. В последнее время я приведу здесь код, но позвольте мне объяснить, что меня смущает этот класс.
В этом классе есть делегат PlainHttpConnection, который сначала будет подключен к адресу адреса прокси (серверы HttpConnection). как соединение TCP)
Я вставляю свой вопрос в исходный код.
/**
* A plain text socket tunnel through a proxy. Uses "CONNECT" but does not
* encrypt. Used by WebSocket, as well as HTTP over SSL + Proxy.
* Wrapped in SSLTunnelConnection or AsyncSSLTunnelConnection for encryption.
*/
final class PlainTunnelingConnection extends HttpConnection {
final PlainHttpConnection delegate;
final HttpHeaders proxyHeaders;
final InetSocketAddress proxyAddr;
private volatile boolean connected;
protected PlainTunnelingConnection(InetSocketAddress addr,
InetSocketAddress proxy,
HttpClientImpl client,
HttpHeaders proxyHeaders) {
super(addr, client);
this.proxyAddr = proxy;
this.proxyHeaders = proxyHeaders;
delegate = new PlainHttpConnection(proxy, client);
}
@Override
public CompletableFuture<Void> connectAsync(Exchange<?> exchange) {
if (debug.on()) debug.log("Connecting plain connection");
return delegate.connectAsync(exchange)
.thenCompose(unused -> delegate.finishConnect())
.thenCompose((Void v) -> {
if (debug.on()) debug.log("sending HTTP/1.1 CONNECT");
HttpClientImpl client = client();
assert client != null;
// here, I guess that req is the new request that the proxy uses to connect the
// next inbound server
HttpRequestImpl req = new HttpRequestImpl("CONNECT", address, proxyHeaders);
MultiExchange<Void> mulEx = new MultiExchange<>(null, req,
client, discarding(), null, null);
Exchange<Void> connectExchange = mulEx.getExchange();
// why the exchange responses the delegate connection?
return connectExchange
.responseAsyncImpl(delegate)
// who responses this resp ?
.thenCompose((Response resp) -> {
CompletableFuture<Void> cf = new MinimalFuture<>();
if (debug.on()) debug.log("got response: %d", resp.statusCode());
if (resp.statusCode() == 407) {
return connectExchange.ignoreBody().handle((r,t) -> {
// auth ...
}).thenCompose(Function.identity());
} else if (resp.statusCode() != 200) {
// build fail
} else {
// get the initial/remaining bytes
// successfully ....
cf.complete(null);
}
return cf;
})
.handle((result, ex) -> {
//...
})
.thenCompose(Function.identity());
});
}