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.
/*
* 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.buffer.ByteBuf;
import com.arangodb.shaded.netty.buffer.Unpooled;
import com.arangodb.shaded.netty.channel.*;
import com.arangodb.shaded.netty.handler.ssl.SslHandler;
import com.arangodb.shaded.netty.handler.stream.ChunkedNioFile;
import com.arangodb.shaded.netty.handler.timeout.IdleStateEvent;
import com.arangodb.shaded.netty.util.AttributeKey;
import com.arangodb.shaded.netty.util.ReferenceCountUtil;
import com.arangodb.shaded.netty.util.concurrent.EventExecutor;
import com.arangodb.shaded.netty.util.concurrent.FutureListener;
import com.arangodb.shaded.vertx.core.*;
import com.arangodb.shaded.vertx.core.impl.ContextInternal;
import com.arangodb.shaded.vertx.core.impl.future.PromiseInternal;
import com.arangodb.shaded.vertx.core.impl.VertxInternal;
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.SocketAddress;
import com.arangodb.shaded.vertx.core.spi.metrics.NetworkMetrics;
import com.arangodb.shaded.vertx.core.spi.metrics.TCPMetrics;
import javax.net.ssl.SSLPeerUnverifiedException;
import javax.net.ssl.SSLSession;
import javax.security.cert.X509Certificate;
import java.io.IOException;
import java.io.RandomAccessFile;
import java.net.InetSocketAddress;
import java.security.cert.Certificate;
import java.util.Arrays;
import java.util.List;
import static com.arangodb.shaded.vertx.core.spi.metrics.Metrics.METRICS_ENABLED;
/**
* Abstract base class for TCP connections.
*
* This class is optimised for performance when used on the same event loop. However it can be used safely from other threads.
*
* The internal state is protected using the synchronized keyword. If always used on the same event loop, then
* we benefit from biased locking which makes the overhead of synchronized near zero.
*
* @author Tim Fox
*/
public abstract class ConnectionBase {
private static final long METRICS_REPORTED_BYTES_LOW_MASK = 0xFFF; // 4K
private static final long METRICS_REPORTED_BYTES_HIGH_MASK = ~METRICS_REPORTED_BYTES_LOW_MASK; // 4K
/**
* An exception used to signal a closed connection to an exception handler. Exception are
* expensive to create, this instance can be used for this purpose. It does not capture a stack
* trace to not be misleading.
*/
public static final VertxException CLOSED_EXCEPTION = new VertxException("Connection was closed", true);
public static final AttributeKey REMOTE_ADDRESS_OVERRIDE = AttributeKey.valueOf("RemoteAddressOverride");
public static final AttributeKey LOCAL_ADDRESS_OVERRIDE = AttributeKey.valueOf("LocalAddressOverride");
private static final Logger log = LoggerFactory.getLogger(ConnectionBase.class);
private static final int MAX_REGION_SIZE = 1024 * 1024;
public final VoidChannelPromise voidPromise;
protected final VertxInternal vertx;
protected final ChannelHandlerContext chctx;
protected final ContextInternal context;
private Handler exceptionHandler;
private Handler closeHandler;
private int writeInProgress;
private Object metric;
private SocketAddress remoteAddress;
private SocketAddress realRemoteAddress;
private SocketAddress localAddress;
private SocketAddress realLocalAddress;
private ChannelPromise closePromise;
private Future closeFuture;
private long remainingBytesRead;
private long remainingBytesWritten;
// State accessed exclusively from the event loop thread
private boolean read;
private boolean needsFlush;
private boolean closed;
protected ConnectionBase(ContextInternal context, ChannelHandlerContext chctx) {
this.vertx = context.owner();
this.chctx = chctx;
this.context = context;
this.voidPromise = new VoidChannelPromise(chctx.channel(), false);
this.closePromise = chctx.newPromise();
PromiseInternal p = context.promise();
closePromise.addListener(p);
closeFuture = p.future();
// Add close handler callback
closeFuture.onComplete(this::checkCloseHandler);
}
/**
* @return a promise that will be completed when the connection becomes closed
*/
public Future closeFuture() {
return closeFuture;
}
/**
* Fail the connection, the {@code error} will be sent to the pipeline and the connection will
* stop processing any further message.
*
* @param error the {@code Throwable} to propagate
*/
public void fail(Throwable error) {
chctx.pipeline().fireExceptionCaught(error);
}
void close(ChannelPromise promise) {
closePromise.addListener(l -> {
if (l.isSuccess()) {
promise.setSuccess();
} else {
promise.setFailure(l.cause());
}
});
close();
}
/**
* This method is exclusively called by {@code VertxHandler} to signal read completion on the event-loop thread.
*/
final void endReadAndFlush() {
if (read) {
read = false;
if (needsFlush) {
needsFlush = false;
chctx.flush();
}
}
}
/**
* This method is exclusively called by {@code VertxHandler} to read a message on the event-loop thread.
*/
final void read(Object msg) {
read = true;
if (METRICS_ENABLED) {
reportBytesRead(msg);
}
handleMessage(msg);
}
/**
* This method is exclusively called on the event-loop thread
*
* @param msg the message to write
* @param flush a {@code null} {@code flush} value means to flush when there is no read in progress, otherwise it will apply the policy
* @param promise the promise receiving the completion event
*/
private void write(Object msg, Boolean flush, ChannelPromise promise) {
if (METRICS_ENABLED) {
reportsBytesWritten(msg);
}
boolean writeAndFlush;
if (flush == null) {
writeAndFlush = !read;
} else {
writeAndFlush = flush;
}
needsFlush = !writeAndFlush;
if (writeAndFlush) {
chctx.writeAndFlush(msg, promise);
} else {
chctx.write(msg, promise);
}
}
/**
* This method is exclusively called on the event-loop thread
*
* @param promise the promise receiving the completion event
*/
private void writeClose(PromiseInternal promise) {
if (closed) {
promise.complete();
return;
}
closed = true;
// Make sure everything is flushed out on close
ChannelPromise channelPromise = chctx
.newPromise()
.addListener((ChannelFutureListener) f -> {
chctx.close().addListener(promise);
});
writeToChannel(Unpooled.EMPTY_BUFFER, true, channelPromise);
}
private ChannelPromise wrap(FutureListener handler) {
ChannelPromise promise = chctx.newPromise();
promise.addListener(handler);
return promise;
}
public final void writeToChannel(Object msg, FutureListener listener) {
writeToChannel(msg, listener == null ? voidPromise : wrap(listener));
}
public final void writeToChannel(Object msg, ChannelPromise promise) {
writeToChannel(msg, false, promise);
}
public final void writeToChannel(Object msg, boolean forceFlush, ChannelPromise promise) {
synchronized (this) {
if (!chctx.executor().inEventLoop() || writeInProgress > 0) {
// Make sure we serialize all the messages as this method can be called from various threads:
// two "sequential" calls to writeToChannel (we can say that as it is synchronized) should preserve
// the message order independently of the thread. To achieve this we need to reschedule messages
// not on the event loop or if there are pending async message for the channel.
queueForWrite(msg, forceFlush, promise);
return;
}
}
// On the event loop thread
write(msg, forceFlush ? true : null, promise);
}
private void queueForWrite(Object msg, boolean forceFlush, ChannelPromise promise) {
writeInProgress++;
chctx.executor().execute(() -> {
boolean flush;
if (forceFlush) {
flush = true;
} else {
synchronized (this) {
flush = --writeInProgress == 0;
}
}
write(msg, flush, promise);
});
}
public void writeToChannel(Object obj) {
writeToChannel(obj, voidPromise);
}
/**
* Asynchronous flush.
*/
public final void flush() {
flush(voidPromise);
}
/**
* Asynchronous flush.
*
* @param promise the promise resolved when flush occurred
*/
public final void flush(ChannelPromise promise) {
writeToChannel(Unpooled.EMPTY_BUFFER, true, promise);
}
// This is a volatile read inside the Netty channel implementation
public boolean isNotWritable() {
return !chctx.channel().isWritable();
}
/**
* Close the connection
*/
public Future close() {
PromiseInternal promise = context.promise();
EventExecutor exec = chctx.executor();
if (exec.inEventLoop()) {
writeClose(promise);
} else {
exec.execute(() -> writeClose(promise));
}
return promise.future();
}
/**
* Close the connection and notifies the {@code handler}
*/
public final void close(Handler> handler) {
close().onComplete(handler);
}
public synchronized ConnectionBase closeHandler(Handler handler) {
closeHandler = handler;
return this;
}
public synchronized ConnectionBase exceptionHandler(Handler handler) {
this.exceptionHandler = handler;
return this;
}
protected synchronized Handler exceptionHandler() {
return exceptionHandler;
}
public void doPause() {
chctx.channel().config().setAutoRead(false);
}
public void doResume() {
chctx.channel().config().setAutoRead(true);
}
public void doSetWriteQueueMaxSize(int size) {
ChannelConfig config = chctx.channel().config();
config.setWriteBufferWaterMark(new WriteBufferWaterMark(size / 2, size));
}
/**
* @return the Netty channel - for internal usage only
*/
public final Channel channel() {
return chctx.channel();
}
public final ChannelHandlerContext channelHandlerContext() {
return chctx;
}
public final ContextInternal getContext() {
return context;
}
public final synchronized void metric(Object metric) {
this.metric = metric;
}
public final synchronized Object metric() {
return metric;
}
public abstract NetworkMetrics metrics();
protected void handleException(Throwable t) {
NetworkMetrics metrics = metrics();
if (metrics != null) {
metrics.exceptionOccurred(metric, remoteAddress(), t);
}
context.emit(t, err -> {
Handler handler;
synchronized (ConnectionBase.this) {
handler = exceptionHandler;
}
if (handler != null) {
handler.handle(err);
} else {
if (log.isDebugEnabled()) {
log.error(t.getMessage(), t);
} else {
log.error(t.getMessage());
}
}
});
}
protected void handleClosed() {
closed = true;
NetworkMetrics metrics = metrics();
if (metrics != null) {
flushBytesRead();
flushBytesWritten();
if (metrics instanceof TCPMetrics) {
((TCPMetrics) metrics).disconnected(metric(), remoteAddress());
}
}
closePromise.setSuccess();
}
private void checkCloseHandler(AsyncResult ar) {
Handler handler;
synchronized (ConnectionBase.this) {
handler = closeHandler;
}
if (handler != null) {
handler.handle(null);
}
}
protected void handleEvent(Object evt) {
// Will release the event if needed
ReferenceCountUtil.release(evt);
}
/**
* Called by the Netty handler when the connection becomes idle. The default implementation closes the
* connection.
*
* Subclasses can override it to prevent the idle event to happen (e.g when the connection is pooled) or
* perform extra work when the idle event happens.
*/
protected void handleIdle(IdleStateEvent event) {
log.debug("The connection will be closed due to timeout");
chctx.close();
}
protected abstract void handleInterestedOpsChanged();
protected boolean supportsFileRegion() {
return vertx.transport().supportFileRegion() && !isSsl();
}
public final void reportBytesRead(Object msg) {
NetworkMetrics metrics = metrics();
if (metrics != null) {
doReportBytesRead(msg, metrics);
}
}
private void doReportBytesRead(Object msg, NetworkMetrics metrics) {
long bytes = remainingBytesRead;
long numberOfBytes = sizeof(msg);
bytes += numberOfBytes;
long val = bytes & METRICS_REPORTED_BYTES_HIGH_MASK;
if (val > 0) {
bytes &= METRICS_REPORTED_BYTES_LOW_MASK;
metrics.bytesRead(metric(), remoteAddress(), val);
}
remainingBytesRead = bytes;
}
protected long sizeof(Object msg) {
if (msg instanceof ByteBuf) {
return ((ByteBuf)msg).readableBytes();
}
return 0L;
}
public final void reportBytesRead(long numberOfBytes) {
if (numberOfBytes < 0L) {
throw new IllegalArgumentException();
}
NetworkMetrics metrics = metrics();
if (metrics != null) {
long bytes = remainingBytesRead;
bytes += numberOfBytes;
long val = bytes & METRICS_REPORTED_BYTES_HIGH_MASK;
if (val > 0) {
bytes &= METRICS_REPORTED_BYTES_LOW_MASK;
metrics.bytesRead(metric(), remoteAddress(), val);
}
remainingBytesRead = bytes;
}
}
public final void reportsBytesWritten(Object msg) {
NetworkMetrics metrics = metrics();
if (metrics != null) {
long numberOfBytes = sizeof(msg);
long bytes = remainingBytesWritten;
bytes += numberOfBytes;
long val = bytes & METRICS_REPORTED_BYTES_HIGH_MASK;
if (val > 0) {
bytes &= METRICS_REPORTED_BYTES_LOW_MASK;
metrics.bytesWritten(metric, remoteAddress(), val);
}
remainingBytesWritten = bytes;
}
}
public final void reportBytesWritten(long numberOfBytes) {
if (numberOfBytes < 0L) {
throw new IllegalArgumentException();
}
NetworkMetrics metrics = metrics();
if (metrics != null) {
long bytes = remainingBytesWritten;
bytes += numberOfBytes;
long val = bytes & METRICS_REPORTED_BYTES_HIGH_MASK;
if (val > 0) {
bytes &= METRICS_REPORTED_BYTES_LOW_MASK;
metrics.bytesWritten(metric, remoteAddress(), val);
}
remainingBytesWritten = bytes;
}
}
public void flushBytesRead() {
NetworkMetrics metrics = metrics();
if (metrics != null) {
long val = remainingBytesRead;
if (val > 0L) {
remainingBytesRead = 0L;
metrics.bytesRead(metric(), remoteAddress(), val);
}
}
}
public void flushBytesWritten() {
NetworkMetrics metrics = metrics();
if (metrics != null) {
long val = remainingBytesWritten;
if (val > 0L) {
remainingBytesWritten = 0L;
metrics.bytesWritten(metric(), remoteAddress(), val);
}
}
}
/**
* Send a file as a file region for zero copy transfer to the socket.
*
* The implementation splits the file into multiple regions to avoid stalling the pipeline
* and producing idle timeouts for very large files.
*
* @param file the file to send
* @param offset the file offset
* @param length the file length
* @param writeFuture the write future to be completed when the transfer is done or failed
*/
private void sendFileRegion(RandomAccessFile file, long offset, long length, ChannelPromise writeFuture) {
if (length < MAX_REGION_SIZE) {
writeToChannel(new DefaultFileRegion(file.getChannel(), offset, length), writeFuture);
} else {
ChannelPromise promise = chctx.newPromise();
FileRegion region = new DefaultFileRegion(file.getChannel(), offset, MAX_REGION_SIZE);
// Retain explicitly this file region so the underlying channel is not closed by the NIO channel when it
// as been sent as we need it again
region.retain();
writeToChannel(region, promise);
promise.addListener(future -> {
if (future.isSuccess()) {
sendFileRegion(file, offset + MAX_REGION_SIZE, length - MAX_REGION_SIZE, writeFuture);
} else {
log.error(future.cause().getMessage(), future.cause());
writeFuture.setFailure(future.cause());
}
});
}
}
public final ChannelFuture sendFile(RandomAccessFile raf, long offset, long length) throws IOException {
// Write the content.
ChannelPromise writeFuture = chctx.newPromise();
if (!supportsFileRegion()) {
// Cannot use zero-copy
writeToChannel(new ChunkedNioFile(raf.getChannel(), offset, length, 8192), writeFuture);
} else {
// No encryption - use zero-copy.
sendFileRegion(raf, offset, length, writeFuture);
}
if (writeFuture != null) {
writeFuture.addListener(fut -> raf.close());
} else {
raf.close();
}
return writeFuture;
}
public boolean isSsl() {
return chctx.pipeline().get(SslHandler.class) != null;
}
public SSLSession sslSession() {
ChannelHandlerContext sslHandlerContext = chctx.pipeline().context(SslHandler.class);
if (sslHandlerContext != null) {
SslHandler sslHandler = (SslHandler) sslHandlerContext.handler();
return sslHandler.engine().getSession();
} else {
return null;
}
}
public X509Certificate[] peerCertificateChain() throws SSLPeerUnverifiedException {
SSLSession session = sslSession();
if (session != null) {
return session.getPeerCertificateChain();
} else {
return null;
}
}
public List peerCertificates() throws SSLPeerUnverifiedException {
SSLSession session = sslSession();
if (session != null) {
return Arrays.asList(session.getPeerCertificates());
} else {
return null;
}
}
public String indicatedServerName() {
if (chctx.channel().hasAttr(SslHandshakeCompletionHandler.SERVER_NAME_ATTR)) {
return chctx.channel().attr(SslHandshakeCompletionHandler.SERVER_NAME_ATTR).get();
} else {
return null;
}
}
public ChannelPromise channelFuture() {
return chctx.newPromise();
}
public String remoteName() {
java.net.SocketAddress addr = chctx.channel().remoteAddress();
if (addr instanceof InetSocketAddress) {
// Use hostString that does not trigger a DNS resolution
return ((InetSocketAddress)addr).getHostString();
}
return null;
}
private SocketAddress channelRemoteAddress() {
java.net.SocketAddress addr = chctx.channel().remoteAddress();
return addr != null ? vertx.transport().convert(addr) : null;
}
private SocketAddress socketAdressOverride(AttributeKey key) {
Channel ch = chctx.channel();
return ch.hasAttr(key) ? ch.attr(key).getAndSet(null) : null;
}
public SocketAddress remoteAddress() {
SocketAddress address = remoteAddress;
if (address == null) {
address = socketAdressOverride(REMOTE_ADDRESS_OVERRIDE);
if (address == null) {
address = channelRemoteAddress();
}
if (address != null) {
remoteAddress = address;
}
}
return address;
}
public SocketAddress remoteAddress(boolean real) {
if (real) {
SocketAddress address = realRemoteAddress;
if (address == null) {
address = channelRemoteAddress();
}
if (address != null) {
realRemoteAddress = address;
}
return address;
} else {
return remoteAddress();
}
}
private SocketAddress channelLocalAddress() {
java.net.SocketAddress addr = chctx.channel().localAddress();
return addr != null ? vertx.transport().convert(addr) : null;
}
public SocketAddress localAddress() {
SocketAddress address = localAddress;
if (address == null) {
address = socketAdressOverride(LOCAL_ADDRESS_OVERRIDE);
if (address == null) {
address = channelLocalAddress();
}
if (address != null) {
localAddress = address;
}
}
return address;
}
public SocketAddress localAddress(boolean real) {
if (real) {
SocketAddress address = realLocalAddress;
if (address == null) {
address = channelLocalAddress();
}
if (address != null) {
realLocalAddress = address;
}
return address;
} else {
return localAddress();
}
}
protected void handleMessage(Object msg) {
}
}