package org.webpieces.nio.api.integ;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import org.webpieces.asyncserver.api.AsyncConfig;
import org.webpieces.asyncserver.api.AsyncServer;
import org.webpieces.asyncserver.api.AsyncServerManager;
import org.webpieces.asyncserver.api.AsyncServerMgrFactory;
import org.webpieces.data.api.BufferCreationPool;
import org.webpieces.nio.api.ChannelManager;
import org.webpieces.nio.api.ChannelManagerFactory;
import org.webpieces.nio.api.channels.Channel;
import org.webpieces.nio.api.channels.TCPChannel;
import org.webpieces.nio.api.handlers.DataListener;
import org.webpieces.util.logging.Logger;
import org.webpieces.util.logging.LoggerFactory;
public class IntegTestClientNotRead {
private static final Logger log = LoggerFactory.getLogger(IntegTestClientNotRead.class);
private Timer timer = new Timer();
private long timeMillis;
private long totalBytes = 0;
private Executor executor = Executors.newFixedThreadPool(1);
/**
* Here, we will simulate a bad hacker client that sets his side so_timeout to infinite
* and then refuses to read response data back in but keeps writing into our server to
* crash the server as it backs up on responses....ie. we keep receiving requests and holding
* on to them so memory keeps growing and growing or our write queue keeps growing unbounded
*
* so this test ensures we fix that scenario
* @throws InterruptedException
*/
public static void main(String[] args) throws InterruptedException {
new IntegTestClientNotRead().testSoTimeoutOnSocket();
}
public void testSoTimeoutOnSocket() throws InterruptedException {
BufferCreationPool pool = new BufferCreationPool();
AsyncServerManager serverMgr = AsyncServerMgrFactory.createAsyncServer("server", pool);
AsyncServer server = serverMgr.createTcpServer(new AsyncConfig("tcpServer"), new IntegTestClientNotReadListener());
server.start(new InetSocketAddress(8080));
BufferCreationPool pool2 = new BufferCreationPool();
ChannelManagerFactory factory = ChannelManagerFactory.createFactory();
ChannelManager mgr = factory.createSingleThreadedChanMgr("client", pool2);
TCPChannel channel = mgr.createTCPChannel("clientChan");
log.info("client");
timer.schedule(new TimerTask() {
@Override
public void run() {
logBytesTxfrd();
}
}, 1000, 5000);
CompletableFuture<Channel> connect = channel.connect(new InetSocketAddress(8080), new ClientDataListener());
connect.thenAccept(p -> runWriting(channel));
Thread.sleep(1000000000);
}
private void logBytesTxfrd() {
long bytesTxfrd = getBytes();
long totalTime = System.currentTimeMillis() - timeMillis;
long bytesPerMs = bytesTxfrd / totalTime;
log.info("time for bytes="+bytesTxfrd+". time="+totalTime+" rate="+bytesPerMs +" Bytes/Ms");
}
private void runWriting(Channel channel) {
log.info("unregister for reads");
channel.unregisterForReads(); //ensures we don't read from the channel
timeMillis = System.currentTimeMillis();
log.info("starting writing");
write(channel, null, 0);
}
private synchronized long getBytes() {
return totalBytes;
}
protected synchronized void recordBytes(int remaining) {
totalBytes += remaining;
}
private void write(Channel channel, String reason, final int counter) {
log.info("write from client. reason="+ reason);
byte[] data = new byte[2000];
ByteBuffer buffer = ByteBuffer.wrap(data);
CompletableFuture<Channel> write = channel.write(buffer);
final int count = counter+1;
if(counter >= 100) {
write
.thenAccept(p -> write(channel, "wrote data from client", count))
.whenComplete((r, e) -> finished(r, e));
} else {
write.thenAcceptAsync(p -> write(channel, "wrote data async", 0), executor)
.whenComplete((r, e) -> finished(r, e));
}
}
private void finished(Void r, Throwable e) {
if(e != null)
log.info("failed due to reason="+e.getMessage(), e);
}
private class ClientDataListener implements DataListener {
@Override
public void incomingData(Channel channel, ByteBuffer b) {
recordBytes(b.remaining());
if(b.remaining() != 2000)
log.info("size of buffer="+b.remaining());
}
@Override
public void farEndClosed(Channel channel) {
log.info("far end closed");
}
@Override
public void failure(Channel channel, ByteBuffer data, Exception e) {
log.info("failure", e);
}
@Override
public void applyBackPressure(Channel channel) {
log.info("client unregistering for reads");
channel.unregisterForReads();
}
@Override
public void releaseBackPressure(Channel channel) {
log.info("client registring for reads");
channel.registerForReads();
}
}
}