Last active
October 7, 2016 08:44
-
-
Save eeichinger/1ed7588b1c2a17459a61dbb5f08fb60e to your computer and use it in GitHub Desktop.
experiment testing Apache HttpAsyncClient HttpCache behaviour
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package de.porsche.pcc.instrumentation; | |
import com.github.tomakehurst.wiremock.client.WireMock; | |
import com.github.tomakehurst.wiremock.junit.WireMockRule; | |
import lombok.SneakyThrows; | |
import lombok.extern.slf4j.Slf4j; | |
import org.apache.http.HttpException; | |
import org.apache.http.HttpHeaders; | |
import org.apache.http.HttpResponse; | |
import org.apache.http.HttpResponseInterceptor; | |
import org.apache.http.client.cache.CacheResponseStatus; | |
import org.apache.http.client.cache.HttpCacheContext; | |
import org.apache.http.client.methods.CloseableHttpResponse; | |
import org.apache.http.client.methods.HttpGet; | |
import org.apache.http.client.utils.DateUtils; | |
import org.apache.http.impl.client.cache.BasicHttpCacheStorage; | |
import org.apache.http.impl.client.cache.CacheConfig; | |
import org.apache.http.impl.nio.client.CloseableHttpAsyncClient; | |
import org.apache.http.impl.nio.client.HttpAsyncClientBuilder; | |
import org.apache.http.protocol.HttpContext; | |
import org.junit.After; | |
import org.junit.Before; | |
import org.junit.Rule; | |
import org.junit.Test; | |
import java.io.IOException; | |
import java.net.URI; | |
import java.util.Date; | |
import static org.hamcrest.Matchers.equalTo; | |
import static org.junit.Assert.assertThat; | |
/** | |
* @author Erich Eichinger | |
* @since 06/10/16 | |
*/ | |
@Slf4j | |
public class CachingCloseableHttpAsyncClientTest { | |
@Rule | |
public WireMockRule wireMockRule = new WireMockRule(0); | |
CloseableHttpAsyncClient client; | |
@Before | |
public void before() { | |
client = HttpAsyncClientBuilder | |
.create() | |
.setMaxConnPerRoute(10) | |
.setMaxConnTotal(10) | |
// ensure we set a date on the response, otherwise for 304 responses, Apache HttpCache miscalculates the age | |
// of a CacheEntry from the original 200 response's date instead of the 304 response's. | |
.addInterceptorFirst(new HttpResponseInterceptor() { | |
@Override | |
public void process(HttpResponse response, HttpContext context) throws HttpException, IOException { | |
if (!response.containsHeader("Date")) { | |
response.addHeader(HttpHeaders.DATE, DateUtils.formatDate(new Date(System.currentTimeMillis()))); | |
} | |
} | |
}) | |
.build(); | |
final BasicHttpCacheStorage httpCacheStorage = new BasicHttpCacheStorage(CacheConfig.custom().setMaxCacheEntries(CacheConfig.DEFAULT_MAX_CACHE_ENTRIES).build()); | |
final CacheConfig cacheConfig = CacheConfig | |
.custom() | |
.setSharedCache(true) | |
.setAsynchronousWorkersCore(10) | |
.setAsynchronousWorkersMax(100) | |
.setAsynchronousWorkerIdleLifetimeSecs(10) | |
.build(); | |
client = new CachingCloseableHttpAsyncClient( | |
client | |
, httpCacheStorage | |
, cacheConfig | |
); | |
client.start(); | |
} | |
@After | |
public void after() throws Exception { | |
client.close(); | |
} | |
@Test | |
public void handles_cacheable_responses() throws Exception { | |
HttpGet httpGet = new HttpGet(new URI("http://localhost:" + wireMockRule.port() + "/foo")); | |
WireMock.stubFor(WireMock | |
.get(WireMock.urlMatching("/foo")) | |
.willReturn(WireMock | |
.aResponse() | |
.withStatus(200) | |
.withHeader(HttpHeaders.CACHE_CONTROL, "public, max-age=1") | |
.withHeader("Content-Type", "text/plain") | |
.withHeader(HttpHeaders.ETAG, "xyz") | |
.withBody("data") | |
) | |
); | |
WireMock.stubFor(WireMock | |
.get(WireMock.urlMatching("/foo")) | |
.withHeader(HttpHeaders.IF_NONE_MATCH, WireMock.equalTo("xyz")) | |
.willReturn(WireMock | |
.aResponse() | |
.withStatus(304) | |
) | |
); | |
HttpCacheContext context; | |
// 1. request CACHE_MISS | |
// 2. request CACHE_HIT | |
// 3. request triggers revalidation with server -> VALIDATED | |
// 4. request is a CACHE_HIT - item has been revalidated with server and got a 304 Response | |
// 5. request is a CACHE_HIT | |
// 6. request triggers revalidation with server again -> VALIDATED | |
// 7. request is a CACHE_HIT - item has been revalidated with server and got a 304 Response | |
context = syncExecute(httpGet); // 1 | |
assertThat(context.getCacheResponseStatus(), equalTo(CacheResponseStatus.CACHE_MISS)); | |
Thread.sleep(500); | |
context = syncExecute(httpGet); // 2 | |
assertThat(context.getCacheResponseStatus(), equalTo(CacheResponseStatus.CACHE_HIT)); | |
Thread.sleep(600); | |
context = syncExecute(httpGet); // 3 | |
assertThat(context.getCacheResponseStatus(), equalTo(CacheResponseStatus.VALIDATED)); | |
Thread.sleep(100); | |
context = syncExecute(httpGet); // 4 | |
assertThat(context.getCacheResponseStatus(), equalTo(CacheResponseStatus.CACHE_HIT)); | |
Thread.sleep(100); | |
context = syncExecute(httpGet); // 5 | |
assertThat(context.getCacheResponseStatus(), equalTo(CacheResponseStatus.CACHE_HIT)); | |
Thread.sleep(800); | |
context = syncExecute(httpGet); // 6 | |
assertThat(context.getCacheResponseStatus(), equalTo(CacheResponseStatus.VALIDATED)); | |
Thread.sleep(100); | |
context = syncExecute(httpGet); // 7 | |
assertThat(context.getCacheResponseStatus(), equalTo(CacheResponseStatus.CACHE_HIT)); | |
WireMock.verify(3, WireMock.getRequestedFor(WireMock.urlEqualTo("/foo"))); | |
} | |
@SneakyThrows | |
private HttpCacheContext syncExecute(HttpGet httpGet) throws InterruptedException, java.util.concurrent.ExecutionException { | |
final HttpCacheContext context = HttpCacheContext.create(); | |
CloseableHttpResponse response = (CloseableHttpResponse) client.execute(httpGet, context, null).get(); | |
response.close(); | |
return context; | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment