Skip to content

Commit ee3e12c

Browse files
authored
Merge pull request #73 from DataDog/xgouchet/RUMM-48_gzip
⚡️RUMM-48 gzip
2 parents 7ccb674 + 193921f commit ee3e12c

File tree

10 files changed

+277
-14
lines changed

10 files changed

+277
-14
lines changed

dd-sdk-android/src/androidTest/kotlin/com/datadog/android/internal/utils/utils.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.datadog.android.internal.utils
22

33
import com.datadog.android.log.internal.domain.Log
44
import com.datadog.android.log.internal.net.NetworkInfo
5+
import com.datadog.android.log.internal.user.UserInfo
56
import fr.xgouchet.elmyr.Forge
67
import fr.xgouchet.elmyr.ForgeryException
78
import java.io.FileNotFoundException
@@ -34,7 +35,12 @@ internal fun randomLog(forge: Forge): Log {
3435
carrierId = forge.anInt()
3536
),
3637
loggerName = forge.anAlphabeticalString(),
37-
threadName = forge.anAlphabeticalString()
38+
threadName = forge.anAlphabeticalString(),
39+
userInfo = UserInfo(
40+
id = forge.anHexadecimalString(),
41+
name = forge.anAlphabeticalString(),
42+
email = forge.aStringMatching("[a-z0-9]+@[a-z0-9]+\\.[a-z]{3}")
43+
)
3844
)
3945
}
4046

