expo-http-server 0.1.14-beta.4 → 0.1.14-beta.5
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 +18 -10
- package/build/index.d.ts +5 -1
- package/build/index.d.ts.map +1 -1
- package/build/index.js +7 -5
- package/build/index.js.map +1 -1
- package/ios/ExpoHttpServerModule.swift +28 -9
- package/package.json +1 -1
- package/src/index.ts +19 -4
|
@@ -10,6 +10,7 @@ import com.safframework.server.core.http.Request
|
|
|
10
10
|
import com.safframework.server.core.http.Response
|
|
11
11
|
import org.json.JSONObject
|
|
12
12
|
import java.net.BindException
|
|
13
|
+
import java.util.UUID
|
|
13
14
|
import java.util.concurrent.ConcurrentHashMap
|
|
14
15
|
|
|
15
16
|
class ExpoHttpServerModule : Module() {
|
|
@@ -60,6 +61,7 @@ class ExpoHttpServerModule : Module() {
|
|
|
60
61
|
// rebuilding in that case only adds an unnecessary close/rebind race.
|
|
61
62
|
private var hasBoundSocket = false
|
|
62
63
|
private var configuredPort: Int = 0
|
|
64
|
+
private var configuredLoopbackOnly = false
|
|
63
65
|
private val routeConfigs = mutableListOf<RouteConfig>()
|
|
64
66
|
private val responses = ConcurrentHashMap<String, SimpleHttpResponse>()
|
|
65
67
|
private val onBindFailure: (String) -> Unit = { message ->
|
|
@@ -75,8 +77,10 @@ class ExpoHttpServerModule : Module() {
|
|
|
75
77
|
val headers: Map<String, String> = request.headers()
|
|
76
78
|
val params: Map<String, String> = request.params()
|
|
77
79
|
val cookies: Map<String, String> = request.cookies().associate { it.name() to it.value() }
|
|
80
|
+
val requestId = UUID.randomUUID().toString()
|
|
78
81
|
sendEvent("onRequest", bundleOf(
|
|
79
82
|
"uuid" to uuid,
|
|
83
|
+
"requestId" to requestId,
|
|
80
84
|
"method" to request.method().name,
|
|
81
85
|
"path" to request.url(),
|
|
82
86
|
"body" to request.content(),
|
|
@@ -85,13 +89,13 @@ class ExpoHttpServerModule : Module() {
|
|
|
85
89
|
"cookiesJson" to JSONObject(cookies).toString(),
|
|
86
90
|
))
|
|
87
91
|
val deadline = System.currentTimeMillis() + 30_000
|
|
88
|
-
while (!responses.containsKey(
|
|
92
|
+
while (!responses.containsKey(requestId) && System.currentTimeMillis() < deadline) {
|
|
89
93
|
Thread.sleep(10)
|
|
90
94
|
}
|
|
91
|
-
val res = responses.remove(
|
|
95
|
+
val res = responses.remove(requestId) ?: return response
|
|
92
96
|
response.setBodyText(res.body)
|
|
93
97
|
response.setStatus(res.statusCode)
|
|
94
|
-
response.addHeader("Content-Length", "" + res.body.
|
|
98
|
+
response.addHeader("Content-Length", "" + res.body.toByteArray(Charsets.UTF_8).size)
|
|
95
99
|
response.addHeader("Content-Type", res.contentType)
|
|
96
100
|
for ((key, value) in res.headers) {
|
|
97
101
|
response.addHeader(key, value)
|
|
@@ -99,10 +103,15 @@ class ExpoHttpServerModule : Module() {
|
|
|
99
103
|
return response
|
|
100
104
|
}
|
|
101
105
|
|
|
106
|
+
private fun buildServer(): Server = AndroidServer.Builder {
|
|
107
|
+
port { configuredPort }
|
|
108
|
+
if (configuredLoopbackOnly) {
|
|
109
|
+
address { "127.0.0.1" }
|
|
110
|
+
}
|
|
111
|
+
}.build()
|
|
112
|
+
|
|
102
113
|
private fun buildServerWithRoutes(): Server {
|
|
103
|
-
var s: Server =
|
|
104
|
-
port { configuredPort }
|
|
105
|
-
}.build()
|
|
114
|
+
var s: Server = buildServer()
|
|
106
115
|
for (route in routeConfigs) {
|
|
107
116
|
s = s.request(HttpMethod.getMethod(route.method), route.path) { request: Request, response: Response ->
|
|
108
117
|
handleRequest(route.uuid, request, response)
|
|
@@ -126,15 +135,14 @@ class ExpoHttpServerModule : Module() {
|
|
|
126
135
|
statusUpdateListeners.remove(onBindFailure)
|
|
127
136
|
}
|
|
128
137
|
|
|
129
|
-
Function("setup") { port: Int ->
|
|
138
|
+
Function("setup") { port: Int, loopbackOnly: Boolean ->
|
|
130
139
|
configuredPort = port
|
|
140
|
+
configuredLoopbackOnly = loopbackOnly
|
|
131
141
|
server?.close()
|
|
132
142
|
started = false
|
|
133
143
|
hasBoundSocket = false
|
|
134
144
|
routeConfigs.clear()
|
|
135
|
-
server =
|
|
136
|
-
port { configuredPort }
|
|
137
|
-
}.build()
|
|
145
|
+
server = buildServer()
|
|
138
146
|
}
|
|
139
147
|
|
|
140
148
|
Function("route") { path: String, method: String, uuid: String ->
|
package/build/index.d.ts
CHANGED
|
@@ -9,6 +9,7 @@ export interface StatusEvent {
|
|
|
9
9
|
}
|
|
10
10
|
export interface RequestEvent {
|
|
11
11
|
uuid: string;
|
|
12
|
+
requestId: string;
|
|
12
13
|
method: string;
|
|
13
14
|
path: string;
|
|
14
15
|
body: string;
|
|
@@ -31,6 +32,9 @@ export interface Callback {
|
|
|
31
32
|
}
|
|
32
33
|
export declare const start: () => void;
|
|
33
34
|
export declare const route: (path: string, method: HttpMethod, callback: (request: RequestEvent) => Promise<Response>) => void;
|
|
34
|
-
export
|
|
35
|
+
export interface SetupOptions {
|
|
36
|
+
loopbackOnly?: boolean;
|
|
37
|
+
}
|
|
38
|
+
export declare const setup: (port: number, onStatusUpdate?: ((event: StatusEvent) => void) | undefined, options?: SetupOptions) => void;
|
|
35
39
|
export declare const stop: () => void;
|
|
36
40
|
//# 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":"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,
|
|
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,SAAS,EAAE,MAAM,CAAC;IAClB,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,YAqCjB,CAAC;AAEF,eAAO,MAAM,KAAK,SACV,MAAM,UACJ,UAAU,sBACE,YAAY,KAAK,QAAQ,QAAQ,CAAC,SAUvD,CAAC;AAEF,MAAM,WAAW,YAAY;IAC3B,YAAY,CAAC,EAAE,OAAO,CAAC;CACxB;AAED,eAAO,MAAM,KAAK,SACV,MAAM,4BACa,WAAW,KAAK,IAAI,yBACpC,YAAY,SAUtB,CAAC;AAEF,eAAO,MAAM,IAAI,YAIhB,CAAC"}
|
package/build/index.js
CHANGED
|
@@ -9,13 +9,15 @@ export const start = () => {
|
|
|
9
9
|
try {
|
|
10
10
|
const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);
|
|
11
11
|
if (!responseHandler) {
|
|
12
|
-
ExpoHttpServerModule.respond(event.
|
|
12
|
+
ExpoHttpServerModule.respond(event.requestId, 404, "Not Found", "application/json", {}, JSON.stringify({ error: "Handler not found" }));
|
|
13
13
|
return;
|
|
14
14
|
}
|
|
15
15
|
const response = await responseHandler.callback(event);
|
|
16
|
-
ExpoHttpServerModule.respond(event.
|
|
16
|
+
ExpoHttpServerModule.respond(event.requestId, response.statusCode || 200, response.statusDescription || "OK", response.contentType || "application/json", response.headers || {}, response.body || "{}");
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
ExpoHttpServerModule.respond(event.requestId, 500, "Internal Server Error", "application/json", {}, JSON.stringify({ error: "Handler failed" }));
|
|
17
20
|
}
|
|
18
|
-
catch { }
|
|
19
21
|
});
|
|
20
22
|
ExpoHttpServerModule.start();
|
|
21
23
|
};
|
|
@@ -29,7 +31,7 @@ export const route = (path, method, callback) => {
|
|
|
29
31
|
});
|
|
30
32
|
ExpoHttpServerModule.route(path, method, uuid);
|
|
31
33
|
};
|
|
32
|
-
export const setup = (port, onStatusUpdate) => {
|
|
34
|
+
export const setup = (port, onStatusUpdate, options = {}) => {
|
|
33
35
|
if (onStatusUpdate) {
|
|
34
36
|
emitter.addListener("onStatusUpdate", async (event) => {
|
|
35
37
|
try {
|
|
@@ -38,7 +40,7 @@ export const setup = (port, onStatusUpdate) => {
|
|
|
38
40
|
catch { }
|
|
39
41
|
});
|
|
40
42
|
}
|
|
41
|
-
ExpoHttpServerModule.setup(port);
|
|
43
|
+
ExpoHttpServerModule.setup(port, options.loopbackOnly ?? false);
|
|
42
44
|
};
|
|
43
45
|
export const stop = () => {
|
|
44
46
|
requestSubscription?.remove();
|
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;AACxC,IAAI,mBAAmB,GAAkD,IAAI,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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","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 requestId: 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.requestId,\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.requestId,\n response.statusCode || 200,\n response.statusDescription || \"OK\",\n response.contentType || \"application/json\",\n response.headers || {},\n response.body || \"{}\",\n );\n } catch {\n ExpoHttpServerModule.respond(\n event.requestId,\n 500,\n \"Internal Server Error\",\n \"application/json\",\n {},\n JSON.stringify({ error: \"Handler failed\" }),\n );\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 interface SetupOptions {\n loopbackOnly?: boolean;\n}\n\nexport const setup = (\n port: number,\n onStatusUpdate?: (event: StatusEvent) => void,\n options: SetupOptions = {},\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, options.loopbackOnly ?? false);\n};\n\nexport const stop = () => {\n requestSubscription?.remove();\n requestSubscription = null;\n ExpoHttpServerModule.stop();\n};\n"]}
|
|
@@ -4,7 +4,10 @@ import Criollo
|
|
|
4
4
|
public class ExpoHttpServerModule: Module {
|
|
5
5
|
private let server = CRHTTPServer()
|
|
6
6
|
private var port: Int?
|
|
7
|
+
private var loopbackOnly = false
|
|
7
8
|
private var stopped = false
|
|
9
|
+
private var isListening = false
|
|
10
|
+
private var foregroundObserver: NSObjectProtocol?
|
|
8
11
|
private var responses = [String: CRResponse]()
|
|
9
12
|
private var bgTaskIdentifier = UIBackgroundTaskIdentifier.invalid
|
|
10
13
|
|
|
@@ -20,13 +23,17 @@ public class ExpoHttpServerModule: Module {
|
|
|
20
23
|
Function("stop", stopHandler)
|
|
21
24
|
}
|
|
22
25
|
|
|
23
|
-
private func setupHandler(port: Int) {
|
|
26
|
+
private func setupHandler(port: Int, loopbackOnly: Bool) {
|
|
24
27
|
self.port = port;
|
|
28
|
+
self.loopbackOnly = loopbackOnly;
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
private func startHandler() {
|
|
28
|
-
|
|
29
|
-
|
|
32
|
+
if (foregroundObserver == nil) {
|
|
33
|
+
foregroundObserver = NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [weak self] _ in
|
|
34
|
+
guard let self = self, !self.stopped, !self.isListening else {
|
|
35
|
+
return
|
|
36
|
+
}
|
|
30
37
|
self.startServer(status: "RESUMED", message: "Server resumed")
|
|
31
38
|
}
|
|
32
39
|
}
|
|
@@ -41,9 +48,11 @@ public class ExpoHttpServerModule: Module {
|
|
|
41
48
|
if let body = req.body, let bodyData = try? JSONSerialization.data(withJSONObject: body) {
|
|
42
49
|
bodyString = String(data: bodyData, encoding: .utf8) ?? "{}"
|
|
43
50
|
}
|
|
44
|
-
|
|
51
|
+
let requestId = UUID().uuidString
|
|
52
|
+
self.responses[requestId] = res
|
|
45
53
|
self.sendEvent("onRequest", [
|
|
46
54
|
"uuid": uuid,
|
|
55
|
+
"requestId": requestId,
|
|
47
56
|
"method": req.method.toString(),
|
|
48
57
|
"path": path,
|
|
49
58
|
"body": bodyString,
|
|
@@ -55,22 +64,22 @@ public class ExpoHttpServerModule: Module {
|
|
|
55
64
|
}, recursive: false, method: CRHTTPMethod.fromString(method))
|
|
56
65
|
}
|
|
57
66
|
|
|
58
|
-
private func respondHandler(
|
|
67
|
+
private func respondHandler(requestId: String,
|
|
59
68
|
statusCode: Int,
|
|
60
69
|
statusDescription: String,
|
|
61
70
|
contentType: String,
|
|
62
71
|
headers: [String: String],
|
|
63
72
|
body: String) {
|
|
64
73
|
DispatchQueue.main.async {
|
|
65
|
-
if let response = self.responses[
|
|
74
|
+
if let response = self.responses[requestId] {
|
|
66
75
|
response.setStatusCode(UInt(statusCode), description: statusDescription)
|
|
67
76
|
response.setValue(contentType, forHTTPHeaderField: "Content-type")
|
|
68
|
-
response.setValue("\(body.count)", forHTTPHeaderField: "Content-Length")
|
|
77
|
+
response.setValue("\(body.utf8.count)", forHTTPHeaderField: "Content-Length")
|
|
69
78
|
for (key, value) in headers {
|
|
70
79
|
response.setValue(value, forHTTPHeaderField: key)
|
|
71
80
|
}
|
|
72
81
|
response.send(body);
|
|
73
|
-
self.responses[
|
|
82
|
+
self.responses[requestId] = nil;
|
|
74
83
|
}
|
|
75
84
|
}
|
|
76
85
|
|
|
@@ -78,6 +87,10 @@ public class ExpoHttpServerModule: Module {
|
|
|
78
87
|
|
|
79
88
|
private func stopHandler() {
|
|
80
89
|
stopped = true;
|
|
90
|
+
if let observer = foregroundObserver {
|
|
91
|
+
NotificationCenter.default.removeObserver(observer)
|
|
92
|
+
foregroundObserver = nil
|
|
93
|
+
}
|
|
81
94
|
stopServer(status: "STOPPED", message: "Server stopped")
|
|
82
95
|
}
|
|
83
96
|
|
|
@@ -85,13 +98,14 @@ public class ExpoHttpServerModule: Module {
|
|
|
85
98
|
stopServer()
|
|
86
99
|
if let port = port {
|
|
87
100
|
var error: NSError?
|
|
88
|
-
server.startListening(&error, portNumber: UInt(port))
|
|
101
|
+
server.startListening(&error, portNumber: UInt(port), interface: loopbackOnly ? "localhost" : nil)
|
|
89
102
|
if (error != nil) {
|
|
90
103
|
sendEvent("onStatusUpdate", [
|
|
91
104
|
"status": "ERROR",
|
|
92
105
|
"message": error?.localizedDescription ?? "Unknown error starting server"
|
|
93
106
|
])
|
|
94
107
|
} else {
|
|
108
|
+
isListening = true
|
|
95
109
|
beginBackgroundTask()
|
|
96
110
|
sendEvent("onStatusUpdate", [
|
|
97
111
|
"status": status,
|
|
@@ -108,6 +122,7 @@ public class ExpoHttpServerModule: Module {
|
|
|
108
122
|
|
|
109
123
|
private func stopServer(status: String? = nil, message: String? = nil) {
|
|
110
124
|
server.stopListening()
|
|
125
|
+
isListening = false
|
|
111
126
|
endBackgroundTask()
|
|
112
127
|
if let status = status, let message = message {
|
|
113
128
|
sendEvent("onStatusUpdate", [
|
|
@@ -151,6 +166,8 @@ extension CRHTTPMethod {
|
|
|
151
166
|
return "PUT"
|
|
152
167
|
case .delete:
|
|
153
168
|
return "DELETE"
|
|
169
|
+
case .options:
|
|
170
|
+
return "OPTIONS"
|
|
154
171
|
default:
|
|
155
172
|
return "GET"
|
|
156
173
|
}
|
|
@@ -165,6 +182,8 @@ extension CRHTTPMethod {
|
|
|
165
182
|
httpMethod = .put
|
|
166
183
|
case "DELETE":
|
|
167
184
|
httpMethod = .delete
|
|
185
|
+
case "OPTIONS":
|
|
186
|
+
httpMethod = .options
|
|
168
187
|
default:
|
|
169
188
|
httpMethod = .get
|
|
170
189
|
}
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -19,6 +19,7 @@ export interface StatusEvent {
|
|
|
19
19
|
|
|
20
20
|
export interface RequestEvent {
|
|
21
21
|
uuid: string;
|
|
22
|
+
requestId: string;
|
|
22
23
|
method: string;
|
|
23
24
|
path: string;
|
|
24
25
|
body: string;
|
|
@@ -49,7 +50,7 @@ export const start = () => {
|
|
|
49
50
|
const responseHandler = requestCallbacks.find((c) => c.uuid === event.uuid);
|
|
50
51
|
if (!responseHandler) {
|
|
51
52
|
ExpoHttpServerModule.respond(
|
|
52
|
-
event.
|
|
53
|
+
event.requestId,
|
|
53
54
|
404,
|
|
54
55
|
"Not Found",
|
|
55
56
|
"application/json",
|
|
@@ -60,14 +61,23 @@ export const start = () => {
|
|
|
60
61
|
}
|
|
61
62
|
const response = await responseHandler.callback(event);
|
|
62
63
|
ExpoHttpServerModule.respond(
|
|
63
|
-
event.
|
|
64
|
+
event.requestId,
|
|
64
65
|
response.statusCode || 200,
|
|
65
66
|
response.statusDescription || "OK",
|
|
66
67
|
response.contentType || "application/json",
|
|
67
68
|
response.headers || {},
|
|
68
69
|
response.body || "{}",
|
|
69
70
|
);
|
|
70
|
-
} catch {
|
|
71
|
+
} catch {
|
|
72
|
+
ExpoHttpServerModule.respond(
|
|
73
|
+
event.requestId,
|
|
74
|
+
500,
|
|
75
|
+
"Internal Server Error",
|
|
76
|
+
"application/json",
|
|
77
|
+
{},
|
|
78
|
+
JSON.stringify({ error: "Handler failed" }),
|
|
79
|
+
);
|
|
80
|
+
}
|
|
71
81
|
});
|
|
72
82
|
ExpoHttpServerModule.start();
|
|
73
83
|
};
|
|
@@ -87,9 +97,14 @@ export const route = (
|
|
|
87
97
|
ExpoHttpServerModule.route(path, method, uuid);
|
|
88
98
|
};
|
|
89
99
|
|
|
100
|
+
export interface SetupOptions {
|
|
101
|
+
loopbackOnly?: boolean;
|
|
102
|
+
}
|
|
103
|
+
|
|
90
104
|
export const setup = (
|
|
91
105
|
port: number,
|
|
92
106
|
onStatusUpdate?: (event: StatusEvent) => void,
|
|
107
|
+
options: SetupOptions = {},
|
|
93
108
|
) => {
|
|
94
109
|
if (onStatusUpdate) {
|
|
95
110
|
emitter.addListener<StatusEvent>("onStatusUpdate", async (event) => {
|
|
@@ -98,7 +113,7 @@ export const setup = (
|
|
|
98
113
|
} catch {}
|
|
99
114
|
});
|
|
100
115
|
}
|
|
101
|
-
ExpoHttpServerModule.setup(port);
|
|
116
|
+
ExpoHttpServerModule.setup(port, options.loopbackOnly ?? false);
|
|
102
117
|
};
|
|
103
118
|
|
|
104
119
|
export const stop = () => {
|