package org.limewire.http;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executor;
import junit.framework.Test;
import org.apache.http.ConnectionReuseStrategy;
import org.apache.http.HttpEntityEnclosingRequest;
import org.apache.http.HttpException;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
import org.apache.http.HttpResponseFactory;
import org.apache.http.HttpStatus;
import org.apache.http.HttpVersion;
import org.apache.http.entity.BasicHttpEntity;
import org.apache.http.impl.DefaultConnectionReuseStrategy;
import org.apache.http.impl.DefaultHttpRequestFactory;
import org.apache.http.impl.DefaultHttpResponseFactory;
import org.apache.http.impl.nio.DefaultServerIOEventDispatch;
import org.apache.http.message.BasicHttpEntityEnclosingRequest;
import org.apache.http.message.BasicHttpRequest;
import org.apache.http.nio.entity.ConsumingNHttpEntity;
import org.apache.http.nio.entity.NStringEntity;
import org.apache.http.nio.protocol.NHttpRequestHandlerRegistry;
import org.apache.http.nio.protocol.SimpleNHttpRequestHandler;
import org.apache.http.nio.reactor.EventMask;
import org.apache.http.nio.reactor.IOEventDispatch;
import org.apache.http.params.BasicHttpParams;
import org.apache.http.params.HttpConnectionParams;
import org.apache.http.params.HttpParams;
import org.apache.http.protocol.BasicHttpProcessor;
import org.apache.http.protocol.HttpContext;
import org.limewire.http.protocol.ExtendedAsyncNHttpServiceHandler;
import org.limewire.http.reactor.HttpChannel;
import org.limewire.http.reactor.HttpIOSession;
import org.limewire.util.BaseTestCase;
import org.limewire.util.BufferUtils;
public class HttpServiceHandlerTest extends BaseTestCase {
private HttpParams parms;
private ConnectionReuseStrategy connStrategy;
private HttpResponseFactory responseFactory;
private DefaultHttpRequestFactory requestFactory;
private MockHttpNIOEntity nioEntity;
private NStringEntity stringEntity;
private DefaultServerIOEventDispatch eventDispatch;
private ExtendedAsyncNHttpServiceHandler serviceHandler;
private NHttpRequestHandlerRegistry registry;
public HttpServiceHandlerTest(String name) {
super(name);
}
public static Test suite() {
return buildTestSuite(HttpServiceHandlerTest.class);
}
@Override
protected void setUp() throws Exception {
parms = new BasicHttpParams();
HttpConnectionParams.setSocketBufferSize(parms, 512);
connStrategy = new DefaultConnectionReuseStrategy();
responseFactory = new DefaultHttpResponseFactory();
requestFactory = new DefaultHttpRequestFactory();
BasicHttpProcessor httpProcessor = new BasicHttpProcessor();
registry = new NHttpRequestHandlerRegistry();
stringEntity = new NStringEntity("abc");
registry.register("/get/string", new SimpleNHttpRequestHandler() {
public ConsumingNHttpEntity entityRequest(HttpEntityEnclosingRequest request,
HttpContext context) throws HttpException, IOException {
return null;
}
@Override
public void handle(HttpRequest request, HttpResponse response,
HttpContext context) throws HttpException, IOException {
response.setEntity(stringEntity);
}
});
nioEntity = new MockHttpNIOEntity("abc");
registry.register("/get/nio", new SimpleNHttpRequestHandler() {
public ConsumingNHttpEntity entityRequest(HttpEntityEnclosingRequest request,
HttpContext context) throws HttpException, IOException {
return null;
}
@Override
public void handle(HttpRequest request, HttpResponse response,
HttpContext context) throws HttpException, IOException {
response.setEntity(nioEntity);
}
});
serviceHandler = new ExtendedAsyncNHttpServiceHandler(
httpProcessor, responseFactory, connStrategy, parms);
serviceHandler.setHandlerResolver(registry);
eventDispatch = new DefaultServerIOEventDispatch(serviceHandler, parms);
}
public void testEventListener() throws Exception {
MockHttpServiceEventListener listener = new MockHttpServiceEventListener();
serviceHandler.setEventListener(listener);
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
assertTrue(listener.connectionOpened);
HttpRequest request = new BasicHttpRequest("GET", "/get/nio");
conn.setHttpRequest(request);
serviceHandler.requestReceived(conn);
conn.produceOutput(serviceHandler);
MockContentEncoder encoder = new MockContentEncoder();
conn.setContentEncoder(encoder);
assertFalse(listener.responseSent);
conn.produceOutput(serviceHandler);
conn.produceOutput(serviceHandler);
conn.produceOutput(serviceHandler);
assertEquals("abc", encoder.data.toString());
assertTrue(listener.responseSent);
assertEquals(EventMask.READ, session.getEventMask());
}
public void testEventListenerException() throws Exception {
MockHttpServiceEventListener listener = new MockHttpServiceEventListener();
serviceHandler.setEventListener(listener);
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
assertTrue(listener.connectionOpened);
HttpRequest request = new BasicHttpRequest("GET", "/get/nio");
conn.setHttpRequest(request);
serviceHandler.requestReceived(conn);
nioEntity.exception = new IOException();
conn.produceOutput(serviceHandler);
conn.produceOutput(serviceHandler);
assertNotNull(listener.exception);
assertTrue(session.isClosed());
serviceHandler.closed(conn);
assertTrue(listener.connectionClosed);
}
public void testEventListenerTimeout() throws Exception {
MockHttpServiceEventListener listener = new MockHttpServiceEventListener();
serviceHandler.setEventListener(listener);
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
serviceHandler.closed(conn);
assertTrue(listener.connectionClosed);
}
public void testGetRequestNIOEntity() throws Exception {
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
HttpRequest request = new BasicHttpRequest("GET", "/get/nio");
conn.setHttpRequest(request);
serviceHandler.requestReceived(conn);
conn.produceOutput(serviceHandler);
assertEquals(nioEntity, conn.getHttpResponse().getEntity());
MockContentEncoder encoder = new MockContentEncoder();
conn.setContentEncoder(encoder);
conn.produceOutput(serviceHandler);
assertEquals("a", encoder.data.toString());
assertFalse(encoder.isCompleted());
assertTrue(nioEntity.initialized);
assertFalse(nioEntity.finished);
conn.produceOutput(serviceHandler);
conn.produceOutput(serviceHandler);
assertEquals("abc", encoder.data.toString());
assertTrue(encoder.isCompleted());
assertTrue(nioEntity.finished);
}
public void testGetRequestNIOEntityThrowingException() throws Exception {
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
HttpRequest request = new BasicHttpRequest("GET", "/get/nio");
conn.setHttpRequest(request);
serviceHandler.requestReceived(conn);
conn.produceOutput(serviceHandler);
MockContentEncoder encoder = new MockContentEncoder();
nioEntity.exception = new IOException();
conn.setContentEncoder(encoder);
conn.produceOutput(serviceHandler);
assertTrue(session.isClosed());
assertTrue(nioEntity.finished);
}
public void testGetRequestNIOEntityTimeout() throws Exception {
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
HttpRequest request = new BasicHttpRequest("GET", "/get/nio");
conn.setHttpRequest(request);
serviceHandler.requestReceived(conn);
conn.produceOutput(serviceHandler);
assertFalse(nioEntity.finished);
serviceHandler.closed(conn);
assertTrue(nioEntity.finished);
}
public void testGetRequestStringEntity() throws Exception {
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
HttpRequest request = new BasicHttpRequest("GET", "/get/string");
conn.setHttpRequest(request);
serviceHandler.requestReceived(conn);
conn.produceOutput(serviceHandler);
assertEquals(stringEntity, conn.getHttpResponse().getEntity());
MockContentEncoder encoder = new MockContentEncoder();
conn.setContentEncoder(encoder);
conn.produceOutput(serviceHandler);
assertEquals("abc", encoder.data.toString());
assertTrue(encoder.isCompleted());
}
public void testHeadRequest() throws Exception {
MockHttpServiceEventListener listener = new MockHttpServiceEventListener();
serviceHandler.setEventListener(listener);
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
HttpRequest request = new BasicHttpRequest("HEAD", "/get/string");
conn.setHttpRequest(request);
serviceHandler.requestReceived(conn);
conn.produceOutput(serviceHandler);
assertNotNull(listener.response);
assertNull(listener.response.getEntity());
}
public void testPostRequest() throws Exception {
MockHttpServiceEventListener listener = new MockHttpServiceEventListener();
serviceHandler.setEventListener(listener);
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
serviceHandler.connected(conn);
HttpRequest request = new BasicHttpRequest("POST", "/get/string");
conn.setHttpRequest(request);
serviceHandler.requestReceived(conn);
conn.produceOutput(serviceHandler);
conn.produceOutput(serviceHandler);
assertNotNull(listener.response);
assertEquals(HttpStatus.SC_OK, listener.response.getStatusLine().getStatusCode());
}
public void testReadWriteInterestHead() throws Exception {
StubSocket socket = new StubSocket();
MockExecutor executor = new MockExecutor();
StubIOSession session = new StubIOSession(socket, executor);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
serviceHandler.connected(conn);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
assertNull(executor.command);
conn.setHttpRequest(new BasicHttpRequest("HEAD", "/get/string"));
conn.consumeInput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
// now that a request is processed & a response is pending,
// send it.
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
// response is already sent now, just waiting for requests...
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
assertNull(executor.command);
conn.setHttpRequest(new BasicHttpRequest("HEAD", "/get/string"));
conn.consumeInput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
// write out the first response & immediately process the next.
conn.setHasBufferedInput(true);
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNotNull(executor.command);
// Now run the buffered input consumer.
executor.command.run();
executor.command = null;
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
// write out the second response, and turn interest on for wanting more
conn.setHasBufferedInput(false);
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
// No more to process, so nothing to write.
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
assertNull(executor.command);
}
public void testReadWriteInterestGet() throws Exception {
StubSocket socket = new StubSocket();
MockExecutor executor = new MockExecutor();
StubIOSession session = new StubIOSession(socket, executor);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
serviceHandler.connected(conn);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
assertNull(executor.command);
conn.setHttpRequest(new BasicHttpRequest("GET", "/get/string"));
conn.consumeInput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
// start writing the response headers
conn.produceOutput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
// write the response body
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
assertNull(executor.command);
conn.setHttpRequest(new BasicHttpRequest("GET", "/get/string"));
conn.consumeInput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
// start writing the response headers
conn.produceOutput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
// write the response body, and process the next request immediately...
conn.setHasBufferedInput(true);
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
assertNotNull(executor.command);
// Run the buffered input consumer...
executor.command.run();
executor.command = null;
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
// start writing the response headers
conn.setHasBufferedInput(false);
conn.produceOutput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
assertNull(executor.command);
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
assertNull(executor.command);
}
public void testReadWriteInterestPost() throws Exception {
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
serviceHandler.connected(conn);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
HttpEntityEnclosingRequest request = new BasicHttpEntityEnclosingRequest("POST", "/get/string");
request.setEntity(new BasicHttpEntity());
conn.setHttpRequest(request);
conn.consumeInput(serviceHandler);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
MockContentDecoder decoder = new MockContentDecoder("abc");
conn.setContentDecoder(decoder);
serviceHandler.inputReady(conn, decoder);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
serviceHandler.inputReady(conn, decoder);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
decoder.completed = true;
serviceHandler.inputReady(conn, decoder);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
// start writing the response headers
conn.produceOutput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
// write the response body
conn.produceOutput(serviceHandler);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
}
public void testReadWriteInterestCloseGet() throws Exception {
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
serviceHandler.connected(conn);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
conn.setHttpRequest(new BasicHttpRequest("GET", "/get/string", HttpVersion.HTTP_1_0));
conn.consumeInput(serviceHandler);
assertFalse(conn.isClosed());
assertFalse(session.isClosed());
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
// start writing the response headers
conn.produceOutput(serviceHandler);
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
// write response body
// expect service handler to send response and close connection since it is HTTP 1.0
conn.produceOutput(serviceHandler);
assertTrue(conn.isClosed());
assertTrue(session.isClosed());
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
}
public void testReadWriteInterestCloseHead() throws Exception {
StubSocket socket = new StubSocket();
StubIOSession session = new StubIOSession(socket);
MockHttpChannel channel = new MockHttpChannel(session, eventDispatch);
session.setHttpChannel(channel);
MockHttpServerConnection conn = new MockHttpServerConnection(session,
requestFactory, parms);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
serviceHandler.connected(conn);
assertTrue(channel.isReadInterest());
assertFalse(channel.isWriteInterest());
conn.setHttpRequest(new BasicHttpRequest("HEAD", "/get/string", HttpVersion.HTTP_1_0));
conn.consumeInput(serviceHandler);
assertFalse(conn.isClosing());
assertFalse(conn.isClosed());
assertFalse(session.isClosed());
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
// start writing the response headers
conn.produceOutput(serviceHandler);
assertTrue(conn.isClosing());
assertFalse(session.isClosed());
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
conn.produceOutput(serviceHandler);
assertTrue(conn.isClosed());
assertTrue(session.isClosed());
assertFalse(channel.isReadInterest());
assertTrue(channel.isWriteInterest());
}
public static class MockHttpChannel extends HttpChannel {
private StringBuilder out = new StringBuilder();
public MockHttpChannel(HttpIOSession session,
IOEventDispatch eventDispatch) {
super(session, eventDispatch);
}
@Override
public int write(ByteBuffer buffer) throws IOException {
return BufferUtils.transfer(buffer, out);
}
}
private static class MockExecutor implements Executor {
private Runnable command;
public void execute(Runnable command) {
this.command = command;
}
}
}