taler-android

Android apps for GNU Taler (wallet, PoS, cashier)
Log | Files | Refs | README | LICENSE

HttpHelper.kt (4963B)


      1 /*
      2  * This file is part of GNU Taler
      3  * (C) 2020 Taler Systems S.A.
      4  *
      5  * GNU Taler is free software; you can redistribute it and/or modify it under the
      6  * terms of the GNU General Public License as published by the Free Software
      7  * Foundation; either version 3, or (at your option) any later version.
      8  *
      9  * GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
     10  * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
     11  * A PARTICULAR PURPOSE.  See the GNU General Public License for more details.
     12  *
     13  * You should have received a copy of the GNU General Public License along with
     14  * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
     15  */
     16 
     17 package net.taler.cashier
     18 
     19 import android.util.Log
     20 import androidx.annotation.WorkerThread
     21 import net.taler.cashier.config.Config
     22 import okhttp3.Authenticator
     23 import okhttp3.MediaType.Companion.toMediaTypeOrNull
     24 import okhttp3.OkHttpClient
     25 import okhttp3.Request
     26 import okhttp3.RequestBody.Companion.toRequestBody
     27 import okhttp3.Response
     28 import okhttp3.Route
     29 import org.json.JSONException
     30 import org.json.JSONObject
     31 
     32 object HttpHelper {
     33 
     34     private val TAG = HttpHelper::class.java.simpleName
     35     private const val MIME_TYPE_JSON = "application/json"
     36 
     37     @WorkerThread
     38     fun makeJsonGetRequest(url: String, config: Config): HttpJsonResult {
     39         val request = Request.Builder()
     40             .addHeader("Accept", MIME_TYPE_JSON)
     41             .url(url)
     42             .apply {
     43                 config.bearerAuth?.let { header("Authorization", it) }
     44             }
     45             .get()
     46             .build()
     47         val response = try {
     48             getHttpClient(config)
     49                 .newCall(request)
     50                 .execute()
     51         } catch (e: Exception) {
     52             Log.e(TAG, "Error retrieving $url", e)
     53             return HttpJsonResult.Error(0)
     54         }
     55         return if (response.code == 204) {
     56             HttpJsonResult.Success(JSONObject())
     57         } else if (response.code in 200..299 && response.body != null) {
     58             val jsonObject = JSONObject(response.body!!.string())
     59             HttpJsonResult.Success(jsonObject)
     60         } else {
     61             Log.e(TAG, "Received status ${response.code} from $url expected 2xx")
     62             HttpJsonResult.Error(response.code, getErrorBody(response))
     63         }
     64     }
     65 
     66     private val MEDIA_TYPE_JSON = "$MIME_TYPE_JSON; charset=utf-8".toMediaTypeOrNull()
     67 
     68     @WorkerThread
     69     fun makeJsonPostRequest(url: String, body: JSONObject, config: Config): HttpJsonResult {
     70         val request = Request.Builder()
     71             .addHeader("Accept", MIME_TYPE_JSON)
     72             .url(url)
     73             .apply {
     74                 config.bearerAuth?.let { header("Authorization", it) }
     75             }
     76             .post(body.toString().toRequestBody(MEDIA_TYPE_JSON))
     77             .build()
     78         val response = try {
     79             getHttpClient(config)
     80                 .newCall(request)
     81                 .execute()
     82         } catch (e: Exception) {
     83             Log.e(TAG, "Error retrieving $url", e)
     84             return HttpJsonResult.Error(0)
     85         }
     86         return if (response.code == 204) {
     87             HttpJsonResult.Success(JSONObject())
     88         } else if (response.code in 200..299 && response.body != null) {
     89             val jsonObject = JSONObject(response.body!!.string())
     90             HttpJsonResult.Success(jsonObject)
     91         } else {
     92             Log.e(TAG, "Received status ${response.code} from $url expected 2xx")
     93             HttpJsonResult.Error(response.code, getErrorBody(response))
     94         }
     95     }
     96 
     97     private fun getHttpClient(config: Config) =
     98         OkHttpClient.Builder().authenticator(object : Authenticator {
     99             override fun authenticate(route: Route?, response: Response): Request? {
    100                 if (config.password.isEmpty()) return null
    101                 val credential = config.basicAuth
    102                 if (credential == response.request.header("Authorization")) {
    103                     // If we already failed with these credentials, don't retry
    104                     return null
    105                 }
    106                 return response
    107                     .request
    108                     .newBuilder()
    109                     .header("Authorization", credential)
    110                     .build()
    111             }
    112         }).build()
    113 
    114     private fun getErrorBody(response: Response): String? {
    115         val body = response.body?.string() ?: return null
    116         Log.e(TAG, "Response body: $body")
    117         return try {
    118             val json = JSONObject(body)
    119             "${json.optString("ec")} ${json.optString("error")}"
    120         } catch (e: JSONException) {
    121             null
    122         }
    123     }
    124 
    125 }
    126 
    127 sealed class HttpJsonResult {
    128     class Error(val statusCode: Int, private val errorMsg: String? = null) : HttpJsonResult() {
    129         val msg: String
    130             get() = errorMsg?.let { "\n\n$statusCode $it" } ?: "$statusCode"
    131     }
    132 
    133     class Success(val json: JSONObject) : HttpJsonResult()
    134 }