Skip to content

Commit

Permalink
feat: added gzip support to web
Browse files Browse the repository at this point in the history
feat: added gzip support to web
  • Loading branch information
itsdebs authored Sep 18, 2023
2 parents cc5b0a6 + 6a980bc commit 2d3c625
Show file tree
Hide file tree
Showing 4 changed files with 233 additions and 82 deletions.
11 changes: 11 additions & 0 deletions web/src/main/java/com/rudderstack/web/WebService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,7 @@ interface WebService {
body: String?,
endpoint: String,
responseClass: Class<T>,
isGzipEnabled: Boolean = false,
): Future<HttpResponse<T>>

/**
Expand All @@ -132,6 +133,7 @@ interface WebService {
body: String?,
endpoint: String,
responseTypeAdapter: RudderTypeAdapter<T>,
isGzipEnabled: Boolean = false,
): Future<HttpResponse<T>>

/**
Expand All @@ -151,6 +153,7 @@ interface WebService {
body: String?,
endpoint: String,
responseClass: Class<T>,
isGzipEnabled: Boolean = false,
callback: (HttpResponse<T>) -> Unit,
)

Expand All @@ -171,6 +174,7 @@ interface WebService {
body: String?,
endpoint: String,
responseTypeAdapter: RudderTypeAdapter<T>,
isGzipEnabled: Boolean = false,
callback: (HttpResponse<T>) -> Unit,
)

Expand All @@ -181,4 +185,11 @@ interface WebService {
* @see HttpInterceptor
*/
fun setInterceptor(httpInterceptor: HttpInterceptor)

/**
* Performs Cleanup. Also shuts down the executor service. Do not call this method if the
* same executor service is being used by other objects. In that case handle the executor
* shutdown yourself.
*/
fun shutdown(shutdownExecutor: Boolean = true)
}
128 changes: 79 additions & 49 deletions web/src/main/java/com/rudderstack/web/internal/WebServiceImpl.kt
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import com.rudderstack.rudderjsonadapter.RudderTypeAdapter
import com.rudderstack.web.HttpInterceptor
import com.rudderstack.web.HttpResponse
import com.rudderstack.web.WebService
import com.rudderstack.web.utils.GzipUtils
import java.io.BufferedInputStream
import java.io.ByteArrayOutputStream
import java.io.IOException
Expand All @@ -38,8 +39,7 @@ class WebServiceImpl internal constructor(
private val baseUrl: String

private enum class HttpMethod {
POST,
GET
POST, GET
}

init {
Expand All @@ -53,7 +53,7 @@ class WebServiceImpl internal constructor(
responseClass: Class<T>
): Future<HttpResponse<T>> {
return executor.submit(Callable {
httpCall(headers, query, null, endpoint, HttpMethod.GET, responseClass)
httpCall(headers, query, null, endpoint, HttpMethod.GET, responseClass, false)
})
}

Expand All @@ -64,7 +64,7 @@ class WebServiceImpl internal constructor(
responseTypeAdapter: RudderTypeAdapter<T>
): Future<HttpResponse<T>> {
return executor.submit(Callable {
httpCall(headers, query, null, endpoint, HttpMethod.GET, responseTypeAdapter)
httpCall(headers, query, null, endpoint, HttpMethod.GET, responseTypeAdapter, false)
})
}

Expand All @@ -77,7 +77,7 @@ class WebServiceImpl internal constructor(
) {
executor.execute {
callback.invoke(
httpCall(headers, query, null, endpoint, HttpMethod.GET, responseTypeAdapter)
httpCall(headers, query, null, endpoint, HttpMethod.GET, responseTypeAdapter, false)
)
}
}
Expand All @@ -92,7 +92,7 @@ class WebServiceImpl internal constructor(

executor.execute {
callback.invoke(
httpCall(headers, query, null, endpoint, HttpMethod.GET, responseClass)
httpCall(headers, query, null, endpoint, HttpMethod.GET, responseClass, false)
)
}
}
Expand All @@ -102,10 +102,11 @@ class WebServiceImpl internal constructor(
query: Map<String, String>?,
body: String?,
endpoint: String,
responseClass: Class<T>
responseClass: Class<T>,
isGzipEnabled: Boolean
): Future<HttpResponse<T>> {
return executor.submit(Callable {
httpCall(headers, query, body, endpoint, HttpMethod.POST, responseClass)
httpCall(headers, query, body, endpoint, HttpMethod.POST, responseClass, isGzipEnabled)
})
}

Expand All @@ -114,10 +115,19 @@ class WebServiceImpl internal constructor(
query: Map<String, String>?,
body: String?,
endpoint: String,
responseTypeAdapter: RudderTypeAdapter<T>
responseTypeAdapter: RudderTypeAdapter<T>,
isGzipEnabled: Boolean
): Future<HttpResponse<T>> {
return executor.submit(Callable {
httpCall(headers, query, body, endpoint, HttpMethod.POST, responseTypeAdapter)
httpCall(
headers,
query,
body,
endpoint,
HttpMethod.POST,
responseTypeAdapter,
isGzipEnabled
)
})
}

Expand All @@ -127,11 +137,20 @@ class WebServiceImpl internal constructor(
body: String?,
endpoint: String,
responseClass: Class<T>,
isGzipEnabled: Boolean,
callback: (HttpResponse<T>) -> Unit
) {
executor.execute {
callback.invoke(
httpCall(headers, query, body, endpoint, HttpMethod.POST, responseClass)
httpCall(
headers,
query,
body,
endpoint,
HttpMethod.POST,
responseClass,
isGzipEnabled
)
)
}
}
Expand All @@ -142,6 +161,7 @@ class WebServiceImpl internal constructor(
body: String?,
endpoint: String,
responseTypeAdapter: RudderTypeAdapter<T>,
isGzipEnabled: Boolean,
callback: (HttpResponse<T>) -> Unit
) {
executor.execute {
Expand All @@ -152,7 +172,8 @@ class WebServiceImpl internal constructor(
body,
endpoint,
HttpMethod.POST,
responseTypeAdapter
responseTypeAdapter,
isGzipEnabled
)
)
}
Expand All @@ -163,20 +184,26 @@ class WebServiceImpl internal constructor(
_interceptor = httpInterceptor
}

override fun shutdown(shutdownExecutor: Boolean) {
if(shutdownExecutor)
executor.shutdown()
_interceptor = null
}

// @Throws(Throwable::class)
private fun <T : Any> httpCall(
headers: Map<String, String>?,
query: Map<String, String>?,
body: String?,
endpoint: String,
type: HttpMethod,

responseClass: Class<T>
responseClass: Class<T>,
isGzipEncoded: Boolean
): HttpResponse<T> {
return rawHttpCall(headers, query, body, endpoint, type, deserializer = { json ->
jsonAdapter.readJson(json, responseClass)
?: throw IllegalArgumentException("Json adapter not able to parse response body")
})
?: throw IllegalArgumentException("Json adapter not able to parse response body")
}, isGzipEncoded = isGzipEncoded)
}