dd-sdk-android/src/main/kotlin/com/datadog/android/Datadog.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ package com.datadog.android
99
import android.content.Context
1010
import android.os.Build
1111
import android.util.Log
12+
import com.datadog.android.core.internal.net.GzipRequestInterceptor
1213
import com.datadog.android.core.internal.net.NetworkTimeInterceptor
1314
import com.datadog.android.core.internal.time.DatadogTimeProvider
1415
import com.datadog.android.core.internal.time.MutableTimeProvider
@@ -104,6 +105,7 @@ object Datadog {
104105
// prepare time management
105106
timeProvider = DatadogTimeProvider(appContext)
106107
val networkTimeInterceptor = NetworkTimeInterceptor(timeProvider)
108+
val gzipInterceptor = GzipRequestInterceptor()
107109

108110
// Prepare user info management
109111
userInfoProvider = DatadogUserInfoProvider()
@@ -123,7 +125,9 @@ object Datadog {
123125
}
124126
val okHttpClient = OkHttpClient.Builder()
125127
.addInterceptor(networkTimeInterceptor)
128+
.addInterceptor(gzipInterceptor)
126129
.callTimeout(NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)
130+
.writeTimeout(NETWORK_TIMEOUT_MS, TimeUnit.MILLISECONDS)
127131
.protocols(listOf(Protocol.HTTP_2, Protocol.HTTP_1_1))
128132
.connectionSpecs(listOf(connectionSpec))
129133
.build()
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-2019 Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.core.internal.net
8+
9+
import okhttp3.Interceptor
10+
import okhttp3.MediaType
11+
import okhttp3.Request
12+
import okhttp3.RequestBody
13+
import okhttp3.Response
14+
import okio.BufferedSink
15+
import okio.GzipSink
16+
import okio.buffer
17+
18+
/**
19+
* This interceptor compresses the HTTP request body.
20+
*
21+
* This class uses the [GzipSink] to compress the body content.
22+
*/
23+
class GzipRequestInterceptor : Interceptor {
24+
25+
// region Interceptor
26+
27+
/**
28+
* Observes, modifies, or short-circuits requests going out and the responses coming back in.
29+
*/
30+
override fun intercept(chain: Interceptor.Chain): Response {
31+
val originalRequest: Request = chain.request()
32+
val body = originalRequest.body
33+
34+
return if (body == null || originalRequest.header(HEADER_ENCODING) != null) {
35+
chain.proceed(originalRequest)
36+
} else {
37+
val compressedRequest = originalRequest.newBuilder()
38+
.header(HEADER_ENCODING, ENCODING_GZIP)
39+
.method(originalRequest.method, gzip(body))
40+
.build()
41+
chain.proceed(compressedRequest)
42+
}
43+
}
44+
45+
// endregion
46+
47+
// region Internal
48+
49+
private fun gzip(body: RequestBody): RequestBody? {
50+
return object : RequestBody() {
51+
override fun contentType(): MediaType? {
52+
return body.contentType()
53+
}
54+
55+
override fun contentLength(): Long {
56+
return -1 // We don't know the compressed length in advance!
57+
}
58+
59+
override fun writeTo(sink: BufferedSink) {
60+
val gzipSink: BufferedSink = GzipSink(sink).buffer()
61+
body.writeTo(gzipSink)
62+
gzipSink.close()
63+
}
64+
}
65+
}
66+
67+
// endregion
68+
companion object {
69+
private const val HEADER_ENCODING = "Content-Encoding"
70+
private const val ENCODING_GZIP = "gzip"
71+
}
72+
}

dd-sdk-android/src/main/kotlin/com/datadog/android/log/Logger.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -140,10 +140,18 @@ internal constructor(private val handler: LogHandler) {
140140
* @param priority the priority level (must be one of the Android Log.* constants)
141141
* @param message the message to be logged
142142
* @param throwable a (nullable) throwable to be logged with the message
143+
* @param attributes a map of attributes to include only for this message. If an attribute with
144+
* the same key already exist in this logger, it will be overridden (just for this message)
143145
*
144146
*/
145-
fun log(priority: Int, message: String, throwable: Throwable? = null) {
146-
internalLog(priority, message, throwable, emptyMap())
147+
@JvmOverloads
148+
fun log(
149+
priority: Int,
150+
message: String,
151+
throwable: Throwable? = null,
152+
attributes: Map<String, Any?> = emptyMap()
153+
) {
154+
internalLog(priority, message, throwable, attributes)
147155
}
148156

149157
// endregion

dd-sdk-android/src/main/kotlin/com/datadog/android/log/internal/net/LogOkHttpUploader.kt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ import java.io.IOException
1313
import java.util.Locale
1414
import okhttp3.OkHttpClient
1515
import okhttp3.Request
16-
import okhttp3.RequestBody
16+
import okhttp3.RequestBody.Companion.toRequestBody
1717

1818
internal class LogOkHttpUploader(
1919
endpoint: String,
@@ -70,7 +70,7 @@ internal class LogOkHttpUploader(
7070
sdkLogger.d("$TAG: Sending logs to $url")
7171
return Request.Builder()
7272
.url(url)
73-
.post(RequestBody.create(null, data))
73+
.post(data.toRequestBody())
7474
.addHeader(HEADER_UA, userAgent)
7575
.addHeader(HEADER_CT, CONTENT_TYPE)
7676
.build()
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
/*
2+
* Unless explicitly stated otherwise all files in this repository are licensed under the Apache License Version 2.0.
3+
* This product includes software developed at Datadog (https://www.datadoghq.com/).
4+
* Copyright 2016-2019 Datadog, Inc.
5+
*/
6+
7+
package com.datadog.android.core.internal.net
8+
9+
import com.datadog.android.log.forge.Configurator
10+
import com.nhaarman.mockitokotlin2.any
11+
import com.nhaarman.mockitokotlin2.argumentCaptor
12+
import com.nhaarman.mockitokotlin2.doReturn
13+
import com.nhaarman.mockitokotlin2.verify
14+
import com.nhaarman.mockitokotlin2.whenever
15+
import fr.xgouchet.elmyr.Forge
16+
import fr.xgouchet.elmyr.junit5.ForgeConfiguration
17+
import fr.xgouchet.elmyr.junit5.ForgeExtension
18+
import java.io.ByteArrayOutputStream
19+
import okhttp3.Interceptor
20+
import okhttp3.Protocol
21+
import okhttp3.Request
22+
import okhttp3.RequestBody.Companion.toRequestBody
23+
import okhttp3.Response
24+
import okio.Buffer
25+
import org.assertj.core.api.Assertions.assertThat
26+
import org.junit.jupiter.api.BeforeEach
27+
import org.junit.jupiter.api.Test
28+
import org.junit.jupiter.api.extension.ExtendWith
29+
import org.junit.jupiter.api.extension.Extensions
30+
import org.mockito.Mock
31+
import org.mockito.junit.jupiter.MockitoExtension
32+
import org.mockito.junit.jupiter.MockitoSettings
33+
import org.mockito.quality.Strictness
34+
35+
@Extensions(
36+
ExtendWith(MockitoExtension::class),
37+
ExtendWith(ForgeExtension::class)
38+
)
39+
@MockitoSettings(strictness = Strictness.LENIENT)
40+
@ForgeConfiguration(Configurator::class)
41+
internal class GzipRequestInterceptorTest {
42+
lateinit var testedInterceptor: Interceptor
43+
44+
@Mock
45+
lateinit var mockChain: Interceptor.Chain
46+
47+
lateinit var fakeRequest: Request
48+
lateinit var fakeResponse: Response
49+
lateinit var fakeBody: String
50+
51+
@BeforeEach
52+
fun `set up`(forge: Forge) {
53+
val fakeUrl = forge.aStringMatching("http://[a-z0-9_]{8}\\.[a-z]{3}")
54+
fakeBody = forge.anAlphabeticalString()
55+
fakeRequest = Request.Builder()
56+
.url(fakeUrl)
57+
.post(fakeBody.toByteArray().toRequestBody())
58+
.build()
59+
testedInterceptor = GzipRequestInterceptor()
60+
}
61+
62+
@Test
63+
fun `compress body when no encoding is used`() {
64+
setupFakeResponse()
65+
66+
val response = testedInterceptor.intercept(mockChain)
67+
68+
argumentCaptor<Request> {
69+
verify(mockChain).proceed(capture())
70+
val buffer = Buffer()
71+
val stream = ByteArrayOutputStream()
72+
lastValue.body!!.writeTo(buffer)
73+
buffer.copyTo(stream)
74+
75+
assertThat(stream.toString())
76+
.isNotEqualTo(fakeBody)
77+
78+
assertThat(lastValue.header("Content-Encoding"))
79+
.isEqualTo("gzip")
80+
}
81+
assertThat(response)
82+
.isSameAs(fakeResponse)
83+
}
84+
85+
@Test
86+
fun `ignores body when encoding is set`() {
87+
fakeRequest = fakeRequest.newBuilder()
88+
.header("Content-Encoding", "identity")
89+
.build()
90+
setupFakeResponse()
91+
92+
val response = testedInterceptor.intercept(mockChain)
93+
94+
argumentCaptor<Request> {
95+
verify(mockChain).proceed(capture())
96+
val buffer = Buffer()
97+
val stream = ByteArrayOutputStream()
98+
lastValue.body!!.writeTo(buffer)
99+
buffer.copyTo(stream)
100+
101+
assertThat(stream.toString())
102+
.isEqualTo(fakeBody)
103+
assertThat(lastValue.header("Content-Encoding"))
104+
.isEqualTo("identity")
105+
}
106+
assertThat(response)
107+
.isSameAs(fakeResponse)
108+
}
109+
110+
@Test
111+
fun `ignores body when body is null`() {
112+
fakeRequest = fakeRequest.newBuilder()
113+
.get()
114+
.build()
115+
setupFakeResponse()
116+
117+
val response = testedInterceptor.intercept(mockChain)
118+
119+
argumentCaptor<Request> {
120+
verify(mockChain).proceed(capture())
121+
assertThat(lastValue.body)
122+
.isNull()
123+
assertThat(lastValue.header("Content-Encoding"))
124+
.isNull()
125+
}
126+
assertThat(response)
127+
.isSameAs(fakeResponse)
128+
}
129+
130+
// region Internal
131+
132+
private fun setupFakeResponse() {
133+
134+
val builder = Response.Builder()
135+
.request(fakeRequest)
136+
.protocol(Protocol.HTTP_2)
137+
.code(200)
138+
.message("{}")
139+
140+
fakeResponse = builder.build()
141+
142+
whenever(mockChain.request()) doReturn fakeRequest
143+
whenever(mockChain.proceed(any())) doReturn fakeResponse
144+
}
145+
146+
// endregion
147+
}

sample/java/src/main/java/com/datadog/android/sample/webview/WebFragment.java

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
import android.os.Build;
1414
import android.os.Bundle;
1515
import android.os.Message;
16+
import android.util.Log;
1617
import android.view.KeyEvent;
1718
import android.view.LayoutInflater;
1819
import android.view.View;
@@ -212,7 +213,28 @@ public boolean onJsAlert(
212213

213214
@Override
214215
public boolean onConsoleMessage(final ConsoleMessage consoleMessage) {
215-
mLogger.w(
216+
int level;
217+
switch (consoleMessage.messageLevel()) {
218+
case TIP:
219+
level = Log.INFO;
220+
break;
221+
case LOG:
222+
level = Log.VERBOSE;
223+
break;
224+
case WARNING:
225+
level = Log.WARN;
226+
break;
227+
case ERROR:
228+
level = Log.ERROR;
229+
break;
230+
case DEBUG:
231+
default:
232+
level = Log.DEBUG;
233+
break;
234+
}
235+
236+
mLogger.log(
237+
level,
216238
"onConsoleMessage",
217239
null,
218240
new HashMap<String, Object>() {{

sample/kotlin-timber/build.gradle.kts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,12 @@
66

77
import com.datadog.gradle.Dependencies
88
import com.datadog.gradle.config.AndroidConfig
9-
import com.datadog.gradle.config.bintrayConfig
109
import com.datadog.gradle.config.dependencyUpdateConfig
1110
import com.datadog.gradle.config.detektConfig
12-
import com.datadog.gradle.config.jacocoConfig
1311
import com.datadog.gradle.config.javadocConfig
1412
import com.datadog.gradle.config.junitConfig
1513
import com.datadog.gradle.config.kotlinConfig
1614
import com.datadog.gradle.config.ktLintConfig
17-
import com.datadog.gradle.config.publishingConfig
1815

1916
plugins {
2017
id("com.android.application")

sample/kotlin/build.gradle.kts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,12 @@
66

77
import com.datadog.gradle.Dependencies
88
import com.datadog.gradle.config.AndroidConfig
9-
import com.datadog.gradle.config.bintrayConfig
109
import com.datadog.gradle.config.dependencyUpdateConfig
1110
import com.datadog.gradle.config.detektConfig
12-
import com.datadog.gradle.config.jacocoConfig
1311
import com.datadog.gradle.config.javadocConfig
1412
import com.datadog.gradle.config.junitConfig
1513
import com.datadog.gradle.config.kotlinConfig
1614
import com.datadog.gradle.config.ktLintConfig
17-
import com.datadog.gradle.config.publishingConfig
1815

1916
plugins {
2017
id("com.android.application")

0 commit comments

Comments
 (0)