155 lines
3.7 KiB
Markdown
155 lines
3.7 KiB
Markdown
# Kotlin cloudflare worker
|
|||
|
|
|
||
|
|
A library to running kotlin/js on cloudflare worker.
|
||
|
|
|
||
|
|
# Get started
|
||
|
|
|
||
|
|
## Setup
|
||
|
|
|
||
|
|
> `kotlinx.coroutines` is required
|
||
|
|
|
||
|
|
```kotlin
|
||
|
|
plugins {
|
||
|
|
kotlin("multiplatform") version "2.2.21"
|
||
|
|
}
|
||
|
|
|
||
|
|
repositories {
|
||
|
|
mavenCentral()
|
||
|
|
maven("https://repo.maven.rtast.cn/releases")
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
```kotlin
|
||
|
|
// to get latest version of this lib, go to
|
||
|
|
// https://next.pkg.rtast.cn/#/releases/cn/rtast/kotlin-cfworker/kotlin-cloudflare-worker/
|
||
|
|
implementation("cn.rtast.kotlin-cfworker:kotlin-cloudflare-worker:1.0.1")
|
||
|
|
```
|
||
|
|
|
||
|
|
## Run app
|
||
|
|
|
||
|
|
```kotlin
|
||
|
|
kotlin {
|
||
|
|
js(IR) {
|
||
|
|
nodejs {
|
||
|
|
outputModuleName = "kotlin-cloudflare-worker"
|
||
|
|
binaries.executable()
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
> Minimal hello world app
|
||
|
|
|
||
|
|
```kotlin
|
||
|
|
/**
|
||
|
|
* DO NOT EDIT THIS FUNCTION
|
||
|
|
*/
|
||
|
|
fun main() {
|
||
|
|
@Suppress("unused_expression")
|
||
|
|
val eventListener = EventListener { event ->
|
||
|
|
val dyn = event.asDynamic()
|
||
|
|
event.asDynamic().respondWith(handleRequest(dyn.request as Request))
|
||
|
|
Unit
|
||
|
|
}
|
||
|
|
js("addEventListener('fetch', eventListener)")
|
||
|
|
}
|
||
|
|
|
||
|
|
@JsExport
|
||
|
|
fun handleRequest(request: Request): Promise<Response> = GlobalScope.promise {
|
||
|
|
val server = WorkerApplication().apply {
|
||
|
|
route("/") {
|
||
|
|
respondText("Hello kotlin cloudflare worker")
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return@promise server.handle(request)
|
||
|
|
}
|
||
|
|
|
||
|
|
```
|
||
|
|
|
||
|
|
> Before run worker locally, you need to install wrangler and login
|
||
|
|
|
||
|
|
First, create `wrangler.toml` in your root project and configure it
|
||
|
|
|
||
|
|
```toml
|
||
|
|
name = "kotlin-cloudflare-worker" # any name you want
|
||
|
|
account_id = "<your account id>"
|
||
|
|
workers_dev = false
|
||
|
|
preview_urls = false
|
||
|
|
compatibility_date = "2022-08-11"
|
||
|
|
main = "kotlin-cloudflare-worker.js" # Set this value same as `outputModuleName`
|
||
|
|
```
|
||
|
|
|
||
|
|
Then run gradle task `gradlew compileProductionExecutableKotlinJs`,
|
||
|
|
copy wrangler.toml into `build/compileSync/js/main/productionExecutable/kotlin`,
|
||
|
|
and run `wrangler dev` command in `build/compileSync/js/main/productionExecutable/kotlin`,
|
||
|
|
|
||
|
|
## Deploy
|
||
|
|
|
||
|
|
Run gradle task, copy wrangler.toml, but use `wrangler deploy` to deploy to cloudflare worker
|
||
|
|
|
||
|
|
# Note
|
||
|
|
|
||
|
|
JavaScript uses a single-threaded execution model. All asynchronous work is ultimately driven by the event loop (
|
||
|
|
including Kotlin coroutines on JS).
|
||
|
|
|
||
|
|
Therefore, DO NOT create a CoroutineScope(Dispatchers.Default) or launch CPU-bound coroutines, as there is no real
|
||
|
|
background thread on JS targets.
|
||
|
|
|
||
|
|
All coroutines run on same event loop thread, and all coroutines not in same event loop will be dropped.
|
||
|
|
|
||
|
|
Example:
|
||
|
|
|
||
|
|
```kotlin
|
||
|
|
// DO NOT DO THIS
|
||
|
|
val scope = CoroutineScope(Dispatcher.DEFAULT)
|
||
|
|
|
||
|
|
fun blockingFunction() {
|
||
|
|
scope.launch {
|
||
|
|
// this block will never execute
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
// Instead of do this
|
||
|
|
|
||
|
|
fun main() {
|
||
|
|
@Suppress("unused_expression")
|
||
|
|
val eventListener = EventListener { event ->
|
||
|
|
val dyn = event.asDynamic()
|
||
|
|
event.asDynamic().respondWith(handleRequest(dyn.request as Request))
|
||
|
|
Unit
|
||
|
|
}
|
||
|
|
js("addEventListener('fetch', eventListener)")
|
||
|
|
}
|
||
|
|
|
||
|
|
@JsExport
|
||
|
|
fun handleRequest(request: Request): Promise<Response> = GlobalScope.promise {
|
||
|
|
return Promise.resolve(Response("Hello"))
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
# Http client
|
||
|
|
|
||
|
|
Cloudflare worker is not a standard nodejs environment, do http requests must
|
||
|
|
use fetch api(not `window.fetch`, this is browser api), use `cn.rtast.cfworker.client.fetch` instead,
|
||
|
|
|
||
|
|
# ByteArray and ByteBuffer cast
|
||
|
|
|
||
|
|
Kotlin cloudflare worker provided api to mutual conversion,
|
||
|
|
|
||
|
|
```kotlin
|
||
|
|
import cn.rtast.cfworker.util.toByteArray
|
||
|
|
import cn.rtast.cfworker.util.toArrayBuffer
|
||
|
|
|
||
|
|
val bb: ByteBuffer = ...
|
||
|
|
// convert ByteBuffer to ByteArray
|
||
|
|
val ba: ByteArray = bb.toByteArray()
|
||
|
|
|
||
|
|
// convert ByteArray to ByteBuffer
|
||
|
|
val bb2: ByteBuffer = ba.toArrayBuffer()
|
||
|
|
```
|
||
|
|
|
||
|
|
# Real instance
|
||
|
|
|
||
|
|
https://repo.maven.rtast.cn
|
||
|
|
|
||
|
|
> Yes, it the maven repository but built with kotlin/js with this lib
|