expo-http-server 0.1.11 → 0.1.14-beta.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,85 +1,43 @@
1
1
  apply plugin: 'com.android.library'
2
- apply plugin: 'kotlin-android'
3
- apply plugin: 'maven-publish'
4
2
 
5
- group = 'expo.modules.httpserver'
6
- version = '0.1.0'
7
-
8
- buildscript {
9
- def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
10
- if (expoModulesCorePlugin.exists()) {
11
- apply from: expoModulesCorePlugin
12
- applyKotlinExpoModulesCorePlugin()
13
- }
14
-
15
- // Simple helper that allows the root project to override versions declared by this library.
16
- ext.safeExtGet = { prop, fallback ->
17
- rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
18
- }
19
-
20
- // Ensures backward compatibility
21
- ext.getKotlinVersion = {
22
- if (ext.has("kotlinVersion")) {
23
- ext.kotlinVersion()
24
- } else {
25
- ext.safeExtGet("kotlinVersion", "1.8.10")
3
+ def expoModulesCorePlugin = new File(project(":expo-modules-core").projectDir.absolutePath, "ExpoModulesCorePlugin.gradle")
4
+
5
+ apply from: expoModulesCorePlugin
6
+ applyKotlinExpoModulesCorePlugin()
7
+ useCoreDependencies()
8
+ useExpoPublishing()
9
+
10
+ // If you want to use the managed Android SDK versions from expo-modules-core, set this to true.
11
+ // The Android SDK versions will be bumped from time to time in SDK releases and may introduce breaking changes in your module code.
12
+ // Most of the time, you may like to manage the Android SDK versions yourself.
13
+ def useManagedAndroidSdkVersions = true
14
+ if (useManagedAndroidSdkVersions) {
15
+ useDefaultAndroidSdkVersions()
16
+ } else {
17
+ buildscript {
18
+ // Simple helper that allows the root project to override versions declared by this library.
19
+ ext.safeExtGet = { prop, fallback ->
20
+ rootProject.ext.has(prop) ? rootProject.ext.get(prop) : fallback
26
21
  }
27
22
  }
28
-
29
- repositories {
30
- mavenCentral()
31
- }
32
-
33
- dependencies {
34
- classpath("org.jetbrains.kotlin:kotlin-gradle-plugin:${getKotlinVersion()}")
35
- }
36
- }
37
-
38
- afterEvaluate {
39
- publishing {
40
- publications {
41
- release(MavenPublication) {
42
- from components.release
43
- }
44
- }
45
- repositories {
46
- maven {
47
- url = mavenLocal().url
48
- }
23
+ project.android {
24
+ compileSdkVersion safeExtGet("compileSdkVersion", 34)
25
+ defaultConfig {
26
+ minSdkVersion safeExtGet("minSdkVersion", 21)
27
+ targetSdkVersion safeExtGet("targetSdkVersion", 34)
49
28
  }
50
29
  }
51
30
  }
52
31
 
53
32
  android {
54
- compileSdkVersion safeExtGet("compileSdkVersion", 33)
55
-
56
- def agpVersion = com.android.Version.ANDROID_GRADLE_PLUGIN_VERSION
57
- if (agpVersion.tokenize('.')[0].toInteger() < 8) {
58
- compileOptions {
59
- sourceCompatibility JavaVersion.VERSION_11
60
- targetCompatibility JavaVersion.VERSION_11
61
- }
62
-
63
- kotlinOptions {
64
- jvmTarget = JavaVersion.VERSION_11.majorVersion
65
- }
66
- }
67
-
68
33
  namespace "expo.modules.httpserver"
69
34
  defaultConfig {
70
- minSdkVersion safeExtGet("minSdkVersion", 21)
71
- targetSdkVersion safeExtGet("targetSdkVersion", 34)
72
35
  versionCode 1
73
36
  versionName "0.1.0"
74
37
  }
75
38
  lintOptions {
76
39
  abortOnError false
77
40
  }
78
- publishing {
79
- singleVariant("release") {
80
- withSourcesJar()
81
- }
82
- }
83
41
  }
84
42
 
85
43
  repositories {
@@ -88,7 +46,6 @@ repositories {
88
46
 
89
47
  dependencies {
90
48
  implementation project(':expo-modules-core')
91
- implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:${getKotlinVersion()}"
92
49
  implementation 'com.github.fengzhizi715.AndroidServer:core:1.3.3'
93
50
  implementation 'com.github.fengzhizi715.AndroidServer:gson:1.3.3'
94
- }
51
+ }
@@ -9,6 +9,7 @@ import com.safframework.server.core.http.HttpMethod
9
9
  import com.safframework.server.core.http.Request
10
10
  import com.safframework.server.core.http.Response
11
11
  import org.json.JSONObject
12
+ import java.util.concurrent.ConcurrentHashMap
12
13
 
13
14
  class ExpoHttpServerModule : Module() {
14
15
  class SimpleHttpResponse(val statusCode: Int,
@@ -19,7 +20,7 @@ class ExpoHttpServerModule : Module() {
19
20
 
20
21
  private var server: Server? = null;
21
22
  private var started = false;
22
- private val responses = HashMap<String, SimpleHttpResponse>()
23
+ private val responses = ConcurrentHashMap<String, SimpleHttpResponse>()
23
24
 
24
25
  override fun definition() = ModuleDefinition {
25
26
 
@@ -28,6 +29,8 @@ class ExpoHttpServerModule : Module() {
28
29
  Events("onStatusUpdate", "onRequest")
29
30
 
30
31
  Function("setup") { port: Int ->
32
+ server?.close()
33
+ started = false
31
34
  server = AndroidServer.Builder{
32
35
  port {
33
36
  port
@@ -49,10 +52,11 @@ class ExpoHttpServerModule : Module() {
49
52
  "paramsJson" to JSONObject(params).toString(),
50
53
  "cookiesJson" to JSONObject(cookies).toString(),
51
54
  ))
52
- while (!responses.containsKey(uuid)) {
55
+ val deadline = System.currentTimeMillis() + 30_000
56
+ while (!responses.containsKey(uuid) && System.currentTimeMillis() < deadline) {
53
57
  Thread.sleep(10)
54
58
  }
55
- val res = responses[uuid]!!
59
+ val res = responses.remove(uuid) ?: return@request response
56
60
  response.setBodyText(res.body)
57
61
  response.setStatus(res.statusCode)
58
62
  response.addHeader("Content-Length", "" + res.body.length)
@@ -60,7 +64,6 @@ class ExpoHttpServerModule : Module() {
60
64
  for ((key, value) in res.headers) {
61
65
  response.addHeader(key, value)
62
66
  }
63
- responses.remove(uuid);
64
67
  return@request response
65
68
  };
66
69
  }
@@ -73,12 +76,20 @@ class ExpoHttpServerModule : Module() {
73
76
  ))
74
77
  } else {
75
78
  if (!started) {
76
- started = true
77
- server?.start()
78
- sendEvent("onStatusUpdate", bundleOf(
79
- "status" to "STARTED",
80
- "message" to "Server started"
81
- ))
79
+ try {
80
+ started = true
81
+ server?.start()
82
+ sendEvent("onStatusUpdate", bundleOf(
83
+ "status" to "STARTED",
84
+ "message" to "Server started"
85
+ ))
86
+ } catch (e: Exception) {
87
+ started = false
88
+ sendEvent("onStatusUpdate", bundleOf(
89
+ "status" to "ERROR",
90
+ "message" to (e.message ?: "Failed to start server")
91
+ ))
92
+ }
82
93
  }
83
94
  }
84
95
  }
@@ -101,4 +112,4 @@ class ExpoHttpServerModule : Module() {
101
112
  ))
102
113
  }
103
114
  }
104
- }
115
+ }
package/build/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
1
+ export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS";
2
2
  /**
3
3
  * PAUSED AND RESUMED are iOS only
4
4
  */
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,CAAC;AAC3D;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxD;AAED,eAAO,MAAM,KAAK,YAyBjB,CAAC;AAEF,eAAO,MAAM,KAAK,SACV,MAAM,UACJ,UAAU,sBACE,YAAY,KAAK,QAAQ,QAAQ,CAAC,SAUvD,CAAC;AAEF,eAAO,MAAM,KAAK,SACV,MAAM,4BACa,WAAW,KAAK,IAAI,sBAQ9C,CAAC;AAEF,eAAO,MAAM,IAAI,WAAoC,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,MAAM,MAAM,UAAU,GAAG,KAAK,GAAG,MAAM,GAAG,KAAK,GAAG,QAAQ,GAAG,SAAS,CAAC;AACvE;;GAEG;AACH,MAAM,MAAM,MAAM,GAAG,SAAS,GAAG,QAAQ,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAC;AAE5E,MAAM,WAAW,WAAW;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,QAAQ;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,CAAC,OAAO,EAAE,YAAY,KAAK,OAAO,CAAC,QAAQ,CAAC,CAAC;CACxD;AAED,eAAO,MAAM,KAAK,YA2BjB,CAAC;AAEF,eAAO,MAAM,KAAK,SACV,MAAM,UACJ,UAAU,sBACE,YAAY,KAAK,QAAQ,QAAQ,CAAC,SAUvD,CAAC;AAEF,eAAO,MAAM,KAAK,SACV,MAAM,4BACa,WAAW,KAAK,IAAI,sBAU9C,CAAC;AAEF,eAAO,MAAM,IAAI,WAAoC,CAAC"}
package/build/index.js CHANGED
@@ -4,13 +4,16 @@ const emitter = new EventEmitter(ExpoHttpServerModule);
4
4
  const requestCallbacks = [];
5
5
  export const start = () => {
6
6
  emitter.addListener("onRequest", async (event) => {
7
- const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);
8
- if (!responseHandler) {
9
- ExpoHttpServerModule.respond(event.uuid, 404, "Not Found", "application/json", {}, JSON.stringify({ error: "Handler not found" }));
10
- return;
7
+ try {
8
+ const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);
9
+ if (!responseHandler) {
10
+ ExpoHttpServerModule.respond(event.uuid, 404, "Not Found", "application/json", {}, JSON.stringify({ error: "Handler not found" }));
11
+ return;
12
+ }
13
+ const response = await responseHandler.callback(event);
14
+ ExpoHttpServerModule.respond(event.uuid, response.statusCode || 200, response.statusDescription || "OK", response.contentType || "application/json", response.headers || {}, response.body || "{}");
11
15
  }
12
- const response = await responseHandler.callback(event);
13
- ExpoHttpServerModule.respond(event.uuid, response.statusCode || 200, response.statusDescription || "OK", response.contentType || "application/json", response.headers || {}, response.body || "{}");
16
+ catch { }
14
17
  });
15
18
  ExpoHttpServerModule.start();
16
19
  };
@@ -27,7 +30,10 @@ export const route = (path, method, callback) => {
27
30
  export const setup = (port, onStatusUpdate) => {
28
31
  if (onStatusUpdate) {
29
32
  emitter.addListener("onStatusUpdate", async (event) => {
30
- onStatusUpdate(event);
33
+ try {
34
+ await onStatusUpdate(event);
35
+ }
36
+ catch { }
31
37
  });
32
38
  }
33
39
  ExpoHttpServerModule.setup(port);
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,oBAAoB,MAAM,wBAAwB,CAAC;AAE1D,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,oBAAoB,CAAC,CAAC;AACvD,MAAM,gBAAgB,GAAe,EAAE,CAAC;AAsCxC,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,EAAE;IACxB,OAAO,CAAC,WAAW,CAAe,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC7D,MAAM,eAAe,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC;QAC5E,IAAI,CAAC,eAAe,EAAE,CAAC;YACrB,oBAAoB,CAAC,OAAO,CAC1B,KAAK,CAAC,IAAI,EACV,GAAG,EACH,WAAW,EACX,kBAAkB,EAClB,EAAE,EACF,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAC/C,CAAC;YACF,OAAO;QACT,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;QACvD,oBAAoB,CAAC,OAAO,CAC1B,KAAK,CAAC,IAAI,EACV,QAAQ,CAAC,UAAU,IAAI,GAAG,EAC1B,QAAQ,CAAC,iBAAiB,IAAI,IAAI,EAClC,QAAQ,CAAC,WAAW,IAAI,kBAAkB,EAC1C,QAAQ,CAAC,OAAO,IAAI,EAAE,EACtB,QAAQ,CAAC,IAAI,IAAI,IAAI,CACtB,CAAC;IACJ,CAAC,CAAC,CAAC;IACH,oBAAoB,CAAC,KAAK,EAAE,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,IAAY,EACZ,MAAkB,EAClB,QAAsD,EACtD,EAAE;IACF,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjD,gBAAgB,CAAC,IAAI,CAAC;QACpB,MAAM;QACN,IAAI;QACJ,IAAI;QACJ,QAAQ;KACT,CAAC,CAAC;IACH,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,IAAY,EACZ,cAA6C,EAC7C,EAAE;IACF,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,CAAC,WAAW,CAAc,gBAAgB,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACjE,cAAc,CAAC,KAAK,CAAC,CAAC;QACxB,CAAC,CAAC,CAAC;IACL,CAAC;IACD,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC","sourcesContent":["import { EventEmitter } from \"expo-modules-core\";\n\nimport ExpoHttpServerModule from \"./ExpoHttpServerModule\";\n\nconst emitter = new EventEmitter(ExpoHttpServerModule);\nconst requestCallbacks: Callback[] = [];\n\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\";\n/**\n * PAUSED AND RESUMED are iOS only\n */\nexport type Status = \"STARTED\" | \"PAUSED\" | \"RESUMED\" | \"STOPPED\" | \"ERROR\";\n\nexport interface StatusEvent {\n status: Status;\n message: string;\n}\n\nexport interface RequestEvent {\n uuid: string;\n method: string;\n path: string;\n body: string;\n headersJson: string;\n paramsJson: string;\n cookiesJson: string;\n}\n\nexport interface Response {\n statusCode?: number;\n statusDescription?: string;\n contentType?: string;\n headers?: Record<string, string>;\n body?: string;\n}\n\nexport interface Callback {\n method: string;\n path: string;\n uuid: string;\n callback: (request: RequestEvent) => Promise<Response>;\n}\n\nexport const start = () => {\n emitter.addListener<RequestEvent>(\"onRequest\", async (event) => {\n const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);\n if (!responseHandler) {\n ExpoHttpServerModule.respond(\n event.uuid,\n 404,\n \"Not Found\",\n \"application/json\",\n {},\n JSON.stringify({ error: \"Handler not found\" }),\n );\n return;\n }\n const response = await responseHandler.callback(event);\n ExpoHttpServerModule.respond(\n event.uuid,\n response.statusCode || 200,\n response.statusDescription || \"OK\",\n response.contentType || \"application/json\",\n response.headers || {},\n response.body || \"{}\",\n );\n });\n ExpoHttpServerModule.start();\n};\n\nexport const route = (\n path: string,\n method: HttpMethod,\n callback: (request: RequestEvent) => Promise<Response>,\n) => {\n const uuid = Math.random().toString(16).slice(2);\n requestCallbacks.push({\n method,\n path,\n uuid,\n callback,\n });\n ExpoHttpServerModule.route(path, method, uuid);\n};\n\nexport const setup = (\n port: number,\n onStatusUpdate?: (event: StatusEvent) => void,\n) => {\n if (onStatusUpdate) {\n emitter.addListener<StatusEvent>(\"onStatusUpdate\", async (event) => {\n onStatusUpdate(event);\n });\n }\n ExpoHttpServerModule.setup(port);\n};\n\nexport const stop = () => ExpoHttpServerModule.stop();\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAEjD,OAAO,oBAAoB,MAAM,wBAAwB,CAAC;AAE1D,MAAM,OAAO,GAAG,IAAI,YAAY,CAAC,oBAAoB,CAAC,CAAC;AACvD,MAAM,gBAAgB,GAAe,EAAE,CAAC;AAsCxC,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,EAAE;IACxB,OAAO,CAAC,WAAW,CAAe,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC7D,IAAI,CAAC;YACH,MAAM,eAAe,GAAG,gBAAgB,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,KAAK,CAAC,IAAI,CAAC,CAAC;YAC5E,IAAI,CAAC,eAAe,EAAE,CAAC;gBACrB,oBAAoB,CAAC,OAAO,CAC1B,KAAK,CAAC,IAAI,EACV,GAAG,EACH,WAAW,EACX,kBAAkB,EAClB,EAAE,EACF,IAAI,CAAC,SAAS,CAAC,EAAE,KAAK,EAAE,mBAAmB,EAAE,CAAC,CAC/C,CAAC;gBACF,OAAO;YACT,CAAC;YACD,MAAM,QAAQ,GAAG,MAAM,eAAe,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACvD,oBAAoB,CAAC,OAAO,CAC1B,KAAK,CAAC,IAAI,EACV,QAAQ,CAAC,UAAU,IAAI,GAAG,EAC1B,QAAQ,CAAC,iBAAiB,IAAI,IAAI,EAClC,QAAQ,CAAC,WAAW,IAAI,kBAAkB,EAC1C,QAAQ,CAAC,OAAO,IAAI,EAAE,EACtB,QAAQ,CAAC,IAAI,IAAI,IAAI,CACtB,CAAC;QACJ,CAAC;QAAC,MAAM,CAAC,CAAA,CAAC;IACZ,CAAC,CAAC,CAAC;IACH,oBAAoB,CAAC,KAAK,EAAE,CAAC;AAC/B,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,IAAY,EACZ,MAAkB,EAClB,QAAsD,EACtD,EAAE;IACF,MAAM,IAAI,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACjD,gBAAgB,CAAC,IAAI,CAAC;QACpB,MAAM;QACN,IAAI;QACJ,IAAI;QACJ,QAAQ;KACT,CAAC,CAAC;IACH,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;AACjD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,IAAY,EACZ,cAA6C,EAC7C,EAAE;IACF,IAAI,cAAc,EAAE,CAAC;QACnB,OAAO,CAAC,WAAW,CAAc,gBAAgB,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;YACjE,IAAI,CAAC;gBACH,MAAM,cAAc,CAAC,KAAK,CAAC,CAAC;YAC9B,CAAC;YAAC,MAAM,CAAC,CAAA,CAAC;QACZ,CAAC,CAAC,CAAC;IACL,CAAC;IACD,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACnC,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,IAAI,GAAG,GAAG,EAAE,CAAC,oBAAoB,CAAC,IAAI,EAAE,CAAC","sourcesContent":["import { EventEmitter } from \"expo-modules-core\";\n\nimport ExpoHttpServerModule from \"./ExpoHttpServerModule\";\n\nconst emitter = new EventEmitter(ExpoHttpServerModule);\nconst requestCallbacks: Callback[] = [];\n\nexport type HttpMethod = \"GET\" | \"POST\" | \"PUT\" | \"DELETE\" | \"OPTIONS\";\n/**\n * PAUSED AND RESUMED are iOS only\n */\nexport type Status = \"STARTED\" | \"PAUSED\" | \"RESUMED\" | \"STOPPED\" | \"ERROR\";\n\nexport interface StatusEvent {\n status: Status;\n message: string;\n}\n\nexport interface RequestEvent {\n uuid: string;\n method: string;\n path: string;\n body: string;\n headersJson: string;\n paramsJson: string;\n cookiesJson: string;\n}\n\nexport interface Response {\n statusCode?: number;\n statusDescription?: string;\n contentType?: string;\n headers?: Record<string, string>;\n body?: string;\n}\n\nexport interface Callback {\n method: string;\n path: string;\n uuid: string;\n callback: (request: RequestEvent) => Promise<Response>;\n}\n\nexport const start = () => {\n emitter.addListener<RequestEvent>(\"onRequest\", async (event) => {\n try {\n const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);\n if (!responseHandler) {\n ExpoHttpServerModule.respond(\n event.uuid,\n 404,\n \"Not Found\",\n \"application/json\",\n {},\n JSON.stringify({ error: \"Handler not found\" }),\n );\n return;\n }\n const response = await responseHandler.callback(event);\n ExpoHttpServerModule.respond(\n event.uuid,\n response.statusCode || 200,\n response.statusDescription || \"OK\",\n response.contentType || \"application/json\",\n response.headers || {},\n response.body || \"{}\",\n );\n } catch {}\n });\n ExpoHttpServerModule.start();\n};\n\nexport const route = (\n path: string,\n method: HttpMethod,\n callback: (request: RequestEvent) => Promise<Response>,\n) => {\n const uuid = Math.random().toString(16).slice(2);\n requestCallbacks.push({\n method,\n path,\n uuid,\n callback,\n });\n ExpoHttpServerModule.route(path, method, uuid);\n};\n\nexport const setup = (\n port: number,\n onStatusUpdate?: (event: StatusEvent) => void,\n) => {\n if (onStatusUpdate) {\n emitter.addListener<StatusEvent>(\"onStatusUpdate\", async (event) => {\n try {\n await onStatusUpdate(event);\n } catch {}\n });\n }\n ExpoHttpServerModule.setup(port);\n};\n\nexport const stop = () => ExpoHttpServerModule.stop();\n"]}
@@ -5,35 +5,43 @@ public class ExpoHttpServerModule: Module {
5
5
  private let server = CRHTTPServer()
6
6
  private var port: Int?
7
7
  private var stopped = false
8
+ private var isRunning = false
8
9
  private var responses = [String: CRResponse]()
9
10
  private var bgTaskIdentifier = UIBackgroundTaskIdentifier.invalid
10
-
11
+ private var foregroundObserver: NSObjectProtocol?
12
+
11
13
  public func definition() -> ModuleDefinition {
12
14
  Name("ExpoHttpServer")
13
15
 
14
16
  Events("onStatusUpdate", "onRequest")
15
-
17
+
16
18
  Function("setup", setupHandler)
17
19
  Function("start", startHandler)
18
20
  Function("route", routeHandler)
19
21
  Function("respond", respondHandler)
20
22
  Function("stop", stopHandler)
21
23
  }
22
-
24
+
23
25
  private func setupHandler(port: Int) {
24
26
  self.port = port;
25
27
  }
26
28
 
27
29
  private func startHandler() {
28
- NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [unowned self] notification in
29
- if (!self.stopped) {
30
- self.startServer(status: "RESUMED", message: "Server resumed")
31
- }
30
+ if let obs = foregroundObserver {
31
+ NotificationCenter.default.removeObserver(obs)
32
32
  }
33
- stopped = false;
33
+ foregroundObserver = NotificationCenter.default.addObserver(
34
+ forName: UIApplication.willEnterForegroundNotification,
35
+ object: nil,
36
+ queue: .main
37
+ ) { [weak self] _ in
38
+ guard let self, !self.stopped else { return }
39
+ self.startServer(status: "RESUMED", message: "Server resumed")
40
+ }
41
+ stopped = false
34
42
  startServer(status: "STARTED", message: "Server started")
35
43
  }
36
-
44
+
37
45
  private func routeHandler(path: String, method: String, uuid: String) {
38
46
  server.add(path, block: { (req, res, next) in
39
47
  DispatchQueue.main.async {
@@ -54,7 +62,7 @@ public class ExpoHttpServerModule: Module {
54
62
  }
55
63
  }, recursive: false, method: CRHTTPMethod.fromString(method))
56
64
  }
57
-
65
+
58
66
  private func respondHandler(udid: String,
59
67
  statusCode: Int,
60
68
  statusDescription: String,
@@ -73,41 +81,58 @@ public class ExpoHttpServerModule: Module {
73
81
  self.responses[udid] = nil;
74
82
  }
75
83
  }
76
-
77
84
  }
78
-
85
+
79
86
  private func stopHandler() {
80
- stopped = true;
87
+ stopped = true
88
+ if let obs = foregroundObserver {
89
+ NotificationCenter.default.removeObserver(obs)
90
+ foregroundObserver = nil
91
+ }
81
92
  stopServer(status: "STOPPED", message: "Server stopped")
82
93
  }
83
-
94
+
84
95
  private func startServer(status: String, message: String) {
85
- stopServer()
86
- if let port = port {
87
- var error: NSError?
88
- server.startListening(&error, portNumber: UInt(port))
89
- if (error != nil) {
90
- sendEvent("onStatusUpdate", [
91
- "status": "ERROR",
92
- "message": error?.localizedDescription ?? "Unknown error starting server"
93
- ])
94
- } else {
95
- beginBackgroundTask()
96
- sendEvent("onStatusUpdate", [
97
- "status": status,
98
- "message": message
99
- ])
96
+ if isRunning {
97
+ server.stopListening()
98
+ isRunning = false
99
+ endBackgroundTask()
100
+ DispatchQueue.main.asyncAfter(deadline: .now() + 0.3) { [weak self] in
101
+ self?.doStart(status: status, message: message)
100
102
  }
101
103
  } else {
104
+ doStart(status: status, message: message)
105
+ }
106
+ }
107
+
108
+ private func doStart(status: String, message: String) {
109
+ guard let port = port else {
102
110
  sendEvent("onStatusUpdate", [
103
111
  "status": "ERROR",
104
112
  "message": "Can't start server with port configured"
105
113
  ])
114
+ return
115
+ }
116
+ var error: NSError?
117
+ server.startListening(&error, portNumber: UInt(port))
118
+ if let error = error {
119
+ sendEvent("onStatusUpdate", [
120
+ "status": "ERROR",
121
+ "message": error.localizedDescription
122
+ ])
123
+ } else {
124
+ isRunning = true
125
+ beginBackgroundTask()
126
+ sendEvent("onStatusUpdate", [
127
+ "status": status,
128
+ "message": message
129
+ ])
106
130
  }
107
131
  }
108
-
132
+
109
133
  private func stopServer(status: String? = nil, message: String? = nil) {
110
134
  server.stopListening()
135
+ isRunning = false
111
136
  endBackgroundTask()
112
137
  if let status = status, let message = message {
113
138
  sendEvent("onStatusUpdate", [
@@ -116,7 +141,7 @@ public class ExpoHttpServerModule: Module {
116
141
  ])
117
142
  }
118
143
  }
119
-
144
+
120
145
  private func beginBackgroundTask() {
121
146
  if (bgTaskIdentifier == UIBackgroundTaskIdentifier.invalid) {
122
147
  self.bgTaskIdentifier = UIApplication.shared.beginBackgroundTask(withName: "BgTask", expirationHandler: {
@@ -155,7 +180,7 @@ extension CRHTTPMethod {
155
180
  return "GET"
156
181
  }
157
182
  }
158
-
183
+
159
184
  static func fromString(_ string: String) -> Self {
160
185
  var httpMethod: CRHTTPMethod
161
186
  switch (string) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-http-server",
3
- "version": "0.1.11",
3
+ "version": "0.1.14-beta.0",
4
4
  "description": "A simple HTTP server expo module (iOS/Android)",
5
5
  "main": "build/index.js",
6
6
  "types": "build/index.d.ts",
package/src/index.ts CHANGED
@@ -5,7 +5,7 @@ import ExpoHttpServerModule from "./ExpoHttpServerModule";
5
5
  const emitter = new EventEmitter(ExpoHttpServerModule);
6
6
  const requestCallbacks: Callback[] = [];
7
7
 
8
- export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
8
+ export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS";
9
9
  /**
10
10
  * PAUSED AND RESUMED are iOS only
11
11
  */
@@ -43,27 +43,29 @@ export interface Callback {
43
43
 
44
44
  export const start = () => {
45
45
  emitter.addListener<RequestEvent>("onRequest", async (event) => {
46
- const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);
47
- if (!responseHandler) {
46
+ try {
47
+ const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);
48
+ if (!responseHandler) {
49
+ ExpoHttpServerModule.respond(
50
+ event.uuid,
51
+ 404,
52
+ "Not Found",
53
+ "application/json",
54
+ {},
55
+ JSON.stringify({ error: "Handler not found" }),
56
+ );
57
+ return;
58
+ }
59
+ const response = await responseHandler.callback(event);
48
60
  ExpoHttpServerModule.respond(
49
61
  event.uuid,
50
- 404,
51
- "Not Found",
52
- "application/json",
53
- {},
54
- JSON.stringify({ error: "Handler not found" }),
62
+ response.statusCode || 200,
63
+ response.statusDescription || "OK",
64
+ response.contentType || "application/json",
65
+ response.headers || {},
66
+ response.body || "{}",
55
67
  );
56
- return;
57
- }
58
- const response = await responseHandler.callback(event);
59
- ExpoHttpServerModule.respond(
60
- event.uuid,
61
- response.statusCode || 200,
62
- response.statusDescription || "OK",
63
- response.contentType || "application/json",
64
- response.headers || {},
65
- response.body || "{}",
66
- );
68
+ } catch {}
67
69
  });
68
70
  ExpoHttpServerModule.start();
69
71
  };
@@ -89,7 +91,9 @@ export const setup = (
89
91
  ) => {
90
92
  if (onStatusUpdate) {
91
93
  emitter.addListener<StatusEvent>("onStatusUpdate", async (event) => {
92
- onStatusUpdate(event);
94
+ try {
95
+ await onStatusUpdate(event);
96
+ } catch {}
93
97
  });
94
98
  }
95
99
  ExpoHttpServerModule.setup(port);