mirror of
https://github.com/square/okhttp.git
synced 2025-11-26 06:43:09 +03:00
Add a canceled event to EventListener
This commit is contained in:
@@ -151,6 +151,10 @@ class LoggingEventListener private constructor(
|
|||||||
logWithTime("callFailed: $ioe")
|
logWithTime("callFailed: $ioe")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun canceled(call: Call) {
|
||||||
|
logWithTime("canceled")
|
||||||
|
}
|
||||||
|
|
||||||
private fun logWithTime(message: String) {
|
private fun logWithTime(message: String) {
|
||||||
val timeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
|
val timeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
|
||||||
logger.log("[$timeMs ms] $message")
|
logger.log("[$timeMs ms] $message")
|
||||||
|
|||||||
@@ -134,6 +134,11 @@ sealed class CallEvent {
|
|||||||
val ioe: IOException
|
val ioe: IOException
|
||||||
) : CallEvent()
|
) : CallEvent()
|
||||||
|
|
||||||
|
data class Canceled(
|
||||||
|
override val timestampNs: Long,
|
||||||
|
override val call: Call
|
||||||
|
) : CallEvent()
|
||||||
|
|
||||||
data class RequestHeadersStart(
|
data class RequestHeadersStart(
|
||||||
override val timestampNs: Long,
|
override val timestampNs: Long,
|
||||||
override val call: Call
|
override val call: Call
|
||||||
|
|||||||
@@ -184,6 +184,12 @@ class ClientRuleEventListener(val delegate: EventListener = NONE, var logger: (S
|
|||||||
delegate.callFailed(call, ioe)
|
delegate.callFailed(call, ioe)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun canceled(call: Call) {
|
||||||
|
logWithTime("canceled")
|
||||||
|
|
||||||
|
delegate.canceled(call)
|
||||||
|
}
|
||||||
|
|
||||||
private fun logWithTime(message: String) {
|
private fun logWithTime(message: String) {
|
||||||
val timeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
|
val timeMs = TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - startNs)
|
||||||
logger.invoke("[$timeMs ms] $message")
|
logger.invoke("[$timeMs ms] $message")
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ import java.util.concurrent.TimeUnit
|
|||||||
import okhttp3.CallEvent.CallEnd
|
import okhttp3.CallEvent.CallEnd
|
||||||
import okhttp3.CallEvent.CallFailed
|
import okhttp3.CallEvent.CallFailed
|
||||||
import okhttp3.CallEvent.CallStart
|
import okhttp3.CallEvent.CallStart
|
||||||
|
import okhttp3.CallEvent.Canceled
|
||||||
import okhttp3.CallEvent.ConnectEnd
|
import okhttp3.CallEvent.ConnectEnd
|
||||||
import okhttp3.CallEvent.ConnectFailed
|
import okhttp3.CallEvent.ConnectFailed
|
||||||
import okhttp3.CallEvent.ConnectStart
|
import okhttp3.CallEvent.ConnectStart
|
||||||
@@ -249,4 +250,8 @@ open class RecordingEventListener : EventListener() {
|
|||||||
call: Call,
|
call: Call,
|
||||||
ioe: IOException
|
ioe: IOException
|
||||||
) = logEvent(CallFailed(System.nanoTime(), call, ioe))
|
) = logEvent(CallFailed(System.nanoTime(), call, ioe))
|
||||||
|
|
||||||
|
override fun canceled(
|
||||||
|
call: Call
|
||||||
|
) = logEvent(Canceled(System.nanoTime(), call))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -401,6 +401,27 @@ abstract class EventListener {
|
|||||||
) {
|
) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Invoked when a call is canceled.
|
||||||
|
*
|
||||||
|
* Like all methods in this interface, this is invoked on the thread that triggered the event. But
|
||||||
|
* while other events occur sequentially; cancels may occur concurrently with other events. For
|
||||||
|
* example, thread A may be executing [responseBodyStart] while thread B executes [canceled].
|
||||||
|
* Implementations must support such concurrent calls.
|
||||||
|
*
|
||||||
|
* Note that cancellation is best-effort and that a call may proceed normally after it has been
|
||||||
|
* canceled. For example, happy-path events like [requestHeadersStart] and [requestHeadersEnd] may
|
||||||
|
* occur after a call is canceled. Typically cancellation takes effect when an expensive I/O
|
||||||
|
* operation is required.
|
||||||
|
*
|
||||||
|
* This is invoked at most once, even if [Call.cancel] is invoked multiple times. It may be
|
||||||
|
* invoked at any point in a call's life, including before [callStart] and after [callEnd].
|
||||||
|
*/
|
||||||
|
open fun canceled(
|
||||||
|
call: Call
|
||||||
|
) {
|
||||||
|
}
|
||||||
|
|
||||||
interface Factory {
|
interface Factory {
|
||||||
/**
|
/**
|
||||||
* Creates an instance of the [EventListener] for a particular [Call]. The returned
|
* Creates an instance of the [EventListener] for a particular [Call]. The returned
|
||||||
|
|||||||
@@ -121,11 +121,13 @@ class RealCall(
|
|||||||
val exchangeToCancel: Exchange?
|
val exchangeToCancel: Exchange?
|
||||||
val connectionToCancel: RealConnection?
|
val connectionToCancel: RealConnection?
|
||||||
synchronized(connectionPool) {
|
synchronized(connectionPool) {
|
||||||
|
if (canceled) return // Already canceled.
|
||||||
canceled = true
|
canceled = true
|
||||||
exchangeToCancel = exchange
|
exchangeToCancel = exchange
|
||||||
connectionToCancel = exchangeFinder?.connectingConnection() ?: connection
|
connectionToCancel = exchangeFinder?.connectingConnection() ?: connection
|
||||||
}
|
}
|
||||||
exchangeToCancel?.cancel() ?: connectionToCancel?.cancel()
|
exchangeToCancel?.cancel() ?: connectionToCancel?.cancel()
|
||||||
|
eventListener.canceled(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun isCanceled(): Boolean {
|
override fun isCanceled(): Boolean {
|
||||||
|
|||||||
@@ -226,7 +226,41 @@ public final class EventListenerTest {
|
|||||||
assertThat(expected.getMessage()).isEqualTo("Canceled");
|
assertThat(expected.getMessage()).isEqualTo("Canceled");
|
||||||
}
|
}
|
||||||
|
|
||||||
assertThat(listener.recordedEventTypes()).containsExactly("CallStart", "CallFailed");
|
assertThat(listener.recordedEventTypes()).containsExactly(
|
||||||
|
"Canceled", "CallStart", "CallFailed");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void cancelAsyncCall() throws IOException {
|
||||||
|
server.enqueue(new MockResponse()
|
||||||
|
.setBody("abc"));
|
||||||
|
|
||||||
|
Call call = client.newCall(new Request.Builder()
|
||||||
|
.url(server.url("/"))
|
||||||
|
.build());
|
||||||
|
call.enqueue(new Callback() {
|
||||||
|
@Override public void onFailure(Call call, IOException e) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public void onResponse(Call call, Response response) throws IOException {
|
||||||
|
response.close();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
call.cancel();
|
||||||
|
|
||||||
|
assertThat(listener.recordedEventTypes()).contains("Canceled");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test public void multipleCancelsEmitsOnlyOneEvent() throws IOException {
|
||||||
|
server.enqueue(new MockResponse()
|
||||||
|
.setBody("abc"));
|
||||||
|
|
||||||
|
Call call = client.newCall(new Request.Builder()
|
||||||
|
.url(server.url("/"))
|
||||||
|
.build());
|
||||||
|
call.cancel();
|
||||||
|
call.cancel();
|
||||||
|
|
||||||
|
assertThat(listener.recordedEventTypes()).containsExactly("Canceled");
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertSuccessfulEventOrder(Matcher<Response> responseMatcher) throws IOException {
|
private void assertSuccessfulEventOrder(Matcher<Response> responseMatcher) throws IOException {
|
||||||
|
|||||||
@@ -387,6 +387,7 @@ class KotlinSourceModernTest {
|
|||||||
override fun responseFailed(call: Call, ioe: IOException) = TODO()
|
override fun responseFailed(call: Call, ioe: IOException) = TODO()
|
||||||
override fun callEnd(call: Call) = TODO()
|
override fun callEnd(call: Call) = TODO()
|
||||||
override fun callFailed(call: Call, ioe: IOException) = TODO()
|
override fun callFailed(call: Call, ioe: IOException) = TODO()
|
||||||
|
override fun canceled(call: Call) = TODO()
|
||||||
}
|
}
|
||||||
val none: EventListener = EventListener.NONE
|
val none: EventListener = EventListener.NONE
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -196,5 +196,9 @@ public final class PrintEvents {
|
|||||||
@Override public void callFailed(Call call, IOException ioe) {
|
@Override public void callFailed(Call call, IOException ioe) {
|
||||||
printEvent("callFailed");
|
printEvent("callFailed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void canceled(Call call) {
|
||||||
|
printEvent("canceled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -171,5 +171,9 @@ public final class PrintEventsNonConcurrent {
|
|||||||
@Override public void callFailed(Call call, IOException ioe) {
|
@Override public void callFailed(Call call, IOException ioe) {
|
||||||
printEvent("callFailed");
|
printEvent("callFailed");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override public void canceled(Call call) {
|
||||||
|
printEvent("canceled");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user