package org.webpieces.asyncserver.impl;
import java.nio.ByteBuffer;
import java.util.concurrent.CompletableFuture;
import org.webpieces.nio.api.channels.Channel;
import org.webpieces.nio.api.channels.RegisterableChannel;
import org.webpieces.nio.api.handlers.ConnectionListener;
import org.webpieces.nio.api.handlers.DataListener;
import org.webpieces.util.logging.Logger;
import org.webpieces.util.logging.LoggerFactory;
public class DefaultConnectionListener implements ConnectionListener {
private static final Logger log = LoggerFactory.getLogger(DefaultConnectionListener.class);
private ConnectedChannels connectedChannels;
private ByteBuffer overloadResponse;
private ProxyDataListener listener;
public DefaultConnectionListener(ConnectedChannels channels, ProxyDataListener listener) {
this.connectedChannels = channels;
this.listener = listener;
}
@Override
public CompletableFuture<DataListener> connected(Channel tcpChannel, boolean isReadyForWrites) {
if(overloadResponse != null) {
//This is annoying.....
//1. we canNOT do synchronous write as it could block forever (if hacker simulates full nic)
//2. we can do async but it may never fire data was written so firing close after write is complete may not close forever(leak connection)
//3. soooo...we must do both async and close on complete with timer to timeout and close on write regardless of success
//4. lastly, these all could throw exceptions and we don't really care about them at all
handleOverload(tcpChannel);
return new CompletableFuture<DataListener>(); //return a future that will never resolve so we do not register for reads
}
boolean added = connectedChannels.addChannel(tcpChannel);
if(added) {
//represent the begin of an incoming connection with 0 data so that clients can
//start a timer task if no data comes in...
listener.connectionOpened(tcpChannel, isReadyForWrites);
}
return CompletableFuture.completedFuture(listener);
}
private void handleOverload(Channel tcpChannel) {
overloadResponse.mark();
try {
tcpChannel.write(overloadResponse);
} catch(Exception e) {
//normal behavior in cases where people connect and disconnect before response so
//we only log it at info...
log.info("exception trying to send overload response. exc type="+e.getClass());
}
//close should be moved to two places
//1. write complete callback should close
//2. timer in 2 seconds should close if write did not complete in that time
close(tcpChannel);
overloadResponse.reset();
}
private void close(Channel tcpChannel) {
try {
tcpChannel.close();
} catch (Exception e) {
//normal behavior in cases where people connect and disconnect before response so
//we only log it at info...
log.info("exception trying to close after sending overload response. exc type="+e.getClass());
}
}
@Override
public void failed(RegisterableChannel channel, Throwable e) {
log.error("exception from client connecting in. channel="+channel, e);
}
public void enableOverloadMode(ByteBuffer overloadResponse) {
if(overloadResponse.remaining() <= 0)
throw new IllegalArgumentException("There is 0 remaining bytes in this buffer");
this.overloadResponse = overloadResponse;
}
public void disableOverloadMode() {
this.overloadResponse = null;
}
public CompletableFuture<Void> closeChannels() {
return connectedChannels.closeChannels();
}
}