Hello Oleg, I extended the TestCacheEntryUpdater by the following test, which fails in the last line:
@Test public void testContentLengthIsNotAddedWhenTransferEncodingIsPresent() throws IOException { final Header[] headers = { new BasicHeader("Date", DateUtils.formatDate(requestDate)), new BasicHeader("ETag", "\"etag\""), new BasicHeader("Transfer-Encoding", "chunked")}; entry = HttpTestUtils.makeCacheEntry(headers); response.setHeaders(new Header[]{ new BasicHeader("Last-Modified", DateUtils.formatDate(responseDate)), new BasicHeader("Cache-Control", "public"), new BasicHeader("Content-Length", "0")}); final HttpCacheEntry updatedEntry = impl.updateCacheEntry(null, entry, new Date(), new Date(), response); final Header[] updatedHeaders = updatedEntry.getAllHeaders(); headersContain(updatedHeaders, "Transfer-Encoding", "chunked"); headersNotContain(updatedHeaders, "Content-Length", "0"); } My proposal is to make a change in method CacheEntryUpdater::mergeHeaders. It already retains the original Content-Encoding header and it should handle the original Content-Length header in the same way. The change in the last loop is sufficient to fix the test above. The change in the first loop is not necessary to fix it, but IMHO it's consistent and probably the method CachedHttpResponseGenerator::addMissingContentLengthHeader becomes obsolete and may be removed. protected Header[] mergeHeaders(final HttpCacheEntry entry, final HttpResponse response) { if (entryAndResponseHaveDateHeader(entry, response) && entryDateHeaderNewerThenResponse(entry, response)) { // Don't merge headers, keep the entry's headers as they are newer. return entry.getAllHeaders(); } final HeaderGroup headerGroup = new HeaderGroup(); headerGroup.setHeaders(entry.getAllHeaders()); // Remove cache headers that match response for (final HeaderIterator it = response.headerIterator(); it.hasNext(); ) { final Header responseHeader = it.nextHeader(); - // Since we do not expect a content in a 304 response, should retain the original Content-Encoding header - if (HTTP.CONTENT_ENCODING.equals(responseHeader.getName())) { + // Since we do not expect a content in a 304 response, should retain the original Content-Encoding and Content-Length header + if (HTTP.CONTENT_ENCODING.equals(responseHeader.getName()) + || HTTP.CONTENT_LEN.equals(responseHeader.getName())) { continue; } final Header[] matchingHeaders = headerGroup.getHeaders(responseHeader.getName()); for (final Header matchingHeader : matchingHeaders) { headerGroup.removeHeader(matchingHeader); } } // remove cache entry 1xx warnings for (final HeaderIterator it = headerGroup.iterator(); it.hasNext(); ) { final Header cacheHeader = it.nextHeader(); if (HeaderConstants.WARNING.equalsIgnoreCase(cacheHeader.getName())) { final String warningValue = cacheHeader.getValue(); if (warningValue != null && warningValue.startsWith("1")) { it.remove(); } } } for (final HeaderIterator it = response.headerIterator(); it.hasNext(); ) { final Header responseHeader = it.nextHeader(); - // Since we do not expect a content in a 304 response, should avoid updating Content-Encoding header - if (HTTP.CONTENT_ENCODING.equals(responseHeader.getName())) { + // Since we do not expect a content in a 304 response, should avoid updating Content-Encoding and Content-Length header + if (HTTP.CONTENT_ENCODING.equals(responseHeader.getName()) + || HTTP.CONTENT_LEN.equals(responseHeader.getName())) { continue; } headerGroup.addHeader(responseHeader); } return headerGroup.getAllHeaders(); } Best regards Dirk -----Ursprüngliche Nachricht----- Von: Oleg Kalnichevski <ol...@apache.org> Gesendet: Freitag, 14. August 2020 11:17 An: HttpClient User Discussion <httpclient-users@hc.apache.org> Betreff: Re: AW: CachingHttpClient fails to revalidate cache entry in case of chunked transfer encoding On Thu, 2020-08-13 at 19:32 +0000, Henselin, Dirk (G-GCPS) wrote: > Hello Oleg, > > yes, `Transfer-Encoding` and `Content-Length` headers should be > mutually exclusive and because I use chunked transfer, the `Transfer- > Encoding` header is set in the response while the `Content-Length` > header is not. In case of a 304 during a revalidation, the header > contains Content-Length=0. Probably a proxy is responsible for this, > just like the comment "Some well-known proxies respond with Content- > Length=0, when returning 304" in the method > CachedHttpResponseGenerator::addMissingContentLengthHeader is saying. > In CacheEntryUpdater::mergeHeaders the Content-Length=0 is merged into > the cached entry, but the cached entry contains also a > `Transfer-Encoding` header, so in the cached entry these headers > aren't mutually exclusive anymore. Because of the `Transfer-Encoding` > header the method > CachedHttpResponseGenerator::addMissingContentLengthHeader isn't > fixing the `Content-Length` header and Content-Length=0 causes > returning null instead of the cached content. IMHO the `Content- > Length` header should not be merged into the cached response in case > of a 304, at least if the cached entry contains a `Transfer-Encoding` > header. > > Best regards > Dirk > Dirk The original developers of the cache module are no longer with the project. I do not know if there was a rationale for this decision. Please either propose a patch as a PR at GitHub or produce a reproducer I could run locally if you expect me to look into the problem. Oleg > -----Ursprüngliche Nachricht----- > Von: Oleg Kalnichevski <ol...@apache.org> > Gesendet: Mittwoch, 12. August 2020 17:53 > An: HttpClient User Discussion <httpclient-users@hc.apache.org> > Betreff: Re: CachingHttpClient fails to revalidate cache entry in case > of chunked transfer encoding > > On Wed, 2020-08-12 at 08:35 +0000, Henselin, Dirk (G-GCPS) wrote: > > Hello, > > > > English is not my native language; please excuse typing errors. > > I use HttpClient 4.5.12. > > > > I found that despite a cache hit, the CachingHttpClient returns > > null, if the content-length header was missing in the original > > response. > > It > > worked fine after forcing the server to add the content-length > > header, but this isn't possible in case of chunked transfer > > encoding. > > I looked into the source code and found, that the content-length > > header of a subsequent response is merged into the cache entry's > > response headers, if the content-length header was missing in the > > original response. In case of a 304 content-length=0 is merged into > > the cache entry and this causes returning null instead of the cached > > entity. > > In CachedHttpResponseGenerator I found the following method: > > > > private void addMissingContentLengthHeader(final HttpResponse > > response, final HttpEntity entity) { > > if (transferEncodingIsPresent(response)) { > > return; > > } > > // Some well known proxies respond with Content-Length=0, > > when returning 304. For robustness, always > > // use the cached entity's content length, as modern > > browsers do. > > final Header contentLength = new > > BasicHeader(HTTP.CONTENT_LEN, > > Long.toString(entity.getContentLength())); > > response.setHeader(contentLength); > > } > > > > Obviously, this method adds a missing content length header, but not > > in case of chunked transfer encoding. How can I solve this? > > > > Dirk, > > The behavior of this method looks correct to me as `Transfer- > Encoding` and `Content-Length` headers are mutually exclusive. > > Could you please provide a reproducer for your original problem? > > Oleg > > > > --------------------------------------------------------------------- > To unsubscribe, e-mail: httpclient-users-unsubscr...@hc.apache.org > For additional commands, e-mail: httpclient-users-h...@hc.apache.org > > --------------------------------------------------------------------- > To unsubscribe, e-mail: httpclient-users-unsubscr...@hc.apache.org > For additional commands, e-mail: httpclient-users-h...@hc.apache.org > --------------------------------------------------------------------- To unsubscribe, e-mail: httpclient-users-unsubscr...@hc.apache.org For additional commands, e-mail: httpclient-users-h...@hc.apache.org