private void ensure304DoesNotContainExtraEntityHeaders(final HttpResponse response) { final String[] disallowedEntityHeaders = { HeaderConstants.ALLOW, HttpHeaders.CONTENT_ENCODING, "Content-Language", HttpHeaders.CONTENT_LENGTH, "Content-MD5", "Content-Range", HttpHeaders.CONTENT_TYPE, HeaderConstants.LAST_MODIFIED }; if (response.getCode() == HttpStatus.SC_NOT_MODIFIED) { for(final String hdr : disallowedEntityHeaders) { response.removeHeaders(hdr); } } }
private void ensurePartialContentIsNotSentToAClientThatDidNotRequestIt(final HttpRequest request, final HttpResponse response) throws IOException { if (request.getFirstHeader(HeaderConstants.RANGE) != null || response.getCode() != HttpStatus.SC_PARTIAL_CONTENT) { return; } throw new ClientProtocolException(UNEXPECTED_PARTIAL_CONTENT); }
private void ensure200ForOPTIONSRequestWithNoBodyHasContentLengthZero(final HttpRequest request, final HttpResponse response) { if (!request.getMethod().equalsIgnoreCase(HeaderConstants.OPTIONS_METHOD)) { return; } if (response.getCode() != HttpStatus.SC_OK) { return; } if (response.getFirstHeader(HttpHeaders.CONTENT_LENGTH) == null) { response.addHeader(HttpHeaders.CONTENT_LENGTH, "0"); } }
private void requestDidNotExpect100ContinueButResponseIsOne( final HttpRequest originalRequest, final HttpResponse response) throws IOException { if (response.getCode() != HttpStatus.SC_CONTINUE) { return; } final Header header = originalRequest.getFirstHeader(HttpHeaders.EXPECT); if (header != null && header.getValue().equalsIgnoreCase(HeaderElements.CONTINUE)) { return; } throw new ClientProtocolException(UNEXPECTED_100_CONTINUE); }
/** * For 304 Not modified responses, adds a "Last-Modified" header with the * value of the "If-Modified-Since" header passed in the request. This * header is required to be able to reuse match the cache entry for * subsequent requests but as defined in http specifications it is not * included in 304 responses by backend servers. This header will not be * included in the resulting response. */ void storeRequestIfModifiedSinceFor304Response(final HttpRequest request, final HttpResponse backendResponse) { if (backendResponse.getCode() == HttpStatus.SC_NOT_MODIFIED) { final Header h = request.getFirstHeader("If-Modified-Since"); if (h != null) { backendResponse.addHeader("Last-Modified", h.getValue()); } } }
final int status = response.getCode(); if (CACHEABLE_STATUS_CODES.contains(status)) {
@Override public HttpCacheEntry createCacheEntry( final HttpHost host, final HttpRequest request, final HttpResponse originResponse, final ByteArrayBuffer content, final Date requestSent, final Date responseReceived) { if (log.isDebugEnabled()) { log.debug("Create cache entry: " + host + "; " + new RequestLine(request)); } final String cacheKey = cacheKeyGenerator.generateKey(host, request); try { final HttpCacheEntry entry = cacheUpdateHandler.createtCacheEntry(request, originResponse, content, requestSent, responseReceived); storeInCache(cacheKey, host, request, entry); return entry; } catch (final ResourceIOException ex) { if (log.isWarnEnabled()) { log.warn("I/O error creating cache entry with key " + cacheKey); } return new HttpCacheEntry( requestSent, responseReceived, originResponse.getCode(), originResponse.getHeaders(), content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null); } }
/** * Creates a cache entry for the given request, origin response message and response content. */ public HttpCacheEntry createtCacheEntry( final HttpRequest request, final HttpResponse originResponse, final ByteArrayBuffer content, final Date requestSent, final Date responseReceived) throws ResourceIOException { return new HttpCacheEntry( requestSent, responseReceived, originResponse.getCode(), originResponse.getHeaders(), content != null ? resourceFactory.generate(request.getRequestUri(), content.array(), 0, content.length()) : null); }
requestSent, responseReceived, originResponse.getCode(), originResponse.getHeaders(), content != null ? HeapResourceFactory.INSTANCE.generate(null, content.array(), 0, content.length()) : null));
final HttpAsyncCacheStorage storage, final FutureCallback<Boolean> callback) { final int status = response.getCode(); if (status >= HttpStatus.SC_SUCCESS && status < HttpStatus.SC_REDIRECTION) { final String s = HttpCacheSupport.getRequestUri(request, host);
/** * Update the entry with the new information from the response. Should only be used for * 304 responses. */ public HttpCacheEntry updateCacheEntry( final String requestId, final HttpCacheEntry entry, final Date requestDate, final Date responseDate, final HttpResponse response) throws ResourceIOException { Args.check(response.getCode() == HttpStatus.SC_NOT_MODIFIED, "Response must have 304 status code"); final Header[] mergedHeaders = mergeHeaders(entry, response); Resource resource = null; if (entry.getResource() != null) { resource = resourceFactory.copy(requestId, entry.getResource()); } return new HttpCacheEntry( requestDate, responseDate, entry.getStatus(), mergedHeaders, resource); }
if (backendResponse.getCode() != HttpStatus.SC_NOT_MODIFIED) { callback = new BackendResponseHandler(target, request, requestDate, responseDate, scope, asyncExecCallback); } else {
@Override public void flushCacheEntriesInvalidatedByExchange( final HttpHost host, final HttpRequest request, final HttpResponse response, final Resolver<URI, String> cacheKeyResolver, final HttpCacheStorage storage) { final int status = response.getCode(); if (status < 200 || status > 299) { return; } final String s = HttpCacheSupport.getRequestUri(request, host); final URI uri = HttpCacheSupport.normalizeQuetly(s); if (uri == null) { return; } final URI contentLocation = getContentLocationURI(uri, response); if (contentLocation != null && isSameHost(uri, contentLocation)) { flushLocationCacheEntry(response, contentLocation, storage, cacheKeyResolver); } final URI location = getLocationURI(uri, response); if (location != null && isSameHost(uri, location)) { flushLocationCacheEntry(response, location, storage, cacheKeyResolver); } }
AsyncExecCallback evaluateResponse(final HttpResponse backendResponse, final Date responseDate) { backendResponse.addHeader(HeaderConstants.VIA, generateViaHeader(backendResponse)); final int statusCode = backendResponse.getCode(); if (statusCode == HttpStatus.SC_NOT_MODIFIED || statusCode == HttpStatus.SC_OK) { recordCacheUpdate(scope.clientContext); } if (statusCode == HttpStatus.SC_NOT_MODIFIED) { return new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() { @Override public void run() { triggerUpdatedCacheEntryResponse(backendResponse, responseDate); } }); } if (staleIfErrorAppliesTo(statusCode) && !staleResponseNotAllowed(request, cacheEntry, getCurrentDate()) && validityPolicy.mayReturnStaleIfError(request, cacheEntry, responseDate)) { return new AsyncExecCallbackWrapper(asyncExecCallback, new Runnable() { @Override public void run() { triggerResponseStaleCacheEntry(); } }); } return new BackendResponseHandler(target, conditionalRequest, requestDate, responseDate, scope, asyncExecCallback); }
@Test public void testNonSharedCacheReturnsStaleResponseWhenRevalidationFailsForProxyRevalidate() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/"); final Date now = new Date(); final Date tenSecondsAgo = new Date(now.getTime() - 10 * 1000L); originResponse.setHeader("Date", DateUtils.formatDate(tenSecondsAgo)); originResponse.setHeader("Cache-Control","max-age=5,proxy-revalidate"); originResponse.setHeader("Etag","\"etag\""); backendExpectsAnyRequest().andReturn(originResponse); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/"); backendExpectsAnyRequest().andThrow(new SocketTimeoutException()); replayMocks(); behaveAsNonSharedCache(); execute(req1); final HttpResponse result = execute(req2); verifyMocks(); Assert.assertEquals(HttpStatus.SC_OK, result.getCode()); }
@Ignore public void testHTTP1_1RequestsWithBodiesOfKnownLengthMustHaveContentLength() throws Exception { final ClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/"); post.setEntity(mockEntity); replayMocks(); final HttpResponse response = execute(post); verifyMocks(); Assert.assertEquals(HttpStatus.SC_LENGTH_REQUIRED, response.getCode()); }
Assert.assertNotNull(forwarded.getFirstHeader("Content-Length")); } else { final int status = result.getCode(); Assert.assertTrue(HttpStatus.SC_LENGTH_REQUIRED == status || HttpStatus.SC_BAD_REQUEST == status);
@Test public void testPartialContentIsNotReturnedToAClientThatDidNotAskForIt() throws Exception { // tester's note: I don't know what the cache will *do* in // this situation, but it better not just pass the response // on. request.removeHeaders("Range"); originResponse = new BasicClassicHttpResponse(HttpStatus.SC_PARTIAL_CONTENT, "Partial Content"); originResponse.setHeader("Content-Range", "bytes 0-499/1234"); originResponse.setEntity(makeBody(500)); EasyMock.expect( mockExecChain.proceed( EasyMock.isA(ClassicHttpRequest.class), EasyMock.isA(ExecChain.Scope.class))).andReturn(originResponse); replayMocks(); try { final HttpResponse result = execute(request); Assert.assertTrue(HttpStatus.SC_PARTIAL_CONTENT != result.getCode()); } catch (final ClientProtocolException acceptableBehavior) { // this is probably ok } }
@Test public void testNonSharedCacheMayCacheResponsesWithCacheControlPrivate() throws Exception { final ClassicHttpRequest req1 = new BasicClassicHttpRequest("GET","/"); originResponse.setHeader("Cache-Control","private,max-age=3600"); backendExpectsAnyRequest().andReturn(originResponse); final ClassicHttpRequest req2 = new BasicClassicHttpRequest("GET","/"); replayMocks(); behaveAsNonSharedCache(); execute(req1); final HttpResponse result = execute(req2); verifyMocks(); Assert.assertEquals(HttpStatus.SC_OK, result.getCode()); } }