Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
- Prevent duplicated breadcrumbs on tombstone-merged native crash events ([#5888](https://github.com/getsentry/sentry-java/pull/5888))
- Prevent a class of Session Replay deadlocks by confining lifecycle state changes to Android's main thread ([#5965](https://github.com/getsentry/sentry-java/pull/5965))
- Symbolicate tombstone native frames for libraries loaded directly from APKs ([#5992](https://github.com/getsentry/sentry-java/pull/5992))
- Keep the `EventListener` wrapped by `SentryOkHttpEventListener` per `Call` ([#6003](https://github.com/getsentry/sentry-java/pull/6003))

### Features

Expand Down
1 change: 1 addition & 0 deletions sentry-okhttp/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ dependencies {
testImplementation(libs.mockito.inline)
testImplementation(libs.okhttp)
testImplementation(libs.okhttp.mockwebserver)
testImplementation(libs.google.truth)
}

buildConfig {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Expand All @@ -370,26 +397,26 @@ public open class SentryOkHttpEventListener(
}

override fun canceled(call: Call) {
originalEventListener?.canceled(call)
originalEventListenerMap[call]?.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
Expand Down
Loading
Loading