Skip to content

Commit

Permalink
Added ThrottlingTest. Removed AdaptiveRecvByteBufAllocator.
Browse files Browse the repository at this point in the history
  • Loading branch information
jekh committed Dec 14, 2014
1 parent d532c7c commit e3df91f
Show file tree
Hide file tree
Showing 4 changed files with 299 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ private DefaultHttpProxyServer(ServerGroup serverGroup,
this.serverResolver = serverResolver;

ScheduledExecutorService executor = new ScheduledThreadPoolExecutor(ServerGroup.INCOMING_WORKER_THREADS);
this.globalTrafficShapingHandler = new GlobalTrafficShapingHandler(executor, writeThrottleBytesPerSecond, readThrottleBytesPerSecond, 250L);
this.globalTrafficShapingHandler = new GlobalTrafficShapingHandler(executor, writeThrottleBytesPerSecond, readThrottleBytesPerSecond, 250L, Long.MAX_VALUE);
}

boolean isTransparent() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -763,20 +763,6 @@ private void initChannelPipeline(ChannelPipeline pipeline,
HttpRequest httpRequest) {

if (trafficHandler != null) {
if (trafficHandler.getReadLimit() > 0) {
long initial = trafficHandler.getReadLimit() / 4;
if (initial < MINIMUM_RECV_BUFFER_SIZE_BYTES) {
initial = MINIMUM_RECV_BUFFER_SIZE_BYTES;
}

long max = trafficHandler.getReadLimit() / 2;
if (max < MINIMUM_RECV_BUFFER_SIZE_BYTES) {
max = MINIMUM_RECV_BUFFER_SIZE_BYTES;
}

AdaptiveRecvByteBufAllocator adaptiveRecvByteBufAllocator = new AdaptiveRecvByteBufAllocator(MINIMUM_RECV_BUFFER_SIZE_BYTES, (int)initial, (int)max);
pipeline.channel().config().setRecvByteBufAllocator(adaptiveRecvByteBufAllocator);
}
pipeline.addLast("global-traffic-shaping", trafficHandler);
}

Expand Down
91 changes: 91 additions & 0 deletions src/test/java/org/littleshoot/proxy/TestUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,20 @@ public static Server startWebServer(final int port) throws Exception {
return startWebServer(port, null);
}

/**
* Creates and starts embedded web server that is running on given port.
* Each response has a body that contains the specified contents.
*
* @param port
* The port
* @return Instance of Server
* @throws Exception
* if failed to start
*/
public static Server startWebServerWithResponse(final int port, byte[] content) throws Exception {
return startWebServerWithResponse(port, null, content);
}

/**
* Creates and starts embedded web server that is running on given port,
* including an SSL connector on the other given port. Each response has a
Expand Down Expand Up @@ -130,6 +144,83 @@ public void handle(String target, Request baseRequest,
return httpServer;
}

/**
* Creates and starts embedded web server that is running on given port,
* including an SSL connector on the other given port. Each response has a
* body that contains the specified contents.
*
* @param port
* The port
* @param sslPort
* (optional) The ssl port
* @param content
* The response the server will return
* @return Instance of Server
* @throws Exception
* if failed to start
*/
public static Server startWebServerWithResponse(final int port, final Integer sslPort, final byte[] content)
throws Exception {
final Server httpServer = new Server(port);
httpServer.setHandler(new AbstractHandler() {
public void handle(String target, Request baseRequest,
HttpServletRequest request, HttpServletResponse response)
throws IOException, ServletException {
if (request.getRequestURI().contains("hang")) {
System.out.println("Hanging as requested");
try {
Thread.sleep(90000);
} catch (InterruptedException ie) {
System.out.println("Stopped hanging due to interruption");
}
}

long numberOfBytesRead = 0;
InputStream in = new BufferedInputStream(request
.getInputStream());
while (in.read() != -1) {
numberOfBytesRead += 1;
}
System.out.println("Done reading # of bytes: "
+ numberOfBytesRead);
response.setStatus(HttpServletResponse.SC_OK);
baseRequest.setHandled(true);

response.addHeader("Content-Length", Integer.toString(content.length));
response.getOutputStream().write(content);
}
});
if (sslPort != null) {
// Add SSL connector
org.eclipse.jetty.util.ssl.SslContextFactory sslContextFactory = new org.eclipse.jetty.util.ssl.SslContextFactory();

SelfSignedSslEngineSource contextSource = new SelfSignedSslEngineSource();
SSLContext sslContext = contextSource.getSslContext();

sslContextFactory.setSslContext(sslContext);
SslSocketConnector connector = new SslSocketConnector(
sslContextFactory);
connector.setPort(sslPort);
/*
* <p>Ox: For some reason, on OS X, a non-zero timeout can causes
* sporadic issues. <a href="http://stackoverflow.com/questions
* /16191236/tomcat-startup-fails
* -due-to-java-net-socketexception-invalid-argument-on-mac-o">This
* StackOverflow thread</a> has some insights into it, but I don't
* quite get it.</p>
*
* <p>This can cause problems with Jetty's SSL handshaking, so I
* have to set the handshake timeout and the maxIdleTime to 0 so
* that the SSLSocket has an infinite timeout.</p>
*/
connector.setHandshakeTimeout(0);
connector.setMaxIdleTime(0);
httpServer.addConnector(connector);
}
httpServer.start();
return httpServer;
}

/**
* Creates instance HttpClient that is configured to use proxy server. The
* proxy server should run on 127.0.0.1 and given port
Expand Down
207 changes: 207 additions & 0 deletions src/test/java/org/littleshoot/proxy/ThrottlingTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package org.littleshoot.proxy;

import org.apache.http.HttpHost;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.params.ConnRoutePNames;
import org.apache.http.entity.ByteArrayEntity;
import org.apache.http.impl.client.DefaultHttpClient;
import org.apache.http.params.CoreConnectionPNames;
import org.apache.http.util.EntityUtils;
import org.eclipse.jetty.server.Server;
import org.junit.After;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
import org.littleshoot.proxy.impl.DefaultHttpProxyServer;

public class ThrottlingTest {

private static final int WRITE_WEB_SERVER_PORT = 8926;
private static final int READ_WEB_SERVER_PORT = 8927;
private static final long THROTTLED_READ_BYTES_PER_SECOND = 25000L;
private static final long THROTTLED_WRITE_BYTES_PER_SECOND = 25000L;

// throttling is not guaranteed to be exact, so allow some variation in the amount of time the call takes. since we want
// these tests to take just a few seconds, allow significant variation. even with this large variation, if throttling
// is broken it should take much less time than expected.
private static final double ALLOWABLE_VARIATION = 0.30;

private Server writeWebServer;
private Server readWebServer;

private byte[] largeData;

private int msToWriteThrottled;
private int msToReadThrottled;

// time to allow for an unthrottled local request
private static final int UNTRHOTTLED_REQUEST_TIME_MS = 1000;

@Before
public void setUp() throws Exception {
// Set up some large data
largeData = new byte[100000];
for (int i = 0; i < largeData.length; i++) {
largeData[i] = 1 % 256;
}

msToWriteThrottled = largeData.length * 1000 / (int)THROTTLED_WRITE_BYTES_PER_SECOND;
msToReadThrottled = largeData.length * 1000 / (int)THROTTLED_READ_BYTES_PER_SECOND;

writeWebServer = TestUtils.startWebServer(WRITE_WEB_SERVER_PORT);
readWebServer = TestUtils.startWebServerWithResponse(READ_WEB_SERVER_PORT, largeData);
}

@After
public void tearDown() throws Exception {
writeWebServer.stop();
readWebServer.stop();
}

@Test
public void testThrottledWrite() throws Exception {
HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(0)
.withThrottling(0, THROTTLED_WRITE_BYTES_PER_SECOND)
.start();

int proxyPort = proxyServer.getListenAddress().getPort();

final HttpPost request = new HttpPost("/");
request.getParams().setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);
final ByteArrayEntity entity = new ByteArrayEntity(largeData);
entity.setChunked(true);
request.setEntity(entity);

DefaultHttpClient httpClient = new DefaultHttpClient();
final HttpHost proxy = new HttpHost("127.0.0.1", proxyPort, "http");
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
proxy);

long start = System.currentTimeMillis();
final org.apache.http.HttpResponse response = httpClient.execute(
new HttpHost("127.0.0.1",
WRITE_WEB_SERVER_PORT), request);
long finish = System.currentTimeMillis();

Assert.assertEquals("Received " + largeData.length + " bytes\n",
EntityUtils.toString(response.getEntity()));

Assert.assertTrue("Expected throttled write to complete in approximately " + msToWriteThrottled + "ms" + " but took " + (finish - start) + "ms",
finish - start > msToWriteThrottled * (1 - ALLOWABLE_VARIATION)
&& (finish - start < msToWriteThrottled * (1 + ALLOWABLE_VARIATION)));

proxyServer.stop();
}

@Test
public void testUnthrottledWrite() throws Exception {
HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(0)
.start();

int proxyPort = proxyServer.getListenAddress().getPort();

final HttpPost request = new HttpPost("/");
request.getParams().setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);
final ByteArrayEntity entity = new ByteArrayEntity(largeData);
entity.setChunked(true);
request.setEntity(entity);

DefaultHttpClient httpClient = new DefaultHttpClient();
final HttpHost proxy = new HttpHost("127.0.0.1", proxyPort, "http");
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
proxy);

long start = System.currentTimeMillis();
final org.apache.http.HttpResponse response = httpClient.execute(
new HttpHost("127.0.0.1",
WRITE_WEB_SERVER_PORT), request);
long finish = System.currentTimeMillis();

Assert.assertEquals("Received " + largeData.length + " bytes\n",
EntityUtils.toString(response.getEntity()));

Assert.assertTrue("Unthrottled write took " + (finish - start) + "ms, but expected to complete in " + UNTRHOTTLED_REQUEST_TIME_MS + "ms", finish - start < UNTRHOTTLED_REQUEST_TIME_MS);

proxyServer.stop();
}

@Test
public void testThrottledRead() throws Exception {
HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(0)
.withThrottling(THROTTLED_READ_BYTES_PER_SECOND, 0)
.start();

int proxyPort = proxyServer.getListenAddress().getPort();

final HttpGet request = new HttpGet("/");
request.getParams().setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);

DefaultHttpClient httpClient = new DefaultHttpClient();
final HttpHost proxy = new HttpHost("127.0.0.1", proxyPort, "http");
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
proxy);

long start = System.currentTimeMillis();
final org.apache.http.HttpResponse response = httpClient.execute(
new HttpHost("127.0.0.1",
READ_WEB_SERVER_PORT), request);
byte[] readContent = new byte[100000];

int bytesRead = 0;
while (bytesRead < largeData.length) {
int read = response.getEntity().getContent().read(readContent, bytesRead, largeData.length - bytesRead);
bytesRead += read;
}

long finish = System.currentTimeMillis();

Assert.assertTrue("Expected throttled read to complete in approximately " + msToReadThrottled + "ms" + " but took " + (finish - start) + "ms",
finish - start > msToReadThrottled * (1 - ALLOWABLE_VARIATION)
&& (finish - start < msToReadThrottled * (1 + ALLOWABLE_VARIATION)));

proxyServer.stop();
}

@Test
public void testUnthrottledRead() throws Exception {
HttpProxyServer proxyServer = DefaultHttpProxyServer.bootstrap()
.withPort(0)
.start();

int proxyPort = proxyServer.getListenAddress().getPort();

final HttpGet request = new HttpGet("/");
request.getParams().setParameter(
CoreConnectionPNames.CONNECTION_TIMEOUT, 5000);

DefaultHttpClient httpClient = new DefaultHttpClient();
final HttpHost proxy = new HttpHost("127.0.0.1", proxyPort, "http");
httpClient.getParams().setParameter(ConnRoutePNames.DEFAULT_PROXY,
proxy);

long start = System.currentTimeMillis();
final org.apache.http.HttpResponse response = httpClient.execute(
new HttpHost("127.0.0.1",
READ_WEB_SERVER_PORT), request);

byte[] readContent = new byte[100000];
int bytesRead = 0;
while (bytesRead < largeData.length) {
int read = response.getEntity().getContent().read(readContent, bytesRead, largeData.length - bytesRead);
bytesRead += read;
}

long finish = System.currentTimeMillis();

Assert.assertTrue("Unthrottled read took " + (finish - start) + "ms, but expected to complete in " + UNTRHOTTLED_REQUEST_TIME_MS + "ms", finish - start < UNTRHOTTLED_REQUEST_TIME_MS);

proxyServer.stop();
}
}

0 comments on commit e3df91f

Please sign in to comment.