1
0
mirror of https://github.com/square/okhttp.git synced 2026-01-24 04:02:07 +03:00

Merge pull request #302 from square/jwilson/old_bad_cache_responses

Continue to read old bad cache responses.
This commit is contained in:
Jake Wharton
2013-08-28 23:18:36 -07:00
4 changed files with 108 additions and 31 deletions

View File

@@ -24,11 +24,14 @@ import java.io.InputStream;
import java.io.OutputStream;
import java.io.Reader;
import java.io.StringWriter;
import java.io.UnsupportedEncodingException;
import java.net.Socket;
import java.net.URI;
import java.net.URL;
import java.nio.ByteOrder;
import java.nio.charset.Charset;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -50,6 +53,9 @@ public final class Util {
public static final Charset UTF_8 = Charset.forName("UTF-8");
private static AtomicReference<byte[]> skipBuffer = new AtomicReference<byte[]>();
private static final char[] DIGITS =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
private Util() {
}
@@ -331,6 +337,30 @@ public final class Util {
return result.toString();
}
/** Returns a 32 character string containing a hash of {@code s}. */
public static String hash(String s) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] md5bytes = messageDigest.digest(s.getBytes("UTF-8"));
return bytesToHexString(md5bytes);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
private static String bytesToHexString(byte[] bytes) {
char[] digits = DIGITS;
char[] buf = new char[bytes.length * 2];
int c = 0;
for (byte b : bytes) {
buf[c++] = digits[(b >> 4) & 0xf];
buf[c++] = digits[b & 0xf];
}
return new String(buf);
}
/** Returns an immutable copy of {@code list}. */
public static <T> List<T> immutableList(List<T> list) {
return Collections.unmodifiableList(new ArrayList<T>(list));

View File

@@ -35,7 +35,6 @@ import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.OutputStreamWriter;
import java.io.UnsupportedEncodingException;
import java.io.Writer;
import java.net.CacheRequest;
import java.net.CacheResponse;
@@ -44,8 +43,6 @@ import java.net.ResponseCache;
import java.net.SecureCacheResponse;
import java.net.URI;
import java.net.URLConnection;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.security.cert.Certificate;
import java.security.cert.CertificateEncodingException;
@@ -119,9 +116,6 @@ import static com.squareup.okhttp.internal.Util.UTF_8;
* }</pre>
*/
public final class HttpResponseCache extends ResponseCache {
private static final char[] DIGITS =
{ '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'a', 'b', 'c', 'd', 'e', 'f' };
// TODO: add APIs to iterate the cache?
private static final int VERSION = 201105;
private static final int ENTRY_METADATA = 0;
@@ -176,26 +170,7 @@ public final class HttpResponseCache extends ResponseCache {
}
private String uriToKey(URI uri) {
try {
MessageDigest messageDigest = MessageDigest.getInstance("MD5");
byte[] md5bytes = messageDigest.digest(uri.toString().getBytes("UTF-8"));
return bytesToHexString(md5bytes);
} catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
} catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
private static String bytesToHexString(byte[] bytes) {
char[] digits = DIGITS;
char[] buf = new char[bytes.length * 2];
int c = 0;
for (byte b : bytes) {
buf[c++] = digits[(b >> 4) & 0xf];
buf[c++] = digits[b & 0xf];
}
return new String(buf);
return Util.hash(uri.toString());
}
@Override public CacheResponse get(URI uri, String requestMethod,

View File

@@ -164,14 +164,17 @@ public final class RawHeaders {
/**
* Add an HTTP header line containing a field name, a literal colon, and a
* value.
* value. This works around empty header names and header names that start
* with a colon (created by old broken SPDY versions of the response cache).
*/
public void addLine(String line) {
int index = line.indexOf(":");
if (index == -1) {
addLenient("", line);
} else {
int index = line.indexOf(":", 1);
if (index != -1) {
addLenient(line.substring(0, index), line.substring(index + 1));
} else if (line.startsWith(":")) {
addLenient("", line.substring(1)); // Empty header name.
} else {
addLenient("", line); // No header name.
}
}

View File

@@ -20,6 +20,7 @@ import com.squareup.okhttp.HttpResponseCache;
import com.squareup.okhttp.OkHttpClient;
import com.squareup.okhttp.ResponseSource;
import com.squareup.okhttp.internal.SslContextBuilder;
import com.squareup.okhttp.internal.Util;
import com.squareup.okhttp.mockwebserver.MockResponse;
import com.squareup.okhttp.mockwebserver.MockWebServer;
import com.squareup.okhttp.mockwebserver.RecordedRequest;
@@ -27,6 +28,7 @@ import java.io.BufferedReader;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
@@ -1733,6 +1735,73 @@ public final class HttpResponseCacheTest {
assertEquals("A", connection.getHeaderField(""));
}
/**
* Old implementations of OkHttp's response cache wrote header fields like
* ":status: 200 OK". This broke our cached response parser because it split
* on the first colon. This regression test exists to help us read these old
* bad cache entries.
*
* https://github.com/square/okhttp/issues/227
*/
@Test public void testGoldenCacheResponse() throws Exception {
cache.close();
server.enqueue(new MockResponse()
.clearHeaders()
.setResponseCode(HttpURLConnection.HTTP_NOT_MODIFIED));
server.play();
URL url = server.getUrl("/");
String urlKey = Util.hash(url.toString());
String entryMetadata = ""
+ "" + url + "\n"
+ "GET\n"
+ "0\n"
+ "HTTP/1.1 200 OK\n"
+ "7\n"
+ ":status: 200 OK\n"
+ ":version: HTTP/1.1\n"
+ "etag: foo\n"
+ "content-length: 3\n"
+ "OkHttp-Received-Millis: " + System.currentTimeMillis() + "\n"
+ "X-Android-Response-Source: NETWORK 200\n"
+ "OkHttp-Sent-Millis: " + System.currentTimeMillis() + "\n"
+ "\n"
+ "TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA\n"
+ "1\n"
+ "MIIBpDCCAQ2gAwIBAgIBATANBgkqhkiG9w0BAQsFADAYMRYwFAYDVQQDEw1qd2lsc29uLmxvY2FsMB4XDTEzMDgy"
+ "OTA1MDE1OVoXDTEzMDgzMDA1MDE1OVowGDEWMBQGA1UEAxMNandpbHNvbi5sb2NhbDCBnzANBgkqhkiG9w0BAQEF"
+ "AAOBjQAwgYkCgYEAlFW+rGo/YikCcRghOyKkJanmVmJSce/p2/jH1QvNIFKizZdh8AKNwojt3ywRWaDULA/RlCUc"
+ "ltF3HGNsCyjQI/+Lf40x7JpxXF8oim1E6EtDoYtGWAseelawus3IQ13nmo6nWzfyCA55KhAWf4VipelEy8DjcuFK"
+ "v6L0xwXnI0ECAwEAATANBgkqhkiG9w0BAQsFAAOBgQAuluNyPo1HksU3+Mr/PyRQIQS4BI7pRXN8mcejXmqyscdP"
+ "7S6J21FBFeRR8/XNjVOp4HT9uSc2hrRtTEHEZCmpyoxixbnM706ikTmC7SN/GgM+SmcoJ1ipJcNcl8N0X6zym4dm"
+ "yFfXKHu2PkTo7QFdpOJFvP3lIigcSZXozfmEDg==\n"
+ "-1\n";
String entryBody = "abc";
String journalBody = ""
+ "libcore.io.DiskLruCache\n"
+ "1\n"
+ "201105\n"
+ "2\n"
+ "\n"
+ "CLEAN " + urlKey + " " + entryMetadata.length() + " " + entryBody.length() + "\n";
writeFile(cache.getDirectory(), urlKey + ".0", entryMetadata);
writeFile(cache.getDirectory(), urlKey + ".1", entryBody);
writeFile(cache.getDirectory(), "journal", journalBody);
cache = new HttpResponseCache(cache.getDirectory(), Integer.MAX_VALUE);
client.setResponseCache(cache);
HttpURLConnection connection = client.open(url);
assertEquals(entryBody, readAscii(connection));
assertEquals("3", connection.getHeaderField("Content-Length"));
assertEquals("foo", connection.getHeaderField("etag"));
}
private void writeFile(File directory, String file, String content) throws IOException {
OutputStream out = new FileOutputStream(new File(directory, file));
out.write(content.getBytes(Util.UTF_8));
out.close();
}
/**
* @param delta the offset from the current date to use. Negative
* values yield dates in the past; positive values yield dates in the