-
-
Notifications
You must be signed in to change notification settings - Fork 475
fix(okhttp): keep the wrapped EventListener per Call #6003
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
105211a
14f6b50
11786ec
3506dcc
247ee38
b508070
552a731
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -45,7 +45,7 @@ public open class SentryOkHttpEventListener( | |
| private val scopes: IScopes = ScopesAdapter.getInstance(), | ||
| private val originalEventListenerCreator: ((call: Call) -> EventListener)? = null, | ||
| ) : EventListener() { | ||
| private var originalEventListener: EventListener? = null | ||
| private val originalEventListenerMap: MutableMap<Call, EventListener> = ConcurrentHashMap() | ||
|
|
||
| public companion object { | ||
| internal const val PROXY_SELECT_EVENT = "http.client.proxy_select_ms" | ||
|
|
@@ -85,27 +85,34 @@ public open class SentryOkHttpEventListener( | |
| ) : this(scopes, originalEventListenerCreator = { originalEventListenerFactory.create(it) }) | ||
|
|
||
| override fun callStart(call: Call) { | ||
| originalEventListener = originalEventListenerCreator?.invoke(call) | ||
| // The EventListener.Factory contract binds a listener to a single call, so the wrapped | ||
| // listener is kept per call instead of in a field shared by all concurrent calls | ||
| val originalEventListener = originalEventListenerCreator?.invoke(call) | ||
| if (originalEventListener != null) { | ||
| originalEventListenerMap[call] = originalEventListener | ||
| } | ||
| originalEventListener?.callStart(call) | ||
| // If the wrapped EventListener is ours, we can just delegate the calls, | ||
| // without creating other events that would create duplicates | ||
| if (canCreateEventSpan()) { | ||
| if (canCreateEventSpan(originalEventListener)) { | ||
| eventMap[call] = SentryOkHttpEvent(scopes, call.request()) | ||
| } | ||
| } | ||
|
|
||
| override fun proxySelectStart(call: Call, url: HttpUrl) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.proxySelectStart(call, url) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(PROXY_SELECT_EVENT) | ||
| } | ||
|
|
||
| override fun proxySelectEnd(call: Call, url: HttpUrl, proxies: List<Proxy>) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.proxySelectEnd(call, url, proxies) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -117,17 +124,19 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun dnsStart(call: Call, domainName: String) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.dnsStart(call, domainName) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(DNS_EVENT) | ||
| } | ||
|
|
||
| override fun dnsEnd(call: Call, domainName: String, inetAddressList: List<InetAddress>) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.dnsEnd(call, domainName, inetAddressList) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -140,26 +149,29 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun connectStart(call: Call, inetSocketAddress: InetSocketAddress, proxy: Proxy) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.connectStart(call, inetSocketAddress, proxy) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(CONNECT_EVENT) | ||
| } | ||
|
|
||
| override fun secureConnectStart(call: Call) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.secureConnectStart(call) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(SECURE_CONNECT_EVENT) | ||
| } | ||
|
|
||
| override fun secureConnectEnd(call: Call, handshake: Handshake?) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.secureConnectEnd(call, handshake) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -172,8 +184,9 @@ public open class SentryOkHttpEventListener( | |
| proxy: Proxy, | ||
| protocol: Protocol?, | ||
| ) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.connectEnd(call, inetSocketAddress, proxy, protocol) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -188,8 +201,9 @@ public open class SentryOkHttpEventListener( | |
| protocol: Protocol?, | ||
| ioe: IOException, | ||
| ) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.connectFailed(call, inetSocketAddress, proxy, protocol, ioe) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -202,53 +216,59 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun connectionAcquired(call: Call, connection: Connection) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.connectionAcquired(call, connection) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(CONNECTION_EVENT) | ||
| } | ||
|
|
||
| override fun connectionReleased(call: Call, connection: Connection) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.connectionReleased(call, connection) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventFinish(CONNECTION_EVENT) | ||
| } | ||
|
|
||
| override fun requestHeadersStart(call: Call) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.requestHeadersStart(call) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(REQUEST_HEADERS_EVENT) | ||
| } | ||
|
|
||
| override fun requestHeadersEnd(call: Call, request: Request) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.requestHeadersEnd(call, request) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventFinish(REQUEST_HEADERS_EVENT) | ||
| } | ||
|
|
||
| override fun requestBodyStart(call: Call) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.requestBodyStart(call) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(REQUEST_BODY_EVENT) | ||
| } | ||
|
|
||
| override fun requestBodyEnd(call: Call, byteCount: Long) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.requestBodyEnd(call, byteCount) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -261,8 +281,9 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun requestFailed(call: Call, ioe: IOException) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.requestFailed(call, ioe) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -282,17 +303,19 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun responseHeadersStart(call: Call) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.responseHeadersStart(call) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(RESPONSE_HEADERS_EVENT) | ||
| } | ||
|
|
||
| override fun responseHeadersEnd(call: Call, response: Response) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.responseHeadersEnd(call, response) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -307,17 +330,19 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun responseBodyStart(call: Call) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.responseBodyStart(call) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
| okHttpEvent.onEventStart(RESPONSE_BODY_EVENT) | ||
| } | ||
|
|
||
| override fun responseBodyEnd(call: Call, byteCount: Long) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.responseBodyEnd(call, byteCount) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -330,8 +355,9 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun responseFailed(call: Call, ioe: IOException) { | ||
| val originalEventListener = originalEventListenerMap[call] | ||
| originalEventListener?.responseFailed(call, ioe) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap[call] ?: return | ||
|
|
@@ -351,14 +377,15 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun callEnd(call: Call) { | ||
| originalEventListener?.callEnd(call) | ||
| originalEventListenerMap.remove(call)?.callEnd(call) | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap.remove(call) ?: return | ||
| okHttpEvent.finish() | ||
| } | ||
|
|
||
| override fun callFailed(call: Call, ioe: IOException) { | ||
| val originalEventListener = originalEventListenerMap.remove(call) | ||
| originalEventListener?.callFailed(call, ioe) | ||
| if (!canCreateEventSpan()) { | ||
| if (!canCreateEventSpan(originalEventListener)) { | ||
| return | ||
| } | ||
| val okHttpEvent: SentryOkHttpEvent = eventMap.remove(call) ?: return | ||
|
|
@@ -370,26 +397,32 @@ public open class SentryOkHttpEventListener( | |
| } | ||
|
|
||
| override fun canceled(call: Call) { | ||
| originalEventListener?.canceled(call) | ||
| // Unlike the other callbacks, canceled() can occur before callStart() and after | ||
| // callEnd()/callFailed(), so there is not always a listener bound to the call. Create one on | ||
| // the fly in that case, but do not put it in the map: nothing would remove it again, because | ||
| // a call that is canceled before it starts never gets a callEnd() or callFailed(). | ||
| val originalEventListener = | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We're close! (and thanks for the great updates) We still need to preserve the contract of EventListener.Factory that ensures only one EventListener instance is produced per Call lifecycle. Ie, the listener returned by the factory for a given call needs to be the listener that captures i) all of that call's lifecycle and ii) no other call's lifecycle. We've fixed (ii), but we're still violating (i) in the case of cancelation because we're creating an extra listener for early and late cancel() invocations. Possible solution Thoughts about using a weak per-call map for the wrapped listener instead? Something like a WeakHashMap<Call, EventListener> guarded by synchronized, with a getOrCreateOriginalEventListener(call) helper used by both callStart and canceled(). That'd^^ let us avoid removing entries on callEnd / callFailed, and completed calls would be gc'd as soon as the Call instance is unreachable. |
||
| originalEventListenerMap[call] ?: originalEventListenerCreator?.invoke(call) ?: return | ||
| originalEventListener.canceled(call) | ||
| } | ||
|
|
||
| override fun satisfactionFailure(call: Call, response: Response) { | ||
| originalEventListener?.satisfactionFailure(call, response) | ||
| originalEventListenerMap[call]?.satisfactionFailure(call, response) | ||
| } | ||
|
|
||
| override fun cacheHit(call: Call, response: Response) { | ||
| originalEventListener?.cacheHit(call, response) | ||
| originalEventListenerMap[call]?.cacheHit(call, response) | ||
| } | ||
|
|
||
| override fun cacheMiss(call: Call) { | ||
| originalEventListener?.cacheMiss(call) | ||
| originalEventListenerMap[call]?.cacheMiss(call) | ||
| } | ||
|
|
||
| override fun cacheConditionalHit(call: Call, cachedResponse: Response) { | ||
| originalEventListener?.cacheConditionalHit(call, cachedResponse) | ||
| originalEventListenerMap[call]?.cacheConditionalHit(call, cachedResponse) | ||
| } | ||
|
|
||
| private fun canCreateEventSpan(): Boolean { | ||
| private fun canCreateEventSpan(originalEventListener: EventListener?): Boolean { | ||
| // If the wrapped EventListener is ours, we shouldn't create spans, as the originalEventListener | ||
| // already did it | ||
| // In case SentryOkHttpEventListener from sentry-android-okhttp is used, the is check won't work | ||
|
|
||
Uh oh!
There was an error while loading. Please reload this page.