1
0
mirror of https://github.com/square/okhttp.git synced 2026-01-15 20:56:41 +03:00

Merge pull request #2659 from square/jwilson.0625.unexpected_exceptions

Fix OkHttpURLConnection to not swallow unexpected exceptions.
This commit is contained in:
Jesse Wilson
2016-06-25 23:22:09 -04:00
committed by GitHub
2 changed files with 75 additions and 6 deletions

View File

@@ -85,9 +85,9 @@ import static java.util.concurrent.TimeUnit.MILLISECONDS;
import static java.util.concurrent.TimeUnit.NANOSECONDS;
import static okhttp3.TestUtil.defaultClient;
import static okhttp3.internal.Util.UTF_8;
import static okhttp3.internal.huc.OkHttpURLConnection.SELECTED_PROTOCOL;
import static okhttp3.internal.http.StatusLine.HTTP_PERM_REDIRECT;
import static okhttp3.internal.http.StatusLine.HTTP_TEMP_REDIRECT;
import static okhttp3.internal.huc.OkHttpURLConnection.SELECTED_PROTOCOL;
import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_AFTER_REQUEST;
import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_AT_END;
import static okhttp3.mockwebserver.SocketPolicy.DISCONNECT_AT_START;
@@ -3503,6 +3503,48 @@ public final class URLConnectionTest {
}
}
/** Confirm that runtime exceptions thrown inside of OkHttp propagate to the caller. */
@Test public void unexpectedExceptionSync() throws Exception {
urlFactory.setClient(urlFactory.client().newBuilder()
.dns(new Dns() {
@Override public List<InetAddress> lookup(String hostname) {
throw new RuntimeException("boom!");
}
})
.build());
server.enqueue(new MockResponse());
HttpURLConnection connection = urlFactory.open(server.url("/").url());
try {
connection.getResponseCode(); // Use the synchronous implementation.
fail();
} catch (RuntimeException expected) {
assertEquals("boom!", expected.getMessage());
}
}
/** Confirm that runtime exceptions thrown inside of OkHttp propagate to the caller. */
@Test public void unexpectedExceptionAsync() throws Exception {
urlFactory.setClient(urlFactory.client().newBuilder()
.dns(new Dns() {
@Override public List<InetAddress> lookup(String hostname) {
throw new RuntimeException("boom!");
}
})
.build());
server.enqueue(new MockResponse());
HttpURLConnection connection = urlFactory.open(server.url("/").url());
try {
connection.connect(); // Force the async implementation.
fail();
} catch (RuntimeException expected) {
assertEquals("boom!", expected.getMessage());
}
}
private void testInstanceFollowsRedirects(String spec) throws Exception {
URL url = new URL(spec);
HttpURLConnection urlConnection = urlFactory.open(url);

View File

@@ -100,7 +100,7 @@ public final class OkHttpURLConnection extends HttpURLConnection implements Call
// These fields are guarded by lock.
private final Object lock = new Object();
private Response response;
private IOException callFailure;
private Throwable callFailure;
Response networkResponse;
boolean connectPending = true;
Proxy proxy;
@@ -129,7 +129,7 @@ public final class OkHttpURLConnection extends HttpURLConnection implements Call
lock.wait(); // Wait 'til the network interceptor is reached or the call fails.
}
if (callFailure != null) {
throw callFailure;
throw propagate(callFailure);
}
} catch (InterruptedException e) {
throw new InterruptedIOException();
@@ -382,6 +382,7 @@ public final class OkHttpURLConnection extends HttpURLConnection implements Call
OkHttpClient.Builder clientBuilder = client.newBuilder();
clientBuilder.interceptors().clear();
clientBuilder.interceptors().add(UnexpectedException.INTERCEPTOR);
clientBuilder.networkInterceptors().clear();
clientBuilder.networkInterceptors().add(networkInterceptor);
@@ -408,7 +409,7 @@ public final class OkHttpURLConnection extends HttpURLConnection implements Call
} else if (networkResponse != null) {
return networkResponse;
} else if (callFailure != null) {
throw callFailure;
throw propagate(callFailure);
}
Call call = buildCall();
@@ -437,7 +438,7 @@ public final class OkHttpURLConnection extends HttpURLConnection implements Call
}
synchronized (lock) {
if (callFailure != null) throw callFailure;
if (callFailure != null) throw propagate(callFailure);
if (response != null) return response;
}
@@ -573,7 +574,7 @@ public final class OkHttpURLConnection extends HttpURLConnection implements Call
@Override public void onFailure(Call call, IOException e) {
synchronized (lock) {
this.callFailure = e;
this.callFailure = (e instanceof UnexpectedException) ? e.getCause() : e;
lock.notifyAll();
}
}
@@ -587,6 +588,32 @@ public final class OkHttpURLConnection extends HttpURLConnection implements Call
}
}
static final class UnexpectedException extends IOException {
static final Interceptor INTERCEPTOR = new Interceptor() {
@Override public Response intercept(Chain chain) throws IOException {
try {
return chain.proceed(chain.request());
} catch (IOException e) {
throw e;
} catch (Error | RuntimeException e) {
throw new UnexpectedException(e);
}
}
};
public UnexpectedException(Throwable cause) {
super(cause);
}
}
/** Throws {@code throwable} as either an IOException, RuntimeException, or Error. */
private static IOException propagate(Throwable throwable) throws IOException {
if (throwable instanceof IOException) throw (IOException) throwable;
if (throwable instanceof Error) throw (Error) throwable;
if (throwable instanceof RuntimeException) throw (RuntimeException) throwable;
throw new AssertionError();
}
/**
* The HttpURLConnection gives the application control between establishing the connection and
* transmitting the request body. This interceptor stalls async calls right at this point. The