@Throws(IOException::class)
Expand All @@ -186,17 +213,17 @@ class WebServiceImpl internal constructor(
body: String?,
endpoint: String,
type: HttpMethod,
typeAdapter: RudderTypeAdapter<T>
typeAdapter: RudderTypeAdapter<T>,
isGzipEncoded: Boolean
): HttpResponse<T> {
return rawHttpCall(headers, query, body, endpoint, type, deserializer = { json ->
if (json.isEmpty()) {
//TODO add logger
// logger.debug("Empty response body")
null
} else
jsonAdapter.readJson(json, typeAdapter)
?: throw IllegalArgumentException("Json adapter not able to parse response body")
})
} else jsonAdapter.readJson(json, typeAdapter)
?: throw IllegalArgumentException("Json adapter not able to parse response body")
}, isGzipEncoded = isGzipEncoded)
}


Expand All @@ -206,15 +233,23 @@ class WebServiceImpl internal constructor(
body: String?,
endpoint: String,
type: HttpMethod,
isGzipEncoded: Boolean,
deserializer: (String) -> T?
): HttpResponse<T> {
try {

val httpConnection =
createHttpConnection(endpoint, headers, type, query, body, defaultTimeout) {
//call interceptor if any changes to HttpConnection required
_interceptor?.intercept(it) ?: it
}
val httpConnection = createHttpConnection(
endpoint,
headers,
type,
query,
body,
defaultTimeout,
isGzipEncoded
) {
//call interceptor if any changes to HttpConnection required
_interceptor?.intercept(it) ?: it
}
// create connection
httpConnection.connect()

Expand All @@ -233,9 +268,7 @@ class WebServiceImpl internal constructor(
return Utils.NetworkResponses.SUCCESS
}*/
HttpResponse(
httpConnection.responseCode,
deserializer.invoke(baos.toString()),
null
httpConnection.responseCode, deserializer.invoke(baos.toString()), null
)

} else {
Expand All @@ -256,10 +289,7 @@ class WebServiceImpl internal constructor(
// RudderLogger.logError(ex)
ex.printStackTrace()
return HttpResponse(
status = HttpResponse.HTTP_STATUS_NONE,
body = null,
errorBody = null,
error = ex
status = HttpResponse.HTTP_STATUS_NONE, body = null, errorBody = null, error = ex
)
}

Expand All @@ -269,9 +299,12 @@ class WebServiceImpl internal constructor(
@Throws(IOException::class)
private fun createHttpConnection(
endpoint: String,
headers: Map<String, String>?, type: HttpMethod,
headers: Map<String, String>?,
type: HttpMethod,
query: Map<String, String>?,
body: String?, defaultTimeout: Int,
body: String?,
defaultTimeout: Int,
isGzipEncoded: Boolean,
onHttpConnectionCreated: (HttpURLConnection) -> HttpURLConnection
): HttpURLConnection {
//the url to hit
Expand All @@ -290,15 +323,10 @@ class WebServiceImpl internal constructor(
}

// set content type for network request if not present
if (headers?.containsKey("Content-Type") == false)
httpConn.setRequestProperty("Content-Type", "application/json")
// set authorization header
/*httpConnection.setRequestProperty(
"Authorization",
String.format(Locale.US, "Basic %s", this.authHeaderString)
)*/
// set anonymousId header for definitive routing
// httpConnection.setRequestProperty("AnonymousId", this.anonymousIdHeaderString)
if (headers?.containsKey("Content-Type") == false) httpConn.setRequestProperty(
"Content-Type", "application/json"
)

// set request method
httpConn.requestMethod = when (type) {
HttpMethod.GET -> "GET"
Expand All @@ -310,7 +338,10 @@ class WebServiceImpl internal constructor(
if (type == HttpMethod.POST) {
// set connection object to return output
httpConn.doOutput = true
val os = httpConn.outputStream
val os = if (isGzipEncoded) GzipUtils.getGzipOutputStream(httpConn.outputStream)
?: //TODO add logger.debug("Gzip compression not supported")
httpConn.outputStream
else httpConn.outputStream
val osw = OutputStreamWriter(os, "UTF-8")

body?.apply {
Expand All @@ -323,10 +354,9 @@ class WebServiceImpl internal constructor(
return httpConn
}

private fun Map<String, String>.createQueryString() =
takeIf { isNotEmpty() }?.map {
"${it.key}=${it.value}"
}?.reduce { acc, s -> "$acc&$s" } ?: ""
private fun Map<String, String>.createQueryString() = takeIf { isNotEmpty() }?.map {
"${it.key}=${it.value}"
}?.reduce { acc, s -> "$acc&$s" } ?: ""

private val String.validatedBaseUrl
get() = if (this.endsWith('/')) this else "$this/"
Expand Down
41 changes: 41 additions & 0 deletions web/src/main/java/com/rudderstack/web/utils/GzipUtils.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
/*
* Creator: Debanjan Chatterjee on 13/09/23, 12:11 pm Last modified: 13/09/23, 12:11 pm
* Copyright: All rights reserved Ⓒ 2023 http://rudderstack.com
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may
* not use this file except in compliance with the License. You may obtain a
* copy of the License at http://www.apache.org/licenses/LICENSE-2.0
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/

package com.rudderstack.web.utils

import java.io.IOException
import java.io.InputStream
import java.io.OutputStream
import java.util.zip.GZIPInputStream
import java.util.zip.GZIPOutputStream

object GzipUtils {
fun getGzipOutputStream(outputStream: OutputStream?): OutputStream? {
try {
return GZIPOutputStream(outputStream)
} catch (e: IOException) {
e.printStackTrace()
}
return null
}
fun getGzipInputStream(inputStream: InputStream?): InputStream? {
try {
return GZIPInputStream(inputStream)
} catch (e: IOException) {
e.printStackTrace()
}
return null
}

}
Loading

0 comments on commit 2d3c625

Please sign in to comment.