Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion encryptedlogging/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -72,11 +72,11 @@ dependencies {
ksp(libs.androidx.room.compiler)
implementation(libs.kotlinx.coroutines.android)
implementation(libs.kotlinx.coroutines.core)
implementation(libs.volley)
testImplementation(libs.androidx.test.core.ktx)
testImplementation(libs.assertj.core)
testImplementation(libs.junit)
testImplementation(libs.kotlinx.coroutines.test)
testImplementation(libs.mockwebserver)
testImplementation(libs.robolectric)
androidTestImplementation(libs.assertj.core)
androidTestImplementation(libs.androidx.test.ext.junit)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,9 @@ package com.automattic.encryptedlogging

import android.content.Context
import android.util.Base64
import com.android.volley.RequestQueue
import com.android.volley.toolbox.BasicNetwork
import com.android.volley.toolbox.DiskBasedCache
import com.android.volley.toolbox.HurlStack
import com.automattic.encryptedlogging.model.encryptedlogging.EncryptedLoggingKey
import com.automattic.encryptedlogging.model.encryptedlogging.LogEncrypter
import com.automattic.encryptedlogging.network.EncryptedLogHttpClient
import com.automattic.encryptedlogging.network.rest.wpcom.encryptedlog.EncryptedLogRestClient
import com.automattic.encryptedlogging.persistence.EncryptedLogDatabase
import com.automattic.encryptedlogging.store.EncryptedLogStore
Expand All @@ -23,22 +20,11 @@ internal class AutomatticEncryptedLogging(
encryptedLoggingKey: String,
clientSecret: String,
) : EncryptedLogging {
private companion object {
private const val MAX_CACHE_SIZE_IN_BYTES = 1024 * 1024 * 10
}

private val encryptedLogStore: EncryptedLogStore

init {
val cache = DiskBasedCache(
File.createTempFile("tempcache", null),
MAX_CACHE_SIZE_IN_BYTES
)
val network = BasicNetwork(HurlStack())
val requestQueue = RequestQueue(cache, network).apply {
start()
}
val encryptedLogRestClient = EncryptedLogRestClient(requestQueue, clientSecret)
val httpClient = EncryptedLogHttpClient(clientSecret)
val encryptedLogRestClient = EncryptedLogRestClient(httpClient)
val database = EncryptedLogDatabase.getInstance(context)
val logEncrypter = LogEncrypter(
EncryptedLoggingKey(Key.fromBytes(Base64.decode(encryptedLoggingKey, Base64.DEFAULT)))
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.automattic.encryptedlogging.network

import java.io.IOException
import java.net.HttpURLConnection
import java.net.URL

private const val AUTHORIZATION_HEADER = "Authorization"
private const val CONTENT_TYPE_HEADER = "Content-Type"
private const val CONTENT_TYPE_JSON = "application/json"
private const val UUID_HEADER = "log-uuid"
private const val DEFAULT_UPLOAD_URL = "https://public-api.wordpress.com/rest/v1.1/encrypted-logging/"

internal class EncryptedLogHttpClient(
private val clientSecret: String,
private val uploadUrl: String = DEFAULT_UPLOAD_URL
) {
@Throws(IOException::class)
fun uploadLog(logUuid: String, contents: String): HttpResponse {
val url = URL(uploadUrl)
val connection = (url.openConnection() as HttpURLConnection).apply {
requestMethod = "POST"
doOutput = true
setRequestProperty(CONTENT_TYPE_HEADER, CONTENT_TYPE_JSON)
setRequestProperty(AUTHORIZATION_HEADER, clientSecret)
setRequestProperty(UUID_HEADER, logUuid)
}

try {
connection.outputStream.use { outputStream ->
outputStream.write(contents.toByteArray())
outputStream.flush()
}

val statusCode = connection.responseCode
val responseBody = if (statusCode in 200..299) {
connection.inputStream.bufferedReader().use { it.readText() }
} else {
connection.errorStream?.bufferedReader()?.use { it.readText() } ?: ""
}

return HttpResponse(statusCode, responseBody)
} finally {
connection.disconnect()
}
}
}

internal data class HttpResponse(
val statusCode: Int,
val body: String
)

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,70 +1,52 @@
package com.automattic.encryptedlogging.network.rest.wpcom.encryptedlog

import android.util.Log
import com.android.volley.NoConnectionError
import com.android.volley.RequestQueue
import com.android.volley.VolleyError
import kotlinx.coroutines.suspendCancellableCoroutine
import com.automattic.encryptedlogging.network.EncryptedLogHttpClient
import com.automattic.encryptedlogging.store.UploadEncryptedLogError
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.json.JSONException
import org.json.JSONObject
import com.automattic.encryptedlogging.network.EncryptedLogUploadRequest
import com.automattic.encryptedlogging.store.UploadEncryptedLogError
import kotlin.coroutines.resume
import java.io.IOException

private const val INVALID_REQUEST = "invalid-request"
private const val TOO_MANY_REQUESTS = "too_many_requests"

internal class EncryptedLogRestClient(
private val requestQueue: RequestQueue,
private val clientSecret: String,
private val httpClient: EncryptedLogHttpClient,
) {
suspend fun uploadLog(logUuid: String, contents: String): UploadEncryptedLogResult {
return suspendCancellableCoroutine { cont ->
val request = EncryptedLogUploadRequest(logUuid, contents, clientSecret, {
cont.resume(UploadEncryptedLogResult.LogUploaded)
}, { error ->
cont.resume(UploadEncryptedLogResult.LogUploadFailed(mapError(error)))
})
cont.invokeOnCancellation { request.cancel() }
requestQueue.add(request)
return withContext(Dispatchers.IO) {
try {
val response = httpClient.uploadLog(logUuid, contents)
if (response.statusCode in 200..299) {
UploadEncryptedLogResult.LogUploaded
} else {
UploadEncryptedLogResult.LogUploadFailed(mapError(response.statusCode, response.body))
}
} catch (e: IOException) {
UploadEncryptedLogResult.LogUploadFailed(UploadEncryptedLogError.NoConnection)
}
}
}

/**
* {
* "error":"too_many_requests",
* "message":"You're sending too many messages. Please slow down."
* }
* {
* "error":"invalid-request",
* "message":"Invalid UUID: uuids must only contain letters, numbers, dashes, and curly brackets"
* }
*/
@Suppress("ReturnCount")
private fun mapError(error: VolleyError): UploadEncryptedLogError {
if (error is NoConnectionError) {
return UploadEncryptedLogError.NoConnection
private fun mapError(statusCode: Int, responseBody: String): UploadEncryptedLogError {
val json = try {
JSONObject(responseBody)
} catch (jsonException: JSONException) {
Log.e(TAG, "Received response not in JSON format: " + jsonException.message)
return UploadEncryptedLogError.Unknown(statusCode = statusCode, message = responseBody)
}
error.networkResponse?.let { networkResponse ->
val statusCode = networkResponse.statusCode
val dataString = String(networkResponse.data)
val json = try {
JSONObject(dataString)
} catch (jsonException: JSONException) {
Log.e(TAG, "Received response not in JSON format: " + jsonException.message)
return UploadEncryptedLogError.Unknown(message = dataString)
}
val errorMessage = json.getString("message")
json.getString("error").let { errorType ->
if (errorType == INVALID_REQUEST) {
return UploadEncryptedLogError.InvalidRequest
} else if (errorType == TOO_MANY_REQUESTS) {
return UploadEncryptedLogError.TooManyRequests
}
val errorMessage = json.getString("message")
json.getString("error").let { errorType ->
if (errorType == INVALID_REQUEST) {
return UploadEncryptedLogError.InvalidRequest
} else if (errorType == TOO_MANY_REQUESTS) {
return UploadEncryptedLogError.TooManyRequests
}
return UploadEncryptedLogError.Unknown(statusCode, errorMessage)
}
return UploadEncryptedLogError.Unknown()
return UploadEncryptedLogError.Unknown(statusCode, errorMessage)
}

companion object {
Expand Down
Loading