use of io.questdb.network.IOContextFactory in project questdb by bluestreak01.
the class IODispatcherTest method testSendHttpGetAndSimpleResponse.
@Test
public void testSendHttpGetAndSimpleResponse() throws Exception {
LOG.info().$("started testSendHttpGetAndSimpleResponse").$();
final String request = "GET /status?x=1&a=%26b&c&d=x HTTP/1.1\r\n" + "Host: localhost:9000\r\n" + "Connection: keep-alive\r\n" + "Cache-Control: max-age=0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36\r\n" + "Accept-Encoding: gzip,deflate,sdch\r\n" + "Accept-Language: en-US,en;q=0.8\r\n" + "Cookie: textwrapon=false; textautoformat=false; wysiwyg=textarea\r\n" + "\r\n";
// the difference between request and expected is url encoding (and ':' padding, which can easily be fixed)
final String expected = "GET /status?x=1&a=&b&c&d=x HTTP/1.1\r\n" + "host:localhost:9000\r\n" + "connection:keep-alive\r\n" + "cache-control:max-age=0\r\n" + "accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + "user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36\r\n" + "accept-encoding:gzip,deflate,sdch\r\n" + "accept-language:en-US,en;q=0.8\r\n" + "cookie:textwrapon=false; textautoformat=false; wysiwyg=textarea\r\n" + "\r\n";
final String expectedResponse = "HTTP/1.1 200 OK\r\n" + "Server: questDB/1.0\r\n" + "Date: Thu, 1 Jan 1970 00:00:00 GMT\r\n" + "Transfer-Encoding: chunked\r\n" + "Content-Type: text/plain; charset=utf-8\r\n" + "\r\n" + "04\r\n" + "OK\r\n" + "\r\n" + "00\r\n" + "\r\n";
assertMemoryLeak(() -> {
HttpServerConfiguration httpServerConfiguration = new DefaultHttpServerConfiguration(new DefaultHttpContextConfiguration() {
@Override
public MillisecondClock getClock() {
return () -> 0;
}
});
SOCountDownLatch connectLatch = new SOCountDownLatch(1);
SOCountDownLatch contextClosedLatch = new SOCountDownLatch(1);
AtomicInteger closeCount = new AtomicInteger(0);
try (IODispatcher<HttpConnectionContext> dispatcher = IODispatchers.create(new DefaultIODispatcherConfiguration(), new IOContextFactory<HttpConnectionContext>() {
@Override
public HttpConnectionContext newInstance(long fd, IODispatcher<HttpConnectionContext> dispatcher1) {
connectLatch.countDown();
return new HttpConnectionContext(httpServerConfiguration.getHttpContextConfiguration()) {
@Override
public void close() {
// context is closed
if (closeCount.incrementAndGet() == 1) {
super.close();
contextClosedLatch.countDown();
}
}
}.of(fd, dispatcher1);
}
})) {
StringSink sink = new StringSink();
final HttpRequestProcessorSelector selector = new HttpRequestProcessorSelector() {
@Override
public HttpRequestProcessor select(CharSequence url) {
return null;
}
@Override
public HttpRequestProcessor getDefaultProcessor() {
return new HttpRequestProcessor() {
@Override
public void onHeadersReady(HttpConnectionContext context) {
HttpRequestHeader headers = context.getRequestHeader();
sink.put(headers.getMethodLine());
sink.put("\r\n");
ObjList<CharSequence> headerNames = headers.getHeaderNames();
for (int i = 0, n = headerNames.size(); i < n; i++) {
sink.put(headerNames.getQuick(i)).put(':');
sink.put(headers.getHeader(headerNames.getQuick(i)));
sink.put("\r\n");
}
sink.put("\r\n");
}
@Override
public void onRequestComplete(HttpConnectionContext context) throws PeerDisconnectedException, PeerIsSlowToReadException {
context.simpleResponse().sendStatusWithDefaultMessage(200);
}
};
}
@Override
public void close() {
}
};
AtomicBoolean serverRunning = new AtomicBoolean(true);
SOCountDownLatch serverHaltLatch = new SOCountDownLatch(1);
new Thread(() -> {
while (serverRunning.get()) {
dispatcher.run(0);
dispatcher.processIOQueue((operation, context) -> context.handleClientOperation(operation, selector, EmptyRescheduleContext));
}
serverHaltLatch.countDown();
}).start();
long fd = Net.socketTcp(true);
try {
long sockAddr = Net.sockaddr("127.0.0.1", 9001);
try {
TestUtils.assertConnect(fd, sockAddr);
connectLatch.await();
int len = request.length();
long buffer = TestUtils.toMemory(request);
try {
Assert.assertEquals(len, Net.send(fd, buffer, len));
// read response we expect
StringSink sink2 = new StringSink();
final int expectedLen = expectedResponse.length();
int read = 0;
while (read < expectedLen) {
int n = Net.recv(fd, buffer, len);
Assert.assertTrue(n > 0);
for (int i = 0; i < n; i++) {
sink2.put((char) Unsafe.getUnsafe().getByte(buffer + i));
}
// copy response bytes to sink
read += n;
}
TestUtils.assertEquals(expectedResponse, sink2);
} finally {
Unsafe.free(buffer, len, MemoryTag.NATIVE_DEFAULT);
}
Assert.assertEquals(0, Net.close(fd));
LOG.info().$("closed [fd=").$(fd).$(']').$();
fd = -1;
contextClosedLatch.await();
serverRunning.set(false);
serverHaltLatch.await();
Assert.assertEquals(0, dispatcher.getConnectionCount());
TestUtils.assertEquals(expected, sink);
} finally {
Net.freeSockAddr(sockAddr);
}
} finally {
if (fd != -1) {
Net.close(fd);
}
}
Assert.assertEquals(1, closeCount.get());
}
});
}
use of io.questdb.network.IOContextFactory in project questdb by bluestreak01.
the class IODispatcherTest method testMaxConnections.
@Test
public void testMaxConnections() throws Exception {
LOG.info().$("started maxConnections").$();
assertMemoryLeak(() -> {
HttpServerConfiguration httpServerConfiguration = new DefaultHttpServerConfiguration();
final int listenBackLog = 10;
final int activeConnectionLimit = 5;
final int nExcessConnections = 20;
AtomicInteger openCount = new AtomicInteger(0);
AtomicInteger closeCount = new AtomicInteger(0);
final IODispatcherConfiguration configuration = new DefaultIODispatcherConfiguration() {
@Override
public int getActiveConnectionLimit() {
return activeConnectionLimit;
}
@Override
public int getListenBacklog() {
return listenBackLog;
}
@Override
public long getQueuedConnectionTimeout() {
return 300_000;
}
};
try (IODispatcher<HttpConnectionContext> dispatcher = IODispatchers.create(configuration, new IOContextFactory<HttpConnectionContext>() {
@SuppressWarnings("resource")
@Override
public HttpConnectionContext newInstance(long fd, IODispatcher<HttpConnectionContext> dispatcher1) {
openCount.incrementAndGet();
return new HttpConnectionContext(httpServerConfiguration.getHttpContextConfiguration()) {
@Override
public void close() {
closeCount.incrementAndGet();
super.close();
}
}.of(fd, dispatcher1);
}
})) {
HttpRequestProcessorSelector selector = new HttpRequestProcessorSelector() {
@Override
public HttpRequestProcessor select(CharSequence url) {
return null;
}
@Override
public HttpRequestProcessor getDefaultProcessor() {
return new HealthCheckProcessor();
}
@Override
public void close() {
}
};
AtomicBoolean serverRunning = new AtomicBoolean(true);
SOCountDownLatch serverHaltLatch = new SOCountDownLatch(1);
new Thread(() -> {
try {
do {
dispatcher.run(0);
dispatcher.processIOQueue((operation, context) -> context.handleClientOperation(operation, selector, EmptyRescheduleContext));
} while (serverRunning.get());
} finally {
serverHaltLatch.countDown();
}
}).start();
LongList openFds = new LongList();
LongHashSet closedFds = new LongHashSet();
final long sockAddr = Net.sockaddr("127.0.0.1", 9001);
final long buf = Unsafe.malloc(4096, MemoryTag.NATIVE_DEFAULT);
final int N = activeConnectionLimit + listenBackLog;
try {
for (int i = 0; i < N; i++) {
long fd = Net.socketTcp(true);
TestUtils.assertConnect(fd, sockAddr);
openFds.add(fd);
}
// let dispatcher catchup
long startNanos = System.nanoTime();
while (dispatcher.isListening()) {
long endNanos = System.nanoTime();
if (TimeUnit.NANOSECONDS.toSeconds(endNanos - startNanos) > 30) {
Assert.fail("Timed out waiting for despatcher to stop listening");
}
LockSupport.parkNanos(1);
}
final String request = "GET /status?x=1&a=%26b&c&d=x HTTP/1.1\r\n" + "Host: localhost:9000\r\n" + "Connection: keep-alive\r\n" + "Cache-Control: max-age=0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36\r\n" + "Accept-Encoding: gzip,deflate,sdch\r\n" + "Accept-Language: en-US,en;q=0.8\r\n" + "Cookie: textwrapon=false; textautoformat=false; wysiwyg=textarea\r\n" + "\r\n";
long mem = TestUtils.toMemory(request);
// Active connection limit is reached and backlog is full, check we get connection closed
for (int i = 0; i < nExcessConnections; i++) {
long fd = Net.socketTcp(true);
if (fd > 0) {
int nSent = Net.send(fd, mem, request.length());
Assert.assertTrue(nSent < 0);
Net.close(fd);
}
}
try {
for (int i = 0; i < N; i++) {
if (i == activeConnectionLimit) {
for (int j = 0; j < activeConnectionLimit; j++) {
long fd = openFds.getQuick(j);
Net.close(fd);
closedFds.add(fd);
}
}
long fd = openFds.getQuick(i);
Assert.assertEquals(request.length(), Net.send(fd, mem, request.length()));
// ensure we have response from server
Assert.assertTrue(0 < Net.recv(fd, buf, 64));
if (i < activeConnectionLimit) {
// dont close any connections until the first connections have been processed and check that the dispatcher
// is not listening
Assert.assertFalse(dispatcher.isListening());
} else {
Net.close(fd);
closedFds.add(fd);
}
}
} finally {
Unsafe.free(mem, request.length(), MemoryTag.NATIVE_DEFAULT);
}
} finally {
for (int i = 0; i < openFds.size(); i++) {
long fd = openFds.getQuick(i);
if (!closedFds.contains(fd)) {
Net.close(fd);
}
}
Net.freeSockAddr(sockAddr);
Unsafe.free(buf, 4096, MemoryTag.NATIVE_DEFAULT);
Assert.assertFalse(configuration.getActiveConnectionLimit() < dispatcher.getConnectionCount());
serverRunning.set(false);
serverHaltLatch.await();
}
}
});
}
use of io.questdb.network.IOContextFactory in project questdb by bluestreak01.
the class IODispatcherTest method testQueuedConnectionTimeout.
@Test
public void testQueuedConnectionTimeout() throws Exception {
LOG.info().$("started testQueuedConnectionTimeout").$();
assertMemoryLeak(() -> {
final int listenBackLog = 10;
final int activeConnectionLimit = 5;
final long queuedConnectionTimeoutInMs = 250;
final IODispatcherConfiguration configuration = new DefaultIODispatcherConfiguration() {
@Override
public int getActiveConnectionLimit() {
return activeConnectionLimit;
}
@Override
public int getListenBacklog() {
return listenBackLog;
}
@Override
public long getQueuedConnectionTimeout() {
return queuedConnectionTimeoutInMs;
}
};
final AtomicInteger nConnected = new AtomicInteger();
final LongHashSet serverConnectedFds = new LongHashSet();
final LongHashSet clientActiveFds = new LongHashSet();
IOContextFactory<IOContext> contextFactory = (fd, dispatcher) -> {
LOG.info().$(fd).$(" connected").$();
serverConnectedFds.add(fd);
nConnected.incrementAndGet();
return new IOContext() {
@Override
public boolean invalid() {
return !serverConnectedFds.contains(fd);
}
@Override
public long getFd() {
return fd;
}
@Override
public IODispatcher<?> getDispatcher() {
return dispatcher;
}
@Override
public void close() {
LOG.info().$(fd).$(" disconnected").$();
serverConnectedFds.remove(fd);
}
};
};
final String request = "\n";
long mem = TestUtils.toMemory(request);
final long sockAddr = Net.sockaddr("127.0.0.1", 9001);
Thread serverThread;
final CountDownLatch serverLatch = new CountDownLatch(1);
try (IODispatcher<IOContext> dispatcher = IODispatchers.create(configuration, contextFactory)) {
serverThread = new Thread("test-io-dispatcher") {
@Override
public void run() {
long smem = Unsafe.malloc(1, MemoryTag.NATIVE_DEFAULT);
try {
IORequestProcessor<IOContext> requestProcessor = (operation, context) -> {
long fd = context.getFd();
int rc;
switch(operation) {
case IOOperation.READ:
rc = Net.recv(fd, smem, 1);
if (rc == 1) {
dispatcher.registerChannel(context, IOOperation.WRITE);
} else {
dispatcher.disconnect(context, IODispatcher.DISCONNECT_REASON_TEST);
}
break;
case IOOperation.WRITE:
rc = Net.send(fd, smem, 1);
if (rc == 1) {
dispatcher.registerChannel(context, IOOperation.READ);
} else {
dispatcher.disconnect(context, IODispatcher.DISCONNECT_REASON_TEST);
}
break;
default:
dispatcher.disconnect(context, IODispatcher.DISCONNECT_REASON_TEST);
}
};
do {
dispatcher.run(0);
dispatcher.processIOQueue(requestProcessor);
Thread.yield();
} while (!isInterrupted());
} finally {
Unsafe.free(smem, 1, MemoryTag.NATIVE_DEFAULT);
serverLatch.countDown();
}
}
};
serverThread.setDaemon(true);
serverThread.start();
// Connect exactly the right amount of clients to fill the active connection and connection backlog, after the
// queuedConnectionTimeoutInMs the connections in the backlog should get refused
int nClientConnects = 0;
int nClientConnectRefused = 0;
for (int i = 0; i < listenBackLog + activeConnectionLimit; i++) {
long fd = Net.socketTcp(true);
Assert.assertTrue(fd > -1);
clientActiveFds.add(fd);
if (Net.connect(fd, sockAddr) != 0) {
nClientConnectRefused++;
continue;
}
int rc = Net.send(fd, mem, request.length());
if (rc < 0) {
nClientConnectRefused++;
continue;
}
rc = Net.recv(fd, mem, request.length());
if (rc < 0) {
nClientConnectRefused++;
} else {
nClientConnects++;
}
}
Assert.assertEquals(activeConnectionLimit, nClientConnects);
Assert.assertEquals(listenBackLog, nClientConnectRefused);
Assert.assertFalse(dispatcher.isListening());
// Close all connections and wait for server to resume listening
while (clientActiveFds.size() > 0) {
long fd = clientActiveFds.get(0);
clientActiveFds.remove(fd);
Net.close(fd);
}
long timeoutMs = System.currentTimeMillis() + TimeUnit.MINUTES.toMillis(1);
while (!dispatcher.isListening()) {
if (System.currentTimeMillis() > timeoutMs) {
Assert.fail("Timeout waiting for server to start listening again");
}
}
// Try connections again to make sure server is listening
nClientConnects = 0;
nClientConnectRefused = 0;
for (int i = 0; i < listenBackLog + activeConnectionLimit; i++) {
long fd = Net.socketTcp(true);
Assert.assertTrue(fd > -1);
clientActiveFds.add(fd);
if (Net.connect(fd, sockAddr) != 0) {
nClientConnectRefused++;
continue;
}
int rc = Net.send(fd, mem, request.length());
if (rc < 0) {
nClientConnectRefused++;
continue;
}
rc = Net.recv(fd, mem, request.length());
if (rc < 0) {
nClientConnectRefused++;
} else {
nClientConnects++;
}
}
Assert.assertEquals(activeConnectionLimit, nClientConnects);
Assert.assertEquals(listenBackLog, nClientConnectRefused);
Assert.assertFalse(dispatcher.isListening());
// Close all remaining client connections
for (int n = 0; n < clientActiveFds.size(); n++) {
long fd = clientActiveFds.get(n);
Net.close(fd);
}
serverThread.interrupt();
if (!serverLatch.await(1, TimeUnit.MINUTES)) {
Assert.fail("Timeout waiting for server to end");
}
} finally {
Net.freeSockAddr(sockAddr);
Unsafe.free(mem, request.length(), MemoryTag.NATIVE_DEFAULT);
}
});
}
use of io.questdb.network.IOContextFactory in project questdb by bluestreak01.
the class IODispatcherTest method testSendTimeout.
@Test
public void testSendTimeout() throws Exception {
LOG.info().$("started testSendHttpGet").$();
final String request = "GET /status?x=1&a=%26b&c&d=x HTTP/1.1\r\n" + "Host: localhost:9000\r\n" + "Connection: keep-alive\r\n" + "Cache-Control: max-age=0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36\r\n" + "Accept-Encoding: gzip,deflate,sdch\r\n" + "Accept-Language: en-US,en;q=0.8\r\n" + "Cookie: textwrapon=false; textautoformat=false; wysiwyg=textarea\r\n" + "\r\n";
assertMemoryLeak(() -> {
HttpServerConfiguration httpServerConfiguration = new DefaultHttpServerConfiguration();
SOCountDownLatch connectLatch = new SOCountDownLatch(1);
SOCountDownLatch contextClosedLatch = new SOCountDownLatch(1);
AtomicInteger closeCount = new AtomicInteger(0);
try (IODispatcher<HttpConnectionContext> dispatcher = IODispatchers.create(new DefaultIODispatcherConfiguration() {
@Override
public long getIdleConnectionTimeout() {
// 0.5s idle timeout
return 500;
}
@Override
public boolean getPeerNoLinger() {
return false;
}
}, new IOContextFactory<HttpConnectionContext>() {
@Override
public HttpConnectionContext newInstance(long fd, IODispatcher<HttpConnectionContext> dispatcher1) {
connectLatch.countDown();
return new HttpConnectionContext(httpServerConfiguration.getHttpContextConfiguration()) {
@Override
public void close() {
// context is closed
if (closeCount.incrementAndGet() == 1) {
super.close();
contextClosedLatch.countDown();
}
}
}.of(fd, dispatcher1);
}
})) {
StringSink sink = new StringSink();
HttpRequestProcessorSelector selector = new HttpRequestProcessorSelector() {
@Override
public HttpRequestProcessor select(CharSequence url) {
return null;
}
@Override
public HttpRequestProcessor getDefaultProcessor() {
return new HttpRequestProcessor() {
@Override
public void onHeadersReady(HttpConnectionContext connectionContext) {
HttpRequestHeader headers = connectionContext.getRequestHeader();
sink.put(headers.getMethodLine());
sink.put("\r\n");
ObjList<CharSequence> headerNames = headers.getHeaderNames();
for (int i = 0, n = headerNames.size(); i < n; i++) {
sink.put(headerNames.getQuick(i)).put(':');
sink.put(headers.getHeader(headerNames.getQuick(i)));
sink.put("\r\n");
}
sink.put("\r\n");
}
};
}
@Override
public void close() {
}
};
AtomicBoolean serverRunning = new AtomicBoolean(true);
SOCountDownLatch serverHaltLatch = new SOCountDownLatch(1);
new Thread(() -> {
while (serverRunning.get()) {
dispatcher.run(0);
dispatcher.processIOQueue((operation, context) -> context.handleClientOperation(operation, selector, EmptyRescheduleContext));
}
serverHaltLatch.countDown();
}).start();
long fd = Net.socketTcp(true);
try {
long sockAddr = Net.sockaddr("127.0.0.1", 9001);
try {
TestUtils.assertConnect(fd, sockAddr);
Net.setTcpNoDelay(fd, true);
connectLatch.await();
int len = request.length();
long buffer = TestUtils.toMemory(request);
try {
int part1 = len / 2;
Assert.assertEquals(part1, Net.send(fd, buffer, part1));
Os.sleep(1000);
Assert.assertEquals(len - part1, Net.send(fd, buffer + part1, len - part1));
} finally {
Unsafe.free(buffer, len, MemoryTag.NATIVE_DEFAULT);
}
contextClosedLatch.await();
Assert.assertEquals(0, dispatcher.getConnectionCount());
serverRunning.set(false);
serverHaltLatch.await();
Assert.assertEquals(0, dispatcher.getConnectionCount());
// do not close client side before server does theirs
Assert.assertTrue(Net.isDead(fd));
TestUtils.assertEquals("", sink);
} finally {
Net.freeSockAddr(sockAddr);
}
} finally {
Net.close(fd);
LOG.info().$("closed [fd=").$(fd).$(']').$();
}
Assert.assertEquals(1, closeCount.get());
}
});
}
use of io.questdb.network.IOContextFactory in project questdb by bluestreak01.
the class IODispatcherTest method testSendHttpGet.
@Test
public void testSendHttpGet() throws Exception {
LOG.info().$("started testSendHttpGet").$();
final String request = "GET /status?x=1&a=%26b&c&d=x HTTP/1.1\r\n" + "Host: localhost:9000\r\n" + "Connection: keep-alive\r\n" + "Cache-Control: max-age=0\r\n" + "Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + "User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36\r\n" + "Accept-Encoding: gzip,deflate,sdch\r\n" + "Accept-Language: en-US,en;q=0.8\r\n" + "Cookie: textwrapon=false; textautoformat=false; wysiwyg=textarea\r\n" + "\r\n";
// the difference between request and expected is url encoding (and ':' padding, which can easily be fixed)
final String expected = "GET /status?x=1&a=&b&c&d=x HTTP/1.1\r\n" + "host:localhost:9000\r\n" + "connection:keep-alive\r\n" + "cache-control:max-age=0\r\n" + "accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8\r\n" + "user-agent:Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/31.0.1650.48 Safari/537.36\r\n" + "accept-encoding:gzip,deflate,sdch\r\n" + "accept-language:en-US,en;q=0.8\r\n" + "cookie:textwrapon=false; textautoformat=false; wysiwyg=textarea\r\n" + "\r\n";
assertMemoryLeak(() -> {
HttpServerConfiguration httpServerConfiguration = new DefaultHttpServerConfiguration();
SOCountDownLatch connectLatch = new SOCountDownLatch(1);
SOCountDownLatch contextClosedLatch = new SOCountDownLatch(1);
SOCountDownLatch requestReceivedLatch = new SOCountDownLatch(1);
AtomicInteger closeCount = new AtomicInteger(0);
try (IODispatcher<HttpConnectionContext> dispatcher = IODispatchers.create(new DefaultIODispatcherConfiguration(), new IOContextFactory<HttpConnectionContext>() {
@Override
public HttpConnectionContext newInstance(long fd, IODispatcher<HttpConnectionContext> dispatcher1) {
connectLatch.countDown();
return new HttpConnectionContext(httpServerConfiguration.getHttpContextConfiguration()) {
@Override
public void close() {
// context is closed
if (closeCount.incrementAndGet() == 1) {
super.close();
contextClosedLatch.countDown();
}
}
}.of(fd, dispatcher1);
}
})) {
StringSink sink = new StringSink();
final HttpRequestProcessorSelector selector = new HttpRequestProcessorSelector() {
@Override
public HttpRequestProcessor select(CharSequence url) {
return new HttpRequestProcessor() {
@Override
public void onHeadersReady(HttpConnectionContext context) {
HttpRequestHeader headers = context.getRequestHeader();
sink.put(headers.getMethodLine());
sink.put("\r\n");
ObjList<CharSequence> headerNames = headers.getHeaderNames();
for (int i = 0, n = headerNames.size(); i < n; i++) {
sink.put(headerNames.getQuick(i)).put(':');
sink.put(headers.getHeader(headerNames.getQuick(i)));
sink.put("\r\n");
}
sink.put("\r\n");
requestReceivedLatch.countDown();
}
};
}
@Override
public HttpRequestProcessor getDefaultProcessor() {
return null;
}
@Override
public void close() {
}
};
AtomicBoolean serverRunning = new AtomicBoolean(true);
SOCountDownLatch serverHaltLatch = new SOCountDownLatch(1);
new Thread(() -> {
while (serverRunning.get()) {
dispatcher.run(0);
dispatcher.processIOQueue((operation, context) -> context.handleClientOperation(operation, selector, EmptyRescheduleContext));
}
serverHaltLatch.countDown();
}).start();
long fd = Net.socketTcp(true);
try {
long sockAddr = Net.sockaddr("127.0.0.1", 9001);
try {
TestUtils.assertConnect(fd, sockAddr);
connectLatch.await();
int len = request.length();
long buffer = TestUtils.toMemory(request);
try {
Assert.assertEquals(len, Net.send(fd, buffer, len));
} finally {
Unsafe.free(buffer, len, MemoryTag.NATIVE_DEFAULT);
}
// do not disconnect right away, wait for server to receive the request
requestReceivedLatch.await();
Assert.assertEquals(0, Net.close(fd));
LOG.info().$("closed [fd=").$(fd).$(']').$();
fd = -1;
contextClosedLatch.await();
serverRunning.set(false);
serverHaltLatch.await();
Assert.assertEquals(0, dispatcher.getConnectionCount());
TestUtils.assertEquals(expected, sink);
} finally {
Net.freeSockAddr(sockAddr);
}
} finally {
if (fd != -1) {
Net.close(fd);
}
}
Assert.assertEquals(1, closeCount.get());
}
});
}
Aggregations