![JAR search and dependency download from the Maven repository](/logo.png)
com.ociweb.pronghorn.network.ClientConnection Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of Pronghorn Show documentation
Show all versions of Pronghorn Show documentation
Primary dependency for any project using the Pronghorn framework
The newest version!
package com.ociweb.pronghorn.network;
import java.io.IOException;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.StandardSocketOptions;
import java.net.UnknownHostException;
import java.nio.channels.NoConnectionPendingException;
import java.nio.channels.SelectionKey;
import java.nio.channels.Selector;
import java.nio.channels.SocketChannel;
import javax.net.ssl.SSLEngine;
import javax.net.ssl.SSLEngineResult.HandshakeStatus;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.ociweb.pronghorn.network.schema.NetPayloadSchema;
import com.ociweb.pronghorn.pipe.Pipe;
import com.ociweb.pronghorn.stage.scheduling.ElapsedTimeRecorder;
import com.ociweb.pronghorn.util.Appendables;
public class ClientConnection extends BaseConnection implements SelectionKeyHashMappable {
//TODO: limitations with client side calls
// only supports 1 struct or 1 JSON parser or 1 set of headers per ClientSession
// this may force developers to use interleaving in some cases to solve this
// paths will need to be pre-defined and given identifications. (future feature)
public static int resolveWithDNSTimeoutMS = 10_000; // §6.1.3.3 of RFC 1123 suggests a value not less than 5 seconds.
private static final long MAX_HIST_VALUE = 40_000_000_000L;
private static final Logger logger = LoggerFactory.getLogger(ClientConnection.class);
private static final byte[] EMPTY = new byte[0];
public static boolean logDisconnects = false;
public static boolean logLatencyData = false;
private SelectionKey key; //only registered after handshake is complete.
private final byte[] connectionGUID;
private final int connectionGUIDLength;
private final int pipeIdx;
private long requestsSent;
private long responsesReceived;
///////////////////////
//TODO: Store the JSON Extractor here so we can apply it when the results come in??
///////////////////////
public final int sessionId;
public final String host;
public final int port;
public final int hostId;
private long closeTimeLimit = Long.MAX_VALUE;
private long TIME_TILL_CLOSE = 10_000;
private ElapsedTimeRecorder histRoundTrip = new ElapsedTimeRecorder();
private final static int maxInFlightBits = 18;//256K about 3MB per client connection
public final static int maxInFlight = 1<1_000_000_000) {
logger.info("warning slow DNS took {} sec to resolve {} to {} ",d/1_000_000_000,host,ipAddresses);
}
} catch (UnknownHostException unknwnHostEx) {
failureDetected = true;
}
} while (null == ipAddresses && System.currentTimeMillis()>8);
target[pos++] = (byte)(port);
target[pos++] = (byte)(userId);
target[pos++] = (byte)(userId>>8);
target[pos++] = (byte)(userId>>16);
target[pos++] = (byte)(userId>>24);
return pos;
}
public byte[] GUID() {
return connectionGUID;
}
public int GUIDLength() {
return connectionGUIDLength;
}
/**
* After construction this must be called until it returns true before using this connection.
*/
public boolean isFinishConnect() {
if (isFinishedConnection) {
return true;
} else {
try {
boolean finishConnect = getSocketChannel().finishConnect();
isFinishedConnection |= finishConnect;
if (!finishConnect ) {
if (System.nanoTime() > creationTimeNS+(resolveWithDNSTimeoutMS*1000000L)) {
logger.info("connection timeout {} {}ms",this,resolveWithDNSTimeoutMS);
beginDisconnect();
}
} else {
clearWaitingForNetwork();
}
return finishConnect;
} catch (IOException io) {
//logger.trace("finish connection exception ",io);
return false;
} catch (NoConnectionPendingException ncpe) {
close();
//logger.trace("no pending connection ",ncpe);
return false;
}
}
}
public boolean isRegistered() {
return this.key!=null;
}
public void registerForUse(Selector selector, Pipe[] handshakeBegin, boolean isTLS) throws IOException {
assert(getSocketChannel().finishConnect());
//logger.trace("now finished connection to : {} ",getSocketChannel().getRemoteAddress().toString());
if (isTLS) {
getEngine().beginHandshake();
HandshakeStatus handshake = getEngine().getHandshakeStatus();
if (HandshakeStatus.NEED_TASK == handshake) {
Runnable task;
while ((task = getEngine().getDelegatedTask()) != null) {
task.run();
}
} else if (HandshakeStatus.NEED_WRAP == handshake) {
int c= (int)getId()%handshakeBegin.length;
int j = handshakeBegin.length;
while (--j>=0) {
final Pipe pipe = handshakeBegin[c];
assert(null!=pipe);
if (Pipe.hasRoomForWrite(pipe)) {
//Warning the follow on calls should be low level...
//logger.warn("Low-Level ClientConnection request wrap for id {} to pipe {}",getId(), pipe, new Exception());
////////////////////////////////////
//NOTE: must repeat this until the handshake is finished.
///////////////////////////////////
final int size = Pipe.addMsgIdx(pipe, NetPayloadSchema.MSG_PLAIN_210);
Pipe.addLongValue(getId(), pipe);
Pipe.addLongValue(System.currentTimeMillis(), pipe);
Pipe.addLongValue(SSLUtil.HANDSHAKE_POS, pipe);
Pipe.addByteArray(EMPTY, 0, 0, pipe);
Pipe.confirmLowLevelWrite(pipe, size);
Pipe.publishWrites(pipe);
break;
}
if (--c<0) {
c = handshakeBegin.length-1;
}
}
if (j<0) {
throw new UnsupportedOperationException("unable to wrap handshake no pipes are avilable.");
}
}
}
isValid = true;
//logger.info("is now valid connection {} ",this.id);
this.key = getSocketChannel().register(selector, SelectionKey.OP_READ, this);
}
public boolean isValid() {
SocketChannel socketChannel = getSocketChannel();
if (!socketChannel.isConnected()) {
if (logDisconnects) {
logger.info("{}:{} session {} is no longer connected. It was opened {} ago.",
host,port,sessionId,
Appendables.appendNearestTimeUnit(new StringBuilder(), System.nanoTime()-creationTimeNS).toString()
);
}
return false;
}
return isValid;
}
public boolean isDisconnecting() {
return isDisconnecting;
}
public void beginDisconnect() {
if (logLatencyData) {
logger.info("closing connection, latencies for {}:{}\n{}",this.host,this.port,histRoundTrip);
}
try {
isDisconnecting = true;
if (isTLS) {
SSLEngine eng = getEngine();
if (null!=eng) {
eng.closeOutbound();
}
}
} catch (Throwable e) {
logger.warn("Error closing connection ",e);
close();
}
}
public ElapsedTimeRecorder histogram() {
return histRoundTrip;
}
public void recordSentTime(long time) {
inFlightTimes[++inFlightTimeSentPos & maxInFlightMask] = time;
}
//important method to determine if the network was dropped while call was outstanding
public long outstandingCallTime(long time) {
if (inFlightRoutesRespPos == inFlightTimeSentPos) {
return -1;
} else {
long sentTime = inFlightTimes[1+inFlightTimeRespPos & maxInFlightMask];
if (sentTime>0) {
return time - sentTime;
} else {
return -1;//we read the value while it was being sent so discard
}
}
}
//returns latency
public long recordArrivalTime(long time) {
assert(inFlightTimes[1+inFlightTimeRespPos & maxInFlightMask]>0);
long value = time - inFlightTimes[++inFlightTimeRespPos & maxInFlightMask];
if (value>=0 && value
© 2015 - 2025 Weber Informatics LLC | Privacy Policy