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 }