expo-http-server 0.1.3 → 0.1.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/README.md CHANGED
@@ -43,7 +43,13 @@ export default function App() {
43
43
  const obj = { app: "expo-http-server", desc: "You can load JSON!" };
44
44
 
45
45
  useEffect(() => {
46
- server.setup(9666);
46
+ server.setup(9666, (event: server.StatusEvent) => {
47
+ if (event.status === "ERROR") {
48
+ // there was an error...
49
+ } else {
50
+ // server was STARTED, PAUSED, RESUMED or STOPPED
51
+ }
52
+ });
47
53
  server.route("/", "GET", async (request) => {
48
54
  console.log("Request", "/", "GET", JSON.stringify(request));
49
55
  setLastCalled(Date.now());
@@ -88,7 +94,12 @@ export default function App() {
88
94
 
89
95
  ```
90
96
 
91
- Note that on iOS when the app is backgrounded the server will inevitably get paused. A background task will be started automatically, however you will likely only get 30 seconds of listening time in the background.
97
+ ## Running in the background
98
+ **iOS**: When the app is backgrounded the server will inevitably get paused. There is no getting around this. expo-http-server will start a [background task](https://developer.apple.com/documentation/uikit/uiapplication/1623031-beginbackgroundtask) that should provide a bit more background time, but this will only be ~25 seconds, which could be lowered by Apple in the future. expo-http-server will automatically pause the server when the time runs out, and resume it when the app is resumed.
99
+
100
+ **Android**: The server can be ran continuously in the background using a foreground service, e.g. a persistent notification. [Notifee](https://notifee.app/react-native/docs/android/foreground-service#building-a-long-lived-task) can be used to do this. Take a look at the example project for how to set this up.
101
+
102
+ ## Testing
92
103
 
93
104
  Send a request to the server in a browser `browser` or `curl`:
94
105
 
@@ -17,7 +17,7 @@ class ExpoHttpServerModule : Module() {
17
17
  val contentType: String,
18
18
  val body: String)
19
19
 
20
- private lateinit var server: Server
20
+ private var server: Server? = null;
21
21
  private val responses = HashMap<String, SimpleHttpResponse>()
22
22
 
23
23
  private val requestHandler: RequestHandler = { request: Request, response: Response ->
@@ -49,7 +49,7 @@ class ExpoHttpServerModule : Module() {
49
49
 
50
50
  Name("ExpoHttpServer")
51
51
 
52
- Events("onRequest")
52
+ Events("onStatusUpdate", "onRequest")
53
53
 
54
54
  Function("setup") { port: Int ->
55
55
  server = AndroidServer.Builder{
@@ -60,11 +60,22 @@ class ExpoHttpServerModule : Module() {
60
60
  }
61
61
 
62
62
  Function("route") { path: String, method: String ->
63
- server = server.request(HttpMethod.getMethod(method), path, requestHandler);
63
+ server = server?.request(HttpMethod.getMethod(method), path, requestHandler);
64
64
  }
65
65
 
66
66
  Function("start") {
67
- server.start()
67
+ if (server == null) {
68
+ sendEvent("onStatusUpdate", bundleOf(
69
+ "status" to "ERROR",
70
+ "message" to "Server not setup / port not configured"
71
+ ))
72
+ } else {
73
+ server?.start()
74
+ sendEvent("onStatusUpdate", bundleOf(
75
+ "status" to "STARTED",
76
+ "message" to "Server started"
77
+ ))
78
+ }
68
79
  }
69
80
 
70
81
  Function("respond") { uuid: String,
@@ -75,7 +86,11 @@ class ExpoHttpServerModule : Module() {
75
86
  }
76
87
 
77
88
  Function("stop") {
78
- server.close()
89
+ server?.close()
90
+ sendEvent("onStatusUpdate", bundleOf(
91
+ "status" to "STOPPED",
92
+ "message" to "Server stopped"
93
+ ))
79
94
  }
80
95
  }
81
96
  }
package/build/index.d.ts CHANGED
@@ -1,4 +1,12 @@
1
1
  export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
2
+ /**
3
+ * PAUSED AND RESUMED are iOS only
4
+ */
5
+ export type Status = "STARTED" | "PAUSED" | "RESUMED" | "STOPPED" | "ERROR";
6
+ export interface StatusEvent {
7
+ status: Status;
8
+ message: string;
9
+ }
2
10
  export interface RequestEvent {
3
11
  uuid: string;
4
12
  method: string;
@@ -20,6 +28,6 @@ export interface Callback {
20
28
  }
21
29
  export declare const start: () => void;
22
30
  export declare const route: (path: string, method: HttpMethod, callback: (request: RequestEvent) => Promise<Response>) => void;
23
- export declare const setup: (port: number) => any;
31
+ export declare const setup: (port: number, onStatusUpdate?: ((event: StatusEvent) => void) | undefined) => void;
24
32
  export declare const stop: () => any;
25
33
  //# sourceMappingURL=index.d.ts.map
@@ -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;AAE3D,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,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,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,SAQvD,CAAC;AAEF,eAAO,MAAM,KAAK,SAAU,MAAM,QAAqC,CAAC;AACxE,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,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,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf;AAED,MAAM,WAAW,QAAQ;IACvB,MAAM,EAAE,MAAM,CAAC;IACf,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,SAQvD,CAAC;AAEF,eAAO,MAAM,KAAK,SACV,MAAM,4BACa,WAAW,KAAK,IAAI,sBAQ9C,CAAC;AAEF,eAAO,MAAM,IAAI,WAAoC,CAAC"}
package/build/index.js CHANGED
@@ -3,7 +3,6 @@ import ExpoHttpServerModule from "./ExpoHttpServerModule";
3
3
  const emitter = new EventEmitter(ExpoHttpServerModule);
4
4
  const requestCallbacks = [];
5
5
  export const start = () => {
6
- ExpoHttpServerModule.start();
7
6
  emitter.addListener("onRequest", async (event) => {
8
7
  const callbacks = requestCallbacks.filter((c) => event.path === c.path && event.method === c.method);
9
8
  if (!callbacks.length) {
@@ -15,6 +14,7 @@ export const start = () => {
15
14
  ExpoHttpServerModule.respond(event.uuid, response.statusCode || 200, response.contentType || "application/json", response.body || "{}");
16
15
  }
17
16
  });
17
+ ExpoHttpServerModule.start();
18
18
  };
19
19
  export const route = (path, method, callback) => {
20
20
  requestCallbacks.push({
@@ -24,6 +24,13 @@ export const route = (path, method, callback) => {
24
24
  });
25
25
  ExpoHttpServerModule.route(path, method);
26
26
  };
27
- export const setup = (port) => ExpoHttpServerModule.setup(port);
27
+ export const setup = (port, onStatusUpdate) => {
28
+ if (onStatusUpdate) {
29
+ emitter.addListener("onStatusUpdate", async (event) => {
30
+ onStatusUpdate(event);
31
+ });
32
+ }
33
+ ExpoHttpServerModule.setup(port);
34
+ };
28
35
  export const stop = () => ExpoHttpServerModule.stop();
29
36
  //# sourceMappingURL=index.js.map
@@ -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;AA0BxC,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,EAAE;IACxB,oBAAoB,CAAC,KAAK,EAAE,CAAC;IAC7B,OAAO,CAAC,WAAW,CAAe,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC7D,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CACvC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAC1D,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YACtB,oBAAoB,CAAC,OAAO,CAC1B,KAAK,CAAC,IAAI,EACV,GAAG,EACH,aAAa,EACb,aAAa,CACd,CAAC;YACF,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACzC,oBAAoB,CAAC,OAAO,CAC1B,KAAK,CAAC,IAAI,EACV,QAAQ,CAAC,UAAU,IAAI,GAAG,EAC1B,QAAQ,CAAC,WAAW,IAAI,kBAAkB,EAC1C,QAAQ,CAAC,IAAI,IAAI,IAAI,CACtB,CAAC;QACJ,CAAC;IACH,CAAC,CAAC,CAAC;AACL,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CACnB,IAAY,EACZ,MAAkB,EAClB,QAAsD,EACtD,EAAE;IACF,gBAAgB,CAAC,IAAI,CAAC;QACpB,MAAM;QACN,IAAI;QACJ,QAAQ;KACT,CAAC,CAAC;IACH,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3C,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,KAAK,GAAG,CAAC,IAAY,EAAE,EAAE,CAAC,oBAAoB,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;AACxE,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\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 contentType?: string;\n body?: string;\n}\n\nexport interface Callback {\n method: string;\n path: string;\n callback: (request: RequestEvent) => Promise<Response>;\n}\n\nexport const start = () => {\n ExpoHttpServerModule.start();\n emitter.addListener<RequestEvent>(\"onRequest\", async (event) => {\n const callbacks = requestCallbacks.filter(\n (c) => event.path === c.path && event.method === c.method,\n );\n if (!callbacks.length) {\n ExpoHttpServerModule.respond(\n event.uuid,\n 200,\n \"No callback\",\n \"No callback\",\n );\n return;\n }\n for (const c of callbacks) {\n const response = await c.callback(event);\n ExpoHttpServerModule.respond(\n event.uuid,\n response.statusCode || 200,\n response.contentType || \"application/json\",\n response.body || \"{}\",\n );\n }\n });\n};\n\nexport const route = (\n path: string,\n method: HttpMethod,\n callback: (request: RequestEvent) => Promise<Response>,\n) => {\n requestCallbacks.push({\n method,\n path,\n callback,\n });\n ExpoHttpServerModule.route(path, method);\n};\n\nexport const setup = (port: number) => ExpoHttpServerModule.setup(port);\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;AAmCxC,MAAM,CAAC,MAAM,KAAK,GAAG,GAAG,EAAE;IACxB,OAAO,CAAC,WAAW,CAAe,WAAW,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE;QAC7D,MAAM,SAAS,GAAG,gBAAgB,CAAC,MAAM,CACvC,CAAC,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,IAAI,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,CAAC,MAAM,CAC1D,CAAC;QACF,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,CAAC;YACtB,oBAAoB,CAAC,OAAO,CAC1B,KAAK,CAAC,IAAI,EACV,GAAG,EACH,aAAa,EACb,aAAa,CACd,CAAC;YACF,OAAO;QACT,CAAC;QACD,KAAK,MAAM,CAAC,IAAI,SAAS,EAAE,CAAC;YAC1B,MAAM,QAAQ,GAAG,MAAM,CAAC,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAC;YACzC,oBAAoB,CAAC,OAAO,CAC1B,KAAK,CAAC,IAAI,EACV,QAAQ,CAAC,UAAU,IAAI,GAAG,EAC1B,QAAQ,CAAC,WAAW,IAAI,kBAAkB,EAC1C,QAAQ,CAAC,IAAI,IAAI,IAAI,CACtB,CAAC;QACJ,CAAC;IACH,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,gBAAgB,CAAC,IAAI,CAAC;QACpB,MAAM;QACN,IAAI;QACJ,QAAQ;KACT,CAAC,CAAC;IACH,oBAAoB,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;AAC3C,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 contentType?: string;\n body?: string;\n}\n\nexport interface Callback {\n method: string;\n path: string;\n callback: (request: RequestEvent) => Promise<Response>;\n}\n\nexport const start = () => {\n emitter.addListener<RequestEvent>(\"onRequest\", async (event) => {\n const callbacks = requestCallbacks.filter(\n (c) => event.path === c.path && event.method === c.method,\n );\n if (!callbacks.length) {\n ExpoHttpServerModule.respond(\n event.uuid,\n 200,\n \"No callback\",\n \"No callback\",\n );\n return;\n }\n for (const c of callbacks) {\n const response = await c.callback(event);\n ExpoHttpServerModule.respond(\n event.uuid,\n response.statusCode || 200,\n response.contentType || \"application/json\",\n response.body || \"{}\",\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 requestCallbacks.push({\n method,\n path,\n callback,\n });\n ExpoHttpServerModule.route(path, method);\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"]}
@@ -4,13 +4,14 @@ import Criollo
4
4
  public class ExpoHttpServerModule: Module {
5
5
  private let server = CRHTTPServer()
6
6
  private var port: Int?
7
+ private var stopped = false
7
8
  private var responses = [String: CRResponse]()
8
9
  private var bgTaskIdentifier = UIBackgroundTaskIdentifier.invalid
9
10
 
10
11
  public func definition() -> ModuleDefinition {
11
12
  Name("ExpoHttpServer")
12
13
 
13
- Events("onRequest")
14
+ Events("onStatusUpdate", "onRequest")
14
15
 
15
16
  Function("setup", setupHandler)
16
17
  Function("start", startHandler)
@@ -20,24 +21,17 @@ public class ExpoHttpServerModule: Module {
20
21
  }
21
22
 
22
23
  private func setupHandler(port: Int) {
23
- NotificationCenter.default.addObserver(forName: UIApplication.willEnterForegroundNotification, object: nil, queue: .main) { [unowned self] notification in
24
- if (self.server.isListening) {
25
- self.endBackgroundTask()
26
- self.beginBackgroundTask()
27
- }
28
- }
29
24
  self.port = port;
30
25
  }
31
26
 
32
27
  private func startHandler() {
33
- if (server.isListening) { return }
34
- beginBackgroundTask()
35
- var error: NSError?
36
- if let port = port {
37
- server.startListening(&error, portNumber: UInt(port))
38
- } else {
39
- server.startListening(&error)
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
  }
33
+ stopped = false;
34
+ startServer(status: "STARTED", message: "Server started")
41
35
  }
42
36
 
43
37
  private func routeHandler(path: String, method: String) {
@@ -74,16 +68,52 @@ public class ExpoHttpServerModule: Module {
74
68
  }
75
69
 
76
70
  private func stopHandler() {
71
+ stopped = true;
72
+ stopServer(status: "STOPPED", message: "Server stopped")
73
+ }
74
+
75
+ private func startServer(status: String, message: String) {
76
+ stopServer()
77
+ if let port = port {
78
+ var error: NSError?
79
+ server.startListening(&error, portNumber: UInt(port))
80
+ if (error != nil) {
81
+ sendEvent("onStatusUpdate", [
82
+ "status": "ERROR",
83
+ "message": error?.localizedDescription ?? "Unknown error starting server"
84
+ ])
85
+ } else {
86
+ beginBackgroundTask()
87
+ sendEvent("onStatusUpdate", [
88
+ "status": status,
89
+ "message": message
90
+ ])
91
+ }
92
+ } else {
93
+ sendEvent("onStatusUpdate", [
94
+ "status": "ERROR",
95
+ "message": "Can't start server with port configured"
96
+ ])
97
+ }
98
+ }
99
+
100
+ private func stopServer(status: String? = nil, message: String? = nil) {
77
101
  if (server.isListening) {
78
102
  server.stopListening()
79
- endBackgroundTask()
103
+ }
104
+ endBackgroundTask()
105
+ if let status = status, let message = message {
106
+ sendEvent("onStatusUpdate", [
107
+ "status": status,
108
+ "message": message
109
+ ])
80
110
  }
81
111
  }
82
112
 
83
113
  private func beginBackgroundTask() {
84
114
  if (bgTaskIdentifier == UIBackgroundTaskIdentifier.invalid) {
85
115
  self.bgTaskIdentifier = UIApplication.shared.beginBackgroundTask(withName: "BgTask", expirationHandler: {
86
- self.endBackgroundTask()
116
+ self.stopServer(status: "PAUSED", message: "Server paused")
87
117
  })
88
118
  }
89
119
  }
@@ -133,4 +163,4 @@ extension CRHTTPMethod {
133
163
  }
134
164
  return httpMethod
135
165
  }
136
- }
166
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "expo-http-server",
3
- "version": "0.1.3",
3
+ "version": "0.1.4",
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
@@ -6,6 +6,15 @@ const emitter = new EventEmitter(ExpoHttpServerModule);
6
6
  const requestCallbacks: Callback[] = [];
7
7
 
8
8
  export type HttpMethod = "GET" | "POST" | "PUT" | "DELETE";
9
+ /**
10
+ * PAUSED AND RESUMED are iOS only
11
+ */
12
+ export type Status = "STARTED" | "PAUSED" | "RESUMED" | "STOPPED" | "ERROR";
13
+
14
+ export interface StatusEvent {
15
+ status: Status;
16
+ message: string;
17
+ }
9
18
 
10
19
  export interface RequestEvent {
11
20
  uuid: string;
@@ -30,7 +39,6 @@ export interface Callback {
30
39
  }
31
40
 
32
41
  export const start = () => {
33
- ExpoHttpServerModule.start();
34
42
  emitter.addListener<RequestEvent>("onRequest", async (event) => {
35
43
  const callbacks = requestCallbacks.filter(
36
44
  (c) => event.path === c.path && event.method === c.method,
@@ -54,6 +62,7 @@ export const start = () => {
54
62
  );
55
63
  }
56
64
  });
65
+ ExpoHttpServerModule.start();
57
66
  };
58
67
 
59
68
  export const route = (
@@ -69,5 +78,16 @@ export const route = (
69
78
  ExpoHttpServerModule.route(path, method);
70
79
  };
71
80
 
72
- export const setup = (port: number) => ExpoHttpServerModule.setup(port);
81
+ export const setup = (
82
+ port: number,
83
+ onStatusUpdate?: (event: StatusEvent) => void,
84
+ ) => {
85
+ if (onStatusUpdate) {
86
+ emitter.addListener<StatusEvent>("onStatusUpdate", async (event) => {
87
+ onStatusUpdate(event);
88
+ });
89
+ }
90
+ ExpoHttpServerModule.setup(port);
91
+ };
92
+
73
93
  export const stop = () => ExpoHttpServerModule.stop();