1
0
mirror of https://github.com/square/okhttp.git synced 2026-01-25 16:01:38 +03:00

Improve cache strategy.

This renames ResponseStrategy to CacheStrategy and cleans up
the code that calls into it.

This fixes a bug where we were incorrectly reporting stats
when the caller was requesting only-if-cached. In those cases
we were tracking stats before applying that constraint. Tests
for these cases have been added
This commit is contained in:
jwilson
2013-12-31 18:28:33 -05:00
parent 611b00530b
commit 29fb61cca0
13 changed files with 178 additions and 173 deletions

View File

@@ -118,10 +118,7 @@ public final class Request {
}
public Builder newBuilder() {
return new Builder(url)
.method(method, body)
.headers(headers)
.tag(tag);
return new Builder(this);
}
public boolean isChunked() {
@@ -397,23 +394,27 @@ public final class Request {
public static class Builder {
private URL url;
private String method = "GET";
private Headers.Builder headers = new Headers.Builder();
private String method;
private final Headers.Builder headers;
private Body body;
private Object tag;
public Builder(String url) {
url(url);
public Builder() {
this.method = "GET";
this.headers = new Headers.Builder();
}
public Builder(URL url) {
url(url);
private Builder(Request request) {
this.url = request.url;
this.method = request.method;
this.body = request.body;
this.tag = request.tag;
this.headers = request.headers.newBuilder();
}
public Builder url(String url) {
try {
this.url = new URL(url);
return this;
return url(new URL(url));
} catch (MalformedURLException e) {
throw new IllegalArgumentException("Malformed URL: " + url);
}
@@ -443,12 +444,6 @@ public final class Request {
return this;
}
// TODO: this shouldn't be public.
public Builder headers(Headers headers) {
this.headers = headers.newBuilder();
return this;
}
public Builder setChunked() {
headers.set("Transfer-Encoding", "chunked");
return this;
@@ -514,25 +509,6 @@ public final class Request {
return sb.toString();
}
// TODO: this shouldn't be public.
/**
* @param method like "GET", "POST", "HEAD", etc.
* @param path like "/foo/bar.html"
* @param version like "HTTP/1.1"
* @param host like "www.android.com:1234"
* @param scheme like "https"
*/
public Builder addSpdyRequestHeaders(
String method, String path, String version, String host, String scheme) {
// TODO: populate the statusLine for the client's benefit?
headers.set(":method", method);
headers.set(":scheme", scheme);
headers.set(":path", path);
headers.set(":version", version);
headers.set(":host", host);
return this;
}
public Builder get() {
return method("GET", null);
}
@@ -569,6 +545,7 @@ public final class Request {
}
public Request build() {
if (url == null) throw new IllegalStateException("url == null");
return new Request(this);
}
}

View File

@@ -15,11 +15,11 @@
*/
package com.squareup.okhttp;
import com.squareup.okhttp.internal.Platform;
import com.squareup.okhttp.internal.Util;
import com.squareup.okhttp.internal.http.HeaderParser;
import com.squareup.okhttp.internal.http.Headers;
import com.squareup.okhttp.internal.http.HttpDate;
import com.squareup.okhttp.internal.http.SyntheticHeaders;
import com.squareup.okhttp.internal.http.StatusLine;
import java.io.ByteArrayOutputStream;
import java.io.Closeable;
@@ -47,21 +47,6 @@ import static com.squareup.okhttp.internal.Util.equal;
* This class is in beta. APIs are subject to change!
*/
public final class Response {
/** HTTP header name for the local time when the request was sent. */
private static final String SENT_MILLIS = Platform.get().getPrefix() + "-Sent-Millis";
/** HTTP header name for the local time when the response was received. */
private static final String RECEIVED_MILLIS = Platform.get().getPrefix() + "-Received-Millis";
/** HTTP synthetic header with the response source. */
// TODO: this shouldn't be public.
public static final String RESPONSE_SOURCE = Platform.get().getPrefix() + "-Response-Source";
/** HTTP synthetic header with the selected transport (spdy/3, http/1.1, etc). */
// TODO: this shouldn't be public.
public static final String SELECTED_TRANSPORT
= Platform.get().getPrefix() + "-Selected-Transport";
private final Request request;
private final StatusLine statusLine;
private final Handshake handshake;
@@ -561,9 +546,9 @@ public final class Response {
contentType = value;
} else if ("Connection".equalsIgnoreCase(fieldName)) {
connection = value;
} else if (SENT_MILLIS.equalsIgnoreCase(fieldName)) {
} else if (SyntheticHeaders.SENT_MILLIS.equalsIgnoreCase(fieldName)) {
sentRequestMillis = Long.parseLong(value);
} else if (RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
} else if (SyntheticHeaders.RECEIVED_MILLIS.equalsIgnoreCase(fieldName)) {
receivedResponseMillis = Long.parseLong(value);
}
}
@@ -691,14 +676,14 @@ public final class Response {
// TODO: this shouldn't be public.
public Builder setLocalTimestamps(long sentRequestMillis, long receivedResponseMillis) {
headers.set(SENT_MILLIS, Long.toString(sentRequestMillis));
headers.set(RECEIVED_MILLIS, Long.toString(receivedResponseMillis));
headers.set(SyntheticHeaders.SENT_MILLIS, Long.toString(sentRequestMillis));
headers.set(SyntheticHeaders.RECEIVED_MILLIS, Long.toString(receivedResponseMillis));
return this;
}
// TODO: this shouldn't be public.
public Builder setResponseSource(ResponseSource responseSource) {
headers.set(RESPONSE_SOURCE, responseSource.toString() + " " + statusLine.code());
headers.set(SyntheticHeaders.RESPONSE_SOURCE, responseSource + " " + statusLine.code());
return this;
}

View File

@@ -29,7 +29,14 @@ public enum ResponseSource {
CONDITIONAL_CACHE,
/** The response was returned from the network. */
NETWORK;
NETWORK,
/**
* The request demanded a cached response that the cache couldn't satisfy.
* This yields a 504 (Gateway Timeout) response as specified by
* http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4.
*/
NONE;
public boolean requiresConnection() {
return this == CONDITIONAL_CACHE || this == NETWORK;

View File

@@ -60,7 +60,8 @@ public final class TunnelRequest {
* the proxy unencrypted.
*/
Request getRequest() throws IOException {
Request.Builder result = new Request.Builder(new URL("https", host, port, "/"));
Request.Builder result = new Request.Builder()
.url(new URL("https", host, port, "/"));
// Always set Host and User-Agent.
result.header("Host", port == getDefaultPort("https") ? host : (host + ":" + port));

View File

@@ -7,16 +7,19 @@ import java.net.HttpURLConnection;
import java.util.concurrent.TimeUnit;
/**
* Given a request and cached response, this figures out the next action. It
* may also update the request to add conditions, or the response to add
* warnings.
* Given a request and cached response, this figures out whether to use the
* network, the cache, or both.
*
* <p>Selecting the next action may have side effects. The request may gain
* conditions such as an "If-None-Match" or "If-Modified-Since" header. The
* response may gain a warning if it is potentially stale.
*/
public final class ResponseStrategy {
public final class CacheStrategy {
public final Request request;
public final Response response;
public final ResponseSource source;
private ResponseStrategy(
private CacheStrategy(
Request request, Response response, ResponseSource source) {
this.request = request;
this.response = response;
@@ -111,17 +114,17 @@ public final class ResponseStrategy {
* Returns a strategy to satisfy {@code request} using the a cached response
* {@code response}.
*/
public static ResponseStrategy get(
public static CacheStrategy get(
long nowMillis, Response response, Request request) {
// If this response shouldn't have been stored, it should never be used
// as a response source. This check should be redundant as long as the
// persistence store is well-behaved and the rules are constant.
if (!isCacheable(response, request)) {
return new ResponseStrategy(request, response, ResponseSource.NETWORK);
return new CacheStrategy(request, response, ResponseSource.NETWORK);
}
if (request.isNoCache() || request.hasConditions()) {
return new ResponseStrategy(request, response, ResponseSource.NETWORK);
return new CacheStrategy(request, response, ResponseSource.NETWORK);
}
long ageMillis = computeAge(response, nowMillis);
@@ -142,7 +145,8 @@ public final class ResponseStrategy {
}
if (!response.isNoCache() && ageMillis + minFreshMillis < freshMillis + maxStaleMillis) {
Response.Builder builder = response.newBuilder();
Response.Builder builder = response.newBuilder()
.setResponseSource(ResponseSource.CACHE); // Overwrite any stored response source.
if (ageMillis + minFreshMillis >= freshMillis) {
builder.addWarning("110 HttpURLConnection \"Response is stale\"");
}
@@ -150,7 +154,7 @@ public final class ResponseStrategy {
if (ageMillis > oneDayMillis && isFreshnessLifetimeHeuristic(response)) {
builder.addWarning("113 HttpURLConnection \"Heuristic expiration\"");
}
return new ResponseStrategy(request, builder.build(), ResponseSource.CACHE);
return new CacheStrategy(request, builder.build(), ResponseSource.CACHE);
}
Request.Builder conditionalRequestBuilder = request.newBuilder();
@@ -169,6 +173,6 @@ public final class ResponseStrategy {
ResponseSource responseSource = conditionalRequest.hasConditions()
? ResponseSource.CONDITIONAL_CACHE
: ResponseSource.NETWORK;
return new ResponseStrategy(conditionalRequest, response, responseSource);
return new CacheStrategy(conditionalRequest, response, responseSource);
}
}

View File

@@ -27,7 +27,6 @@ import com.squareup.okhttp.Response;
import com.squareup.okhttp.ResponseSource;
import com.squareup.okhttp.TunnelRequest;
import com.squareup.okhttp.internal.Dns;
import com.squareup.okhttp.internal.Util;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
@@ -40,6 +39,7 @@ import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import static com.squareup.okhttp.internal.Util.EMPTY_INPUT_STREAM;
import static com.squareup.okhttp.internal.Util.closeQuietly;
import static com.squareup.okhttp.internal.Util.getDefaultPort;
import static com.squareup.okhttp.internal.Util.getEffectivePort;
import static com.squareup.okhttp.internal.http.StatusLine.HTTP_CONTINUE;
@@ -155,33 +155,10 @@ public class HttpEngine {
* writing the request body if it exists.
*/
public final void sendRequest() throws IOException {
if (responseSource != null) {
return;
}
if (responseSource != null) return;
prepareRawRequestHeaders();
initResponseSource();
OkResponseCache responseCache = client.getOkResponseCache();
if (responseCache != null) {
responseCache.trackResponse(responseSource);
}
// The raw response source may require the network, but the request
// headers may forbid network use. In that case, dispose of the network
// response and use a gateway timeout response instead, as specified
// by http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9.4.
if (request.isOnlyIfCached() && responseSource.requiresConnection()) {
if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
Util.closeQuietly(validatingResponse.body());
}
this.responseSource = ResponseSource.CACHE;
this.validatingResponse = new Response.Builder(request)
.statusLine(new StatusLine("HTTP/1.1 504 Gateway Timeout"))
.body(EMPTY_BODY)
.build();
promoteValidatingResponse();
}
responseSource = chooseResponseSource();
if (responseSource.requiresConnection()) {
sendSocketRequest();
@@ -191,38 +168,56 @@ public class HttpEngine {
}
}
/**
* Initialize the source for this response. It may be corrected later if the
* request headers forbids network use.
*/
private void initResponseSource() throws IOException {
responseSource = ResponseSource.NETWORK;
/** Returns a source that can satisfy the request. */
private ResponseSource chooseResponseSource() throws IOException {
OkResponseCache responseCache = client.getOkResponseCache();
if (responseCache == null) return;
if (responseCache == null) return ResponseSource.NETWORK; // No cache? Easy decision.
Response candidate = responseCache.get(request);
if (candidate == null) return;
// Drop the cached response if it's missing a required handshake.
if (request.isHttps() && candidate.handshake() == null) {
Util.closeQuietly(candidate.body());
return;
ResponseSource result;
if (candidate == null) {
result = ResponseSource.NETWORK;
} else if (request.isHttps() && candidate.handshake() == null) {
// Drop the cached response if it's missing a required handshake.
result = ResponseSource.NETWORK;
} else {
// We've got a lead on a cached response. Ask response strategy to analyze it.
long now = System.currentTimeMillis();
CacheStrategy cacheStrategy = CacheStrategy.get(now, candidate, request);
result = cacheStrategy.source;
this.request = cacheStrategy.request;
if (result == ResponseSource.CACHE || result == ResponseSource.CONDITIONAL_CACHE) {
this.validatingResponse = cacheStrategy.response;
}
}
long now = System.currentTimeMillis();
ResponseStrategy responseStrategy = ResponseStrategy.get(now, candidate, request);
this.responseSource = responseStrategy.source;
this.request = responseStrategy.request;
if (candidate != null && result == ResponseSource.NETWORK) {
closeQuietly(candidate.body()); // We aren't using the cached response. Close it.
}
if (responseSource == ResponseSource.CACHE) {
this.validatingResponse = responseStrategy.response;
if (result == ResponseSource.CACHE) {
promoteValidatingResponse();
} else if (request.isOnlyIfCached()) {
// We're forbidden from using the network, but the cache is insufficient.
if (result == ResponseSource.CONDITIONAL_CACHE) {
closeQuietly(validatingResponse.body());
}
result = ResponseSource.NONE;
this.validatingResponse = new Response.Builder(request)
.statusLine(new StatusLine("HTTP/1.1 504 Gateway Timeout"))
.setResponseSource(result)
.body(EMPTY_BODY)
.build();
promoteValidatingResponse();
} else if (responseSource == ResponseSource.CONDITIONAL_CACHE) {
this.validatingResponse = responseStrategy.response;
} else if (responseSource == ResponseSource.NETWORK) {
Util.closeQuietly(candidate.body());
}
responseCache.trackResponse(result);
return result;
}
private Response cacheableResponse() {
@@ -314,9 +309,7 @@ public class HttpEngine {
/** Returns the request body or null if this request doesn't have a body. */
public final OutputStream getRequestBody() {
if (responseSource == null) {
throw new IllegalStateException();
}
if (responseSource == null) throw new IllegalStateException();
return requestBodyOut;
}
@@ -349,7 +342,7 @@ public class HttpEngine {
if (responseCache == null) return;
// Should we cache this response for this request?
if (!ResponseStrategy.isCacheable(response, request)) {
if (!CacheStrategy.isCacheable(response, request)) {
responseCache.maybeRemove(request);
return;
}
@@ -382,7 +375,7 @@ public class HttpEngine {
if (validatingResponse != null
&& validatingResponse.body() != null
&& responseBodyIn == validatingResponse.body().byteStream()) {
Util.closeQuietly(responseBodyIn);
closeQuietly(responseBodyIn);
}
if (!connectionReleased && connection != null) {
@@ -390,7 +383,7 @@ public class HttpEngine {
if (transport == null
|| !transport.makeReusable(streamCanceled, requestBodyOut, responseTransferIn)) {
Util.closeQuietly(connection);
closeQuietly(connection);
connection = null;
} else if (automaticallyReleaseConnectionToPool) {
client.getConnectionPool().recycle(connection);
@@ -460,7 +453,7 @@ public class HttpEngine {
}
if (request.getHost() == null) {
result.setHost(getHostHeader(request.url()));
result.setHost(hostHeader(request.url()));
}
if ((connection == null || connection.getHttpMinorVersion() != 0)
@@ -490,7 +483,7 @@ public class HttpEngine {
return agent != null ? agent : ("Java" + System.getProperty("java.version"));
}
public static String getHostHeader(URL url) {
public static String hostHeader(URL url) {
return getEffectivePort(url) != getDefaultPort(url.getProtocol())
? url.getHost() + ":" + url.getPort()
: url.getHost();
@@ -501,19 +494,9 @@ public class HttpEngine {
* headers and starts reading the HTTP response body if it exists.
*/
public final void readResponse() throws IOException {
if (hasResponse()) {
// TODO: this doesn't make much sense.
response = response.newBuilder().setResponseSource(responseSource).build();
return;
}
if (responseSource == null) {
throw new IllegalStateException("readResponse() without sendRequest()");
}
if (!responseSource.requiresConnection()) {
return;
}
if (response != null) return;
if (responseSource == null) throw new IllegalStateException("call sendRequest() first!");
if (!responseSource.requiresConnection()) return;
if (sentRequestMillis == -1) {
if (request.getContentLength() == -1
@@ -556,7 +539,7 @@ public class HttpEngine {
}
return;
} else {
Util.closeQuietly(validatingResponse.body());
closeQuietly(validatingResponse.body());
}
}

View File

@@ -174,7 +174,7 @@ public final class HttpTransport implements Transport {
Response.Builder responseBuilder = new Response.Builder(request);
responseBuilder.statusLine(statusLine);
responseBuilder.header(Response.SELECTED_TRANSPORT, "http/1.1");
responseBuilder.header(SyntheticHeaders.SELECTED_TRANSPORT, "http/1.1");
Headers.Builder headersBuilder = new Headers.Builder();
headersBuilder.readHeaders(in);

View File

@@ -172,7 +172,7 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy {
}
// For the request line property assigned to the null key, just use no proxy and HTTP 1.1.
Request request = new Request.Builder(getURL()).method(method, null).build();
Request request = new Request.Builder().url(getURL()).method(method, null).build();
String requestLine = RequestLine.get(request, null, 1);
return requestHeaders.build().toMultimap(requestLine);
}
@@ -271,10 +271,14 @@ public class HttpURLConnectionImpl extends HttpURLConnection implements Policy {
private HttpEngine newHttpEngine(String method, Connection connection,
RetryableOutputStream requestBody) throws IOException {
Request request = new Request.Builder(getURL())
.method(method, null) // No body: that's provided later!
.headers(requestHeaders.build())
.build();
Request.Builder builder = new Request.Builder()
.url(getURL())
.method(method, null /* No body; that's passed separately. */);
Headers headers = requestHeaders.build();
for (int i = 0; i < headers.length(); i++) {
builder.addHeader(headers.getFieldName(i), headers.getValue(i));
}
Request request = builder.build();
// If we're currently not using caches, make sure the engine's client doesn't have one.
OkHttpClient engineClient = client;

View File

@@ -26,7 +26,6 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.net.CacheRequest;
import java.net.ProtocolException;
import java.net.URL;
import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
@@ -44,12 +43,12 @@ public final class SpdyTransport implements Transport {
}
@Override public Request prepareRequest(Request request) {
Request.Builder builder = request.newBuilder();
String version = RequestLine.version(httpEngine.connection.getHttpMinorVersion());
URL url = request.url();
builder.addSpdyRequestHeaders(request.method(), RequestLine.requestPath(url), version,
HttpEngine.getHostHeader(url), httpEngine.getRequest().url().getProtocol());
Request.Builder builder = request.newBuilder()
.header(":method", request.method())
.header(":scheme", httpEngine.getRequest().url().getProtocol())
.header(":path", RequestLine.requestPath(request.url()))
.header(":version", RequestLine.version(httpEngine.connection.getHttpMinorVersion()))
.header(":host", HttpEngine.hostHeader(request.url()));
if (httpEngine.hasRequestBody()) {
long fixedContentLength = httpEngine.policy.getFixedContentLength();
@@ -145,7 +144,7 @@ public final class SpdyTransport implements Transport {
String version = null;
Headers.Builder headersBuilder = new Headers.Builder();
headersBuilder.set(Response.SELECTED_TRANSPORT, "spdy/3");
headersBuilder.set(SyntheticHeaders.SELECTED_TRANSPORT, "spdy/3");
for (int i = 0; i < nameValueBlock.size(); i += 2) {
String name = nameValueBlock.get(i);
String values = nameValueBlock.get(i + 1);

View File

@@ -0,0 +1,23 @@
package com.squareup.okhttp.internal.http;
import com.squareup.okhttp.internal.Platform;
/** Headers added to the HTTP response for internal use by OkHttp. */
public final class SyntheticHeaders {
static final String PREFIX = Platform.get().getPrefix();
/** The local time when the request was sent. */
public static final String SENT_MILLIS = PREFIX + "-Sent-Millis";
/** The local time when the response was received. */
public static final String RECEIVED_MILLIS = PREFIX + "-Received-Millis";
/** The response source. */
public static final String RESPONSE_SOURCE = PREFIX + "-Response-Source";
/** The selected transport (spdy/3, http/1.1, etc). */
public static final String SELECTED_TRANSPORT = PREFIX + "-Selected-Transport";
private SyntheticHeaders() {
}
}

View File

@@ -57,7 +57,8 @@ public final class AsyncApiTest {
.addHeader("Content-Type: text/plain"));
server.play();
Request request = new Request.Builder(server.getUrl("/"))
Request request = new Request.Builder()
.url(server.getUrl("/"))
.header("User-Agent", "AsyncApiTest")
.build();
client.enqueue(request, receiver);
@@ -80,7 +81,9 @@ public final class AsyncApiTest {
client.setSslSocketFactory(sslContext.getSocketFactory());
client.setHostnameVerifier(new RecordingHostnameVerifier());
Request request = new Request.Builder(server.getUrl("/")).build();
Request request = new Request.Builder()
.url(server.getUrl("/"))
.build();
client.enqueue(request, receiver);
receiver.await(request.url()).assertHandshake();
@@ -90,7 +93,8 @@ public final class AsyncApiTest {
server.enqueue(new MockResponse().setBody("abc"));
server.play();
Request request = new Request.Builder(server.getUrl("/"))
Request request = new Request.Builder()
.url(server.getUrl("/"))
.post(Request.Body.create(MediaType.parse("text/plain"), "def"))
.build();
client.enqueue(request, receiver);
@@ -112,12 +116,16 @@ public final class AsyncApiTest {
client.setOkResponseCache(cache);
Request request1 = new Request.Builder(server.getUrl("/")).build();
Request request1 = new Request.Builder()
.url(server.getUrl("/"))
.build();
client.enqueue(request1, receiver);
receiver.await(request1.url()).assertCode(200).assertBody("A");
assertNull(server.takeRequest().getHeader("If-None-Match"));
Request request2 = new Request.Builder(server.getUrl("/")).build();
Request request2 = new Request.Builder()
.url(server.getUrl("/"))
.build();
client.enqueue(request2, receiver);
receiver.await(request2.url()).assertCode(200).assertBody("A");
assertEquals("v1", server.takeRequest().getHeader("If-None-Match"));

View File

@@ -32,15 +32,15 @@ public final class HeadersTest {
"set-cookie", "Cookie1\u0000Cookie2",
":status", "200 OK",
":version", "HTTP/1.1");
Request request = new Request.Builder("http://square.com/").build();
Request request = new Request.Builder().url("http://square.com/").build();
Response response = SpdyTransport.readNameValueBlock(request, nameValueBlock).build();
Headers headers = response.headers();
assertEquals(4, headers.length());
assertEquals("HTTP/1.1 200 OK", response.statusLine());
assertEquals("no-cache, no-store", headers.get("cache-control"));
assertEquals("Cookie2", headers.get("set-cookie"));
assertEquals("spdy/3", headers.get(Response.SELECTED_TRANSPORT));
assertEquals(Response.SELECTED_TRANSPORT, headers.getFieldName(0));
assertEquals("spdy/3", headers.get(SyntheticHeaders.SELECTED_TRANSPORT));
assertEquals(SyntheticHeaders.SELECTED_TRANSPORT, headers.getFieldName(0));
assertEquals("spdy/3", headers.getValue(0));
assertEquals("cache-control", headers.getFieldName(1));
assertEquals("no-cache, no-store", headers.getValue(1));

View File

@@ -204,7 +204,7 @@ public final class HttpResponseCacheTest {
// exhaust the content stream
readAscii(conn);
Response cached = cache.get(new Request.Builder(url).build());
Response cached = cache.get(new Request.Builder().url(url).build());
if (shouldPut) {
assertNotNull(Integer.toString(responseCode), cached);
cached.body().close();
@@ -1048,6 +1048,9 @@ public final class HttpResponseCacheTest {
HttpURLConnection connection = openConnection(server.getUrl("/"));
connection.addRequestProperty("Cache-Control", "only-if-cached");
assertGatewayTimeout(connection);
assertEquals(1, cache.getRequestCount());
assertEquals(0, cache.getNetworkCount());
assertEquals(0, cache.getHitCount());
}
@Test public void requestOnlyIfCachedWithFullResponseCached() throws IOException {
@@ -1060,6 +1063,9 @@ public final class HttpResponseCacheTest {
URLConnection connection = openConnection(server.getUrl("/"));
connection.addRequestProperty("Cache-Control", "only-if-cached");
assertEquals("A", readAscii(connection));
assertEquals(2, cache.getRequestCount());
assertEquals(1, cache.getNetworkCount());
assertEquals(1, cache.getHitCount());
}
@Test public void requestOnlyIfCachedWithConditionalResponseCached() throws IOException {
@@ -1072,6 +1078,9 @@ public final class HttpResponseCacheTest {
HttpURLConnection connection = openConnection(server.getUrl("/"));
connection.addRequestProperty("Cache-Control", "only-if-cached");
assertGatewayTimeout(connection);
assertEquals(2, cache.getRequestCount());
assertEquals(1, cache.getNetworkCount());
assertEquals(0, cache.getHitCount());
}
@Test public void requestOnlyIfCachedWithUnhelpfulResponseCached() throws IOException {
@@ -1082,6 +1091,9 @@ public final class HttpResponseCacheTest {
HttpURLConnection connection = openConnection(server.getUrl("/"));
connection.addRequestProperty("Cache-Control", "only-if-cached");
assertGatewayTimeout(connection);
assertEquals(2, cache.getRequestCount());
assertEquals(1, cache.getNetworkCount());
assertEquals(0, cache.getHitCount());
}
@Test public void requestCacheControlNoCache() throws Exception {
@@ -1684,8 +1696,8 @@ public final class HttpResponseCacheTest {
connection.addRequestProperty("Cache-Control", "only-if-cached");
assertEquals("A", readAscii(connection));
String source = connection.getHeaderField(Response.RESPONSE_SOURCE);
assertEquals(ResponseSource.CACHE.toString() + " 200", source);
String source = connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE);
assertEquals(ResponseSource.CACHE + " 200", source);
}
@Test public void responseSourceHeaderConditionalCacheFetched() throws IOException {
@@ -1701,8 +1713,8 @@ public final class HttpResponseCacheTest {
HttpURLConnection connection = openConnection(server.getUrl("/"));
assertEquals("B", readAscii(connection));
String source = connection.getHeaderField(Response.RESPONSE_SOURCE);
assertEquals(ResponseSource.CONDITIONAL_CACHE.toString() + " 200", source);
String source = connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE);
assertEquals(ResponseSource.CONDITIONAL_CACHE + " 200", source);
}
@Test public void responseSourceHeaderConditionalCacheNotFetched() throws IOException {
@@ -1716,8 +1728,8 @@ public final class HttpResponseCacheTest {
HttpURLConnection connection = openConnection(server.getUrl("/"));
assertEquals("A", readAscii(connection));
String source = connection.getHeaderField(Response.RESPONSE_SOURCE);
assertEquals(ResponseSource.CONDITIONAL_CACHE.toString() + " 304", source);
String source = connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE);
assertEquals(ResponseSource.CONDITIONAL_CACHE + " 304", source);
}
@Test public void responseSourceHeaderFetched() throws IOException {
@@ -1727,8 +1739,8 @@ public final class HttpResponseCacheTest {
URLConnection connection = openConnection(server.getUrl("/"));
assertEquals("A", readAscii(connection));
String source = connection.getHeaderField(Response.RESPONSE_SOURCE);
assertEquals(ResponseSource.NETWORK.toString() + " 200", source);
String source = connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE);
assertEquals(ResponseSource.NETWORK + " 200", source);
}
@Test public void emptyResponseHeaderNameFromCacheIsLenient() throws Exception {
@@ -1943,6 +1955,8 @@ public final class HttpResponseCacheTest {
}
assertEquals(504, connection.getResponseCode());
assertEquals(-1, connection.getErrorStream().read());
assertEquals(ResponseSource.NONE + " 504",
connection.getHeaderField(SyntheticHeaders.RESPONSE_SOURCE));
}
enum TransferKind {