commit d7d3834f215bc28dd82bb58f2b8a19e40cef8de8
parent 8f06688f55ef2aef6178d08c7a21722fbfef7db8
Author: rhunk <101876869+rhunk@users.noreply.github.com>
Date: Thu, 28 Dec 2023 23:11:17 +0100
fea(scripting): networking
Diffstat:
2 files changed, 165 insertions(+), 0 deletions(-)
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/JSModule.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/JSModule.kt
@@ -4,6 +4,7 @@ import android.os.Handler
import android.widget.Toast
import me.rhunk.snapenhance.common.scripting.bindings.AbstractBinding
import me.rhunk.snapenhance.common.scripting.bindings.BindingsContext
+import me.rhunk.snapenhance.common.scripting.impl.Networking
import me.rhunk.snapenhance.common.scripting.impl.JavaInterfaces
import me.rhunk.snapenhance.common.scripting.ktx.contextScope
import me.rhunk.snapenhance.common.scripting.ktx.putFunction
@@ -55,6 +56,7 @@ class JSModule(
registerBindings(
JavaInterfaces(),
InterfaceManager(),
+ Networking(),
)
moduleObject.putFunction("setField") { args ->
diff --git a/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/impl/Networking.kt b/common/src/main/kotlin/me/rhunk/snapenhance/common/scripting/impl/Networking.kt
@@ -0,0 +1,162 @@
+package me.rhunk.snapenhance.common.scripting.impl
+
+import me.rhunk.snapenhance.common.scripting.bindings.AbstractBinding
+import me.rhunk.snapenhance.common.scripting.bindings.BindingSide
+import me.rhunk.snapenhance.common.scripting.ktx.contextScope
+import okhttp3.OkHttpClient
+import okhttp3.Request
+import okhttp3.Response
+import okhttp3.WebSocket
+import okhttp3.WebSocketListener
+import okio.ByteString
+import okio.ByteString.Companion.toByteString
+import org.mozilla.javascript.Function
+import org.mozilla.javascript.Scriptable
+import org.mozilla.javascript.annotations.JSFunction
+import org.mozilla.javascript.annotations.JSGetter
+
+
+class Networking : AbstractBinding("networking", BindingSide.COMMON) {
+ private val defaultHttpClient = OkHttpClient()
+
+ inner class RequestBuilderWrapper(
+ val requestBuilder: Request.Builder
+ ) {
+ @JSFunction
+ fun url(url: String) = requestBuilder.url(url).let { this }
+
+ @JSFunction
+ fun addHeader(name: String, value: String) = requestBuilder.addHeader(name, value).let { this }
+
+ @JSFunction
+ fun removeHeader(name: String) = requestBuilder.removeHeader(name).let { this }
+
+ @JSFunction
+ fun method(method: String, body: String) = requestBuilder.method(method, okhttp3.RequestBody.create(null, body)).let { this }
+
+ @JSFunction
+ fun method(method: String, body: java.io.InputStream) = requestBuilder.method(method, okhttp3.RequestBody.create(null, body.readBytes())).let { this }
+
+ @JSFunction
+ fun method(method: String, body: ByteArray) = requestBuilder.method(method, okhttp3.RequestBody.create(null, body)).let { this }
+ }
+
+ inner class ResponseWrapper(
+ private val response: Response
+ ) {
+ @get:JSGetter
+ val statusCode get() = response.code
+ @get:JSGetter
+ val statusMessage get() = response.message
+ @get:JSGetter
+ val headers get() = response.headers.toMultimap().mapValues { it.value.joinToString(", ") }
+ @get:JSGetter
+ val bodyAsString get() = response.body.string()
+ @get:JSGetter
+ val bodyAsStream get() = response.body.byteStream()
+ @get:JSGetter
+ val bodyAsByteArray get() = response.body.bytes()
+ @get:JSGetter
+ val contentLength get() = response.body.contentLength()
+ @JSFunction fun getHeader(name: String) = response.header(name)
+ @JSFunction fun close() = response.close()
+ }
+
+ inner class WebsocketWrapper(
+ private val websocket: WebSocket
+ ) {
+ @JSFunction fun cancel() = websocket.cancel()
+ @JSFunction fun close(code: Int, reason: String) = websocket.close(code, reason)
+ @JSFunction fun queueSize() = websocket.queueSize()
+ @JSFunction fun send(bytes: ByteArray) = websocket.send(bytes.toByteString())
+ @JSFunction fun send(text: String) = websocket.send(text)
+ }
+
+ @JSFunction
+ fun getUrl(url: String, callback: (error: String?, response: String) -> Unit) {
+ defaultHttpClient.newCall(Request.Builder().url(url).build()).enqueue(object : okhttp3.Callback {
+ override fun onFailure(call: okhttp3.Call, e: java.io.IOException) {
+ callback(e.message, "")
+ }
+
+ override fun onResponse(call: okhttp3.Call, response: Response) {
+ response.use {
+ callback(null, it.body.string())
+ }
+ }
+ })
+ }
+
+ @JSFunction
+ fun getUrlAsStream(url: String, callback: (error: String?, response: java.io.InputStream) -> Unit) {
+ defaultHttpClient.newCall(Request.Builder().url(url).build()).enqueue(object : okhttp3.Callback {
+ override fun onFailure(call: okhttp3.Call, e: java.io.IOException) {
+ callback(e.message, java.io.ByteArrayInputStream(byteArrayOf()))
+ }
+
+ override fun onResponse(call: okhttp3.Call, response: Response) {
+ response.use {
+ callback(null, it.body.byteStream())
+ }
+ }
+ })
+ }
+
+ @JSFunction
+ fun newRequest() = RequestBuilderWrapper(Request.Builder())
+
+ @JSFunction
+ fun newWebSocket(requestBuilder: RequestBuilderWrapper, listener: Scriptable): WebsocketWrapper {
+ return defaultHttpClient.newWebSocket(requestBuilder.requestBuilder.build(), object: WebSocketListener() {
+ private fun callListener(name: String, websocket: WebSocket, vararg args: Any?) {
+ contextScope {
+ (listener.get(name, listener) as? Function)?.call(this, listener, listener, arrayOf(WebsocketWrapper(websocket), *args))
+ }
+ }
+
+ override fun onOpen(webSocket: WebSocket, response: Response) {
+ callListener("onOpen", webSocket, ResponseWrapper(response))
+ }
+
+ override fun onClosed(webSocket: WebSocket, code: Int, reason: String) {
+ callListener("onClosed", webSocket, code, reason)
+ }
+
+ override fun onClosing(webSocket: WebSocket, code: Int, reason: String) {
+ callListener("onClosing", webSocket, code, reason)
+ }
+
+ override fun onFailure(webSocket: WebSocket, t: Throwable, response: Response?) {
+ callListener("onFailure", webSocket, t.message, response?.let { ResponseWrapper(it) })
+ }
+
+ override fun onMessage(webSocket: WebSocket, bytes: ByteString) {
+ callListener("onMessageBytes", webSocket, bytes.toByteArray())
+ }
+
+ override fun onMessage(webSocket: WebSocket, text: String) {
+ callListener("onMessageText", webSocket, text)
+ }
+ }).let { WebsocketWrapper(it) }
+ }
+
+ @JSFunction
+ fun enqueue(requestBuilder: RequestBuilderWrapper, callback: (error: String?, response: ResponseWrapper?) -> Unit) {
+ defaultHttpClient.newCall(requestBuilder.requestBuilder.build()).enqueue(object : okhttp3.Callback {
+ override fun onFailure(call: okhttp3.Call, e: java.io.IOException) {
+ callback(e.message, null)
+ }
+
+ override fun onResponse(call: okhttp3.Call, response: Response) {
+ response.use {
+ callback(null, ResponseWrapper(it))
+ }
+ }
+ })
+ }
+
+ @JSFunction
+ fun execute(requestBuilder: RequestBuilderWrapper) = ResponseWrapper(defaultHttpClient.newCall(requestBuilder.requestBuilder.build()).execute())
+
+ override fun getObject() = this
+}+
\ No newline at end of file