Skip to content

Commit a4862e4

Browse files
authored
Merge pull request #2378 from bugsnag/PLAT-15564/decode-http-errors
Fix decoding request/response entities
2 parents 44c59f0 + 91e9fcf commit a4862e4

7 files changed

Lines changed: 159 additions & 18 deletions

File tree

bugsnag-android-core/src/androidTest/java/com/bugsnag/android/RequestTest.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -105,8 +105,8 @@ class RequestTest {
105105
request.addHeader("X-Test", "value")
106106
request.addHeader("Another-Header", "another-value")
107107

108-
assertEquals("X-Test", request.getHeader("X-Test"))
109-
assertEquals("Another-Header", request.getHeader("Another-Header"))
108+
assertEquals("value", request.getHeader("X-Test"))
109+
assertEquals("another-value", request.getHeader("Another-Header"))
110110
assertEquals(setOf("X-Test", "Another-Header"), request.headerNames)
111111

112112
request.removeHeader("X-Test")

bugsnag-android-core/src/main/java/com/bugsnag/android/AbstractHttpEntity.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ public String getHeader(@NonNull String headerName) {
6767
}
6868

6969
String headerValue = headers.get(headerName);
70-
return headerValue != null ? headerName : "";
70+
return headerValue != null ? headerValue : "";
7171
}
7272

