Api.kt
⎇
Raw
1package org.opentracker
2
3import java.io.IOException
4import java.net.HttpURLConnection
5import java.net.URL
6import org.json.JSONObject
7
8class HttpError(val status: Int, message: String) : IOException(message)
9
10/** Blocking HTTP calls. Run them off the main thread. Connections stay open between calls (keep-alive). */
11class Api(private val baseUrl: String, private val token: String? = null) {
12
13 /** A JSON array of points, as the server's `Point` type. */
14 fun upload(points: String) {
15 call("POST", "/api/points", points)
16 }
17
18 /** Checks the token. */
19 fun device() {
20 call("GET", "/api/device", null)
21 }
22
23 fun pair(code: String, verifier: String): String {
24 val body = JSONObject().put("code", code).put("verifier", verifier).toString()
25 return JSONObject(call("POST", "/api/devices/pair", body)).getString("token")
26 }
27
28 private fun call(method: String, path: String, body: String?): String {
29 val conn = URL(baseUrl + path).openConnection() as HttpURLConnection
30 try {
31 conn.requestMethod = method
32 conn.connectTimeout = 15_000
33 conn.readTimeout = 30_000
34 token?.let { conn.setRequestProperty("Authorization", "Bearer $it") }
35 if (body != null) {
36 conn.doOutput = true
37 conn.setRequestProperty("Content-Type", "application/json")
38 conn.outputStream.use { it.write(body.toByteArray()) }
39 }
40 val status = conn.responseCode
41 if (status !in 200..299) {
42 val msg = conn.errorStream?.bufferedReader()?.use { it.readText() }.orEmpty()
43 throw HttpError(status, msg.ifEmpty { "HTTP $status" })
44 }
45 return conn.inputStream.bufferedReader().use { it.readText() }
46 } finally {
47 conn.disconnect()
48 }
49 }
50
51 companion object {
52 /** `track.example.com` becomes `https://track.example.com`. Returns null for an unusable address. */
53 fun normalize(input: String): String? {
54 var url = input.trim().trimEnd('/')
55 if (url.isEmpty()) return null
56 if (!url.contains("://")) url = "https://$url"
57 val parsed = runCatching { URL(url) }.getOrNull() ?: return null
58 if (parsed.protocol !in listOf("http", "https") || parsed.host.isEmpty()) return null
59 return url
60 }
61 }
62}
63