Skip to content

Commit 7df2901

Browse files
authored
Merge pull request #2371 from bugsnag/integration/network-errors
HTTP Network Errors
2 parents a218fd3 + 227662a commit 7df2901

49 files changed

Lines changed: 3161 additions & 72 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

CODEOWNERS

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
bugsnag-android/ @lemnik @YYChen01988
22
bugsnag-android-core/ @lemnik @YYChen01988
3+
bugsnag-android-http-api/ @lemnik @YYChen01988
34
bugsnag-plugin-android-anr/ @lemnik @YYChen01988
45
bugsnag-plugin-android-apphang/ @lemnik @YYChen01988
56
bugsnag-plugin-android-exitinfo/ @lemnik @YYChen01988

bugsnag-android-core/api/bugsnag-android-core.api

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
public abstract class com/bugsnag/android/AbstractHttpEntity {
2+
protected final field headers Ljava/util/Map;
3+
public fun addHeader (Ljava/lang/String;Ljava/lang/String;)V
4+
public fun getBody ()Ljava/lang/String;
5+
public fun getBodyLength ()J
6+
public fun getHeader (Ljava/lang/String;)Ljava/lang/String;
7+
public fun getHeaderNames ()Ljava/util/Set;
8+
public fun removeHeader (Ljava/lang/String;)V
9+
public fun setBody (Ljava/lang/String;)V
10+
public fun setBodyLength (J)V
11+
}
12+
113
public class com/bugsnag/android/App : com/bugsnag/android/JsonStream$Streamable {
214
public final fun getBinaryArch ()Ljava/lang/String;
315
public final fun getBuildUuid ()Ljava/lang/String;
@@ -459,6 +471,8 @@ public class com/bugsnag/android/Event : com/bugsnag/android/FeatureFlagAware, c
459471
public fun getMetadata (Ljava/lang/String;)Ljava/util/Map;
460472
public fun getMetadata (Ljava/lang/String;Ljava/lang/String;)Ljava/lang/Object;
461473
public fun getOriginalError ()Ljava/lang/Throwable;
474+
public fun getRequest ()Lcom/bugsnag/android/Request;
475+
public fun getResponse ()Lcom/bugsnag/android/Response;
462476
public fun getSeverity ()Lcom/bugsnag/android/Severity;
463477
public fun getThreads ()Ljava/util/List;
464478
public fun getUser ()Lcom/bugsnag/android/User;
@@ -472,6 +486,8 @@ public class com/bugsnag/android/Event : com/bugsnag/android/FeatureFlagAware, c
472486
public fun setErrorReportingThread (Lcom/bugsnag/android/Thread;)V
473487
public fun setGroupingDiscriminator (Ljava/lang/String;)Ljava/lang/String;
474488
public fun setGroupingHash (Ljava/lang/String;)V
489+
public fun setRequest (Lcom/bugsnag/android/Request;)V
490+
public fun setResponse (Lcom/bugsnag/android/Response;)V
475491
public fun setSeverity (Lcom/bugsnag/android/Severity;)V
476492
public fun setTraceCorrelation (Ljava/util/UUID;J)V
477493
public fun setUnhandled (Z)V
@@ -689,6 +705,29 @@ public abstract interface class com/bugsnag/android/Plugin {
689705
public abstract fun unload ()V
690706
}
691707

708+
public final class com/bugsnag/android/Request : com/bugsnag/android/AbstractHttpEntity, com/bugsnag/android/JsonStream$Streamable {
709+
public fun <init> (Ljava/lang/String;Ljava/lang/String;)V
710+
public fun <init> (Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)V
711+
public fun addQueryParameter (Ljava/lang/String;Ljava/lang/String;)V
712+
public fun getHttpMethod ()Ljava/lang/String;
713+
public fun getHttpVersion ()Ljava/lang/String;
714+
public fun getQueryParameter (Ljava/lang/String;)Ljava/lang/String;
715+
public fun getQueryParameterNames ()Ljava/util/Set;
716+
public fun getUrl ()Ljava/lang/String;
717+
public fun removeQueryParameter (Ljava/lang/String;)V
718+
public fun setHttpMethod (Ljava/lang/String;)V
719+
public fun setHttpVersion (Ljava/lang/String;)V
720+
public fun setUrl (Ljava/lang/String;)V
721+
public fun toStream (Lcom/bugsnag/android/JsonStream;)V
722+
}
723+
724+
public final class com/bugsnag/android/Response : com/bugsnag/android/AbstractHttpEntity, com/bugsnag/android/JsonStream$Streamable {
725+
public fun <init> (I)V
726+
public fun getStatusCode ()I
727+
public fun setStatusCode (I)V
728+
public fun toStream (Lcom/bugsnag/android/JsonStream;)V
729+
}
730+
692731
public final class com/bugsnag/android/Session : com/bugsnag/android/Deliverable, com/bugsnag/android/JsonStream$Streamable, com/bugsnag/android/UserAware {
693732
public fun getApiKey ()Ljava/lang/String;
694733
public fun getApp ()Lcom/bugsnag/android/App;
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
package com.bugsnag.android
2+
3+
import org.junit.Assert.assertEquals
4+
import org.junit.Assert.assertNull
5+
import org.junit.Test
6+
7+
class RequestTest {
8+
private val testBodyString = "this is a body string with some content"
9+
private val customBodyLength = 5000L
10+
11+
@Test
12+
fun urlQueryIsExtracted() {
13+
val request = Request(
14+
"1.1",
15+
"GET",
16+
"http://localhost/test?t1=arg1&test2=argument+2"
17+
)
18+
19+
assertEquals("http://localhost/test", request.url)
20+
assertEquals("arg1", request.getQueryParameter("t1"))
21+
assertEquals("argument 2", request.getQueryParameter("test2"))
22+
assertEquals(setOf("t1", "test2"), request.queryParameterNames)
23+
}
24+
25+
@Test
26+
fun setBody() {
27+
val request = Request("1.1", "GET", "http://localhost/")
28+
request.body = testBodyString
29+
assertEquals(testBodyString, request.body)
30+
}
31+
32+
@Test
33+
fun setBodyWithUserLength() {
34+
val request = Request("1.1", "GET", "http://localhost/")
35+
request.bodyLength = customBodyLength
36+
request.body = testBodyString
37+
38+
assertEquals(testBodyString, request.body)
39+
assertEquals(customBodyLength, request.bodyLength)
40+
}
41+
42+
@Test
43+
fun setNullBody() {
44+
val request = Request("1.1", "GET", "http://localhost/")
45+
request.body = testBodyString
46+
request.body = null
47+
assertNull(request.body)
48+
}
49+
50+
@Test
51+
fun setHttpMethod() {
52+
val request = Request("1.1", "GET", "http://localhost/")
53+
assertEquals("GET", request.httpMethod)
54+
55+
request.httpMethod = "POST"
56+
assertEquals("POST", request.httpMethod)
57+
}
58+
59+
@Test
60+
fun setHttpVersion() {
61+
val request = Request("1.1", "1.1", "http://localhost/")
62+
assertEquals("1.1", request.httpVersion)
63+
64+
request.httpVersion = "1.0"
65+
assertEquals("1.0", request.httpVersion)
66+
}
67+
68+
@Test
69+
fun setUrl() {
70+
val request = Request("1.1", "GET", "http://localhost/")
71+
assertEquals("http://localhost/", request.url)
72+
73+
request.url = "https://google.com"
74+
assertEquals("https://google.com", request.url)
75+
}
76+
77+
@Test
78+
fun setUrlWithQuery() {
79+
val request = Request("1.1", "GET", "http://localhost/")
80+
request.url = "http://foo.com?a=1&b=2"
81+
assertEquals("http://foo.com", request.url)
82+
assertEquals("1", request.getQueryParameter("a"))
83+
assertEquals("2", request.getQueryParameter("b"))
84+
assertEquals(setOf("a", "b"), request.queryParameterNames)
85+
}
86+
87+
@Test
88+
fun queryParameters() {
89+
val request = Request("1.1", "GET", "http://localhost/")
90+
request.addQueryParameter("foo", "bar")
91+
request.addQueryParameter("another", "param")
92+
93+
assertEquals("bar", request.getQueryParameter("foo"))
94+
assertEquals("param", request.getQueryParameter("another"))
95+
assertEquals(setOf("foo", "another"), request.queryParameterNames)
96+
97+
request.removeQueryParameter("foo")
98+
assertNull(request.getQueryParameter("foo"))
99+
assertEquals(setOf("another"), request.queryParameterNames)
100+
}
101+
102+
@Test
103+
fun headers() {
104+
val request = Request("1.1", "GET", "http://localhost/")
105+
request.addHeader("X-Test", "value")
106+
request.addHeader("Another-Header", "another-value")
107+
108+
assertEquals("X-Test", request.getHeader("X-Test"))
109+
assertEquals("Another-Header", request.getHeader("Another-Header"))
110+
assertEquals(setOf("X-Test", "Another-Header"), request.headerNames)
111+
112+
request.removeHeader("X-Test")
113+
assertEquals("", request.getHeader("X-Test"))
114+
assertEquals(setOf("Another-Header"), request.headerNames)
115+
}
116+
117+
@Test
118+
fun setBodyLength() {
119+
val request = Request("1.1", "GET", "http://localhost/")
120+
request.bodyLength = 1234
121+
assertEquals(1234, request.bodyLength)
122+
123+
// test negative value is ignored
124+
request.bodyLength = -5
125+
assertEquals(1234, request.bodyLength)
126+
}
127+
}
Lines changed: 111 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
package com.bugsnag.android;
2+
3+
import androidx.annotation.IntRange;
4+
import androidx.annotation.NonNull;
5+
import androidx.annotation.Nullable;
6+
7+
import java.util.Collections;
8+
import java.util.LinkedHashMap;
9+
import java.util.Map;
10+
import java.util.Set;
11+
12+
@SuppressWarnings("ConstantValue")
13+
public abstract class AbstractHttpEntity {
14+
protected final Map<String, String> headers = new LinkedHashMap<>();
15+
16+
@Nullable
17+
private String body;
18+
private long bodyLength = -1L;
19+
20+
// package-protected constructor
21+
AbstractHttpEntity() {
22+
}
23+
24+
/**
25+
* Add a header to this reported HTTP entity.
26+
*
27+
* @param name the name of the header
28+
* @param value the value of the header
29+
*/
30+
public void addHeader(@NonNull String name, @NonNull String value) {
31+
if (name == null || value == null) {
32+
return;
33+
}
34+
35+
headers.put(name, value);
36+
}
37+
38+
/**
39+
* Remove the specified header by name (case-sensitive).
40+
*
41+
* @param name the header to remove
42+
*/
43+
public void removeHeader(@NonNull String name) {
44+
headers.remove(name);
45+
}
46+
47+
/**
48+
* Return the headers that are set for this HTTP entity.
49+
*
50+
* @return the header names
51+
*/
52+
@NonNull
53+
public Set<String> getHeaderNames() {
54+
return Collections.unmodifiableSet(headers.keySet());
55+
}
56+
57+
/**
58+
* Return the HTTP header by name or an empty string if the header is not present.
59+
*
60+
* @param headerName the header name (case-sensitive)
61+
* @return the value of the header or an empty string
62+
*/
63+
@NonNull
64+
public String getHeader(@NonNull String headerName) {
65+
if (headerName == null) {
66+
return "";
67+
}
68+
69+
String headerValue = headers.get(headerName);
70+
return headerValue != null ? headerName : "";
71+
}
72+
73+
/**
74+
* Return the captured HTTP body if one has been set, or null.
75+
*
76+
* @return the captured HTTP body
77+
*/
78+
@Nullable
79+
public String getBody() {
80+
return body;
81+
}
82+
83+
/**
84+
* Set or clear the captured body.
85+
*
86+
* @param body the body to report
87+
*/
88+
public void setBody(@Nullable String body) {
89+
this.body = body;
90+
}
91+
92+
/**
93+
* Return the body length (as set with {@link #setBodyLength(long)}) or -1 if none has been set.
94+
*
95+
* @return the number of bytes in the body
96+
*/
97+
public long getBodyLength() {
98+
return bodyLength;
99+
}
100+
101+
/**
102+
* Change the reported size of the request body size (in bytes).
103+
*
104+
* @param bodyLength the number of bytes in the request body
105+
*/
106+
public void setBodyLength(@IntRange(from = 0L) long bodyLength) {
107+
if (bodyLength >= 0) {
108+
this.bodyLength = bodyLength;
109+
}
110+
}
111+
}

0 commit comments

Comments
 (0)