summaryrefslogtreecommitdiff
path: root/cashier/src/main/java/net/taler/cashier/HttpHelper.kt
blob: fd48b2dee50e870ccb73da8835821fd77f67afa7 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
/*
 * This file is part of GNU Taler
 * (C) 2020 Taler Systems S.A.
 *
 * GNU Taler 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, or (at your option) any later version.
 *
 * GNU Taler 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
 * GNU Taler; see the file COPYING.  If not, see <http://www.gnu.org/licenses/>
 */

package net.taler.cashier

import android.util.Log
import androidx.annotation.WorkerThread
import net.taler.cashier.config.Config
import okhttp3.Authenticator
import okhttp3.Credentials
import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.OkHttpClient
import okhttp3.Request
import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response
import okhttp3.Route
import org.json.JSONException
import org.json.JSONObject

object HttpHelper {

    private val TAG = HttpHelper::class.java.simpleName
    private const val MIME_TYPE_JSON = "application/json"

    @WorkerThread
    fun makeJsonGetRequest(url: String, config: Config): HttpJsonResult {
        val request = Request.Builder()
            .addHeader("Accept", MIME_TYPE_JSON)
            .url(url)
            .get()
            .build()
        val response = try {
            getHttpClient(config.username, config.password)
                .newCall(request)
                .execute()
        } catch (e: Exception) {
            Log.e(TAG, "Error retrieving $url", e)
            return HttpJsonResult.Error(0)
        }
        return if (response.code == 200 && response.body != null) {
            val jsonObject = JSONObject(response.body!!.string())
            HttpJsonResult.Success(jsonObject)
        } else {
            Log.e(TAG, "Received status ${response.code} from $url expected 200")
            HttpJsonResult.Error(response.code, getErrorBody(response))
        }
    }

    private val MEDIA_TYPE_JSON = "$MIME_TYPE_JSON; charset=utf-8".toMediaTypeOrNull()

    @WorkerThread
    fun makeJsonPostRequest(url: String, body: JSONObject, config: Config): HttpJsonResult {
        val request = Request.Builder()
            .addHeader("Accept", MIME_TYPE_JSON)
            .url(url)
            .post(body.toString().toRequestBody(MEDIA_TYPE_JSON))
            .build()
        val response = try {
            getHttpClient(config.username, config.password)
                .newCall(request)
                .execute()
        } catch (e: Exception) {
            Log.e(TAG, "Error retrieving $url", e)
            return HttpJsonResult.Error(0)
        }
        return if (response.code == 200 && response.body != null) {
            val jsonObject = JSONObject(response.body!!.string())
            HttpJsonResult.Success(jsonObject)
        } else {
            Log.e(TAG, "Received status ${response.code} from $url expected 200")
            HttpJsonResult.Error(response.code, getErrorBody(response))
        }
    }

    private fun getHttpClient(username: String, password: String) =
        OkHttpClient.Builder().authenticator(object : Authenticator {
            override fun authenticate(route: Route?, response: Response): Request? {
                val credential = Credentials.basic(username, password)
                if (credential == response.request.header("Authorization")) {
                    // If we already failed with these credentials, don't retry
                    return null
                }
                return response
                    .request
                    .newBuilder()
                    .header("Authorization", credential)
                    .build()
            }
        }).build()

    private fun getErrorBody(response: Response): String? {
        val body = response.body?.string() ?: return null
        Log.e(TAG, "Response body: $body")
        return try {
            val json = JSONObject(body)
            "${json.optString("ec")} ${json.optString("error")}"
        } catch (e: JSONException) {
            null
        }
    }

}

sealed class HttpJsonResult {
    class Error(val statusCode: Int, private val errorMsg: String? = null) : HttpJsonResult() {
        val msg: String
            get() = errorMsg?.let { "\n\n$statusCode $it" } ?: "$statusCode"
    }

    class Success(val json: JSONObject) : HttpJsonResult()
}