expo-http-server 0.1.14-beta.2 → 0.1.14-beta.4
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.
- package/android/src/main/java/expo/modules/httpserver/ExpoHttpServerModule.kt +70 -5
- package/build/index.d.ts +1 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +8 -2
- package/build/index.js.map +1 -1
- package/ios/ExpoHttpServerModule.swift +32 -57
- package/package.json +1 -1
- package/src/index.ts +8 -2
|
@@ -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.net.BindException
|
|
12
13
|
import java.util.concurrent.ConcurrentHashMap
|
|
13
14
|
|
|
14
15
|
class ExpoHttpServerModule : Module() {
|
|
@@ -20,11 +21,55 @@ class ExpoHttpServerModule : Module() {
|
|
|
20
21
|
|
|
21
22
|
data class RouteConfig(val path: String, val method: String, val uuid: String)
|
|
22
23
|
|
|
24
|
+
companion object {
|
|
25
|
+
// The underlying AndroidServer/Netty bind happens on Netty's own event loop
|
|
26
|
+
// thread, asynchronously, so a BindException there can never be caught by a
|
|
27
|
+
// try/catch around server.start() - it surfaces as an uncaught exception and
|
|
28
|
+
// crashes the app. We install a single, narrowly-scoped default uncaught
|
|
29
|
+
// exception handler (chained to whatever was already registered) that only
|
|
30
|
+
// swallows BindExceptions thrown from within Netty's own call stack, so we
|
|
31
|
+
// can report the failure via onStatusUpdate instead of crashing.
|
|
32
|
+
private var bindExceptionGuardInstalled = false
|
|
33
|
+
private val statusUpdateListeners = ConcurrentHashMap.newKeySet<(String) -> Unit>()
|
|
34
|
+
|
|
35
|
+
@Synchronized
|
|
36
|
+
private fun ensureBindExceptionGuardInstalled() {
|
|
37
|
+
if (bindExceptionGuardInstalled) return
|
|
38
|
+
bindExceptionGuardInstalled = true
|
|
39
|
+
val previousHandler = Thread.getDefaultUncaughtExceptionHandler()
|
|
40
|
+
Thread.setDefaultUncaughtExceptionHandler { thread, throwable ->
|
|
41
|
+
if (isNettyBindException(throwable)) {
|
|
42
|
+
val message = throwable.message ?: "Address already in use"
|
|
43
|
+
statusUpdateListeners.forEach { it(message) }
|
|
44
|
+
} else {
|
|
45
|
+
previousHandler?.uncaughtException(thread, throwable)
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
private fun isNettyBindException(throwable: Throwable): Boolean =
|
|
51
|
+
throwable is BindException &&
|
|
52
|
+
throwable.stackTrace.any { it.className.startsWith("io.netty") }
|
|
53
|
+
}
|
|
54
|
+
|
|
23
55
|
private var server: Server? = null
|
|
24
56
|
private var started = false
|
|
57
|
+
// Tracks whether the *current* server instance has actually bound a socket.
|
|
58
|
+
// A freshly built server (from setup()/buildServerWithRoutes()) holds no
|
|
59
|
+
// socket, so there's nothing to release before starting it - closing and
|
|
60
|
+
// rebuilding in that case only adds an unnecessary close/rebind race.
|
|
61
|
+
private var hasBoundSocket = false
|
|
25
62
|
private var configuredPort: Int = 0
|
|
26
63
|
private val routeConfigs = mutableListOf<RouteConfig>()
|
|
27
64
|
private val responses = ConcurrentHashMap<String, SimpleHttpResponse>()
|
|
65
|
+
private val onBindFailure: (String) -> Unit = { message ->
|
|
66
|
+
started = false
|
|
67
|
+
hasBoundSocket = false
|
|
68
|
+
sendEvent("onStatusUpdate", bundleOf(
|
|
69
|
+
"status" to "ERROR",
|
|
70
|
+
"message" to message
|
|
71
|
+
))
|
|
72
|
+
}
|
|
28
73
|
|
|
29
74
|
private fun handleRequest(uuid: String, request: Request, response: Response): Response {
|
|
30
75
|
val headers: Map<String, String> = request.headers()
|
|
@@ -72,10 +117,20 @@ class ExpoHttpServerModule : Module() {
|
|
|
72
117
|
|
|
73
118
|
Events("onStatusUpdate", "onRequest")
|
|
74
119
|
|
|
120
|
+
OnCreate {
|
|
121
|
+
ensureBindExceptionGuardInstalled()
|
|
122
|
+
statusUpdateListeners.add(onBindFailure)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
OnDestroy {
|
|
126
|
+
statusUpdateListeners.remove(onBindFailure)
|
|
127
|
+
}
|
|
128
|
+
|
|
75
129
|
Function("setup") { port: Int ->
|
|
76
130
|
configuredPort = port
|
|
77
131
|
server?.close()
|
|
78
132
|
started = false
|
|
133
|
+
hasBoundSocket = false
|
|
79
134
|
routeConfigs.clear()
|
|
80
135
|
server = AndroidServer.Builder {
|
|
81
136
|
port { configuredPort }
|
|
@@ -99,12 +154,22 @@ class ExpoHttpServerModule : Module() {
|
|
|
99
154
|
if (!started) {
|
|
100
155
|
try {
|
|
101
156
|
started = true
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
//
|
|
105
|
-
|
|
106
|
-
|
|
157
|
+
// Only close and rebuild when the current server instance has actually
|
|
158
|
+
// bound a socket before - a freshly built instance (from setup(), or
|
|
159
|
+
// because it was never started) holds nothing to release, so closing
|
|
160
|
+
// and immediately rebinding it would just add an unnecessary race with
|
|
161
|
+
// the OS releasing the port (BindException: Address already in use).
|
|
162
|
+
if (hasBoundSocket) {
|
|
163
|
+
server?.close()
|
|
164
|
+
// Give the OS a moment to release the previous socket before rebinding
|
|
165
|
+
// to the same port - reduces (but, due to the underlying library
|
|
166
|
+
// configuring SO_REUSEADDR on the wrong socket scope, cannot fully
|
|
167
|
+
// eliminate) the "Address already in use" race.
|
|
168
|
+
Thread.sleep(300)
|
|
169
|
+
server = buildServerWithRoutes()
|
|
170
|
+
}
|
|
107
171
|
server?.start()
|
|
172
|
+
hasBoundSocket = true
|
|
108
173
|
sendEvent("onStatusUpdate", bundleOf(
|
|
109
174
|
"status" to "STARTED",
|
|
110
175
|
"message" to "Server started"
|
package/build/index.d.ts
CHANGED
|
@@ -32,5 +32,5 @@ export interface Callback {
|
|
|
32
32
|
export declare const start: () => void;
|
|
33
33
|
export declare const route: (path: string, method: HttpMethod, callback: (request: RequestEvent) => Promise<Response>) => void;
|
|
34
34
|
export declare const setup: (port: number, onStatusUpdate?: ((event: StatusEvent) => void) | undefined) => void;
|
|
35
|
-
export declare const stop: () =>
|
|
35
|
+
export declare const stop: () => void;
|
|
36
36
|
//# sourceMappingURL=index.d.ts.map
|
package/build/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAQA,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,YA4BjB,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,YAIhB,CAAC"}
|
package/build/index.js
CHANGED
|
@@ -2,8 +2,10 @@ import { EventEmitter } from "expo-modules-core";
|
|
|
2
2
|
import ExpoHttpServerModule from "./ExpoHttpServerModule";
|
|
3
3
|
const emitter = new EventEmitter(ExpoHttpServerModule);
|
|
4
4
|
const requestCallbacks = [];
|
|
5
|
+
let requestSubscription = null;
|
|
5
6
|
export const start = () => {
|
|
6
|
-
|
|
7
|
+
requestSubscription?.remove();
|
|
8
|
+
requestSubscription = emitter.addListener("onRequest", async (event) => {
|
|
7
9
|
try {
|
|
8
10
|
const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);
|
|
9
11
|
if (!responseHandler) {
|
|
@@ -38,5 +40,9 @@ export const setup = (port, onStatusUpdate) => {
|
|
|
38
40
|
}
|
|
39
41
|
ExpoHttpServerModule.setup(port);
|
|
40
42
|
};
|
|
41
|
-
export const stop = () =>
|
|
43
|
+
export const stop = () => {
|
|
44
|
+
requestSubscription?.remove();
|
|
45
|
+
requestSubscription = null;
|
|
46
|
+
ExpoHttpServerModule.stop();
|
|
47
|
+
};
|
|
42
48
|
//# sourceMappingURL=index.js.map
|
package/build/index.js.map
CHANGED
|
@@ -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;
|
|
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;AACxC,IAAI,mBAAmB,GAAkD,IAAI,CAAC;AAsC9E,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,EAAE;IACxB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,mBAAmB,GAAG,OAAO,CAAC,WAAW,CAAe,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QACnF,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;IACvB,mBAAmB,EAAE,MAAM,EAAE,CAAC;IAC9B,mBAAmB,GAAG,IAAI,CAAC;IAC3B,oBAAoB,CAAC,IAAI,EAAE,CAAC;AAC9B,CAAC,CAAC","sourcesContent":["import { EventEmitter } from \"expo-modules-core\";\n\nimport ExpoHttpServerModule from \"./ExpoHttpServerModule\";\n\nconst emitter = new EventEmitter(ExpoHttpServerModule);\nconst requestCallbacks: Callback[] = [];\nlet requestSubscription: ReturnType<typeof emitter.addListener> | null = null;\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 requestSubscription?.remove();\n requestSubscription = 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 = () => {\n requestSubscription?.remove();\n requestSubscription = null;\n ExpoHttpServerModule.stop();\n};\n"]}
|
|
@@ -5,43 +5,35 @@ 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
|
|
9
8
|
private var responses = [String: CRResponse]()
|
|
10
9
|
private var bgTaskIdentifier = UIBackgroundTaskIdentifier.invalid
|
|
11
|
-
|
|
12
|
-
|
|
10
|
+
|
|
13
11
|
public func definition() -> ModuleDefinition {
|
|
14
12
|
Name("ExpoHttpServer")
|
|
15
13
|
|
|
16
14
|
Events("onStatusUpdate", "onRequest")
|
|
17
|
-
|
|
15
|
+
|
|
18
16
|
Function("setup", setupHandler)
|
|
19
17
|
Function("start", startHandler)
|
|
20
18
|
Function("route", routeHandler)
|
|
21
19
|
Function("respond", respondHandler)
|
|
22
20
|
Function("stop", stopHandler)
|
|
23
21
|
}
|
|
24
|
-
|
|
22
|
+
|
|
25
23
|
private func setupHandler(port: Int) {
|
|
26
24
|
self.port = port;
|
|
27
25
|
}
|
|
28
26
|
|
|
29
27
|
private func startHandler() {
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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")
|
|
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
|
+
}
|
|
40
32
|
}
|
|
41
|
-
stopped = false
|
|
33
|
+
stopped = false;
|
|
42
34
|
startServer(status: "STARTED", message: "Server started")
|
|
43
35
|
}
|
|
44
|
-
|
|
36
|
+
|
|
45
37
|
private func routeHandler(path: String, method: String, uuid: String) {
|
|
46
38
|
server.add(path, block: { (req, res, next) in
|
|
47
39
|
DispatchQueue.main.async {
|
|
@@ -62,7 +54,7 @@ public class ExpoHttpServerModule: Module {
|
|
|
62
54
|
}
|
|
63
55
|
}, recursive: false, method: CRHTTPMethod.fromString(method))
|
|
64
56
|
}
|
|
65
|
-
|
|
57
|
+
|
|
66
58
|
private func respondHandler(udid: String,
|
|
67
59
|
statusCode: Int,
|
|
68
60
|
statusDescription: String,
|
|
@@ -81,58 +73,41 @@ public class ExpoHttpServerModule: Module {
|
|
|
81
73
|
self.responses[udid] = nil;
|
|
82
74
|
}
|
|
83
75
|
}
|
|
76
|
+
|
|
84
77
|
}
|
|
85
|
-
|
|
78
|
+
|
|
86
79
|
private func stopHandler() {
|
|
87
|
-
stopped = true
|
|
88
|
-
if let obs = foregroundObserver {
|
|
89
|
-
NotificationCenter.default.removeObserver(obs)
|
|
90
|
-
foregroundObserver = nil
|
|
91
|
-
}
|
|
80
|
+
stopped = true;
|
|
92
81
|
stopServer(status: "STOPPED", message: "Server stopped")
|
|
93
82
|
}
|
|
94
|
-
|
|
83
|
+
|
|
95
84
|
private func startServer(status: String, message: String) {
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
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
|
+
])
|
|
102
100
|
}
|
|
103
101
|
} else {
|
|
104
|
-
doStart(status: status, message: message)
|
|
105
|
-
}
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
private func doStart(status: String, message: String) {
|
|
109
|
-
guard let port = port else {
|
|
110
102
|
sendEvent("onStatusUpdate", [
|
|
111
103
|
"status": "ERROR",
|
|
112
104
|
"message": "Can't start server with port configured"
|
|
113
105
|
])
|
|
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
|
-
])
|
|
130
106
|
}
|
|
131
107
|
}
|
|
132
|
-
|
|
108
|
+
|
|
133
109
|
private func stopServer(status: String? = nil, message: String? = nil) {
|
|
134
110
|
server.stopListening()
|
|
135
|
-
isRunning = false
|
|
136
111
|
endBackgroundTask()
|
|
137
112
|
if let status = status, let message = message {
|
|
138
113
|
sendEvent("onStatusUpdate", [
|
|
@@ -141,7 +116,7 @@ public class ExpoHttpServerModule: Module {
|
|
|
141
116
|
])
|
|
142
117
|
}
|
|
143
118
|
}
|
|
144
|
-
|
|
119
|
+
|
|
145
120
|
private func beginBackgroundTask() {
|
|
146
121
|
if (bgTaskIdentifier == UIBackgroundTaskIdentifier.invalid) {
|
|
147
122
|
self.bgTaskIdentifier = UIApplication.shared.beginBackgroundTask(withName: "BgTask", expirationHandler: {
|
|
@@ -180,7 +155,7 @@ extension CRHTTPMethod {
|
|
|
180
155
|
return "GET"
|
|
181
156
|
}
|
|
182
157
|
}
|
|
183
|
-
|
|
158
|
+
|
|
184
159
|
static func fromString(_ string: String) -> Self {
|
|
185
160
|
var httpMethod: CRHTTPMethod
|
|
186
161
|
switch (string) {
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ import ExpoHttpServerModule from "./ExpoHttpServerModule";
|
|
|
4
4
|
|
|
5
5
|
const emitter = new EventEmitter(ExpoHttpServerModule);
|
|
6
6
|
const requestCallbacks: Callback[] = [];
|
|
7
|
+
let requestSubscription: ReturnType<typeof emitter.addListener> | null = null;
|
|
7
8
|
|
|
8
9
|
export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE" | "OPTIONS";
|
|
9
10
|
/**
|
|
@@ -42,7 +43,8 @@ export interface Callback {
|
|
|
42
43
|
}
|
|
43
44
|
|
|
44
45
|
export const start = () => {
|
|
45
|
-
|
|
46
|
+
requestSubscription?.remove();
|
|
47
|
+
requestSubscription = emitter.addListener<RequestEvent>("onRequest", async (event) => {
|
|
46
48
|
try {
|
|
47
49
|
const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);
|
|
48
50
|
if (!responseHandler) {
|
|
@@ -99,4 +101,8 @@ export const setup = (
|
|
|
99
101
|
ExpoHttpServerModule.setup(port);
|
|
100
102
|
};
|
|
101
103
|
|
|
102
|
-
export const stop = () =>
|
|
104
|
+
export const stop = () => {
|
|
105
|
+
requestSubscription?.remove();
|
|
106
|
+
requestSubscription = null;
|
|
107
|
+
ExpoHttpServerModule.stop();
|
|
108
|
+
};
|