mirror of https://github.com/M66B/FairEmail.git
parent
fd37d59f6b
commit
b4f82fa99d
@ -1,175 +0,0 @@
|
|||||||
package com.bugsnag.android
|
|
||||||
|
|
||||||
import android.net.TrafficStats
|
|
||||||
import com.bugsnag.android.internal.JsonHelper
|
|
||||||
import java.io.IOException
|
|
||||||
import java.net.HttpURLConnection
|
|
||||||
import java.net.HttpURLConnection.HTTP_BAD_REQUEST
|
|
||||||
import java.net.HttpURLConnection.HTTP_CLIENT_TIMEOUT
|
|
||||||
import java.net.HttpURLConnection.HTTP_OK
|
|
||||||
import java.net.URL
|
|
||||||
|
|
||||||
internal class DefaultDelivery(
|
|
||||||
private val connectivity: Connectivity?,
|
|
||||||
private val apiKey: String,
|
|
||||||
private val maxStringValueLength: Int,
|
|
||||||
private val logger: Logger
|
|
||||||
) : Delivery {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
// 1MB with some fiddle room in case of encoding overhead
|
|
||||||
const val maxPayloadSize = 999700
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deliver(payload: Session, deliveryParams: DeliveryParams): DeliveryStatus {
|
|
||||||
val status = deliver(
|
|
||||||
deliveryParams.endpoint,
|
|
||||||
JsonHelper.serialize(payload),
|
|
||||||
deliveryParams.headers
|
|
||||||
)
|
|
||||||
logger.i("Session API request finished with status $status")
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun serializePayload(payload: EventPayload): ByteArray {
|
|
||||||
var json = JsonHelper.serialize(payload)
|
|
||||||
if (json.size <= maxPayloadSize) {
|
|
||||||
return json
|
|
||||||
}
|
|
||||||
|
|
||||||
var event = payload.event
|
|
||||||
if (event == null) {
|
|
||||||
event = MarshalledEventSource(payload.eventFile!!, apiKey, logger).invoke()
|
|
||||||
payload.event = event
|
|
||||||
payload.apiKey = apiKey
|
|
||||||
}
|
|
||||||
|
|
||||||
val (itemsTrimmed, dataTrimmed) = event.impl.trimMetadataStringsTo(maxStringValueLength)
|
|
||||||
event.impl.internalMetrics.setMetadataTrimMetrics(
|
|
||||||
itemsTrimmed,
|
|
||||||
dataTrimmed
|
|
||||||
)
|
|
||||||
|
|
||||||
json = JsonHelper.serialize(payload)
|
|
||||||
if (json.size <= maxPayloadSize) {
|
|
||||||
return json
|
|
||||||
}
|
|
||||||
|
|
||||||
val breadcrumbAndBytesRemovedCounts =
|
|
||||||
event.impl.trimBreadcrumbsBy(json.size - maxPayloadSize)
|
|
||||||
event.impl.internalMetrics.setBreadcrumbTrimMetrics(
|
|
||||||
breadcrumbAndBytesRemovedCounts.itemsTrimmed,
|
|
||||||
breadcrumbAndBytesRemovedCounts.dataTrimmed
|
|
||||||
)
|
|
||||||
return JsonHelper.serialize(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun deliver(payload: EventPayload, deliveryParams: DeliveryParams): DeliveryStatus {
|
|
||||||
val json = serializePayload(payload)
|
|
||||||
val status = deliver(deliveryParams.endpoint, json, deliveryParams.headers)
|
|
||||||
logger.i("Error API request finished with status $status")
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
fun deliver(
|
|
||||||
urlString: String,
|
|
||||||
json: ByteArray,
|
|
||||||
headers: Map<String, String?>
|
|
||||||
): DeliveryStatus {
|
|
||||||
|
|
||||||
TrafficStats.setThreadStatsTag(1)
|
|
||||||
if (connectivity != null && !connectivity.hasNetworkConnection()) {
|
|
||||||
return DeliveryStatus.UNDELIVERED
|
|
||||||
}
|
|
||||||
var conn: HttpURLConnection? = null
|
|
||||||
|
|
||||||
try {
|
|
||||||
conn = makeRequest(URL(urlString), json, headers)
|
|
||||||
|
|
||||||
// End the request, get the response code
|
|
||||||
val responseCode = conn.responseCode
|
|
||||||
val status = getDeliveryStatus(responseCode)
|
|
||||||
logRequestInfo(responseCode, conn, status)
|
|
||||||
return status
|
|
||||||
} catch (oom: OutOfMemoryError) {
|
|
||||||
// attempt to persist the payload on disk. This approach uses streams to write to a
|
|
||||||
// file, which takes less memory than serializing the payload into a ByteArray, and
|
|
||||||
// therefore has a reasonable chance of retaining the payload for future delivery.
|
|
||||||
logger.w("Encountered OOM delivering payload, falling back to persist on disk", oom)
|
|
||||||
return DeliveryStatus.UNDELIVERED
|
|
||||||
} catch (exception: IOException) {
|
|
||||||
logger.w("IOException encountered in request", exception)
|
|
||||||
return DeliveryStatus.UNDELIVERED
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
logger.w("Unexpected error delivering payload", exception)
|
|
||||||
return DeliveryStatus.FAILURE
|
|
||||||
} finally {
|
|
||||||
conn?.disconnect()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeRequest(
|
|
||||||
url: URL,
|
|
||||||
json: ByteArray,
|
|
||||||
headers: Map<String, String?>
|
|
||||||
): HttpURLConnection {
|
|
||||||
val conn = url.openConnection() as HttpURLConnection
|
|
||||||
conn.doOutput = true
|
|
||||||
|
|
||||||
// avoids creating a buffer within HttpUrlConnection, see
|
|
||||||
// https://developer.android.com/reference/java/net/HttpURLConnection
|
|
||||||
conn.setFixedLengthStreamingMode(json.size)
|
|
||||||
|
|
||||||
// calculate the SHA-1 digest and add all other headers
|
|
||||||
computeSha1Digest(json)?.let { digest ->
|
|
||||||
conn.addRequestProperty(HEADER_BUGSNAG_INTEGRITY, digest)
|
|
||||||
}
|
|
||||||
headers.forEach { (key, value) ->
|
|
||||||
if (value != null) {
|
|
||||||
conn.addRequestProperty(key, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// write the JSON payload
|
|
||||||
conn.outputStream.use {
|
|
||||||
it.write(json)
|
|
||||||
}
|
|
||||||
return conn
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun logRequestInfo(code: Int, conn: HttpURLConnection, status: DeliveryStatus) {
|
|
||||||
runCatching {
|
|
||||||
logger.i(
|
|
||||||
"Request completed with code $code, " +
|
|
||||||
"message: ${conn.responseMessage}, " +
|
|
||||||
"headers: ${conn.headerFields}"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
runCatching {
|
|
||||||
conn.inputStream.bufferedReader().use {
|
|
||||||
logger.d("Received request response: ${it.readText()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
runCatching {
|
|
||||||
if (status != DeliveryStatus.DELIVERED) {
|
|
||||||
conn.errorStream.bufferedReader().use {
|
|
||||||
logger.w("Request error details: ${it.readText()}")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal fun getDeliveryStatus(responseCode: Int): DeliveryStatus {
|
|
||||||
return when {
|
|
||||||
responseCode in HTTP_OK..299 -> DeliveryStatus.DELIVERED
|
|
||||||
isUnrecoverableStatusCode(responseCode) -> DeliveryStatus.FAILURE
|
|
||||||
else -> DeliveryStatus.UNDELIVERED
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun isUnrecoverableStatusCode(responseCode: Int) =
|
|
||||||
responseCode in HTTP_BAD_REQUEST..499 && // 400-499 are considered unrecoverable
|
|
||||||
responseCode != HTTP_CLIENT_TIMEOUT && // except for 408
|
|
||||||
responseCode != 429 // and 429
|
|
||||||
}
|
|
Loading…
Reference in new issue