7373
/**

bugsnag-android-core/src/main/java/com/bugsnag/android/BugsnagEventMapper.kt

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ internal class BugsnagEventMapper(
2727
val exceptions = map["exceptions"] as? List<MutableMap<String, Any?>>
2828
exceptions?.mapTo(event.errors) { Error(convertErrorInternal(it), this.logger) }
2929

30+
event.request = convertRequest(map["request"] as? Map<String, Any?>)
31+
event.response = convertResponse(map["response"] as? Map<String, Any?>)
32+
3033
// populate user
3134
event.userImpl = convertUser(map.readEntry("user"))
3235

@@ -127,6 +130,51 @@ internal class BugsnagEventMapper(
127130
)
128131
}
129132

133+
@Suppress("UNCHECKED_CAST")
134+
internal fun convertRequest(request: Map<String, Any?>?): Request? {
135+
if (request == null) {
136+
return null
137+
}
138+
139+
val out = Request(
140+
request["httpVersion"] as? String,
141+
request["httpMethod"] as? String,
142+
request["url"] as? String,
143+
)
144+
145+
out.body = request["body"] as? String
146+
out.bodyLength = (request["bodyLength"] as? Number)?.toLong() ?: 0
147+
148+
(request["headers"] as? Map<String, String>)?.forEach { (name, value) ->
149+
out.addHeader(name, value)
150+
}
151+
152+
(request["params"] as? Map<String, String>)?.forEach { (key, value) ->
153+
out.addQueryParameter(key, value)
154+
}
155+
156+
return out
157+
}
158+
159+
@Suppress("UNCHECKED_CAST")
160+
internal fun convertResponse(response: Map<String, Any?>?): Response? {
161+
if (response == null) {
162+
return null
163+
}
164+
165+
val statusCode = (response["statusCode"] as? Number)?.toInt() ?: return null
166+
val out = Response(statusCode)
167+
168+
out.body = response["body"] as? String
169+
out.bodyLength = (response["bodyLength"] as? Number)?.toLong() ?: 0
170+
171+
(response["headers"] as? Map<String, String>)?.forEach { (name, value) ->
172+
out.addHeader(name, value)
173+
}
174+
175+
return out
176+
}
177+
130178
internal fun convertUser(user: Map<String, Any?>): User {
131179
return User(
132180
user["id"] as? String,

bugsnag-android-core/src/main/java/com/bugsnag/android/Request.java

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -121,7 +121,11 @@ public void setUrl(@Nullable String url) {
121121
int querySeparatorIndex = url.indexOf('?');
122122

123123
if (querySeparatorIndex > 0) {
124-
setUrlWithQueryString(url);
124+
try {
125+
tryUrlWithQueryString(url);
126+
} catch (RuntimeException re) {
127+
this.url = url.substring(0, querySeparatorIndex);
128+
}
125129
} else {
126130
this.url = url;
127131
}
@@ -131,22 +135,18 @@ public void setUrl(@Nullable String url) {
131135
}
132136
}
133137

134-
private void setUrlWithQueryString(@NonNull String url) {
135-
try {
136-
Uri uri = Uri.parse(url);
138+
private void tryUrlWithQueryString(@NonNull String url) {
139+
Uri uri = Uri.parse(url);
137140

138-
params.clear();
139-
for (String queryName : uri.getQueryParameterNames()) {
140-
params.put(queryName, uri.getQueryParameter(queryName));
141-
}
142-
143-
this.url = uri.buildUpon()
144-
.clearQuery()
145-
.build()
146-
.toString();
147-
} catch (RuntimeException ignored) {
148-
this.url = url;
141+
params.clear();
142+
for (String queryName : uri.getQueryParameterNames()) {
143+
params.put(queryName, uri.getQueryParameter(queryName));
149144
}
145+
146+
this.url = uri.buildUpon()
147+
.clearQuery()
148+
.build()
149+
.toString();
150150
}
151151

152152
/**

bugsnag-android-core/src/test/java/com/bugsnag/android/EventSerializationTest.kt

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,27 @@ internal class EventSerializationTest {
9898

9999
createEvent {
100100
createMetadataStressTest(it)
101+
},
102+
103+
createEvent {
104+
it.updateSeverityReason(SeverityReason.REASON_HTTP_ERROR)
105+
106+
it.request = Request("1.1", "POST", "http://example.com/").apply {
107+
body = "some body"
108+
bodyLength = 9
109+
110+
addHeader("content-type", "text/plain")
111+
addHeader("content-length", "9")
112+
addQueryParameter("message", "hello world")
113+
}
114+
115+
it.response = Response(200).apply {
116+
body = "some body"
117+
bodyLength = 9
118+
119+
addHeader("content-type", "text/plain")
120+
addHeader("content-length", "9")
121+
}
101122
}
102123
)
103124
}
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
{
2+
"metaData": {},
3+
"severity": "warning",
4+
"severityReason": {
5+
"type": "httpError",
6+
"unhandledOverridden": false
7+
},
8+
"unhandled": false,
9+
"exceptions": [],
10+
"request": {
11+
"httpMethod": "POST",
12+
"httpVersion": "1.1",
13+
"url": "http://example.com/",
14+
"body": "some body",
15+
"bodyLength": 9,
16+
"headers": {
17+
"content-type": "text/plain",
18+
"content-length": "9"
19+
},
20+
"params": {
21+
"message": "hello world"
22+
}
23+
},
24+
"response": {
25+
"statusCode": 200,
26+
"body": "some body",
27+
"bodyLength": 9,
28+
"headers": {
29+
"content-type": "text/plain",
30+
"content-length": "9"
31+
}
32+
},
33+
"projectPackages":[
34+
"com.example.foo"
35+
],
36+
"user": {},
37+
"app": {
38+
"type": "android",
39+
"versionCode": 0
40+
},
41+
"device": {
42+
"cpuAbi": [],
43+
"manufacturer": "samsung",
44+
"model": "s7",
45+
"osName": "android",
46+
"osVersion": "7.1",
47+
"runtimeVersions": {
48+
"osBuild": "bulldog",
49+
"androidApiLevel": "24"
50+
},
51+
"totalMemory": 109230923452,
52+
"freeDisk": 22234423124,
53+
"freeMemory": 92340255592,
54+
"orientation": "portrait",
55+
"time": "1970-01-01T00:00:00.000Z"
56+
},
57+
"breadcrumbs": [],
58+
"threads": [],
59+
"featureFlags": []
60+
}

features/fixtures/mazerunner/jvm-scenarios/src/main/java/com/bugsnag/android/mazerunner/scenarios/OkHttpInstrumentationScenario.kt

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,18 @@ open class OkHttpInstrumentationScenario(
6767
"Authorization".toPattern(Pattern.LITERAL or Pattern.CASE_INSENSITIVE),
6868
".*password.*".toPattern(Pattern.CASE_INSENSITIVE)
6969
)
70+
71+
config.addOnSend { event ->
72+
if (event.request == null) {
73+
event.errors.first().errorClass = "No request in OnSendCallback"
74+
}
75+
76+
if (event.response == null) {
77+
event.errors.first().errorClass = "No response in OnErrorCallback"
78+
}
79+
80+
true
81+
}
7082
}
7183

7284
private fun requestType(): Pair<String, Int> {

0 commit comments

Comments
 (0)