Skip to content

Commit e2a5384

Browse files
committed
[futurepress#95] Updates README + Minor fixes in TS code
1 parent 263b8c4 commit e2a5384

File tree

2 files changed

+102
-22
lines changed

2 files changed

+102
-22
lines changed

README.md

Lines changed: 59 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,7 @@ and [old][Old Architecture] RN architectures.
6666
- [Enabling Alias module]
6767
- [Enabling Rewrite module]
6868
- [Enabling WebDAV module]
69+
- [Connecting to an Active Server in the Native Layer]
6970
- [API Reference](#api-reference)
7071
- [Server] — Represents a server instance.
7172
- [constructor()] — Creates a new [Server] instance.
@@ -487,6 +488,50 @@ routes when you create [Server] instance, using `extraConfig` option.
487488
`,
488489
```
489490
491+
### Connecting to an Active Server in the Native Layer
492+
[Connecting to an Active Server in the Native Layer]: #connecting-to-an-active-server-in-the-native-layer
493+
494+
When this library is used the regular way, the [Lighttpd] server in the native
495+
layer is launched when the [.start()] method of a [Server] instance is triggered
496+
on the JavaScript (TypeScript) side, and the native server is terminated when
497+
the [.stop()] method is called on the JS side. In the JS layer we hold most of
498+
the server-related information (`hostname`, `port`, `fileDir`, _etc._),
499+
and take care of the high-level server control (_i.e._ the optional
500+
pause / resume of the server when the app enters background / foreground).
501+
If JS engine is restarted (or just related JS modules are reloaded) the regular
502+
course of action is to explictly terminate the active server just before it,
503+
and to re-create, and re-launch it afterwards. If it is not done, the [Lighttpd]
504+
server will remain active in the native layer across the JS engine restart,
505+
and it won't be possible to launch a new server instance after the restart,
506+
as the library only supports at most one active [Lighttpd] server, and it
507+
throws an error if the server launch command arrives to the native layer while
508+
[Lighttpd] server is already active.
509+
510+
However, in response to
511+
[the ticket #95](https://github.com/birdofpreyru/react-native-static-server/issues/95)
512+
we provide a way to reuse an active native server across JS engine restarts,
513+
without restarting the server. To do so you:
514+
- Use [getActiveServerId()] method to check whether the native server is active
515+
(if so, this method resolves to a non-_null_ ID).
516+
- Create a new [Server] instance passing into its [constructor()] that server ID
517+
as the `id` option, and [STATES]`.ACTIVE` as the `state` option. These options
518+
(usually omitted when creating a regular [Server] instance) ensure that
519+
the created [Server] instance is able to communicate with the already running
520+
native server, and to correctly handle subsequent [.stop()] and [.start()]
521+
calls. Beside these, it is up-to-you to set all other options to the values
522+
you need (_i.e._ setting `id`, and `state` just «connects»
523+
the newly created [Server] instance to the active native server, but it
524+
does not restore any other information about the server — you should
525+
restore or redefine it the way you see fit).
526+
527+
Note, this way it is possible to create multiple [Server] instances connected
528+
to the same active native server. As they have the same `id`, they all will
529+
represent the same server, thus calling [.stop()] and [.start()] commands
530+
on any of them will operate the same server, and update the states of all
531+
these JS server instances, without triggering the error related to
532+
the «at most one active server a time» (though, it has not been
533+
carefully tested yet).
534+
490535
## API Reference
491536
### Server
492537
[Server]: #server
@@ -556,9 +601,8 @@ within `options` argument:
556601
- `id` — **number** — Optional. Allows to enforce a specific ID,
557602
used to communicate with the server instance within the Native layer, thus
558603
it allows to re-connect to an existing native server instance.
559-
See [getActiveServerId()] for details, and also pay attention to set
560-
the correct `state` option value in this case. By default is is assigned
561-
by the library.
604+
See «[Connecting to an Active Server in the Native Layer]»
605+
for details. By default, an `id` is selected by the library.
562606
563607
- `nonLocal` — **boolean** — Optional. By default, if `hostname`
564608
option was not provided, the server starts at the "`127.0.0.1`" (loopback)
@@ -574,10 +618,14 @@ within `options` argument:
574618
- `port` — **number** — Optional. The port at which to start the server.
575619
If 0 (default) an available port will be automatically selected.
576620
577-
- `state` — [STATES] — Optional. Allows to enforce initial
578-
server state value, which is needed when connecting to an existing
579-
native server instance with help of the `id` option.
580-
By default it is set to `STATES.INACTIVE`.
621+
- `state` — [STATES] — Optional. Allows to enforce the initial
622+
server state value, which is necessary [when connecting to an existing
623+
native server instance][Connecting to an Active Server in the Native Layer].
624+
Note, it only influence the logic behind subsequent [.start()] and [.stop()]
625+
calls, _i.e._ the constructor does not attempt to put the server in this
626+
state, nor does it check the value is consistent with the active server,
627+
if any, in the native layer. By default, the state is initialized
628+
to `STATES.INACTIVE`.
581629
582630
- `stopInBackground` — **boolean** — Optional.
583631
@@ -837,10 +885,10 @@ if any exist in the Native layer.
837885
838886
This function is provided in response to
839887
[the ticket #95](https://github.com/birdofpreyru/react-native-static-server/issues/95),
840-
which asked to provide a way to re-connect to a server running within the Native
841-
layer after a reset of TypeScript RN layer. The ID returned by this function can
842-
be passed in into [Server] instance [constructor()] to create server instance
843-
communicating to the existing native layer server.
888+
to allow «[Connecting to an Active Server in the Native Layer]».
889+
The ID returned by this function can be passed in into [Server] instance
890+
[constructor()] to create server instance communicating to the existing native
891+
layer server.
844892
845893
**NOTE:** It is different from [getActiveServer()] function above, which
846894
returns the acurrently active, starting, or stopping [Server] instance based on

src/index.tsx

Lines changed: 43 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,7 @@ export { STATES, resolveAssetsPath };
3232

3333
// ID-to-StaticServer map for all potentially active server instances,
3434
// used to route native events back to JS server objects.
35-
const servers: { [id: string]: StaticServer } = {};
35+
const servers: { [id: string]: Set<StaticServer> } = {};
3636

3737
const nativeEventEmitter = new NativeEventEmitter(ReactNativeStaticServer);
3838

@@ -47,14 +47,18 @@ export type StateListener = (
4747
nativeEventEmitter.addListener(
4848
'RNStaticServer',
4949
({ serverId, event, details }) => {
50-
const server = servers[serverId];
51-
if (server) {
50+
const group = servers[serverId];
51+
if (group) {
5252
switch (event) {
5353
case SIGNALS.CRASHED:
5454
// TODO: We probably can, and should, capture the native stack trace
5555
// and pass it along with the error.
5656
const error = Error(details);
57-
server._setState(STATES.CRASHED, details, error);
57+
58+
group.forEach((item) =>
59+
item._setState(STATES.CRASHED, details, error),
60+
);
61+
5862
// TODO: Should we do here the following?
5963
// delete servers[this._id];
6064
break;
@@ -198,9 +202,24 @@ class StaticServer {
198202
this._hostname = hostname || (nonLocal ? '' : LOOPBACK_ADDRESS);
199203

200204
this._port = port;
201-
this._state = state;
202205
this._stopInBackground = stopInBackground;
203206

207+
this._state = state;
208+
209+
// NOTE: Normally, a server instance is connected to events from the native
210+
// side inside its .start() call, and it is disconnected from them inside
211+
// its .stop() call (or crash clean-up sequence). However, if the server is
212+
// created with a claim that it is already active, we should connect it to
213+
// the events rigth away here.
214+
switch (state) {
215+
case STATES.ACTIVE:
216+
case STATES.STARTING: {
217+
this._registerSelf();
218+
break;
219+
}
220+
default:
221+
}
222+
204223
if (!fileDir) throw Error('`fileDir` MUST BE a non-empty string');
205224
this._fileDir = resolveAssetsPath(fileDir);
206225

@@ -241,6 +260,15 @@ class StaticServer {
241260
}
242261
}
243262

263+
_registerSelf() {
264+
let group = servers[this._id];
265+
if (group) group.add(this);
266+
else {
267+
group = new Set([this]);
268+
servers[this._id] = group;
269+
}
270+
}
271+
244272
/**
245273
* This method throws if server is not in a "stable" state, i.e. not in one
246274
* of these: ACTIVE, CRASHED, INACTIVE.
@@ -282,7 +310,8 @@ class StaticServer {
282310
await this._sem.seize();
283311
this._stableStateGuard();
284312
if (this._state === STATES.ACTIVE) return this._origin!;
285-
servers[this._id] = this;
313+
314+
this._registerSelf();
286315
this._setState(STATES.STARTING, details);
287316
this._configureAppStateHandling();
288317

@@ -352,7 +381,9 @@ class StaticServer {
352381
this._setState(STATES.CRASHED, error.message, error);
353382
throw error;
354383
} finally {
355-
delete servers[this._id];
384+
const set = servers[this._id];
385+
set?.delete(this);
386+
if (!set?.size) delete servers[this._id];
356387
this._sem.setReady(true);
357388
}
358389
}
@@ -423,10 +454,11 @@ export async function extractBundledAssets(
423454
* @return {StaticServer|undefined}
424455
*/
425456
export function getActiveServer() {
426-
return Object.values(servers).find(
427-
(server) =>
428-
server.state !== STATES.INACTIVE && server.state !== STATES.CRASHED,
429-
);
457+
return Object.values(servers).find((group) => {
458+
const server = group.values().next().value;
459+
const state = server?.state;
460+
return state !== STATES.INACTIVE && state !== STATES.CRASHED;
461+
});
430462
}
431463

432464
export const getActiveServerId = ReactNativeStaticServer.getActiveServerId;

0 commit comments

Comments
 (0)