app/src/main/java/dev/equwal/inkupdate/Net.kt (2551 bytes)
1 // SPDX-License-Identifier: GPL-3.0-or-later 2 // Copyright (c) 2026 equwal 3 package dev.equwal.inkupdate 4 5 import java.io.ByteArrayOutputStream 6 import java.io.InputStream 7 import java.net.HttpURLConnection 8 import java.net.URL 9 10 /** 11 * One HTTP GET, with java.net and nothing else. 12 * 13 * The app sends no identifier and no cookie. It sends the package name or the 14 * repository name in the path, and a User-Agent that names the app. Call this 15 * from a background thread only. 16 */ 17 object Net { 18 19 const val AGENT = "InkUpdate/" + BuildConfig.VERSION_NAME + 20 " (+https://github.com/equwal/ink-update)" 21 22 private const val CONNECT_MS = 10_000 23 private const val READ_MS = 15_000 24 25 /** The most bytes to read from one answer. An answer is a few kilobytes. */ 26 private const val MAX_BYTES = 512 * 1024 27 28 /** 29 * @param code the HTTP status, or 0 when the request did not finish. 30 * @param body the text of the answer, or null. 31 */ 32 data class Answer(val code: Int, val body: String?) 33 34 fun get(url: String, accept: String? = null): Answer { 35 var c: HttpURLConnection? = null 36 return try { 37 c = (URL(url).openConnection() as HttpURLConnection).apply { 38 requestMethod = "GET" 39 connectTimeout = CONNECT_MS 40 readTimeout = READ_MS 41 instanceFollowRedirects = true 42 setRequestProperty("User-Agent", AGENT) 43 if (accept != null) setRequestProperty("Accept", accept) 44 } 45 val code = c.responseCode 46 // F-Droid answers 404 with a body that says NOT_FOUND, so the error 47 // stream matters as much as the input stream. 48 val stream: InputStream? = if (code in 200..299) c.inputStream else c.errorStream 49 Answer(code, stream?.let { read(it) }) 50 } catch (e: Exception) { 51 Answer(0, null) 52 } finally { 53 c?.disconnect() 54 } 55 } 56 57 // The bytes go into one buffer and are made into text once. A character of 58 // more than one byte can lie across two reads. 59 private fun read(stream: InputStream): String? = try { 60 stream.use { s -> 61 val out = ByteArrayOutputStream() 62 val buf = ByteArray(8 * 1024) 63 while (out.size() < MAX_BYTES) { 64 val n = s.read(buf) 65 if (n <= 0) break 66 out.write(buf, 0, n) 67 } 68 String(out.toByteArray(), Charsets.UTF_8) 69 } 70 } catch (e: Exception) { 71 null 72 } 73 }