Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
com.arangodb.shaded.vertx.core.net.impl.NetClientImpl Maven / Gradle / Ivy
/*
* Copyright (c) 2011-2019 Contributors to the Eclipse Foundation
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0, or the Apache License, Version 2.0
* which is available at https://www.apache.org/licenses/LICENSE-2.0.
*
* SPDX-License-Identifier: EPL-2.0 OR Apache-2.0
*/
package com.arangodb.shaded.vertx.core.net.impl;
import com.arangodb.shaded.netty.bootstrap.Bootstrap;
import com.arangodb.shaded.netty.channel.Channel;
import com.arangodb.shaded.netty.channel.ChannelOption;
import com.arangodb.shaded.netty.channel.ChannelPipeline;
import com.arangodb.shaded.netty.channel.EventLoop;
import com.arangodb.shaded.netty.channel.group.ChannelGroup;
import com.arangodb.shaded.netty.channel.group.ChannelGroupFuture;
import com.arangodb.shaded.netty.channel.group.DefaultChannelGroup;
import com.arangodb.shaded.netty.handler.logging.LoggingHandler;
import com.arangodb.shaded.netty.handler.stream.ChunkedWriteHandler;
import com.arangodb.shaded.netty.handler.timeout.IdleStateHandler;
import com.arangodb.shaded.netty.util.concurrent.GenericFutureListener;
import com.arangodb.shaded.vertx.core.AsyncResult;
import com.arangodb.shaded.vertx.core.Closeable;
import com.arangodb.shaded.vertx.core.Future;
import com.arangodb.shaded.vertx.core.Handler;
import com.arangodb.shaded.vertx.core.Promise;
import com.arangodb.shaded.vertx.core.buffer.impl.PartialPooledByteBufAllocator;
import com.arangodb.shaded.vertx.core.impl.CloseFuture;
import com.arangodb.shaded.vertx.core.impl.ContextInternal;
import com.arangodb.shaded.vertx.core.impl.VertxInternal;
import com.arangodb.shaded.vertx.core.impl.future.PromiseInternal;
import com.arangodb.shaded.vertx.core.impl.logging.Logger;
import com.arangodb.shaded.vertx.core.impl.logging.LoggerFactory;
import com.arangodb.shaded.vertx.core.net.NetClient;
import com.arangodb.shaded.vertx.core.net.NetClientOptions;
import com.arangodb.shaded.vertx.core.net.NetSocket;
import com.arangodb.shaded.vertx.core.net.ProxyOptions;
import com.arangodb.shaded.vertx.core.net.SSLOptions;
import com.arangodb.shaded.vertx.core.net.SocketAddress;
import com.arangodb.shaded.vertx.core.spi.metrics.Metrics;
import com.arangodb.shaded.vertx.core.spi.metrics.MetricsProvider;
import com.arangodb.shaded.vertx.core.spi.metrics.TCPMetrics;
import java.io.FileNotFoundException;
import java.net.ConnectException;
import java.util.Objects;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.Predicate;
/**
*
* This class is thread-safe
*
* @author Tim Fox
*/
public class NetClientImpl implements MetricsProvider, NetClient, Closeable {
private static final Logger log = LoggerFactory.getLogger(NetClientImpl.class);
protected final int idleTimeout;
protected final int readIdleTimeout;
protected final int writeIdleTimeout;
private final TimeUnit idleTimeoutUnit;
protected final boolean logEnabled;
private final VertxInternal vertx;
private final NetClientOptions options;
private final SSLHelper sslHelper;
private final AtomicReference> sslChannelProvider = new AtomicReference<>();
private final ChannelGroup channelGroup;
private final TCPMetrics metrics;
private final CloseFuture closeFuture;
private final Predicate proxyFilter;
public NetClientImpl(VertxInternal vertx, TCPMetrics metrics, NetClientOptions options, CloseFuture closeFuture) {
this.vertx = vertx;
this.channelGroup = new DefaultChannelGroup(vertx.getAcceptorEventLoopGroup().next());
this.options = new NetClientOptions(options);
this.sslHelper = new SSLHelper(options, options.getApplicationLayerProtocols());
this.metrics = metrics;
this.logEnabled = options.getLogActivity();
this.idleTimeout = options.getIdleTimeout();
this.readIdleTimeout = options.getReadIdleTimeout();
this.writeIdleTimeout = options.getWriteIdleTimeout();
this.idleTimeoutUnit = options.getIdleTimeoutUnit();
this.closeFuture = closeFuture;
this.proxyFilter = options.getNonProxyHosts() != null ? ProxyFilter.nonProxyHosts(options.getNonProxyHosts()) : ProxyFilter.DEFAULT_PROXY_FILTER;
}
protected void initChannel(ChannelPipeline pipeline) {
if (logEnabled) {
pipeline.addLast("logging", new LoggingHandler(options.getActivityLogDataFormat()));
}
if (options.isSsl() || !vertx.transport().supportFileRegion()) {
// only add ChunkedWriteHandler when SSL is enabled otherwise it is not needed as FileRegion is used.
pipeline.addLast("chunkedWriter", new ChunkedWriteHandler()); // For large file / sendfile support
}
if (idleTimeout > 0 || readIdleTimeout > 0 || writeIdleTimeout > 0) {
pipeline.addLast("idle", new IdleStateHandler(readIdleTimeout, writeIdleTimeout, idleTimeout, idleTimeoutUnit));
}
}
@Override
public Future connect(int port, String host) {
return connect(port, host, (String) null);
}
@Override
public Future connect(int port, String host, String serverName) {
return connect(SocketAddress.inetSocketAddress(port, host), serverName);
}
@Override
public Future connect(SocketAddress remoteAddress) {
return connect(remoteAddress, (String) null);
}
@Override
public Future connect(SocketAddress remoteAddress, String serverName) {
return connect(vertx.getOrCreateContext(), remoteAddress, serverName);
}
public Future connect(ContextInternal context, SocketAddress remoteAddress, String serverName) {
Promise promise = context.promise();
connect(remoteAddress, serverName, promise, context);
return promise.future();
}
public NetClient connect(int port, String host, Handler> connectHandler) {
return connect(port, host, null, connectHandler);
}
@Override
public NetClient connect(int port, String host, String serverName, Handler> connectHandler) {
return connect(SocketAddress.inetSocketAddress(port, host), serverName, connectHandler);
}
@Override
public void close(Handler> handler) {
ContextInternal closingCtx = vertx.getOrCreateContext();
closeFuture.close(handler != null ? closingCtx.promise(handler) : null);
}
@Override
public Future close() {
ContextInternal closingCtx = vertx.getOrCreateContext();
PromiseInternal promise = closingCtx.promise();
closeFuture.close(promise);
return promise.future();
}
@Override
public void close(Promise completion) {
ChannelGroupFuture fut = channelGroup.close();
if (metrics != null) {
PromiseInternal p = (PromiseInternal) Promise.promise();
fut.addListener(p);
p.future().compose(v -> {
metrics.close();
return Future.succeededFuture();
}).onComplete(completion);
} else {
fut.addListener((PromiseInternal)completion);
}
}
@Override
public boolean isMetricsEnabled() {
return metrics != null;
}
@Override
public Metrics getMetrics() {
return metrics;
}
@Override
public Future updateSSLOptions(SSLOptions options) {
Future fut = sslHelper.buildChannelProvider(new SSLOptions(options), vertx.getOrCreateContext());
fut.onSuccess(v -> sslChannelProvider.set(fut));
return fut.mapEmpty();
}
@Override
public NetClient connect(SocketAddress remoteAddress, String serverName, Handler> connectHandler) {
Objects.requireNonNull(connectHandler, "No null connectHandler accepted");
ContextInternal ctx = vertx.getOrCreateContext();
Promise promise = ctx.promise();
promise.future().onComplete(connectHandler);
connect(remoteAddress, serverName, promise, ctx);
return this;
}
@Override
public NetClient connect(SocketAddress remoteAddress, Handler> connectHandler) {
return connect(remoteAddress, null, connectHandler);
}
private void connect(SocketAddress remoteAddress, String serverName, Promise connectHandler, ContextInternal ctx) {
if (closeFuture.isClosed()) {
throw new IllegalStateException("Client is closed");
}
SocketAddress peerAddress = remoteAddress;
String peerHost = peerAddress.host();
if (peerHost != null && peerHost.endsWith(".")) {
peerAddress = SocketAddress.inetSocketAddress(peerAddress.port(), peerHost.substring(0, peerHost.length() - 1));
}
ProxyOptions proxyOptions = options.getProxyOptions();
if (proxyFilter != null) {
if (!proxyFilter.test(remoteAddress)) {
proxyOptions = null;
}
}
connectInternal(proxyOptions, remoteAddress, peerAddress, serverName, options.isSsl(), options.isUseAlpn(), options.isRegisterWriteHandler(), connectHandler, ctx, options.getReconnectAttempts());
}
/**
* Open a socket to the {@code remoteAddress} server.
*
* @param proxyOptions optional proxy configuration
* @param remoteAddress the server address
* @param peerAddress the peer address (along with SSL)
* @param serverName the SNI server name (along with SSL)
* @param ssl whether to use SSL
* @param useAlpn wether to use ALPN (along with SSL)
* @param registerWriteHandlers whether to register event-bus write handlers
* @param connectHandler the promise to resolve with the connect result
* @param context the socket context
* @param remainingAttempts how many times reconnection is reattempted
*/
public void connectInternal(ProxyOptions proxyOptions,
SocketAddress remoteAddress,
SocketAddress peerAddress,
String serverName,
boolean ssl,
boolean useAlpn,
boolean registerWriteHandlers,
Promise connectHandler,
ContextInternal context,
int remainingAttempts) {
if (closeFuture.isClosed()) {
connectHandler.fail(new IllegalStateException("Client is closed"));
} else {
Future fut;
while (true) {
fut = sslChannelProvider.get();
if (fut == null) {
fut = sslHelper.buildChannelProvider(options.getSslOptions(), context);
if (sslChannelProvider.compareAndSet(null, fut)) {
break;
}
} else {
break;
}
}
fut.onComplete(ar -> {
if (ar.succeeded()) {
connectInternal2(proxyOptions, remoteAddress, peerAddress, ar.result(), serverName, ssl, useAlpn, registerWriteHandlers, connectHandler, context, remainingAttempts);
} else {
connectHandler.fail(ar.cause());
}
});
}
}
private void connectInternal2(ProxyOptions proxyOptions,
SocketAddress remoteAddress,
SocketAddress peerAddress,
SslChannelProvider sslChannelProvider,
String serverName,
boolean ssl,
boolean useAlpn,
boolean registerWriteHandlers,
Promise connectHandler,
ContextInternal context,
int remainingAttempts) {
EventLoop eventLoop = context.nettyEventLoop();
if (eventLoop.inEventLoop()) {
Objects.requireNonNull(connectHandler, "No null connectHandler accepted");
Bootstrap bootstrap = new Bootstrap();
bootstrap.group(eventLoop);
bootstrap.option(ChannelOption.ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE);
vertx.transport().configure(options, remoteAddress.isDomainSocket(), bootstrap);
ChannelProvider channelProvider = new ChannelProvider(bootstrap, sslChannelProvider, context)
.proxyOptions(proxyOptions);
channelProvider.handler(ch -> connected(context, ch, connectHandler, remoteAddress, sslChannelProvider, channelProvider.applicationProtocol(), registerWriteHandlers));
io.netty.util.concurrent.Future fut = channelProvider.connect(remoteAddress, peerAddress, serverName, ssl, useAlpn);
fut.addListener((GenericFutureListener>) future -> {
if (!future.isSuccess()) {
Throwable cause = future.cause();
// FileNotFoundException for domain sockets
boolean connectError = cause instanceof ConnectException || cause instanceof FileNotFoundException;
if (connectError && (remainingAttempts > 0 || remainingAttempts == -1)) {
context.emit(v -> {
log.debug("Failed to create connection. Will retry in " + options.getReconnectInterval() + " milliseconds");
//Set a timer to retry connection
vertx.setTimer(options.getReconnectInterval(), tid ->
connectInternal(proxyOptions, remoteAddress, peerAddress, serverName, ssl, useAlpn, registerWriteHandlers, connectHandler, context, remainingAttempts == -1 ? remainingAttempts : remainingAttempts - 1)
);
});
} else {
failed(context, null, cause, connectHandler);
}
}
});
} else {
eventLoop.execute(() -> connectInternal2(proxyOptions, remoteAddress, peerAddress, sslChannelProvider, serverName, ssl, useAlpn, registerWriteHandlers, connectHandler, context, remainingAttempts));
}
}
private void connected(ContextInternal context, Channel ch, Promise connectHandler, SocketAddress remoteAddress, SslChannelProvider sslChannelProvider, String applicationLayerProtocol, boolean registerWriteHandlers) {
channelGroup.add(ch);
initChannel(ch.pipeline());
VertxHandler handler = VertxHandler.create(ctx -> new NetSocketImpl(context, ctx, remoteAddress, sslChannelProvider, metrics, applicationLayerProtocol, registerWriteHandlers));
handler.removeHandler(NetSocketImpl::unregisterEventBusHandler);
handler.addHandler(sock -> {
if (metrics != null) {
sock.metric(metrics.connected(sock.remoteAddress(), sock.remoteName()));
}
sock.registerEventBusHandler();
connectHandler.complete(sock);
});
ch.pipeline().addLast("handler", handler);
}
private void failed(ContextInternal context, Channel ch, Throwable th, Promise connectHandler) {
if (ch != null) {
ch.close();
}
context.emit(th, connectHandler::tryFail);
}
}