diff --git a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/RequestTest.kt b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/RequestTest.kt index a3dcf10bf7..3ca8eb5fb9 100644 --- a/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/RequestTest.kt +++ b/bugsnag-android-core/src/androidTest/java/com/bugsnag/android/RequestTest.kt @@ -105,8 +105,8 @@ class RequestTest { request.addHeader("X-Test", "value") request.addHeader("Another-Header", "another-value") - assertEquals("X-Test", request.getHeader("X-Test")) - assertEquals("Another-Header", request.getHeader("Another-Header")) + assertEquals("value", request.getHeader("X-Test")) + assertEquals("another-value", request.getHeader("Another-Header")) assertEquals(setOf("X-Test", "Another-Header"), request.headerNames) request.removeHeader("X-Test") diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/AbstractHttpEntity.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/AbstractHttpEntity.java index 79bd16790e..17a2f6c4b7 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/AbstractHttpEntity.java +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/AbstractHttpEntity.java @@ -67,7 +67,7 @@ public String getHeader(@NonNull String headerName) { } String headerValue = headers.get(headerName); - return headerValue != null ? headerName : ""; + return headerValue != null ? headerValue : ""; } /** diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt b/bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt index dfb2528645..7dde640c4f 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt @@ -27,6 +27,9 @@ internal class BugsnagEventMapper( val exceptions = map["exceptions"] as? List> exceptions?.mapTo(event.errors) { Error(convertErrorInternal(it), this.logger) } + event.request = convertRequest(map["request"] as? Map) + event.response = convertResponse(map["response"] as? Map) + // populate user event.userImpl = convertUser(map.readEntry("user")) @@ -127,6 +130,51 @@ internal class BugsnagEventMapper( ) } + @Suppress("UNCHECKED_CAST") + internal fun convertRequest(request: Map?): Request? { + if (request == null) { + return null + } + + val out = Request( + request["httpVersion"] as? String, + request["httpMethod"] as? String, + request["url"] as? String, + ) + + out.body = request["body"] as? String + out.bodyLength = (request["bodyLength"] as? Number)?.toLong() ?: 0 + + (request["headers"] as? Map)?.forEach { (name, value) -> + out.addHeader(name, value) + } + + (request["params"] as? Map)?.forEach { (key, value) -> + out.addQueryParameter(key, value) + } + + return out + } + + @Suppress("UNCHECKED_CAST") + internal fun convertResponse(response: Map?): Response? { + if (response == null) { + return null + } + + val statusCode = (response["statusCode"] as? Number)?.toInt() ?: return null + val out = Response(statusCode) + + out.body = response["body"] as? String + out.bodyLength = (response["bodyLength"] as? Number)?.toLong() ?: 0 + + (response["headers"] as? Map)?.forEach { (name, value) -> + out.addHeader(name, value) + } + + return out + } + internal fun convertUser(user: Map): User { return User( user["id"] as? String, diff --git a/bugsnag-android-core/src/main/java/com/bugsnag/android/Request.java b/bugsnag-android-core/src/main/java/com/bugsnag/android/Request.java index 848644a14a..844a8eb43f 100644 --- a/bugsnag-android-core/src/main/java/com/bugsnag/android/Request.java +++ b/bugsnag-android-core/src/main/java/com/bugsnag/android/Request.java @@ -121,7 +121,11 @@ public void setUrl(@Nullable String url) { int querySeparatorIndex = url.indexOf('?'); if (querySeparatorIndex > 0) { - setUrlWithQueryString(url); + try { + tryUrlWithQueryString(url); + } catch (RuntimeException re) { + this.url = url.substring(0, querySeparatorIndex); + } } else { this.url = url; } @@ -131,22 +135,18 @@ public void setUrl(@Nullable String url) { } } - private void setUrlWithQueryString(@NonNull String url) { - try { - Uri uri = Uri.parse(url); + private void tryUrlWithQueryString(@NonNull String url) { + Uri uri = Uri.parse(url); - params.clear(); - for (String queryName : uri.getQueryParameterNames()) { - params.put(queryName, uri.getQueryParameter(queryName)); - } - - this.url = uri.buildUpon() - .clearQuery() - .build() - .toString(); - } catch (RuntimeException ignored) { - this.url = url; + params.clear(); + for (String queryName : uri.getQueryParameterNames()) { + params.put(queryName, uri.getQueryParameter(queryName)); } + + this.url = uri.buildUpon() + .clearQuery() + .build() + .toString(); } /** diff --git a/bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt b/bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt index 7a0da9c0e4..4d8b1f9d10 100644 --- a/bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt +++ b/bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt @@ -98,6 +98,27 @@ internal class EventSerializationTest { createEvent { createMetadataStressTest(it) + }, + + createEvent { + it.updateSeverityReason(SeverityReason.REASON_HTTP_ERROR) + + it.request = Request("1.1", "POST", "http://example.com/").apply { + body = "some body" + bodyLength = 9 + + addHeader("content-type", "text/plain") + addHeader("content-length", "9") + addQueryParameter("message", "hello world") + } + + it.response = Response(200).apply { + body = "some body" + bodyLength = 9 + + addHeader("content-type", "text/plain") + addHeader("content-length", "9") + } } ) } diff --git a/bugsnag-android-core/src/test/resources/event_serialization_10.json b/bugsnag-android-core/src/test/resources/event_serialization_10.json new file mode 100644 index 0000000000..34fea1f854 --- /dev/null +++ b/bugsnag-android-core/src/test/resources/event_serialization_10.json @@ -0,0 +1,60 @@ +{ + "metaData": {}, + "severity": "warning", + "severityReason": { + "type": "httpError", + "unhandledOverridden": false + }, + "unhandled": false, + "exceptions": [], + "request": { + "httpMethod": "POST", + "httpVersion": "1.1", + "url": "http://example.com/", + "body": "some body", + "bodyLength": 9, + "headers": { + "content-type": "text/plain", + "content-length": "9" + }, + "params": { + "message": "hello world" + } + }, + "response": { + "statusCode": 200, + "body": "some body", + "bodyLength": 9, + "headers": { + "content-type": "text/plain", + "content-length": "9" + } + }, + "projectPackages":[ + "com.example.foo" + ], + "user": {}, + "app": { + "type": "android", + "versionCode": 0 + }, + "device": { + "cpuAbi": [], + "manufacturer": "samsung", + "model": "s7", + "osName": "android", + "osVersion": "7.1", + "runtimeVersions": { + "osBuild": "bulldog", + "androidApiLevel": "24" + }, + "totalMemory": 109230923452, + "freeDisk": 22234423124, + "freeMemory": 92340255592, + "orientation": "portrait", + "time": "1970-01-01T00:00:00.000Z" + }, + "breadcrumbs": [], + "threads": [], + "featureFlags": [] +} diff --git a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OkHttpInstrumentationScenario.kt b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OkHttpInstrumentationScenario.kt index 07f4fd0fd7..7a715f5de7 100644 --- a/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OkHttpInstrumentationScenario.kt +++ b/features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OkHttpInstrumentationScenario.kt @@ -55,6 +55,18 @@ open class OkHttpInstrumentationScenario( "Authorization".toPattern(Pattern.LITERAL or Pattern.CASE_INSENSITIVE), ".*password.*".toPattern(Pattern.CASE_INSENSITIVE) ) + + config.addOnSend { event -> + if (event.request == null) { + event.errors.first().errorClass = "No request in OnSendCallback" + } + + if (event.response == null) { + event.errors.first().errorClass = "No response in OnErrorCallback" + } + + true + } } private fun requestType(): Pair {