Skip to content

Commit b508070

Browse files
committed
fix(okhttp): delegate canceled() outside of the call window
1 parent 247ee38 commit b508070

2 files changed

Lines changed: 78 additions & 1 deletion

File tree

‎sentry-okhttp/src/main/java/io/sentry/okhttp/SentryOkHttpEventListener.kt‎

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -397,7 +397,13 @@ public open class SentryOkHttpEventListener(
397397
}
398398

399399
override fun canceled(call: Call) {
400-
originalEventListenerMap[call]?.canceled(call)
400+
// Unlike the other callbacks, canceled() can occur before callStart() and after
401+
// callEnd()/callFailed(), so there is not always a listener bound to the call. Create one on
402+
// the fly in that case, but do not put it in the map: nothing would remove it again, because
403+
// a call that is canceled before it starts never gets a callEnd() or callFailed().
404+
val originalEventListener =
405+
originalEventListenerMap[call] ?: originalEventListenerCreator?.invoke(call) ?: return
406+
originalEventListener.canceled(call)
401407
}
402408

403409
override fun satisfactionFailure(call: Call, response: Response) {

‎sentry-okhttp/src/test/java/io/sentry/okhttp/SentryOkHttpEventListenerDelegationTest.kt‎

Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ class SentryOkHttpEventListenerDelegationTest {
3232
received += "callFailed" to call
3333
}
3434

35+
override fun canceled(call: Call) {
36+
received += "canceled" to call
37+
}
38+
3539
fun mismatches(): List<Pair<String, Call>> = received.filter { it.second !== ownCall }
3640
}
3741

@@ -119,4 +123,71 @@ class SentryOkHttpEventListenerDelegationTest {
119123
.containsExactly("callStart", "callFailed")
120124
.inOrder()
121125
}
126+
127+
@Test
128+
fun `cancel during a call is delegated to the listener of that call`() {
129+
val sut = fixture.getSut()
130+
val call = fixture.newCall("1")
131+
132+
sut.callStart(call)
133+
sut.canceled(call)
134+
sut.callFailed(call, IOException())
135+
136+
assertThat(fixture.listeners.single().received.map { it.first })
137+
.containsExactly("callStart", "canceled", "callFailed")
138+
.inOrder()
139+
}
140+
141+
@Test
142+
fun `cancel before callStart is delegated`() {
143+
val sut = fixture.getSut()
144+
val call = fixture.newCall("1")
145+
146+
sut.canceled(call)
147+
148+
assertThat(fixture.listeners.single().received).containsExactly("canceled" to call)
149+
}
150+
151+
@Test
152+
fun `cancel after callEnd is delegated`() {
153+
val sut = fixture.getSut()
154+
val call = fixture.newCall("1")
155+
156+
sut.callStart(call)
157+
sut.callEnd(call)
158+
sut.canceled(call)
159+
160+
assertThat(fixture.listeners.first().received.map { it.first })
161+
.containsExactly("callStart", "callEnd")
162+
.inOrder()
163+
assertThat(fixture.listeners.last().received).containsExactly("canceled" to call)
164+
}
165+
166+
@Test
167+
fun `cancel outside of a call does not bind a listener to the call`() {
168+
val sut = fixture.getSut()
169+
val call = fixture.newCall("1")
170+
171+
sut.canceled(call)
172+
sut.dnsStart(call, "sentry.io")
173+
174+
assertThat(fixture.listeners.single().received.map { it.first }).containsExactly("canceled")
175+
}
176+
177+
@Test
178+
fun `a single wrapped listener receives cancels outside of the call window`() {
179+
whenever(fixture.scopes.options).thenReturn(SentryOptions())
180+
val call = fixture.newCall("1")
181+
val listener = RecordingListener(call)
182+
val sut = SentryOkHttpEventListener(fixture.scopes, listener)
183+
184+
sut.canceled(call)
185+
sut.callStart(call)
186+
sut.callEnd(call)
187+
sut.canceled(call)
188+
189+
assertThat(listener.received.map { it.first })
190+
.containsExactly("canceled", "callStart", "callEnd", "canceled")
191+
.inOrder()
192+
}
122193
}

0 commit comments

Comments
 (0)