diff --git a/src/main/kotlin/de/cyface/uploader/Authenticator.kt b/src/main/kotlin/de/cyface/uploader/Authenticator.kt
deleted file mode 100644
index ca30763..0000000
--- a/src/main/kotlin/de/cyface/uploader/Authenticator.kt
+++ /dev/null
@@ -1,76 +0,0 @@
-/*
- * Copyright 2023 Cyface GmbH
- *
- * This file is part of the Cyface Uploader.
- *
- * The Cyface Uploader is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The Cyface Uploader is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Cyface Uploader. If not, see .
- */
-package de.cyface.uploader
-
-import de.cyface.model.Activation
-import de.cyface.uploader.exception.LoginFailed
-import de.cyface.uploader.exception.RegistrationFailed
-import java.net.MalformedURLException
-import java.net.URL
-import kotlin.jvm.Throws
-
-/**
- * Interface for authenticating to a Cyface Data Collector.
- *
- * @author Armin Schnabel
- * @version 1.0.0
- * @since 1.0.0
- */
-interface Authenticator {
-
- /**
- * Authenticates with the Cyface data collector server available at the API endpoint.
- *
- * @param username The username of the user to authenticate
- * @param password The password of the user to authenticate
- * @throws LoginFailed when an expected error occurred, so that the UI can handle this.
- * @return The auth token as String. This token is only valid for some time. Just call this method before each
- * upload.
- */
- @Throws(LoginFailed::class)
- fun authenticate(username: String, password: String): String
-
- /**
- * Register a new user with the Cyface Data Collector server available at the API endpoint.
- *
- * @param email The email part of the credentials
- * @param password The password part of the credentials
- * @param captcha The captcha token
- * @param activation The template to use for the activation email.
- * @param group The database identifier of the group the user selected during registration
- * @throws RegistrationFailed when an expected error occurred, so that the UI can handle this.
- * @return [Result.UPLOAD_SUCCESSFUL] if successful.
- */
- @Throws(RegistrationFailed::class)
- fun register(email: String, password: String, captcha: String, activation: Activation, group: String): Result
-
- /**
- * @return the endpoint which will be used for authentication.
- * @throws MalformedURLException if the endpoint address provided is malformed.
- */
- @Throws(MalformedURLException::class)
- fun loginEndpoint(): URL
-
- /**
- * @return the endpoint which will be used for registration.
- * @throws MalformedURLException if the endpoint address provided is malformed.
- */
- @Throws(MalformedURLException::class)
- fun registrationEndpoint(): URL
-}
diff --git a/src/main/kotlin/de/cyface/uploader/DefaultAuthenticator.kt b/src/main/kotlin/de/cyface/uploader/DefaultAuthenticator.kt
deleted file mode 100644
index c03948d..0000000
--- a/src/main/kotlin/de/cyface/uploader/DefaultAuthenticator.kt
+++ /dev/null
@@ -1,208 +0,0 @@
-/*
- * Copyright 2023 Cyface GmbH
- *
- * This file is part of the Cyface Uploader.
- *
- * The Cyface Uploader is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * The Cyface Uploader is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with the Cyface Uploader. If not, see .
- */
-package de.cyface.uploader
-
-import de.cyface.model.Activation
-import de.cyface.uploader.exception.AccountNotActivated
-import de.cyface.uploader.exception.BadRequestException
-import de.cyface.uploader.exception.ConflictException
-import de.cyface.uploader.exception.EntityNotParsableException
-import de.cyface.uploader.exception.ForbiddenException
-import de.cyface.uploader.exception.HostUnresolvable
-import de.cyface.uploader.exception.InternalServerErrorException
-import de.cyface.uploader.exception.LoginFailed
-import de.cyface.uploader.exception.NetworkUnavailableException
-import de.cyface.uploader.exception.RegistrationFailed
-import de.cyface.uploader.exception.ServerUnavailableException
-import de.cyface.uploader.exception.SynchronisationException
-import de.cyface.uploader.exception.TooManyRequestsException
-import de.cyface.uploader.exception.UnauthorizedException
-import de.cyface.uploader.exception.UnexpectedResponseCode
-import org.slf4j.LoggerFactory
-import java.net.HttpURLConnection
-import java.net.MalformedURLException
-import java.net.URL
-
-/**
- * Implementation of the [Authenticator].
- *
- * *Attention:* The authentication token is invalid after a few seconds.
- * Just call [DefaultAuthenticator.authenticate] again to get a new token.
- * Usually the token should be generated just before each [DefaultUploader.upload] call.
- *
- * @author Armin Schnabel
- * @version 1.0.1
- * @since 1.0.0
- * @property apiEndpoint An API endpoint running a Cyface data collector service, like `https://some.url/api/v3`
- */
-@Suppress("unused") // Part of the API
-class DefaultAuthenticator(private val apiEndpoint: String) : Authenticator {
-
- private val http: HttpConnection = HttpConnection()
-
- @Suppress("CyclomaticComplexMethod")
- override fun authenticate(username: String, password: String): String {
- var connection: HttpURLConnection? = null
- val authToken: String
- try {
- connection = http.open(loginEndpoint(), false)
-
- // Try to send the request and handle expected errors
- val loginResponse = http.login(connection, username, password, false)
- LOGGER.debug("Response $loginResponse")
-
- // Make sure the successful response contains an Authorization token
- authToken = connection.getHeaderField("Authorization")
- check(!(loginResponse == Result.LOGIN_SUCCESSFUL && authToken == null)) {
- "Login successful but response does not contain a token"
- }
-
- return authToken
- }
-
- // Soft catch "expected" errors in `LoginFailed` exception so that the UI can handle this.
- // As this API is not used by other parties, there is no external definition of expected errors.
- catch (e: SynchronisationException) {
- throw LoginFailed(e) // IOException while reading the response. Try again later.
- } catch (e: UnauthorizedException) {
- throw LoginFailed(e) // `HTTP_UNAUTHORIZED` (401). Handle in UI.
- } catch (e: ForbiddenException) {
- throw LoginFailed(e) // `HTTP_FORBIDDEN` (403). Seems to happen when server is unavailable. Handle in UI.
- } catch (e: NetworkUnavailableException) {
- throw LoginFailed(e) // Network disappeared. Try again later.
- } catch (e: TooManyRequestsException) {
- throw LoginFailed(e) // `HTTP_TOO_MANY_REQUESTS` (429). Try again later.
- } catch (e: HostUnresolvable) {
- throw LoginFailed(e) // Network without internet connection. Try again later.
- } catch (e: ServerUnavailableException) {
- throw LoginFailed(e) // Server not reachable. Try again later.
- } catch (e: AccountNotActivated) {
- throw LoginFailed(e) // User account not activated. Handle in UI.
- } catch (e: UnexpectedResponseCode) {
- // We currently show a UI error. Is this also reported to Sentry? Then it's ok not to throw this hard.
- throw LoginFailed(e) // server returns an unexpected response code
- }
-
- // Crash unexpected errors hard
- catch (e: BadRequestException) {
- error(e) // `HTTP_BAD_REQUEST` (400).
- } catch (e: ConflictException) {
- error(e) // `HTTP_CONFLICT` (409).
- } catch (e: EntityNotParsableException) {
- error(e) // `HTTP_ENTITY_NOT_PROCESSABLE` (422).
- } catch (e: InternalServerErrorException) {
- // If this actually happens, we might want to catch it softly, report to Sentry and show a UI error.
- error(e) // `HTTP_INTERNAL_ERROR` (500).
- } catch (e: MalformedURLException) {
- error(e) // The endpoint url is malformed.
- } finally {
- connection?.disconnect()
- }
- }
-
- @Suppress("CyclomaticComplexMethod")
- override fun register(
- email: String,
- password: String,
- captcha: String,
- activation: Activation,
- group: String
- ): Result {
- var connection: HttpURLConnection? = null
- try {
- connection = http.open(registrationEndpoint(), false)
-
- // Try to send the request and handle expected errors
- val response = http.register(connection, email, password, captcha, activation, group)
- LOGGER.debug("Response $response")
- return response
- }
-
- // Soft catch "expected" errors in `RegistrationFailed` exception so that the UI can handle this.
- // As this API is not used by other parties, there is no external definition of expected errors.
- catch (e: ConflictException) {
- throw RegistrationFailed(e) // `HTTP_CONFLICT` (409). Already registered. Handle in UI.
- } catch (e: SynchronisationException) {
- throw RegistrationFailed(e) // IOException while reading the response. Try again later.
- } catch (e: ForbiddenException) {
- // `HTTP_FORBIDDEN` (403). Seems to happen when server is unavailable. Handle in UI.
- throw RegistrationFailed(e)
- } catch (e: NetworkUnavailableException) {
- throw RegistrationFailed(e) // Network disappeared. Try again later.
- } catch (e: TooManyRequestsException) {
- throw RegistrationFailed(e) // `HTTP_TOO_MANY_REQUESTS` (429). Try again later.
- } catch (e: HostUnresolvable) {
- throw RegistrationFailed(e) // Network without internet connection. Try again later.
- } catch (e: ServerUnavailableException) {
- throw RegistrationFailed(e) // Server not reachable. Try again later.
- } catch (e: UnexpectedResponseCode) {
- // We currently show a UI error. Is this also reported to Sentry? Then it's ok not to throw this hard.
- throw RegistrationFailed(e) // server returns an unexpected response code
- }
-
- // Crash unexpected errors hard
- catch (e: BadRequestException) {
- error(e) // `HTTP_BAD_REQUEST` (400).
- } catch (e: UnauthorizedException) {
- error(e) // `HTTP_UNAUTHORIZED` (401).
- } catch (e: EntityNotParsableException) {
- error(e) // `HTTP_ENTITY_NOT_PROCESSABLE` (422).
- } catch (e: InternalServerErrorException) {
- error(e) // `HTTP_INTERNAL_ERROR` (500).
- } catch (e: AccountNotActivated) {
- error(e) // `PRECONDITION_REQUIRED` (428). Should not happen during registration.
- } catch (e: MalformedURLException) {
- error(e) // The endpoint url is malformed.
- } finally {
- connection?.disconnect()
- }
- }
-
- @Suppress("MemberVisibilityCanBePrivate") // Part of the API
- override fun loginEndpoint(): URL {
- return URL(returnUrlWithTrailingSlash(apiEndpoint) + "login")
- }
-
- @Suppress("MemberVisibilityCanBePrivate") // Part of the API
- override fun registrationEndpoint(): URL {
- return URL(returnUrlWithTrailingSlash(apiEndpoint) + "user")
- }
-
- companion object {
-
- /**
- * The logger used to log messages from this class. Configure it using src/main/resources/logback.xml.
- */
- private val LOGGER = LoggerFactory.getLogger(DefaultAuthenticator::class.java)
-
- /**
- * Adds a trailing slash to the server URL or leaves an existing trailing slash untouched.
- *
- * @param url The url to format.
- * @return The server URL with a trailing slash.
- */
- fun returnUrlWithTrailingSlash(url: String): String {
- return if (url.endsWith("/")) {
- url
- } else {
- "$url/"
- }
- }
- }
-}
diff --git a/src/main/kotlin/de/cyface/uploader/DefaultUploader.kt b/src/main/kotlin/de/cyface/uploader/DefaultUploader.kt
index 251e5d1..9085a1d 100644
--- a/src/main/kotlin/de/cyface/uploader/DefaultUploader.kt
+++ b/src/main/kotlin/de/cyface/uploader/DefaultUploader.kt
@@ -63,8 +63,7 @@ import javax.net.ssl.SSLException
/**
* Implementation of the [Uploader].
*
- * To use this interface just call [DefaultUploader.upload] with an authentication token, e.g. from
- * [DefaultAuthenticator.authenticate].
+ * To use this interface just call [DefaultUploader.upload] with an authentication token.
*
* @author Armin Schnabel
* @version 1.0.0
@@ -197,7 +196,7 @@ class DefaultUploader(private val apiEndpoint: String) : Uploader {
}
override fun endpoint(): URL {
- return URL(DefaultAuthenticator.returnUrlWithTrailingSlash(apiEndpoint) + "measurements")
+ return URL(returnUrlWithTrailingSlash(apiEndpoint) + "measurements")
}
@Throws(
@@ -344,6 +343,20 @@ class DefaultUploader(private val apiEndpoint: String) : Uploader {
*/
private const val PAYLOAD_TOO_LARGE = 413
+ /**
+ * Adds a trailing slash to the server URL or leaves an existing trailing slash untouched.
+ *
+ * @param url The url to format.
+ * @return The server URL with a trailing slash.
+ */
+ private fun returnUrlWithTrailingSlash(url: String): String {
+ return if (url.endsWith("/")) {
+ url
+ } else {
+ "$url/"
+ }
+ }
+
/**
* Assembles a `HttpContent` object which contains the metadata.
*
@@ -355,14 +368,14 @@ class DefaultUploader(private val apiEndpoint: String) : Uploader {
// Location meta data
if (metaData.startLocation != null) {
- attributes["startLocLat"] = metaData.startLocation.latitude.toString()
- attributes["startLocLon"] = metaData.startLocation.longitude.toString()
- attributes["startLocTS"] = metaData.startLocation.timestamp.toString()
+ attributes["startLocLat"] = metaData.startLocation!!.latitude.toString()
+ attributes["startLocLon"] = metaData.startLocation!!.longitude.toString()
+ attributes["startLocTS"] = metaData.startLocation!!.timestamp.toString()
}
if (metaData.endLocation != null) {
- attributes["endLocLat"] = metaData.endLocation.latitude.toString()
- attributes["endLocLon"] = metaData.endLocation.longitude.toString()
- attributes["endLocTS"] = metaData.endLocation.timestamp.toString()
+ attributes["endLocLat"] = metaData.endLocation!!.latitude.toString()
+ attributes["endLocLon"] = metaData.endLocation!!.longitude.toString()
+ attributes["endLocTS"] = metaData.endLocation!!.timestamp.toString()
}
attributes["locationCount"] = metaData.locationCount.toString()
@@ -373,7 +386,7 @@ class DefaultUploader(private val apiEndpoint: String) : Uploader {
attributes["osVersion"] = metaData.operatingSystemVersion
attributes["appVersion"] = metaData.applicationVersion
attributes["length"] = metaData.length.toString()
- attributes["modality"] = metaData.modality.toString()
+ attributes["modality"] = metaData.modality
attributes["formatVersion"] = metaData.formatVersion.toString()
attributes["logCount"] = metaData.logCount.toString()
attributes["imageCount"] = metaData.imageCount.toString()
diff --git a/src/main/kotlin/de/cyface/uploader/Http.kt b/src/main/kotlin/de/cyface/uploader/Http.kt
index d8b34fa..516c3b8 100644
--- a/src/main/kotlin/de/cyface/uploader/Http.kt
+++ b/src/main/kotlin/de/cyface/uploader/Http.kt
@@ -18,20 +18,7 @@
*/
package de.cyface.uploader
-import de.cyface.model.Activation
-import de.cyface.uploader.exception.AccountNotActivated
-import de.cyface.uploader.exception.BadRequestException
-import de.cyface.uploader.exception.ConflictException
-import de.cyface.uploader.exception.EntityNotParsableException
-import de.cyface.uploader.exception.ForbiddenException
-import de.cyface.uploader.exception.HostUnresolvable
-import de.cyface.uploader.exception.InternalServerErrorException
-import de.cyface.uploader.exception.NetworkUnavailableException
-import de.cyface.uploader.exception.ServerUnavailableException
import de.cyface.uploader.exception.SynchronisationException
-import de.cyface.uploader.exception.TooManyRequestsException
-import de.cyface.uploader.exception.UnauthorizedException
-import de.cyface.uploader.exception.UnexpectedResponseCode
import java.net.HttpURLConnection
import java.net.URL
@@ -40,7 +27,7 @@ import java.net.URL
*
* @author Klemens Muthmann
* @author Armin Schnabel
- * @version 12.0.0
+ * @version 13.0.0
* @since 1.0.0
*/
interface Http {
@@ -54,91 +41,4 @@ interface Http {
*/
@Throws(SynchronisationException::class)
fun open(url: URL, hasBinaryContent: Boolean): HttpURLConnection
-
- /**
- * The post request which authenticates a user at the server.
- *
- * @param connection The `HttpURLConnection` to be used for the request.
- * @param username The username part of the credentials
- * @param password The password part of the credentials
- * @param compress True if the {@param payload} should get compressed
- * @throws SynchronisationException If an IOException occurred while reading the response code.
- * @throws BadRequestException When server returns `HttpURLConnection#HTTP_BAD_REQUEST`
- * @throws UnauthorizedException When the server returns `HttpURLConnection#HTTP_UNAUTHORIZED`
- * @throws ForbiddenException When the server returns `HttpURLConnection#HTTP_FORBIDDEN`
- * @throws ConflictException When the server returns `HttpURLConnection#HTTP_CONFLICT`
- * @throws EntityNotParsableException When the server returns [DefaultUploader.HTTP_ENTITY_NOT_PROCESSABLE]
- * @throws InternalServerErrorException When the server returns `HttpURLConnection#HTTP_INTERNAL_ERROR`
- * @throws NetworkUnavailableException When the network used for transmission becomes unavailable.
- * @throws TooManyRequestsException When the server returns [DefaultUploader.HTTP_TOO_MANY_REQUESTS]
- * @throws HostUnresolvable e.g. when the phone is connected to a network which is not connected to the internet
- * @throws ServerUnavailableException When no connection could be established with the server
- * @throws UnexpectedResponseCode When the server returns an unexpected response code
- * @throws AccountNotActivated When the user account is not activated
- * @return [Result.LOGIN_SUCCESSFUL] if successful or else an `Exception`.
- */
- @Throws(
- SynchronisationException::class,
- UnauthorizedException::class,
- BadRequestException::class,
- InternalServerErrorException::class,
- ForbiddenException::class,
- EntityNotParsableException::class,
- ConflictException::class,
- NetworkUnavailableException::class,
- TooManyRequestsException::class,
- HostUnresolvable::class,
- ServerUnavailableException::class,
- UnexpectedResponseCode::class,
- AccountNotActivated::class
- )
- fun login(connection: HttpURLConnection, username: String, password: String, compress: Boolean): Result
-
- /**
- * The post request which registers a new user at the server.
- *
- * @param connection The `HttpURLConnection` to be used for the request.
- * @param email The email part of the credentials
- * @param password The password part of the credentials
- * @param captcha The captcha token
- * @param activation The template to use for the activation email.
- * @param group The database identifier of the group the user selected during registration
- * @throws SynchronisationException If an IOException occurred while reading the response code.
- * @throws BadRequestException When server returns `HttpURLConnection#HTTP_BAD_REQUEST`
- * @throws UnauthorizedException When the server returns `HttpURLConnection#HTTP_UNAUTHORIZED`
- * @throws ForbiddenException When the server returns `HttpURLConnection#HTTP_FORBIDDEN`
- * @throws ConflictException When the server returns `HttpURLConnection#HTTP_CONFLICT`
- * @throws EntityNotParsableException When the server returns [DefaultUploader.HTTP_ENTITY_NOT_PROCESSABLE]
- * @throws InternalServerErrorException When the server returns `HttpURLConnection#HTTP_INTERNAL_ERROR`
- * @throws NetworkUnavailableException When the network used for transmission becomes unavailable.
- * @throws TooManyRequestsException When the server returns [DefaultUploader.HTTP_TOO_MANY_REQUESTS]
- * @throws HostUnresolvable e.g. when the phone is connected to a network which is not connected to the internet
- * @throws ServerUnavailableException When no connection could be established with the server
- * @throws UnexpectedResponseCode When the server returns an unexpected response code
- * @throws AccountNotActivated When the user account is not activated
- * @return [Result.UPLOAD_SUCCESSFUL] if successful or else an `Exception`.
- */
- @Throws(
- SynchronisationException::class,
- UnauthorizedException::class,
- BadRequestException::class,
- InternalServerErrorException::class,
- ForbiddenException::class,
- EntityNotParsableException::class,
- ConflictException::class,
- NetworkUnavailableException::class,
- TooManyRequestsException::class,
- HostUnresolvable::class,
- ServerUnavailableException::class,
- UnexpectedResponseCode::class,
- AccountNotActivated::class
- )
- fun register(
- connection: HttpURLConnection,
- email: String,
- password: String,
- captcha: String,
- activation: Activation,
- group: String
- ): Result
}
diff --git a/src/main/kotlin/de/cyface/uploader/HttpConnection.kt b/src/main/kotlin/de/cyface/uploader/HttpConnection.kt
index 3f665a8..e3e9f38 100644
--- a/src/main/kotlin/de/cyface/uploader/HttpConnection.kt
+++ b/src/main/kotlin/de/cyface/uploader/HttpConnection.kt
@@ -18,7 +18,6 @@
*/
package de.cyface.uploader
-import de.cyface.model.Activation
import de.cyface.uploader.DefaultUploader.Companion.DEFAULT_CHARSET
import de.cyface.uploader.exception.AccountNotActivated
import de.cyface.uploader.exception.BadRequestException
@@ -53,7 +52,7 @@ import javax.net.ssl.SSLSession
*
* @author Klemens Muthmann
* @author Armin Schnabel
- * @version 13.0.0
+ * @version 14.0.0
* @since 2.0.0
*/
class HttpConnection : Http {
@@ -83,227 +82,4 @@ class HttpConnection : Http {
connection.setRequestProperty("User-Agent", System.getProperty("http.agent"))
return connection
}
-
- override fun login(
- connection: HttpURLConnection,
- username: String,
- password: String,
- compress: Boolean
- ): Result {
- // For performance reasons (documentation) set either fixedLength (known length) or chunked streaming mode
- // we currently don't use fixedLengthStreamingMode as we only use this request for small login requests
- connection.setChunkedStreamingMode(0)
- val credentials = credentials(username, password)
- val outputStream = initOutputStream(connection)
- try {
- LOGGER.debug("Transmitting with compression $compress.")
- if (compress) {
- connection.setRequestProperty("Content-Encoding", "gzip")
- outputStream.write(gzip(credentials.toByteArray(DEFAULT_CHARSET)))
- } else {
- outputStream.write(credentials.toByteArray(DEFAULT_CHARSET))
- }
- outputStream.flush()
- outputStream.close()
- } catch (e: SSLException) {
- // This exception is thrown by OkHttp when the network is no longer available
- val message = e.message
- if (message != null && message.contains("I/O error during system call, Broken pipe")) {
- LOGGER.warn("Caught SSLException: ${e.message}")
- throw NetworkUnavailableException("Network became unavailable during transmission.", e)
- } else {
- error(e) // SSLException with unknown cause
- }
- } catch (e: InterruptedIOException) {
- // This exception is thrown when the login request is interrupted, e.g. see MOV-761
- throw NetworkUnavailableException("Network interrupted during login", e)
- } catch (e: IOException) {
- error(e)
- }
- return try {
- readResponse(connection)
- } catch (e: UploadSessionExpired) {
- error(e) // unexpected for login
- }
- }
-
- override fun register(
- connection: HttpURLConnection,
- email: String,
- password: String,
- captcha: String,
- activation: Activation,
- group: String
- ): Result {
- // For performance reasons (documentation) set either fixedLength (known length) or chunked streaming mode
- // we currently don't use fixedLengthStreamingMode as we only use this request for small login requests
- connection.setChunkedStreamingMode(0)
- val payload = registrationPayload(email, password, captcha, activation, group)
- val outputStream = initOutputStream(connection)
- try {
- outputStream.write(payload.toByteArray(DEFAULT_CHARSET))
- outputStream.flush()
- outputStream.close()
- } catch (e: SSLException) {
- // This exception is thrown by OkHttp when the network is no longer available
- val message = e.message
- if (message != null && message.contains("I/O error during system call, Broken pipe")) {
- LOGGER.warn("Caught SSLException: ${e.message}")
- throw NetworkUnavailableException(
- "Network became unavailable during transmission.",
- e
- )
- } else {
- error(e) // SSLException with unknown cause
- }
- } catch (e: InterruptedIOException) {
- // This exception is thrown when the request is interrupted, e.g. see MOV-761
- throw NetworkUnavailableException("Network interrupted during login", e)
- } catch (e: IOException) {
- error(e)
- }
- return try {
- readResponse(connection)
- } catch (e: UploadSessionExpired) {
- error(e)
- }
- }
-
- fun credentials(username: String, password: String): String {
- return "{\"username\":\"$username\",\"password\":\"$password\"}"
- }
-
- private fun registrationPayload(
- email: String,
- password: String,
- captcha: String,
- template: Activation,
- group: String
- ): String {
- return "{\"email\":\"$email\",\"password\":\"$password\",\"captcha\":\"$captcha\",\"template\":\"" +
- "${template.name}\",\"group\":\"$group\"}"
- }
-
- private fun gzip(input: ByteArray): ByteArray {
- return try {
- var gzipOutputStream: GZIPOutputStream? = null
- try {
- val byteArrayOutputStream = ByteArrayOutputStream()
- gzipOutputStream = GZIPOutputStream(byteArrayOutputStream)
- try {
- gzipOutputStream.write(input)
- gzipOutputStream.flush()
- } finally {
- gzipOutputStream.close()
- }
- gzipOutputStream = null
- byteArrayOutputStream.toByteArray()
- } finally {
- gzipOutputStream?.close()
- }
- } catch (@Suppress("SwallowedException") e: IOException) {
- error("Failed to gzip.")
- }
- }
-
- /**
- * Initializes a `BufferedOutputStream` for the provided connection.
- *
- * @param connection the `HttpURLConnection` to create the stream for.
- * @return the `BufferedOutputStream` created.
- * @throws ServerUnavailableException When no connection could be established with the server
- * @throws HostUnresolvable e.g. when the phone is connected to a network which is not connected to the internet.
- */
- @Throws(ServerUnavailableException::class, HostUnresolvable::class)
- private fun initOutputStream(connection: HttpURLConnection): BufferedOutputStream {
- connection.doOutput = true // To upload data to the server
- return try {
- // Wrapping this in a Buffered steam for performance reasons
- BufferedOutputStream(connection.outputStream)
- } catch (e: IOException) {
- val message = e.message
- if (message != null && message.contains("Unable to resolve host")) {
- throw HostUnresolvable(e)
- }
- throw ServerUnavailableException(e)
- }
- }
-
- /**
- * Reads the [HttpResponse] from the [HttpURLConnection] and identifies known errors.
- *
- * @param connection The connection that received the response.
- * @return The [HttpResponse].
- * @throws SynchronisationException If an IOException occurred while reading the response code.
- * @throws BadRequestException When server returns `HttpURLConnection#HTTP_BAD_REQUEST`
- * @throws UnauthorizedException When the server returns `HttpURLConnection#HTTP_UNAUTHORIZED`
- * @throws ForbiddenException When the server returns `HttpURLConnection#HTTP_FORBIDDEN`
- * @throws ConflictException When the server returns `HttpURLConnection#HTTP_CONFLICT`
- * @throws EntityNotParsableException When the server returns [.HTTP_ENTITY_NOT_PROCESSABLE]
- * @throws InternalServerErrorException When the server returns `HttpURLConnection#HTTP_INTERNAL_ERROR`
- * @throws TooManyRequestsException When the server returns [.HTTP_TOO_MANY_REQUESTS]
- * @throws UploadSessionExpired When the server returns [HttpURLConnection.HTTP_NOT_FOUND]
- * @throws UnexpectedResponseCode When the server returns an unexpected response code
- * @throws AccountNotActivated When the user account is not activated
- */
- @Throws(
- SynchronisationException::class,
- BadRequestException::class,
- UnauthorizedException::class,
- ForbiddenException::class,
- ConflictException::class,
- EntityNotParsableException::class,
- InternalServerErrorException::class,
- TooManyRequestsException::class,
- UploadSessionExpired::class,
- UnexpectedResponseCode::class,
- AccountNotActivated::class
- )
- private fun readResponse(connection: HttpURLConnection): Result {
- val responseCode: Int
- val responseMessage: String
- return try {
- responseCode = connection.responseCode
- responseMessage = connection.responseMessage
- val responseBody = readResponseBody(connection)
- if (responseCode in SUCCESS_CODE_START..SUCCESS_CODE_END) {
- DefaultUploader.handleSuccess(HttpResponse(responseCode, responseBody, responseMessage))
- } else {
- DefaultUploader.handleError(HttpResponse(responseCode, responseBody, responseMessage))
- }
- } catch (e: IOException) {
- throw SynchronisationException(e)
- }
- }
-
- /**
- * Reads the body from the [HttpURLConnection]. This contains either the error or the success message.
- *
- * @param connection the [HttpURLConnection] to read the response from
- * @return the [HttpResponse] body
- */
- private fun readResponseBody(connection: HttpURLConnection): String {
- // First try to read and return a success response body
- return try {
- DefaultUploader.readInputStream(connection.inputStream)
- } catch (@Suppress("SwallowedException") e: IOException) {
- // When reading the InputStream fails, we check if there is an ErrorStream to read from
- // (For details see https://developer.android.com/reference/java/net/HttpURLConnection)
- val errorStream = connection.errorStream ?: return ""
-
- // Return empty string if there were no errors, connection is not connected or server sent no useful data.
- // This occurred e.g. on Xiaomi Mi A1 after disabling Wi-Fi instantly after sync start
- DefaultUploader.readInputStream(errorStream)
- }
- }
-
- companion object {
- /**
- * The logger used to log messages from this class. Configure it using src/main/resources/logback.xml.
- */
- private val LOGGER = LoggerFactory.getLogger(HttpConnection::class.java)
-
- private const val SUCCESS_CODE_START = 200
- private const val SUCCESS_CODE_END = 299
- }
}
diff --git a/src/main/kotlin/de/cyface/uploader/RequestInitializeHandler.kt b/src/main/kotlin/de/cyface/uploader/RequestInitializeHandler.kt
index a60fafe..12fea6f 100644
--- a/src/main/kotlin/de/cyface/uploader/RequestInitializeHandler.kt
+++ b/src/main/kotlin/de/cyface/uploader/RequestInitializeHandler.kt
@@ -52,14 +52,14 @@ class RequestInitializeHandler(
private fun addMetaData(metaData: RequestMetaData, headers: HttpHeaders) {
// Location meta data
if (metaData.startLocation != null) {
- headers["startLocLat"] = metaData.startLocation.latitude.toString()
- headers["startLocLon"] = metaData.startLocation.longitude.toString()
- headers["startLocTS"] = metaData.startLocation.timestamp.toString()
+ headers["startLocLat"] = metaData.startLocation!!.latitude.toString()
+ headers["startLocLon"] = metaData.startLocation!!.longitude.toString()
+ headers["startLocTS"] = metaData.startLocation!!.timestamp.toString()
}
if (metaData.endLocation != null) {
- headers["endLocLat"] = metaData.endLocation.latitude.toString()
- headers["endLocLon"] = metaData.endLocation.longitude.toString()
- headers["endLocTS"] = metaData.endLocation.timestamp.toString()
+ headers["endLocLat"] = metaData.endLocation!!.latitude.toString()
+ headers["endLocLon"] = metaData.endLocation!!.longitude.toString()
+ headers["endLocTS"] = metaData.endLocation!!.timestamp.toString()
}
headers["locationCount"] = metaData.locationCount.toString()
@@ -70,7 +70,7 @@ class RequestInitializeHandler(
headers["osVersion"] = metaData.operatingSystemVersion
headers["appVersion"] = metaData.applicationVersion
headers["length"] = metaData.length.toString()
- headers["modality"] = metaData.modality.toString()
+ headers["modality"] = metaData.modality
headers["formatVersion"] = metaData.formatVersion.toString()
headers["logCount"] = metaData.logCount
headers["imageCount"] = metaData.imageCount