Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why 204 are not cached by OkHttp?

Looking at the code in CacheInterceptor I see that response with the code 204 are not cached. Yet I believe 204 are cacheable as discussed here

We use 204 as a response to GET don't indicate an empty response and just recently noticed those are not cached.

like image 327
Gui Forget Avatar asked Nov 26 '22 02:11

Gui Forget


1 Answers

There are a number of reasons why.

Why does the implementation behave like this?

Technically, 204s are skipped because the intercept method will only cache responses if promisesBody returns true:

      if (response.promisesBody() && CacheStrategy.isCacheable(response, networkRequest)) {
        // Offer this request to the cache.

As you might expect, promisesBody returns false for 204 responses, unless they have a Content-length header, or chunked transfer encoding:

/**
 * Returns true if the response headers and status indicate that this response has a (possibly
 * 0-length) body. See RFC 7231.
 */
...
  val responseCode = code
  if ((responseCode < HTTP_CONTINUE || responseCode >= 200) &&
      responseCode != HTTP_NO_CONTENT &&
      responseCode != HTTP_NOT_MODIFIED) {
    return true
  }

  // If the Content-Length or Transfer-Encoding headers disagree with the response code, the
  // response is malformed. For best compatibility, we honor the headers.
  if (headersContentLength() != -1L ||
      "chunked".equals(header("Transfer-Encoding"), ignoreCase = true)) {
    return true
  }

  return false

Why was this design used?

To justify this decision, it's possible that OkHttp's general use case for a cache is to save re-sending large payloads, rather than keeping round-trips to the absolute minimum. In addition, 204 is valid as a response to a GET, but more often used as a response to a POST; it's possible that the design reflects knowledge of this heuristic.

Why hasn't this changed?

Looking through relevant issues, there is one related to changing the default behaviour for certain status codes: OkHttp cache-by-default status codes out of date:

The spec wants these: 200, 203, 204, 206, 300, 301, 404, 405, 410, 414, and 501

which resulted in a test:

    assertCached(true, 204);

However, due to the configuration of the mock server, the response used to test is:

HTTP/1.1 204 OK
...
Content-Length: 0

with exactly the unnecessary Content-Length that its own detection requires, and which a valid response should not include.

tl;dr

It's a bug, which nobody who's run into this case has reported.

like image 128
Joe Avatar answered Dec 09 '22 15:12

Joe