mirror of
https://github.com/square/okhttp.git
synced 2025-11-24 18:41:06 +03:00
Add a strict mode of sorts to InMemoryFileSystem to detect handle leaks.
This commit is contained in:
@@ -18,32 +18,95 @@ package com.squareup.okhttp.internal.io;
|
|||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.FileNotFoundException;
|
import java.io.FileNotFoundException;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.IdentityHashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import okio.Buffer;
|
import okio.Buffer;
|
||||||
|
import okio.ForwardingSink;
|
||||||
|
import okio.ForwardingSource;
|
||||||
import okio.Sink;
|
import okio.Sink;
|
||||||
import okio.Source;
|
import okio.Source;
|
||||||
|
import org.junit.rules.TestRule;
|
||||||
|
import org.junit.runner.Description;
|
||||||
|
import org.junit.runners.model.Statement;
|
||||||
|
|
||||||
/** A simple file system where all files are held in memory. Not safe for concurrent use. */
|
/** A simple file system where all files are held in memory. Not safe for concurrent use. */
|
||||||
public final class InMemoryFileSystem implements FileSystem {
|
public final class InMemoryFileSystem implements FileSystem, TestRule {
|
||||||
private final Map<File, Buffer> files = new LinkedHashMap<>();
|
private final Map<File, Buffer> files = new LinkedHashMap<>();
|
||||||
|
private final Map<Source, File> openSources = new IdentityHashMap<>();
|
||||||
|
private final Map<Sink, File> openSinks = new IdentityHashMap<>();
|
||||||
|
|
||||||
|
@Override public Statement apply(final Statement base, Description description) {
|
||||||
|
return new Statement() {
|
||||||
|
@Override public void evaluate() throws Throwable {
|
||||||
|
base.evaluate();
|
||||||
|
ensureResourcesClosed();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ensureResourcesClosed() {
|
||||||
|
List<String> openResources = new ArrayList<>();
|
||||||
|
for (File file : openSources.values()) {
|
||||||
|
openResources.add("Source for " + file);
|
||||||
|
}
|
||||||
|
for (File file : openSinks.values()) {
|
||||||
|
openResources.add("Sink for " + file);
|
||||||
|
}
|
||||||
|
if (!openResources.isEmpty()) {
|
||||||
|
StringBuilder builder = new StringBuilder("Resources acquired but not closed:");
|
||||||
|
for (String resource : openResources) {
|
||||||
|
builder.append("\n * ").append(resource);
|
||||||
|
}
|
||||||
|
throw new IllegalStateException(builder.toString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override public Source source(File file) throws FileNotFoundException {
|
@Override public Source source(File file) throws FileNotFoundException {
|
||||||
Buffer result = files.get(file);
|
Buffer result = files.get(file);
|
||||||
if (result == null) throw new FileNotFoundException();
|
if (result == null) throw new FileNotFoundException();
|
||||||
return result.clone();
|
|
||||||
|
final Source source = result.clone();
|
||||||
|
openSources.put(source, file);
|
||||||
|
|
||||||
|
return new ForwardingSource(source) {
|
||||||
|
@Override public void close() throws IOException {
|
||||||
|
openSources.remove(source);
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Sink sink(File file) throws FileNotFoundException {
|
@Override public Sink sink(File file) throws FileNotFoundException {
|
||||||
Buffer result = new Buffer();
|
return sink(file, false);
|
||||||
files.put(file, result);
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Sink appendingSink(File file) throws FileNotFoundException {
|
@Override public Sink appendingSink(File file) throws FileNotFoundException {
|
||||||
Buffer result = files.get(file);
|
return sink(file, true);
|
||||||
return result != null ? result : sink(file);
|
}
|
||||||
|
|
||||||
|
private Sink sink(File file, boolean appending) {
|
||||||
|
Buffer result = null;
|
||||||
|
if (appending) {
|
||||||
|
result = files.get(file);
|
||||||
|
}
|
||||||
|
if (result == null) {
|
||||||
|
result = new Buffer();
|
||||||
|
}
|
||||||
|
files.put(file, result);
|
||||||
|
|
||||||
|
final Sink sink = result;
|
||||||
|
openSinks.put(sink, file);
|
||||||
|
|
||||||
|
return new ForwardingSink(sink) {
|
||||||
|
@Override public void close() throws IOException {
|
||||||
|
openSinks.remove(sink);
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void delete(File file) throws IOException {
|
@Override public void delete(File file) throws IOException {
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package com.squareup.okhttp;
|
|||||||
import com.squareup.okhttp.internal.Internal;
|
import com.squareup.okhttp.internal.Internal;
|
||||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||||
import com.squareup.okhttp.internal.Util;
|
import com.squareup.okhttp.internal.Util;
|
||||||
import com.squareup.okhttp.internal.io.FileSystem;
|
|
||||||
import com.squareup.okhttp.internal.io.InMemoryFileSystem;
|
import com.squareup.okhttp.internal.io.InMemoryFileSystem;
|
||||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||||
@@ -76,9 +75,9 @@ public final class CacheTest {
|
|||||||
|
|
||||||
@Rule public MockWebServer server = new MockWebServer();
|
@Rule public MockWebServer server = new MockWebServer();
|
||||||
@Rule public MockWebServer server2 = new MockWebServer();
|
@Rule public MockWebServer server2 = new MockWebServer();
|
||||||
|
@Rule public InMemoryFileSystem fileSystem = new InMemoryFileSystem();
|
||||||
|
|
||||||
private final SSLContext sslContext = SslContextBuilder.localhost();
|
private final SSLContext sslContext = SslContextBuilder.localhost();
|
||||||
private final FileSystem fileSystem = new InMemoryFileSystem();
|
|
||||||
private final OkHttpClient client = new OkHttpClient();
|
private final OkHttpClient client = new OkHttpClient();
|
||||||
private Cache cache;
|
private Cache cache;
|
||||||
private final CookieManager cookieManager = new CookieManager();
|
private final CookieManager cookieManager = new CookieManager();
|
||||||
@@ -93,6 +92,7 @@ public final class CacheTest {
|
|||||||
@After public void tearDown() throws Exception {
|
@After public void tearDown() throws Exception {
|
||||||
ResponseCache.setDefault(null);
|
ResponseCache.setDefault(null);
|
||||||
CookieHandler.setDefault(null);
|
CookieHandler.setDefault(null);
|
||||||
|
cache.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -266,7 +266,7 @@ public final class CacheTest {
|
|||||||
Principal localPrincipal = response1.handshake().localPrincipal();
|
Principal localPrincipal = response1.handshake().localPrincipal();
|
||||||
|
|
||||||
Response response2 = client.newCall(request).execute(); // Cached!
|
Response response2 = client.newCall(request).execute(); // Cached!
|
||||||
assertEquals("ABC", response2.body().source().readUtf8());
|
assertEquals("ABC", response2.body().string());
|
||||||
|
|
||||||
assertEquals(2, cache.getRequestCount());
|
assertEquals(2, cache.getRequestCount());
|
||||||
assertEquals(1, cache.getNetworkCount());
|
assertEquals(1, cache.getNetworkCount());
|
||||||
@@ -1873,6 +1873,7 @@ public final class CacheTest {
|
|||||||
|
|
||||||
Response response = get(server.url("/"));
|
Response response = get(server.url("/"));
|
||||||
assertEquals("A", response.header(""));
|
assertEquals("A", response.header(""));
|
||||||
|
assertEquals("body", response.body().string());
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -22,7 +22,6 @@ import com.squareup.okhttp.internal.SingleInetAddressNetwork;
|
|||||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||||
import com.squareup.okhttp.internal.Util;
|
import com.squareup.okhttp.internal.Util;
|
||||||
import com.squareup.okhttp.internal.Version;
|
import com.squareup.okhttp.internal.Version;
|
||||||
import com.squareup.okhttp.internal.io.FileSystem;
|
|
||||||
import com.squareup.okhttp.internal.io.InMemoryFileSystem;
|
import com.squareup.okhttp.internal.io.InMemoryFileSystem;
|
||||||
import com.squareup.okhttp.mockwebserver.Dispatcher;
|
import com.squareup.okhttp.mockwebserver.Dispatcher;
|
||||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||||
@@ -88,9 +87,9 @@ public final class CallTest {
|
|||||||
@Rule public final TestRule timeout = new Timeout(30_000);
|
@Rule public final TestRule timeout = new Timeout(30_000);
|
||||||
@Rule public final MockWebServer server = new MockWebServer();
|
@Rule public final MockWebServer server = new MockWebServer();
|
||||||
@Rule public final MockWebServer server2 = new MockWebServer();
|
@Rule public final MockWebServer server2 = new MockWebServer();
|
||||||
|
@Rule public final InMemoryFileSystem fileSystem = new InMemoryFileSystem();
|
||||||
|
|
||||||
private SSLContext sslContext = SslContextBuilder.localhost();
|
private SSLContext sslContext = SslContextBuilder.localhost();
|
||||||
private FileSystem fileSystem = new InMemoryFileSystem();
|
|
||||||
private OkHttpClient client = new OkHttpClient();
|
private OkHttpClient client = new OkHttpClient();
|
||||||
private RecordingCallback callback = new RecordingCallback();
|
private RecordingCallback callback = new RecordingCallback();
|
||||||
private TestLogHandler logHandler = new TestLogHandler();
|
private TestLogHandler logHandler = new TestLogHandler();
|
||||||
|
|||||||
@@ -20,7 +20,6 @@ import java.util.ArrayList;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
import okio.Buffer;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Records received HTTP responses so they can be later retrieved by tests.
|
* Records received HTTP responses so they can be later retrieved by tests.
|
||||||
@@ -36,11 +35,8 @@ public class RecordingCallback implements Callback {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override public synchronized void onResponse(Response response) throws IOException {
|
@Override public synchronized void onResponse(Response response) throws IOException {
|
||||||
Buffer buffer = new Buffer();
|
String body = response.body().string();
|
||||||
ResponseBody body = response.body();
|
responses.add(new RecordedResponse(response.request(), response, null, body, null));
|
||||||
body.source().readAll(buffer);
|
|
||||||
|
|
||||||
responses.add(new RecordedResponse(response.request(), response, null, buffer.readUtf8(), null));
|
|
||||||
notifyAll();
|
notifyAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
package com.squareup.okhttp;
|
package com.squareup.okhttp;
|
||||||
|
|
||||||
import com.squareup.okhttp.internal.Platform;
|
import com.squareup.okhttp.internal.Platform;
|
||||||
import com.squareup.okhttp.internal.io.FileSystem;
|
|
||||||
import com.squareup.okhttp.internal.io.InMemoryFileSystem;
|
import com.squareup.okhttp.internal.io.InMemoryFileSystem;
|
||||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||||
@@ -14,6 +13,8 @@ import java.util.Date;
|
|||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.TimeZone;
|
import java.util.TimeZone;
|
||||||
import java.util.concurrent.TimeUnit;
|
import java.util.concurrent.TimeUnit;
|
||||||
|
import okio.BufferedSource;
|
||||||
|
import org.junit.After;
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Rule;
|
import org.junit.Rule;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
@@ -26,16 +27,22 @@ import static org.junit.Assert.fail;
|
|||||||
|
|
||||||
public class OkUrlFactoryTest {
|
public class OkUrlFactoryTest {
|
||||||
@Rule public MockWebServer server = new MockWebServer();
|
@Rule public MockWebServer server = new MockWebServer();
|
||||||
|
@Rule public InMemoryFileSystem fileSystem = new InMemoryFileSystem();
|
||||||
|
|
||||||
private FileSystem fileSystem = new InMemoryFileSystem();
|
|
||||||
private OkUrlFactory factory;
|
private OkUrlFactory factory;
|
||||||
|
private Cache cache;
|
||||||
|
|
||||||
@Before public void setUp() throws IOException {
|
@Before public void setUp() throws IOException {
|
||||||
OkHttpClient client = new OkHttpClient();
|
OkHttpClient client = new OkHttpClient();
|
||||||
client.setCache(new Cache(new File("/cache/"), 10 * 1024 * 1024, fileSystem));
|
cache = new Cache(new File("/cache/"), 10 * 1024 * 1024, fileSystem);
|
||||||
|
client.setCache(cache);
|
||||||
factory = new OkUrlFactory(client);
|
factory = new OkUrlFactory(client);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@After public void tearDown() throws IOException {
|
||||||
|
cache.delete();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response code 407 should only come from proxy servers. Android's client
|
* Response code 407 should only come from proxy servers. Android's client
|
||||||
* throws if it is sent by an origin server.
|
* throws if it is sent by an origin server.
|
||||||
@@ -64,6 +71,7 @@ public class OkUrlFactoryTest {
|
|||||||
|
|
||||||
HttpURLConnection connection = factory.open(server.getUrl("/"));
|
HttpURLConnection connection = factory.open(server.getUrl("/"));
|
||||||
assertResponseHeader(connection, "NETWORK 404");
|
assertResponseHeader(connection, "NETWORK 404");
|
||||||
|
connection.getErrorStream().close();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void conditionalCacheHitResponseSourceHeaders() throws Exception {
|
@Test public void conditionalCacheHitResponseSourceHeaders() throws Exception {
|
||||||
@@ -140,7 +148,9 @@ public class OkUrlFactoryTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void assertResponseBody(HttpURLConnection connection, String expected) throws Exception {
|
private void assertResponseBody(HttpURLConnection connection, String expected) throws Exception {
|
||||||
String actual = buffer(source(connection.getInputStream())).readString(US_ASCII);
|
BufferedSource source = buffer(source(connection.getInputStream()));
|
||||||
|
String actual = source.readString(US_ASCII);
|
||||||
|
source.close();
|
||||||
assertEquals(expected, actual);
|
assertEquals(expected, actual);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -19,7 +19,6 @@ package com.squareup.okhttp;
|
|||||||
import com.squareup.okhttp.internal.Internal;
|
import com.squareup.okhttp.internal.Internal;
|
||||||
import com.squareup.okhttp.internal.SslContextBuilder;
|
import com.squareup.okhttp.internal.SslContextBuilder;
|
||||||
import com.squareup.okhttp.internal.Util;
|
import com.squareup.okhttp.internal.Util;
|
||||||
import com.squareup.okhttp.internal.io.FileSystem;
|
|
||||||
import com.squareup.okhttp.internal.io.InMemoryFileSystem;
|
import com.squareup.okhttp.internal.io.InMemoryFileSystem;
|
||||||
import com.squareup.okhttp.mockwebserver.MockResponse;
|
import com.squareup.okhttp.mockwebserver.MockResponse;
|
||||||
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
import com.squareup.okhttp.mockwebserver.MockWebServer;
|
||||||
@@ -81,9 +80,9 @@ public final class UrlConnectionCacheTest {
|
|||||||
|
|
||||||
@Rule public MockWebServer server = new MockWebServer();
|
@Rule public MockWebServer server = new MockWebServer();
|
||||||
@Rule public MockWebServer server2 = new MockWebServer();
|
@Rule public MockWebServer server2 = new MockWebServer();
|
||||||
|
@Rule public InMemoryFileSystem fileSystem = new InMemoryFileSystem();
|
||||||
|
|
||||||
private final SSLContext sslContext = SslContextBuilder.localhost();
|
private final SSLContext sslContext = SslContextBuilder.localhost();
|
||||||
private final FileSystem fileSystem = new InMemoryFileSystem();
|
|
||||||
private final OkUrlFactory client = new OkUrlFactory(new OkHttpClient());
|
private final OkUrlFactory client = new OkUrlFactory(new OkHttpClient());
|
||||||
private Cache cache;
|
private Cache cache;
|
||||||
private final CookieManager cookieManager = new CookieManager();
|
private final CookieManager cookieManager = new CookieManager();
|
||||||
@@ -98,6 +97,7 @@ public final class UrlConnectionCacheTest {
|
|||||||
@After public void tearDown() throws Exception {
|
@After public void tearDown() throws Exception {
|
||||||
ResponseCache.setDefault(null);
|
ResponseCache.setDefault(null);
|
||||||
CookieHandler.setDefault(null);
|
CookieHandler.setDefault(null);
|
||||||
|
cache.delete();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void responseCacheAccessWithOkHttpMember() throws IOException {
|
@Test public void responseCacheAccessWithOkHttpMember() throws IOException {
|
||||||
@@ -1586,6 +1586,7 @@ public final class UrlConnectionCacheTest {
|
|||||||
|
|
||||||
HttpURLConnection connection = client.open(server.getUrl("/"));
|
HttpURLConnection connection = client.open(server.getUrl("/"));
|
||||||
assertEquals("A", connection.getHeaderField(""));
|
assertEquals("A", connection.getHeaderField(""));
|
||||||
|
assertEquals("body", readAscii(connection));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
Reference in New Issue
Block a user