Networking.kt (4041B)
1 package net.taler.qtart 2 3 import android.util.Log 4 import com.sun.jna.Memory 5 import com.sun.jna.Pointer 6 import com.sun.jna.StringArray 7 import java.util.concurrent.atomic.AtomicInteger 8 import net.taler.qtart.TalerWalletCore.TalerNative.* 9 10 object Networking { 11 private val lastRequestId = AtomicInteger(0) 12 13 // Add your pointer here to protect it from the GC! 14 private val pointerReferences = mutableListOf<Pointer>() 15 16 interface RequestHandler { 17 fun handleRequest(req: RequestInfo, id: Int, sendResponse: (resp: ResponseInfo) -> Unit) 18 fun cancelRequest(id: Int): Boolean 19 } 20 21 enum class RedirectMode(val value: Int) { 22 Unknown(-1), 23 24 /** 25 * Handle redirects transparently. 26 */ 27 Transparent(0), 28 29 /** 30 * Redirect status codes are returned to the client. 31 * The client can choose to follow them manually (or not). 32 */ 33 Manual(1), 34 35 /** 36 * All redirect status codes result in an error. 37 */ 38 Error(2); 39 40 companion object { 41 fun fromValue(value: Int): RedirectMode = 42 RedirectMode.values().find { it.value == value } ?: Unknown 43 } 44 } 45 46 class RequestInfo( 47 val url: String, 48 val method: String, 49 val headers: Array<String>, 50 val redirectMode: RedirectMode, 51 val timeoutMs: Long, 52 val debug: Boolean, 53 val body: ByteArray?, 54 ) 55 56 class ResponseInfo( 57 val requestId: Int, 58 val status: Int, 59 val errorMsg: String?, 60 val headers: Array<String>, 61 val body: ByteArray?, 62 ) 63 64 /** 65 * Function to create a new HTTP fetch request. 66 * The request can still be configured until it is started. 67 * An identifier for the request will be written to @a handle. 68 * 69 * @return negative number on error, positive request_id on success 70 */ 71 internal fun httpCreateRequest( 72 reqInfo: JSHttpRequestInfo, 73 reqHandler: RequestHandler, 74 ): Int { 75 val requestId = lastRequestId.addAndGet(1) 76 77 reqHandler.handleRequest( 78 req = RequestInfo( 79 url = reqInfo.url!!, 80 method = reqInfo.method!!, 81 headers = reqInfo.request_headers!!.getStringArray(0), 82 redirectMode = RedirectMode.fromValue(reqInfo.redirect!!), 83 timeoutMs = reqInfo.timeout_ms!!.toLong(), 84 debug = reqInfo.debug != 0, 85 body = if (reqInfo.req_body_len!! > 0) { 86 reqInfo.req_body!!.getByteArray(0, reqInfo.req_body_len!!) 87 } else null, 88 ), 89 id = requestId, 90 ) { resp -> 91 val rawResp = JSHttpResponseInfo() 92 rawResp.request_id = resp.requestId 93 rawResp.status = resp.status 94 rawResp.errmsg = resp.errorMsg ?: "" 95 rawResp.response_headers = StringArray(resp.headers, false) 96 rawResp.num_response_headers = resp.headers.size 97 if (resp.body != null && resp.body.isNotEmpty()) { 98 // Manually allocate and write memory 99 val pointer = Memory(resp.body.size.toLong()) 100 pointer.write(0, resp.body, 0, resp.body.size) 101 pointerReferences.add(pointer) 102 rawResp.body = pointer 103 rawResp.body_len = resp.body.size 104 } else { 105 rawResp.body_len = 0 106 } 107 108 Log.d("Networking", "Response to request $requestId ready to send to qtart") 109 reqInfo.response_cb?.invoke(reqInfo.response_cb_cls!!, rawResp) 110 } 111 112 return requestId 113 } 114 115 /** 116 * Cancel a request. The request_id will become invalid 117 * and the callback won't be called with request_id. 118 */ 119 internal fun httpCancelRequest( 120 requestId: Int, 121 handler: RequestHandler, 122 ): Int { 123 val success = handler.cancelRequest(requestId) 124 return if (success) requestId else -requestId 125 } 126 }