mirror of
https://github.com/square/okhttp.git
synced 2025-08-01 16:06:56 +03:00
Introduce EventListener.requestFailed, responseFailed events
These replace requestBodyEnd() / responseBodyEnd() in some failure scenarios. They may also be issued in cases where no event was published previously.
This commit is contained in:
@ -124,6 +124,11 @@ public final class LoggingEventListener extends EventListener {
|
|||||||
logWithTime("requestBodyEnd: byteCount=" + byteCount);
|
logWithTime("requestBodyEnd: byteCount=" + byteCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void requestFailed(Call call, IOException ioe) {
|
||||||
|
logWithTime("requestFailed: " + ioe);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void responseHeadersStart(Call call) {
|
public void responseHeadersStart(Call call) {
|
||||||
logWithTime("responseHeadersStart");
|
logWithTime("responseHeadersStart");
|
||||||
@ -144,6 +149,11 @@ public final class LoggingEventListener extends EventListener {
|
|||||||
logWithTime("responseBodyEnd: byteCount=" + byteCount);
|
logWithTime("responseBodyEnd: byteCount=" + byteCount);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void responseFailed(Call call, IOException ioe) {
|
||||||
|
logWithTime("responseFailed: " + ioe);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void callEnd(Call call) {
|
public void callEnd(Call call) {
|
||||||
logWithTime("callEnd");
|
logWithTime("callEnd");
|
||||||
|
@ -56,6 +56,10 @@ import javax.net.ssl.SSLPeerUnverifiedException;
|
|||||||
import javax.net.ssl.SSLProtocolException;
|
import javax.net.ssl.SSLProtocolException;
|
||||||
import javax.net.ssl.SSLSocket;
|
import javax.net.ssl.SSLSocket;
|
||||||
import javax.net.ssl.SSLSocketFactory;
|
import javax.net.ssl.SSLSocketFactory;
|
||||||
|
import okhttp3.RecordingEventListener.CallEnd;
|
||||||
|
import okhttp3.RecordingEventListener.ConnectionAcquired;
|
||||||
|
import okhttp3.RecordingEventListener.ConnectionReleased;
|
||||||
|
import okhttp3.RecordingEventListener.ResponseFailed;
|
||||||
import okhttp3.internal.DoubleInetAddressDns;
|
import okhttp3.internal.DoubleInetAddressDns;
|
||||||
import okhttp3.internal.RecordingOkAuthenticator;
|
import okhttp3.internal.RecordingOkAuthenticator;
|
||||||
import okhttp3.internal.Util;
|
import okhttp3.internal.Util;
|
||||||
@ -104,8 +108,11 @@ public final class CallTest {
|
|||||||
@Rule public final MockWebServer server2 = new MockWebServer();
|
@Rule public final MockWebServer server2 = new MockWebServer();
|
||||||
@Rule public final InMemoryFileSystem fileSystem = new InMemoryFileSystem();
|
@Rule public final InMemoryFileSystem fileSystem = new InMemoryFileSystem();
|
||||||
|
|
||||||
|
private final RecordingEventListener listener = new RecordingEventListener();
|
||||||
private HandshakeCertificates handshakeCertificates = localhost();
|
private HandshakeCertificates handshakeCertificates = localhost();
|
||||||
private OkHttpClient client = defaultClient();
|
private OkHttpClient client = defaultClient().newBuilder()
|
||||||
|
.eventListener(listener)
|
||||||
|
.build();
|
||||||
private RecordingCallback callback = new RecordingCallback();
|
private RecordingCallback callback = new RecordingCallback();
|
||||||
private TestLogHandler logHandler = new TestLogHandler();
|
private TestLogHandler logHandler = new TestLogHandler();
|
||||||
private Cache cache = new Cache(new File("/cache/"), Integer.MAX_VALUE, fileSystem);
|
private Cache cache = new Cache(new File("/cache/"), Integer.MAX_VALUE, fileSystem);
|
||||||
@ -1041,6 +1048,17 @@ public final class CallTest {
|
|||||||
|
|
||||||
executeSynchronously("/").assertBody("seed connection pool");
|
executeSynchronously("/").assertBody("seed connection pool");
|
||||||
executeSynchronously("/").assertBody("retry success");
|
executeSynchronously("/").assertBody("retry success");
|
||||||
|
|
||||||
|
// The call that seeds the connection pool.
|
||||||
|
listener.removeUpToEvent(CallEnd.class);
|
||||||
|
|
||||||
|
// The ResponseFailed event is not necessarily fatal!
|
||||||
|
listener.removeUpToEvent(ConnectionAcquired.class);
|
||||||
|
listener.removeUpToEvent(ResponseFailed.class);
|
||||||
|
listener.removeUpToEvent(ConnectionReleased.class);
|
||||||
|
listener.removeUpToEvent(ConnectionAcquired.class);
|
||||||
|
listener.removeUpToEvent(ConnectionReleased.class);
|
||||||
|
listener.removeUpToEvent(CallEnd.class);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void recoverWhenRetryOnConnectionFailureIsTrue_HTTP2() throws Exception {
|
@Test public void recoverWhenRetryOnConnectionFailureIsTrue_HTTP2() throws Exception {
|
||||||
|
@ -319,7 +319,7 @@ public final class DuplexTest {
|
|||||||
"RequestHeadersStart", "RequestHeadersEnd", "RequestBodyStart", "ResponseHeadersStart",
|
"RequestHeadersStart", "RequestHeadersEnd", "RequestBodyStart", "ResponseHeadersStart",
|
||||||
"ResponseHeadersEnd", "ResponseBodyStart", "ResponseBodyEnd", "RequestHeadersStart",
|
"ResponseHeadersEnd", "ResponseBodyStart", "ResponseBodyEnd", "RequestHeadersStart",
|
||||||
"RequestHeadersEnd", "ResponseHeadersStart", "ResponseHeadersEnd", "ResponseBodyStart",
|
"RequestHeadersEnd", "ResponseHeadersStart", "ResponseHeadersEnd", "ResponseBodyStart",
|
||||||
"ResponseBodyEnd", "ConnectionReleased", "CallEnd", "RequestBodyEnd");
|
"ResponseBodyEnd", "ConnectionReleased", "CallEnd", "RequestFailed");
|
||||||
assertEquals(expectedEvents, listener.recordedEventTypes());
|
assertEquals(expectedEvents, listener.recordedEventTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ import okhttp3.RecordingEventListener.DnsStart;
|
|||||||
import okhttp3.RecordingEventListener.RequestBodyEnd;
|
import okhttp3.RecordingEventListener.RequestBodyEnd;
|
||||||
import okhttp3.RecordingEventListener.RequestHeadersEnd;
|
import okhttp3.RecordingEventListener.RequestHeadersEnd;
|
||||||
import okhttp3.RecordingEventListener.ResponseBodyEnd;
|
import okhttp3.RecordingEventListener.ResponseBodyEnd;
|
||||||
|
import okhttp3.RecordingEventListener.ResponseFailed;
|
||||||
import okhttp3.RecordingEventListener.ResponseHeadersEnd;
|
import okhttp3.RecordingEventListener.ResponseHeadersEnd;
|
||||||
import okhttp3.RecordingEventListener.SecureConnectEnd;
|
import okhttp3.RecordingEventListener.SecureConnectEnd;
|
||||||
import okhttp3.RecordingEventListener.SecureConnectStart;
|
import okhttp3.RecordingEventListener.SecureConnectStart;
|
||||||
@ -168,7 +169,8 @@ public final class EventListenerTest {
|
|||||||
|
|
||||||
List<String> expectedEvents = Arrays.asList("CallStart", "DnsStart", "DnsEnd",
|
List<String> expectedEvents = Arrays.asList("CallStart", "DnsStart", "DnsEnd",
|
||||||
"ConnectStart", "ConnectEnd", "ConnectionAcquired", "RequestHeadersStart",
|
"ConnectStart", "ConnectEnd", "ConnectionAcquired", "RequestHeadersStart",
|
||||||
"RequestHeadersEnd", "ResponseHeadersStart", "ConnectionReleased", "CallFailed");
|
"RequestHeadersEnd", "ResponseHeadersStart", "ResponseFailed", "ConnectionReleased",
|
||||||
|
"CallFailed");
|
||||||
assertEquals(expectedEvents, listener.recordedEventTypes());
|
assertEquals(expectedEvents, listener.recordedEventTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -197,10 +199,10 @@ public final class EventListenerTest {
|
|||||||
List<String> expectedEvents = Arrays.asList("CallStart", "DnsStart", "DnsEnd",
|
List<String> expectedEvents = Arrays.asList("CallStart", "DnsStart", "DnsEnd",
|
||||||
"ConnectStart", "ConnectEnd", "ConnectionAcquired", "RequestHeadersStart",
|
"ConnectStart", "ConnectEnd", "ConnectionAcquired", "RequestHeadersStart",
|
||||||
"RequestHeadersEnd", "ResponseHeadersStart", "ResponseHeadersEnd", "ResponseBodyStart",
|
"RequestHeadersEnd", "ResponseHeadersStart", "ResponseHeadersEnd", "ResponseBodyStart",
|
||||||
"ResponseBodyEnd", "ConnectionReleased", "CallFailed");
|
"ResponseFailed", "ConnectionReleased", "CallFailed");
|
||||||
assertEquals(expectedEvents, listener.recordedEventTypes());
|
assertEquals(expectedEvents, listener.recordedEventTypes());
|
||||||
ResponseBodyEnd bodyEnd = listener.removeUpToEvent(ResponseBodyEnd.class);
|
ResponseFailed responseFailed = listener.removeUpToEvent(ResponseFailed.class);
|
||||||
assertEquals(5, bodyEnd.bytesRead);
|
assertEquals("unexpected end of stream", responseFailed.ioe.getMessage());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test public void canceledCallEventSequence() {
|
@Test public void canceledCallEventSequence() {
|
||||||
@ -1052,7 +1054,7 @@ public final class EventListenerTest {
|
|||||||
|
|
||||||
List<String> expectedEvents = Arrays.asList("CallStart", "DnsStart", "DnsEnd", "ConnectStart",
|
List<String> expectedEvents = Arrays.asList("CallStart", "DnsStart", "DnsEnd", "ConnectStart",
|
||||||
"ConnectEnd", "ConnectionAcquired", "RequestHeadersStart", "RequestHeadersEnd",
|
"ConnectEnd", "ConnectionAcquired", "RequestHeadersStart", "RequestHeadersEnd",
|
||||||
"RequestBodyStart", "RequestBodyEnd", "ConnectionReleased", "CallFailed");
|
"RequestBodyStart", "RequestFailed", "ConnectionReleased", "CallFailed");
|
||||||
assertEquals(expectedEvents, listener.recordedEventTypes());
|
assertEquals(expectedEvents, listener.recordedEventTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,18 +19,18 @@ import java.io.IOException;
|
|||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
import java.net.InetSocketAddress;
|
import java.net.InetSocketAddress;
|
||||||
import java.net.Proxy;
|
import java.net.Proxy;
|
||||||
import java.util.ArrayDeque;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Deque;
|
import java.util.Deque;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.concurrent.ConcurrentLinkedDeque;
|
||||||
import javax.annotation.Nullable;
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
import static org.junit.Assert.assertFalse;
|
import static org.junit.Assert.assertFalse;
|
||||||
import static org.junit.Assert.assertTrue;
|
import static org.junit.Assert.assertTrue;
|
||||||
|
|
||||||
public final class RecordingEventListener extends EventListener {
|
public final class RecordingEventListener extends EventListener {
|
||||||
final Deque<CallEvent> eventSequence = new ArrayDeque<>();
|
final Deque<CallEvent> eventSequence = new ConcurrentLinkedDeque<>();
|
||||||
|
|
||||||
final List<Object> forbiddenLocks = new ArrayList<>();
|
final List<Object> forbiddenLocks = new ArrayList<>();
|
||||||
|
|
||||||
@ -138,6 +138,10 @@ public final class RecordingEventListener extends EventListener {
|
|||||||
logEvent(new RequestBodyEnd(call, byteCount));
|
logEvent(new RequestBodyEnd(call, byteCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void requestFailed(Call call, IOException ioe) {
|
||||||
|
logEvent(new RequestFailed(call, ioe));
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void responseHeadersStart(Call call) {
|
@Override public void responseHeadersStart(Call call) {
|
||||||
logEvent(new ResponseHeadersStart(call));
|
logEvent(new ResponseHeadersStart(call));
|
||||||
}
|
}
|
||||||
@ -154,6 +158,10 @@ public final class RecordingEventListener extends EventListener {
|
|||||||
logEvent(new ResponseBodyEnd(call, byteCount));
|
logEvent(new ResponseBodyEnd(call, byteCount));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void responseFailed(Call call, IOException ioe) {
|
||||||
|
logEvent(new ResponseFailed(call, ioe));
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void callEnd(Call call) {
|
@Override public void callEnd(Call call) {
|
||||||
logEvent(new CallEnd(call));
|
logEvent(new CallEnd(call));
|
||||||
}
|
}
|
||||||
@ -374,6 +382,15 @@ public final class RecordingEventListener extends EventListener {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class RequestFailed extends CallEvent {
|
||||||
|
final IOException ioe;
|
||||||
|
|
||||||
|
RequestFailed(Call call, IOException ioe) {
|
||||||
|
super(call, ioe);
|
||||||
|
this.ioe = ioe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static final class ResponseHeadersStart extends CallEvent {
|
static final class ResponseHeadersStart extends CallEvent {
|
||||||
ResponseHeadersStart(Call call) {
|
ResponseHeadersStart(Call call) {
|
||||||
super(call);
|
super(call);
|
||||||
@ -411,4 +428,13 @@ public final class RecordingEventListener extends EventListener {
|
|||||||
return new ResponseBodyStart(call);
|
return new ResponseBodyStart(call);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static final class ResponseFailed extends CallEvent {
|
||||||
|
final IOException ioe;
|
||||||
|
|
||||||
|
ResponseFailed(Call call, IOException ioe) {
|
||||||
|
super(call, ioe);
|
||||||
|
this.ioe = ioe;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -46,7 +46,7 @@ import javax.annotation.Nullable;
|
|||||||
* events.
|
* events.
|
||||||
*
|
*
|
||||||
* <p>All event methods must execute fast, without external locking, cannot throw exceptions,
|
* <p>All event methods must execute fast, without external locking, cannot throw exceptions,
|
||||||
* attempt to mutate the event parameters, or be reentrant back into the client.
|
* attempt to mutate the event parameters, or be re-entrant back into the client.
|
||||||
* Any IO - writing to files or network should be done asynchronously.
|
* Any IO - writing to files or network should be done asynchronously.
|
||||||
*/
|
*/
|
||||||
public abstract class EventListener {
|
public abstract class EventListener {
|
||||||
@ -210,6 +210,15 @@ public abstract class EventListener {
|
|||||||
public void requestBodyEnd(Call call, long byteCount) {
|
public void requestBodyEnd(Call call, long byteCount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a request fails to be written.
|
||||||
|
*
|
||||||
|
* <p>This method is invoked after {@link #requestHeadersStart} or {@link #requestBodyStart}. Note
|
||||||
|
* that request failures do not necessarily fail the entire call.
|
||||||
|
*/
|
||||||
|
public void requestFailed(Call call, IOException ioe) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked just prior to receiving response headers.
|
* Invoked just prior to receiving response headers.
|
||||||
*
|
*
|
||||||
@ -256,6 +265,15 @@ public abstract class EventListener {
|
|||||||
public void responseBodyEnd(Call call, long byteCount) {
|
public void responseBodyEnd(Call call, long byteCount) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a response fails to be read.
|
||||||
|
*
|
||||||
|
* <p>This method is invoked after {@link #responseHeadersStart} or {@link #responseBodyStart}.
|
||||||
|
* Note that response failures do not necessarily fail the entire call.
|
||||||
|
*/
|
||||||
|
public void responseFailed(Call call, IOException ioe) {
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Invoked immediately after a call has completely ended. This includes delayed consumption
|
* Invoked immediately after a call has completely ended. This includes delayed consumption
|
||||||
* of response body by the caller.
|
* of response body by the caller.
|
||||||
|
@ -66,6 +66,7 @@ public final class Exchange {
|
|||||||
codec.writeRequestHeaders(request);
|
codec.writeRequestHeaders(request);
|
||||||
eventListener.requestHeadersEnd(call, request);
|
eventListener.requestHeadersEnd(call, request);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
eventListener.requestFailed(call, e);
|
||||||
trackFailure(e);
|
trackFailure(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -82,6 +83,7 @@ public final class Exchange {
|
|||||||
try {
|
try {
|
||||||
codec.flushRequest();
|
codec.flushRequest();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
eventListener.requestFailed(call, e);
|
||||||
trackFailure(e);
|
trackFailure(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -91,6 +93,7 @@ public final class Exchange {
|
|||||||
try {
|
try {
|
||||||
codec.finishRequest();
|
codec.finishRequest();
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
eventListener.requestFailed(call, e);
|
||||||
trackFailure(e);
|
trackFailure(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -108,6 +111,7 @@ public final class Exchange {
|
|||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
eventListener.responseFailed(call, e);
|
||||||
trackFailure(e);
|
trackFailure(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -126,6 +130,7 @@ public final class Exchange {
|
|||||||
ResponseBodySource source = new ResponseBodySource(rawSource, contentLength);
|
ResponseBodySource source = new ResponseBodySource(rawSource, contentLength);
|
||||||
return new RealResponseBody(contentType, contentLength, Okio.buffer(source));
|
return new RealResponseBody(contentType, contentLength, Okio.buffer(source));
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
|
eventListener.responseFailed(call, e);
|
||||||
trackFailure(e);
|
trackFailure(e);
|
||||||
throw e;
|
throw e;
|
||||||
}
|
}
|
||||||
@ -167,10 +172,18 @@ public final class Exchange {
|
|||||||
trackFailure(e);
|
trackFailure(e);
|
||||||
}
|
}
|
||||||
if (requestDone) {
|
if (requestDone) {
|
||||||
eventListener.requestBodyEnd(call, bytesRead);
|
if (e != null) {
|
||||||
|
eventListener.requestFailed(call, e);
|
||||||
|
} else {
|
||||||
|
eventListener.requestBodyEnd(call, bytesRead);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (responseDone) {
|
if (responseDone) {
|
||||||
eventListener.responseBodyEnd(call, bytesRead);
|
if (e != null) {
|
||||||
|
eventListener.responseFailed(call, e);
|
||||||
|
} else {
|
||||||
|
eventListener.responseBodyEnd(call, bytesRead);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
transmitter.exchangeMessageDone(this, requestDone, responseDone, e);
|
transmitter.exchangeMessageDone(this, requestDone, responseDone, e);
|
||||||
}
|
}
|
||||||
|
@ -156,6 +156,10 @@ public final class PrintEvents {
|
|||||||
printEvent("requestBodyEnd");
|
printEvent("requestBodyEnd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void requestFailed(Call call, IOException ioe) {
|
||||||
|
printEvent("requestFailed");
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void responseHeadersStart(Call call) {
|
@Override public void responseHeadersStart(Call call) {
|
||||||
printEvent("responseHeadersStart");
|
printEvent("responseHeadersStart");
|
||||||
}
|
}
|
||||||
@ -172,6 +176,10 @@ public final class PrintEvents {
|
|||||||
printEvent("responseBodyEnd");
|
printEvent("responseBodyEnd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void responseFailed(Call call, IOException ioe) {
|
||||||
|
printEvent("responseFailed");
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void callEnd(Call call) {
|
@Override public void callEnd(Call call) {
|
||||||
printEvent("callEnd");
|
printEvent("callEnd");
|
||||||
}
|
}
|
||||||
|
@ -131,6 +131,10 @@ public final class PrintEventsNonConcurrent {
|
|||||||
printEvent("requestBodyEnd");
|
printEvent("requestBodyEnd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void requestFailed(Call call, IOException ioe) {
|
||||||
|
printEvent("requestFailed");
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void responseHeadersStart(Call call) {
|
@Override public void responseHeadersStart(Call call) {
|
||||||
printEvent("responseHeadersStart");
|
printEvent("responseHeadersStart");
|
||||||
}
|
}
|
||||||
@ -147,6 +151,10 @@ public final class PrintEventsNonConcurrent {
|
|||||||
printEvent("responseBodyEnd");
|
printEvent("responseBodyEnd");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void responseFailed(Call call, IOException ioe) {
|
||||||
|
printEvent("responseFailed");
|
||||||
|
}
|
||||||
|
|
||||||
@Override public void callEnd(Call call) {
|
@Override public void callEnd(Call call) {
|
||||||
printEvent("callEnd");
|
printEvent("callEnd");
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user