549 lines
20 KiB
Java
549 lines
20 KiB
Java
package okhttp3;
|
|
|
|
import java.io.Closeable;
|
|
import java.io.File;
|
|
import java.io.Flushable;
|
|
import java.io.IOException;
|
|
import java.security.cert.Certificate;
|
|
import java.security.cert.CertificateEncodingException;
|
|
import java.security.cert.CertificateException;
|
|
import java.security.cert.CertificateFactory;
|
|
import java.util.ArrayList;
|
|
import java.util.Collections;
|
|
import java.util.Iterator;
|
|
import java.util.List;
|
|
import java.util.NoSuchElementException;
|
|
import okhttp3.Headers;
|
|
import okhttp3.Request;
|
|
import okhttp3.Response;
|
|
import okhttp3.internal.Util;
|
|
import okhttp3.internal.cache.CacheRequest;
|
|
import okhttp3.internal.cache.CacheStrategy;
|
|
import okhttp3.internal.cache.DiskLruCache;
|
|
import okhttp3.internal.cache.InternalCache;
|
|
import okhttp3.internal.http.HttpHeaders;
|
|
import okhttp3.internal.http.HttpMethod;
|
|
import okhttp3.internal.http.StatusLine;
|
|
import okhttp3.internal.io.FileSystem;
|
|
import okhttp3.internal.platform.Platform;
|
|
import okio.Buffer;
|
|
import okio.BufferedSink;
|
|
import okio.BufferedSource;
|
|
import okio.ByteString;
|
|
import okio.ForwardingSink;
|
|
import okio.ForwardingSource;
|
|
import okio.Okio;
|
|
import okio.Sink;
|
|
import okio.Source;
|
|
|
|
/* loaded from: classes2.dex */
|
|
public final class Cache implements Closeable, Flushable {
|
|
private static final int ENTRY_BODY = 1;
|
|
private static final int ENTRY_COUNT = 2;
|
|
private static final int ENTRY_METADATA = 0;
|
|
private static final int VERSION = 201105;
|
|
final DiskLruCache cache;
|
|
private int hitCount;
|
|
final InternalCache internalCache;
|
|
private int networkCount;
|
|
private int requestCount;
|
|
int writeAbortCount;
|
|
int writeSuccessCount;
|
|
|
|
private final class CacheRequestImpl implements CacheRequest {
|
|
private Sink body;
|
|
private Sink cacheOut;
|
|
boolean done;
|
|
private final DiskLruCache.Editor editor;
|
|
|
|
CacheRequestImpl(final DiskLruCache.Editor editor) {
|
|
this.editor = editor;
|
|
this.cacheOut = editor.newSink(1);
|
|
this.body = new ForwardingSink(this.cacheOut) { // from class: okhttp3.Cache.CacheRequestImpl.1
|
|
@Override // okio.ForwardingSink, okio.Sink, java.io.Closeable, java.lang.AutoCloseable
|
|
public void close() throws IOException {
|
|
synchronized (Cache.this) {
|
|
if (CacheRequestImpl.this.done) {
|
|
return;
|
|
}
|
|
CacheRequestImpl.this.done = true;
|
|
Cache.this.writeSuccessCount++;
|
|
super.close();
|
|
editor.commit();
|
|
}
|
|
}
|
|
};
|
|
}
|
|
|
|
@Override // okhttp3.internal.cache.CacheRequest
|
|
public void abort() {
|
|
synchronized (Cache.this) {
|
|
if (this.done) {
|
|
return;
|
|
}
|
|
this.done = true;
|
|
Cache.this.writeAbortCount++;
|
|
Util.closeQuietly(this.cacheOut);
|
|
try {
|
|
this.editor.abort();
|
|
} catch (IOException unused) {
|
|
}
|
|
}
|
|
}
|
|
|
|
@Override // okhttp3.internal.cache.CacheRequest
|
|
public Sink body() {
|
|
return this.body;
|
|
}
|
|
}
|
|
|
|
private static class CacheResponseBody extends ResponseBody {
|
|
private final BufferedSource bodySource;
|
|
private final String contentLength;
|
|
private final String contentType;
|
|
final DiskLruCache.Snapshot snapshot;
|
|
|
|
CacheResponseBody(final DiskLruCache.Snapshot snapshot, String str, String str2) {
|
|
this.snapshot = snapshot;
|
|
this.contentType = str;
|
|
this.contentLength = str2;
|
|
this.bodySource = Okio.buffer(new ForwardingSource(snapshot.getSource(1)) { // from class: okhttp3.Cache.CacheResponseBody.1
|
|
@Override // okio.ForwardingSource, okio.Source, java.io.Closeable, java.lang.AutoCloseable
|
|
public void close() throws IOException {
|
|
snapshot.close();
|
|
super.close();
|
|
}
|
|
});
|
|
}
|
|
|
|
@Override // okhttp3.ResponseBody
|
|
public long contentLength() {
|
|
try {
|
|
if (this.contentLength != null) {
|
|
return Long.parseLong(this.contentLength);
|
|
}
|
|
return -1L;
|
|
} catch (NumberFormatException unused) {
|
|
return -1L;
|
|
}
|
|
}
|
|
|
|
@Override // okhttp3.ResponseBody
|
|
public MediaType contentType() {
|
|
String str = this.contentType;
|
|
if (str != null) {
|
|
return MediaType.parse(str);
|
|
}
|
|
return null;
|
|
}
|
|
|
|
@Override // okhttp3.ResponseBody
|
|
public BufferedSource source() {
|
|
return this.bodySource;
|
|
}
|
|
}
|
|
|
|
public Cache(File file, long j) {
|
|
this(file, j, FileSystem.SYSTEM);
|
|
}
|
|
|
|
private void abortQuietly(DiskLruCache.Editor editor) {
|
|
if (editor != null) {
|
|
try {
|
|
editor.abort();
|
|
} catch (IOException unused) {
|
|
}
|
|
}
|
|
}
|
|
|
|
public static String key(HttpUrl httpUrl) {
|
|
return ByteString.encodeUtf8(httpUrl.toString()).md5().hex();
|
|
}
|
|
|
|
static int readInt(BufferedSource bufferedSource) throws IOException {
|
|
try {
|
|
long readDecimalLong = bufferedSource.readDecimalLong();
|
|
String readUtf8LineStrict = bufferedSource.readUtf8LineStrict();
|
|
if (readDecimalLong >= 0 && readDecimalLong <= 2147483647L && readUtf8LineStrict.isEmpty()) {
|
|
return (int) readDecimalLong;
|
|
}
|
|
throw new IOException("expected an int but was \"" + readDecimalLong + readUtf8LineStrict + "\"");
|
|
} catch (NumberFormatException e) {
|
|
throw new IOException(e.getMessage());
|
|
}
|
|
}
|
|
|
|
@Override // java.io.Closeable, java.lang.AutoCloseable
|
|
public void close() throws IOException {
|
|
this.cache.close();
|
|
}
|
|
|
|
public void delete() throws IOException {
|
|
this.cache.delete();
|
|
}
|
|
|
|
public File directory() {
|
|
return this.cache.getDirectory();
|
|
}
|
|
|
|
public void evictAll() throws IOException {
|
|
this.cache.evictAll();
|
|
}
|
|
|
|
@Override // java.io.Flushable
|
|
public void flush() throws IOException {
|
|
this.cache.flush();
|
|
}
|
|
|
|
Response get(Request request) {
|
|
try {
|
|
DiskLruCache.Snapshot snapshot = this.cache.get(key(request.url()));
|
|
if (snapshot == null) {
|
|
return null;
|
|
}
|
|
try {
|
|
Entry entry = new Entry(snapshot.getSource(0));
|
|
Response response = entry.response(snapshot);
|
|
if (entry.matches(request, response)) {
|
|
return response;
|
|
}
|
|
Util.closeQuietly(response.body());
|
|
return null;
|
|
} catch (IOException unused) {
|
|
Util.closeQuietly(snapshot);
|
|
return null;
|
|
}
|
|
} catch (IOException unused2) {
|
|
}
|
|
}
|
|
|
|
public synchronized int hitCount() {
|
|
return this.hitCount;
|
|
}
|
|
|
|
public void initialize() throws IOException {
|
|
this.cache.initialize();
|
|
}
|
|
|
|
public boolean isClosed() {
|
|
return this.cache.isClosed();
|
|
}
|
|
|
|
public long maxSize() {
|
|
return this.cache.getMaxSize();
|
|
}
|
|
|
|
public synchronized int networkCount() {
|
|
return this.networkCount;
|
|
}
|
|
|
|
CacheRequest put(Response response) {
|
|
DiskLruCache.Editor editor;
|
|
String method = response.request().method();
|
|
if (HttpMethod.invalidatesCache(response.request().method())) {
|
|
try {
|
|
remove(response.request());
|
|
} catch (IOException unused) {
|
|
}
|
|
return null;
|
|
}
|
|
if (!method.equals("GET") || HttpHeaders.hasVaryAll(response)) {
|
|
return null;
|
|
}
|
|
Entry entry = new Entry(response);
|
|
try {
|
|
editor = this.cache.edit(key(response.request().url()));
|
|
if (editor == null) {
|
|
return null;
|
|
}
|
|
try {
|
|
entry.writeTo(editor);
|
|
return new CacheRequestImpl(editor);
|
|
} catch (IOException unused2) {
|
|
abortQuietly(editor);
|
|
return null;
|
|
}
|
|
} catch (IOException unused3) {
|
|
editor = null;
|
|
}
|
|
}
|
|
|
|
void remove(Request request) throws IOException {
|
|
this.cache.remove(key(request.url()));
|
|
}
|
|
|
|
public synchronized int requestCount() {
|
|
return this.requestCount;
|
|
}
|
|
|
|
public long size() throws IOException {
|
|
return this.cache.size();
|
|
}
|
|
|
|
synchronized void trackConditionalCacheHit() {
|
|
this.hitCount++;
|
|
}
|
|
|
|
synchronized void trackResponse(CacheStrategy cacheStrategy) {
|
|
this.requestCount++;
|
|
if (cacheStrategy.networkRequest != null) {
|
|
this.networkCount++;
|
|
} else if (cacheStrategy.cacheResponse != null) {
|
|
this.hitCount++;
|
|
}
|
|
}
|
|
|
|
void update(Response response, Response response2) {
|
|
DiskLruCache.Editor editor;
|
|
Entry entry = new Entry(response2);
|
|
try {
|
|
editor = ((CacheResponseBody) response.body()).snapshot.edit();
|
|
if (editor != null) {
|
|
try {
|
|
entry.writeTo(editor);
|
|
editor.commit();
|
|
} catch (IOException unused) {
|
|
abortQuietly(editor);
|
|
}
|
|
}
|
|
} catch (IOException unused2) {
|
|
editor = null;
|
|
}
|
|
}
|
|
|
|
public Iterator<String> urls() throws IOException {
|
|
return new Iterator<String>() { // from class: okhttp3.Cache.2
|
|
boolean canRemove;
|
|
final Iterator<DiskLruCache.Snapshot> delegate;
|
|
String nextUrl;
|
|
|
|
{
|
|
this.delegate = Cache.this.cache.snapshots();
|
|
}
|
|
|
|
@Override // java.util.Iterator
|
|
public boolean hasNext() {
|
|
if (this.nextUrl != null) {
|
|
return true;
|
|
}
|
|
this.canRemove = false;
|
|
while (this.delegate.hasNext()) {
|
|
DiskLruCache.Snapshot next = this.delegate.next();
|
|
try {
|
|
this.nextUrl = Okio.buffer(next.getSource(0)).readUtf8LineStrict();
|
|
return true;
|
|
} catch (IOException unused) {
|
|
} finally {
|
|
next.close();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
@Override // java.util.Iterator
|
|
public void remove() {
|
|
if (!this.canRemove) {
|
|
throw new IllegalStateException("remove() before next()");
|
|
}
|
|
this.delegate.remove();
|
|
}
|
|
|
|
@Override // java.util.Iterator
|
|
public String next() {
|
|
if (!hasNext()) {
|
|
throw new NoSuchElementException();
|
|
}
|
|
String str = this.nextUrl;
|
|
this.nextUrl = null;
|
|
this.canRemove = true;
|
|
return str;
|
|
}
|
|
};
|
|
}
|
|
|
|
public synchronized int writeAbortCount() {
|
|
return this.writeAbortCount;
|
|
}
|
|
|
|
public synchronized int writeSuccessCount() {
|
|
return this.writeSuccessCount;
|
|
}
|
|
|
|
Cache(File file, long j, FileSystem fileSystem) {
|
|
this.internalCache = new InternalCache() { // from class: okhttp3.Cache.1
|
|
@Override // okhttp3.internal.cache.InternalCache
|
|
public Response get(Request request) throws IOException {
|
|
return Cache.this.get(request);
|
|
}
|
|
|
|
@Override // okhttp3.internal.cache.InternalCache
|
|
public CacheRequest put(Response response) throws IOException {
|
|
return Cache.this.put(response);
|
|
}
|
|
|
|
@Override // okhttp3.internal.cache.InternalCache
|
|
public void remove(Request request) throws IOException {
|
|
Cache.this.remove(request);
|
|
}
|
|
|
|
@Override // okhttp3.internal.cache.InternalCache
|
|
public void trackConditionalCacheHit() {
|
|
Cache.this.trackConditionalCacheHit();
|
|
}
|
|
|
|
@Override // okhttp3.internal.cache.InternalCache
|
|
public void trackResponse(CacheStrategy cacheStrategy) {
|
|
Cache.this.trackResponse(cacheStrategy);
|
|
}
|
|
|
|
@Override // okhttp3.internal.cache.InternalCache
|
|
public void update(Response response, Response response2) {
|
|
Cache.this.update(response, response2);
|
|
}
|
|
};
|
|
this.cache = DiskLruCache.create(fileSystem, file, VERSION, 2, j);
|
|
}
|
|
|
|
private static final class Entry {
|
|
private final int code;
|
|
private final Handshake handshake;
|
|
private final String message;
|
|
private final Protocol protocol;
|
|
private final long receivedResponseMillis;
|
|
private final String requestMethod;
|
|
private final Headers responseHeaders;
|
|
private final long sentRequestMillis;
|
|
private final String url;
|
|
private final Headers varyHeaders;
|
|
private static final String SENT_MILLIS = Platform.get().getPrefix() + "-Sent-Millis";
|
|
private static final String RECEIVED_MILLIS = Platform.get().getPrefix() + "-Received-Millis";
|
|
|
|
Entry(Source source) throws IOException {
|
|
try {
|
|
BufferedSource buffer = Okio.buffer(source);
|
|
this.url = buffer.readUtf8LineStrict();
|
|
this.requestMethod = buffer.readUtf8LineStrict();
|
|
Headers.Builder builder = new Headers.Builder();
|
|
int readInt = Cache.readInt(buffer);
|
|
for (int i = 0; i < readInt; i++) {
|
|
builder.addLenient(buffer.readUtf8LineStrict());
|
|
}
|
|
this.varyHeaders = builder.build();
|
|
StatusLine parse = StatusLine.parse(buffer.readUtf8LineStrict());
|
|
this.protocol = parse.protocol;
|
|
this.code = parse.code;
|
|
this.message = parse.message;
|
|
Headers.Builder builder2 = new Headers.Builder();
|
|
int readInt2 = Cache.readInt(buffer);
|
|
for (int i2 = 0; i2 < readInt2; i2++) {
|
|
builder2.addLenient(buffer.readUtf8LineStrict());
|
|
}
|
|
String str = builder2.get(SENT_MILLIS);
|
|
String str2 = builder2.get(RECEIVED_MILLIS);
|
|
builder2.removeAll(SENT_MILLIS);
|
|
builder2.removeAll(RECEIVED_MILLIS);
|
|
this.sentRequestMillis = str != null ? Long.parseLong(str) : 0L;
|
|
this.receivedResponseMillis = str2 != null ? Long.parseLong(str2) : 0L;
|
|
this.responseHeaders = builder2.build();
|
|
if (isHttps()) {
|
|
String readUtf8LineStrict = buffer.readUtf8LineStrict();
|
|
if (readUtf8LineStrict.length() > 0) {
|
|
throw new IOException("expected \"\" but was \"" + readUtf8LineStrict + "\"");
|
|
}
|
|
this.handshake = Handshake.get(!buffer.exhausted() ? TlsVersion.forJavaName(buffer.readUtf8LineStrict()) : TlsVersion.SSL_3_0, CipherSuite.forJavaName(buffer.readUtf8LineStrict()), readCertificateList(buffer), readCertificateList(buffer));
|
|
} else {
|
|
this.handshake = null;
|
|
}
|
|
} finally {
|
|
source.close();
|
|
}
|
|
}
|
|
|
|
private boolean isHttps() {
|
|
return this.url.startsWith("https://");
|
|
}
|
|
|
|
private List<Certificate> readCertificateList(BufferedSource bufferedSource) throws IOException {
|
|
int readInt = Cache.readInt(bufferedSource);
|
|
if (readInt == -1) {
|
|
return Collections.emptyList();
|
|
}
|
|
try {
|
|
CertificateFactory certificateFactory = CertificateFactory.getInstance("X.509");
|
|
ArrayList arrayList = new ArrayList(readInt);
|
|
for (int i = 0; i < readInt; i++) {
|
|
String readUtf8LineStrict = bufferedSource.readUtf8LineStrict();
|
|
Buffer buffer = new Buffer();
|
|
buffer.write(ByteString.decodeBase64(readUtf8LineStrict));
|
|
arrayList.add(certificateFactory.generateCertificate(buffer.inputStream()));
|
|
}
|
|
return arrayList;
|
|
} catch (CertificateException e) {
|
|
throw new IOException(e.getMessage());
|
|
}
|
|
}
|
|
|
|
private void writeCertList(BufferedSink bufferedSink, List<Certificate> list) throws IOException {
|
|
try {
|
|
bufferedSink.writeDecimalLong(list.size()).writeByte(10);
|
|
int size = list.size();
|
|
for (int i = 0; i < size; i++) {
|
|
bufferedSink.writeUtf8(ByteString.of(list.get(i).getEncoded()).base64()).writeByte(10);
|
|
}
|
|
} catch (CertificateEncodingException e) {
|
|
throw new IOException(e.getMessage());
|
|
}
|
|
}
|
|
|
|
public boolean matches(Request request, Response response) {
|
|
return this.url.equals(request.url().toString()) && this.requestMethod.equals(request.method()) && HttpHeaders.varyMatches(response, this.varyHeaders, request);
|
|
}
|
|
|
|
public Response response(DiskLruCache.Snapshot snapshot) {
|
|
String str = this.responseHeaders.get("Content-Type");
|
|
String str2 = this.responseHeaders.get("Content-Length");
|
|
return new Response.Builder().request(new Request.Builder().url(this.url).method(this.requestMethod, null).headers(this.varyHeaders).build()).protocol(this.protocol).code(this.code).message(this.message).headers(this.responseHeaders).body(new CacheResponseBody(snapshot, str, str2)).handshake(this.handshake).sentRequestAtMillis(this.sentRequestMillis).receivedResponseAtMillis(this.receivedResponseMillis).build();
|
|
}
|
|
|
|
public void writeTo(DiskLruCache.Editor editor) throws IOException {
|
|
BufferedSink buffer = Okio.buffer(editor.newSink(0));
|
|
buffer.writeUtf8(this.url).writeByte(10);
|
|
buffer.writeUtf8(this.requestMethod).writeByte(10);
|
|
buffer.writeDecimalLong(this.varyHeaders.size()).writeByte(10);
|
|
int size = this.varyHeaders.size();
|
|
for (int i = 0; i < size; i++) {
|
|
buffer.writeUtf8(this.varyHeaders.name(i)).writeUtf8(": ").writeUtf8(this.varyHeaders.value(i)).writeByte(10);
|
|
}
|
|
buffer.writeUtf8(new StatusLine(this.protocol, this.code, this.message).toString()).writeByte(10);
|
|
buffer.writeDecimalLong(this.responseHeaders.size() + 2).writeByte(10);
|
|
int size2 = this.responseHeaders.size();
|
|
for (int i2 = 0; i2 < size2; i2++) {
|
|
buffer.writeUtf8(this.responseHeaders.name(i2)).writeUtf8(": ").writeUtf8(this.responseHeaders.value(i2)).writeByte(10);
|
|
}
|
|
buffer.writeUtf8(SENT_MILLIS).writeUtf8(": ").writeDecimalLong(this.sentRequestMillis).writeByte(10);
|
|
buffer.writeUtf8(RECEIVED_MILLIS).writeUtf8(": ").writeDecimalLong(this.receivedResponseMillis).writeByte(10);
|
|
if (isHttps()) {
|
|
buffer.writeByte(10);
|
|
buffer.writeUtf8(this.handshake.cipherSuite().javaName()).writeByte(10);
|
|
writeCertList(buffer, this.handshake.peerCertificates());
|
|
writeCertList(buffer, this.handshake.localCertificates());
|
|
buffer.writeUtf8(this.handshake.tlsVersion().javaName()).writeByte(10);
|
|
}
|
|
buffer.close();
|
|
}
|
|
|
|
Entry(Response response) {
|
|
this.url = response.request().url().toString();
|
|
this.varyHeaders = HttpHeaders.varyHeaders(response);
|
|
this.requestMethod = response.request().method();
|
|
this.protocol = response.protocol();
|
|
this.code = response.code();
|
|
this.message = response.message();
|
|
this.responseHeaders = response.headers();
|
|
this.handshake = response.handshake();
|
|
this.sentRequestMillis = response.sentRequestAtMillis();
|
|
this.receivedResponseMillis = response.receivedResponseAtMillis();
|
|
}
|
|
}
|
|
}
|