1
0
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:
Jake Wharton
2015-10-07 23:41:24 -04:00
parent 0c1851710e
commit a9015ebe7d
6 changed files with 94 additions and 24 deletions

View File

@@ -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 {

View File

@@ -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());
} }
/** /**

View File

@@ -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();

View File

@@ -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();
} }

View File

@@ -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);
} }

View File

@@ -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));
} }
/** /**