Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.ooni.probe.config

object BuildTypeDefaults : BuildTypeDefaultsInterface {
override val ooniApiBaseUrl = "https://api.dev.ooni.io"
override val ooniApiFallbackUrl = "https://api.oo-srv.com"
override val ooniRunDomain = "run.test.ooni.org"
override val ooniRunDashboardUrl = "https://run.test.ooni.org"
override val explorerUrl = "https://explorer.dev.ooni.org"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,15 @@ interface OrganizationConfigInterface {
val descriptorLanguageCodes: Set<String>

val ooniApiBaseUrl get() = BuildTypeDefaults.ooniApiBaseUrl
val ooniApiFallbackUrl get() = BuildTypeDefaults.ooniApiFallbackUrl
val ooniRunDomain get() = BuildTypeDefaults.ooniRunDomain
val ooniRunDashboardUrl get() = BuildTypeDefaults.ooniRunDashboardUrl
val explorerUrl get() = BuildTypeDefaults.explorerUrl
}

interface BuildTypeDefaultsInterface {
val ooniApiBaseUrl: String
val ooniApiFallbackUrl: String
val ooniRunDomain: String
val ooniRunDashboardUrl: String
val explorerUrl: String
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ import org.ooni.passport.models.PassportException
import org.ooni.passport.models.SubmitCredentialConfig
import org.ooni.passport.models.SubmitError
import org.ooni.passport.models.VerificationStatus
import org.ooni.probe.config.BuildTypeDefaults
import org.ooni.passport.models.isOfflineFailure
import org.ooni.probe.config.OrganizationConfig
import org.ooni.probe.data.models.Credential
import org.ooni.probe.data.models.Manifest
import org.ooni.probe.data.models.MeasurementModel
Expand Down Expand Up @@ -46,17 +47,49 @@ class SubmitMeasurementWithUser(

val credentialConfig = buildCredentialConfig(manifest, credential, data)

return when (
val primaryUrl = "${OrganizationConfig.ooniApiBaseUrl}/api/v1/submit_measurement"
val fallbackUrl = "${OrganizationConfig.ooniApiFallbackUrl}/api/v1/submit_measurement"

var result = submitToUrl(
url = primaryUrl,
stamped = stamped,
data = data,
credentialConfig = credentialConfig,
credential = credential,
)

if (result is Failure && !result.reason.isOfflineFailure() && primaryUrl != fallbackUrl) {
Logger.w("Primary user submit failed, trying fallback endpoint: $fallbackUrl", result.reason)
result = submitToUrl(
url = fallbackUrl,
stamped = stamped,
data = data,
credentialConfig = credentialConfig,
credential = credential,
)
}

return result
}

private suspend fun submitToUrl(
url: String,
stamped: String,
data: MeasurementData,
credentialConfig: SubmitCredentialConfig?,
credential: Credential?,
): Result<SubmitMeasurement.ResponseData, Throwable?> =
when (
val result = userAuthSubmit(
"${BuildTypeDefaults.ooniApiBaseUrl}/api/v1/submit_measurement",
url,
stamped,
data.probeCc,
data.probeAsn,
credentialConfig,
)
) {
is Failure -> {
Logger.w("Failed to submit measurement with user", result.reason)
Logger.w("Failed to submit measurement with user to $url", result.reason)
Failure(result.reason)
}

Expand All @@ -67,10 +100,11 @@ class SubmitMeasurementWithUser(
statusCode = credentialResponse.response.statusCode,
responseBody = credentialResponse.response.bodyText?.take(MAX_ERROR_BODY_LENGTH),
)
Logger.w("Submit returned non-2XX", exception)
Logger.w("Submit to $url returned non-2XX", exception)
Instrumentation.reportTransaction(
operation = "SubmitHttpError",
data = mapOf(
"url" to url,
"status_code" to credentialResponse.response.statusCode,
"error" to (credentialResponse.response.bodyText?.take(10) ?: "unknown"),
),
Expand Down Expand Up @@ -103,7 +137,6 @@ class SubmitMeasurementWithUser(
}
}
}
}

private fun httpRecoveryError(statusCode: Int): SubmitError? =
when (statusCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package org.ooni.probe.config

object BuildTypeDefaults : BuildTypeDefaultsInterface {
override val ooniApiBaseUrl = "https://api.ooni.org"
override val ooniApiFallbackUrl = "https://api.oo-srv.com"
override val ooniRunDomain = "run.ooni.org"
override val ooniRunDashboardUrl = "https://run.ooni.org"
override val explorerUrl = "https://explorer.ooni.org"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@ import org.ooni.passport.models.CredentialResponse
import org.ooni.passport.models.PassportException
import org.ooni.passport.models.PassportHttpResponse
import org.ooni.passport.models.SubmitError
import org.ooni.probe.config.OrganizationConfig
import org.ooni.probe.data.models.MeasurementModel
import org.ooni.probe.domain.SubmitMeasurement
import org.ooni.testing.factories.ManifestFactory
import kotlin.test.Test
import kotlin.test.assertEquals
Expand Down Expand Up @@ -41,6 +44,103 @@ class SubmitMeasurementWithUserTest {
handleSubmitOutcome = { _, error -> onSubmitOutcome(error) },
)

@Test
fun primaryEndpointIsUsedByDefault() =
runTest {
val calledUrls = mutableListOf<String>()
val subject = SubmitMeasurementWithUser(
getManifest = { flowOf(ManifestFactory.build()) },
getCredential = { null },
setCredential = SetCredential(
writeSecureStorage = { _, _ -> error("setCredential should not be used") },
json = json,
),
stampMeasurement = StampMeasurement(
passportGetProbeId = { _, _, _ -> error("getProbeId should not be used") },
getCredential = { null },
json = json,
),
resolveSubmissionPolicy = ResolveSubmissionPolicy(),
userAuthSubmit = { url, _, _, _, _ ->
calledUrls.add(url)
Success(
CredentialResponse(
response = PassportHttpResponse(
statusCode = 200,
version = "HTTP/1.1",
headersListText = emptyList(),
bodyText = """{"measurement_uid":"uid-123"}""",
),
credential = null,
),
)
},
json = json,
)

val result = subject(measurementData)

val success = assertIs<Success<SubmitMeasurement.ResponseData>>(result)
assertEquals(MeasurementModel.Uid("uid-123"), success.value.uid)
assertEquals(
listOf("${OrganizationConfig.ooniApiBaseUrl}/api/v1/submit_measurement"),
calledUrls,
)
}

@Test
fun fallbackEndpointIsAttemptedWhenPrimaryFails() =
runTest {
val calledUrls = mutableListOf<String>()
val subject = SubmitMeasurementWithUser(
getManifest = { flowOf(ManifestFactory.build()) },
getCredential = { null },
setCredential = SetCredential(
writeSecureStorage = { _, _ -> error("setCredential should not be used") },
json = json,
),
stampMeasurement = StampMeasurement(
passportGetProbeId = { _, _, _ -> error("getProbeId should not be used") },
getCredential = { null },
json = json,
),
resolveSubmissionPolicy = ResolveSubmissionPolicy(),
userAuthSubmit = { url, _, _, _, _ ->
calledUrls.add(url)
when {
calledUrls.size == 1 && (OrganizationConfig.ooniApiBaseUrl != OrganizationConfig.ooniApiFallbackUrl) ->
Failure(PassportException.HttpClientError("Primary failed"))
calledUrls.size == 1 ->
Failure(PassportException.HttpClientError("Single call failed"))
else ->
Success(
CredentialResponse(
response = PassportHttpResponse(
statusCode = 200,
version = "HTTP/1.1",
headersListText = emptyList(),
bodyText = """{"measurement_uid":"fallback-uid"}""",
),
credential = null,
),
)
}
},
json = json,
)

val result = subject(measurementData)

if (OrganizationConfig.ooniApiBaseUrl != OrganizationConfig.ooniApiFallbackUrl) {
val success = assertIs<Success<SubmitMeasurement.ResponseData>>(result)
assertEquals(MeasurementModel.Uid("fallback-uid"), success.value.uid)
assertEquals(2, calledUrls.size)
} else {
assertIs<Failure<*>>(result)
assertEquals(1, calledUrls.size)
}
}

@Test
fun nonSuccessfulResponseSurfacesStatusAndDecodedError() =
runTest {
Expand Down
Loading