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:
@@ -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);
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user