@webqit/webflo 1.0.19 → 1.0.20
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/package.json +4 -3
- package/src/config-pi/runtime/Client.js +50 -46
- package/src/config-pi/runtime/Server.js +37 -14
- package/src/config-pi/runtime/client/Worker.js +22 -20
- package/src/runtime-pi/HttpEvent.js +34 -19
- package/src/runtime-pi/HttpUser.js +35 -36
- package/src/runtime-pi/WebfloCookieStorage.js +8 -8
- package/src/runtime-pi/WebfloRouter.js +2 -2
- package/src/runtime-pi/WebfloRuntime.js +7 -2
- package/src/runtime-pi/WebfloStorage.js +47 -16
- package/src/runtime-pi/client/Capabilities.js +211 -0
- package/src/runtime-pi/client/CookieStorage.js +2 -2
- package/src/runtime-pi/client/SessionStorage.js +2 -2
- package/src/runtime-pi/client/WebfloClient.js +15 -23
- package/src/runtime-pi/client/WebfloRootClient1.js +55 -34
- package/src/runtime-pi/client/WebfloRootClient2.js +2 -2
- package/src/runtime-pi/client/WebfloSubClient.js +9 -5
- package/src/runtime-pi/client/Workport.js +64 -91
- package/src/runtime-pi/client/generate.js +25 -16
- package/src/runtime-pi/client/index.js +3 -2
- package/src/runtime-pi/client/worker/CookieStorage.js +2 -2
- package/src/runtime-pi/client/worker/SessionStorage.js +1 -1
- package/src/runtime-pi/client/worker/WebfloWorker.js +70 -56
- package/src/runtime-pi/client/worker/index.js +3 -2
- package/src/runtime-pi/server/CookieStorage.js +2 -2
- package/src/runtime-pi/server/SessionStorage.js +3 -3
- package/src/runtime-pi/server/WebfloServer.js +26 -11
- package/src/runtime-pi/server/index.js +1 -0
- package/src/runtime-pi/util-http.js +15 -2
- package/src/services-pi/index.js +2 -0
- package/src/services-pi/push/index.js +23 -0
- package/src/static-pi/index.js +1 -1
|
@@ -4,6 +4,7 @@ import Path from 'path';
|
|
|
4
4
|
import Http from 'http';
|
|
5
5
|
import Https from 'https';
|
|
6
6
|
import WebSocket from 'ws';
|
|
7
|
+
import webpush from 'web-push';
|
|
7
8
|
import Mime from 'mime-types';
|
|
8
9
|
import QueryString from 'querystring';
|
|
9
10
|
import { _from as _arrFrom, _any } from '@webqit/util/arr/index.js';
|
|
@@ -196,7 +197,7 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
196
197
|
if (!scope.error) {
|
|
197
198
|
if (!hosts.includes(scope.url.hostname) && !hosts.includes('*')) {
|
|
198
199
|
scope.error = 'Unrecognized host';
|
|
199
|
-
} else if (scope.url.protocol === 'ws:' && this.#cx.server.https.force) {
|
|
200
|
+
} else if (scope.url.protocol === 'ws:' && this.#cx.server.https.port && this.#cx.server.https.force) {
|
|
200
201
|
scope.error = `Only secure connections allowed (wss:)`;
|
|
201
202
|
} else if (scope.url.hostname.startsWith('www.') && this.#cx.server.force_www === 'remove') {
|
|
202
203
|
scope.error = `Connections not allowed over the www subdomain`;
|
|
@@ -208,7 +209,7 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
208
209
|
// Level 3 validation
|
|
209
210
|
// and actual processing
|
|
210
211
|
scope.request = this.createRequest(scope.url.href, requestInit);
|
|
211
|
-
scope.session = this.constructor.SessionStorage.create(scope.request, { secret: this.#cx.env.entries.
|
|
212
|
+
scope.session = this.constructor.SessionStorage.create(scope.request, { secret: this.#cx.env.entries[this.#cx.server.session_key_variable] });
|
|
212
213
|
if (!scope.error) {
|
|
213
214
|
if (!(scope.clientMessagingRegistry = this.#globalMessagingRegistry.get(scope.session.sessionID))) {
|
|
214
215
|
scope.error = `Lost or invalid clientID`;
|
|
@@ -257,7 +258,7 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
257
258
|
if (!hosts.includes(scope.url.hostname) && !hosts.includes('*')) {
|
|
258
259
|
scope.exit = { status: 500 };
|
|
259
260
|
scope.exitMessage = 'Unrecognized host';
|
|
260
|
-
} else if (scope.url.protocol === 'http:' && this.#cx.server.https.force) {
|
|
261
|
+
} else if (scope.url.protocol === 'http:' && this.#cx.server.https.port && this.#cx.server.https.force) {
|
|
261
262
|
scope.exit = {
|
|
262
263
|
status: 302,
|
|
263
264
|
headers: { Location: (scope.url.protocol = 'https:', scope.url.href) }
|
|
@@ -289,7 +290,9 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
289
290
|
if (!scope.response) {
|
|
290
291
|
scope.response = await this.navigate(fullUrl, requestInit, {
|
|
291
292
|
request: nodeRequest,
|
|
292
|
-
response: nodeResponse
|
|
293
|
+
response: nodeResponse,
|
|
294
|
+
ipAddress: nodeRequest.headers['x-forwarded-for']?.split(',')[0]
|
|
295
|
+
|| nodeRequest.socket.remoteAddress
|
|
293
296
|
});
|
|
294
297
|
}
|
|
295
298
|
// -----------------
|
|
@@ -491,7 +494,8 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
491
494
|
},
|
|
492
495
|
respondWith: async (response, isRedirectMessage = false) => {
|
|
493
496
|
if (!isRedirectMessage && scope.eventLifecyclePromises.dirty && !scope.eventLifecyclePromises.size) {
|
|
494
|
-
|
|
497
|
+
console.error('Final response already sent');
|
|
498
|
+
return;
|
|
495
499
|
}
|
|
496
500
|
return await this.execPush(scope.clientMessaging, response);
|
|
497
501
|
},
|
|
@@ -503,7 +507,7 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
503
507
|
: [];
|
|
504
508
|
scope.request = this.createRequest(scope.url.href, scope.init, scope.autoHeaders.filter((header) => header.type === 'request'));
|
|
505
509
|
scope.cookies = this.constructor.CookieStorage.create(scope.request);
|
|
506
|
-
scope.session = this.constructor.SessionStorage.create(scope.request, { secret: this.#cx.env.entries.
|
|
510
|
+
scope.session = this.constructor.SessionStorage.create(scope.request, { secret: this.#cx.env.entries[this.#cx.server.session_key_variable] });
|
|
507
511
|
const sessionID = scope.session.sessionID;
|
|
508
512
|
if (!this.#globalMessagingRegistry.has(sessionID)) {
|
|
509
513
|
this.#globalMessagingRegistry.set(sessionID, new ClientMessagingRegistry(this, sessionID));
|
|
@@ -521,13 +525,24 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
521
525
|
cookies: scope.cookies,
|
|
522
526
|
session: scope.session,
|
|
523
527
|
user: scope.user,
|
|
524
|
-
client: scope.clientMessaging
|
|
528
|
+
client: scope.clientMessaging,
|
|
529
|
+
sdk: { ...(this.#cx.server.capabilities?.webpush ? { webpush } : {}) }
|
|
525
530
|
});
|
|
531
|
+
if (this.#cx.server.capabilities?.webpush
|
|
532
|
+
&& this.#cx.env.entries[this.#cx.server.capabilities.app_vapid_public_key_variable]
|
|
533
|
+
&& this.#cx.env.entries[this.#cx.server.capabilities.app_vapid_private_key_variable]) {
|
|
534
|
+
webpush.setVapidDetails(
|
|
535
|
+
scope.url.origin.replace(/^http:/i, 'https:'),
|
|
536
|
+
this.#cx.env.entries[this.#cx.server.capabilities.app_vapid_public_key_variable],
|
|
537
|
+
this.#cx.env.entries[this.#cx.server.capabilities.app_vapid_private_key_variable]
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
await this.setup(scope.httpEvent);
|
|
526
541
|
// Restore session before dispatching
|
|
527
542
|
if (scope.request.method === 'GET'
|
|
528
543
|
&& (scope.redirectMessageID = scope.httpEvent.url.query['redirect-message'])
|
|
529
544
|
&& (scope.redirectMessage = scope.session.get(`redirect-message:${scope.redirectMessageID}`))) {
|
|
530
|
-
scope.session.delete(`redirect-message:${scope.redirectMessageID}`);
|
|
545
|
+
await scope.session.delete(`redirect-message:${scope.redirectMessageID}`);
|
|
531
546
|
}
|
|
532
547
|
// Dispatch for response
|
|
533
548
|
scope.response = await this.dispatch(scope.httpEvent, {}, async (event) => {
|
|
@@ -535,15 +550,15 @@ export class WebfloServer extends WebfloRuntime {
|
|
|
535
550
|
});
|
|
536
551
|
// ---------------
|
|
537
552
|
// Response processing
|
|
538
|
-
scope.
|
|
539
|
-
scope.
|
|
553
|
+
scope.response = await this.normalizeResponse(scope.httpEvent, scope.response);
|
|
554
|
+
scope.hasBackgroundActivity = scope.clientMessaging.isMessaging() || scope.eventLifecyclePromises.size || (scope.redirectMessage && !scope.response.headers.get('Location'));
|
|
540
555
|
if (scope.hasBackgroundActivity) {
|
|
541
556
|
scope.response.headers.set('X-Background-Messaging', `ws:${scope.clientMessaging.portID}`);
|
|
542
557
|
}
|
|
543
558
|
// Reponse handlers
|
|
544
559
|
if (scope.response.headers.get('Location')) {
|
|
545
560
|
if (scope.redirectMessage) {
|
|
546
|
-
scope.session.set(`redirect-message:${scope.redirectMessageID}`, scope.redirectMessage);
|
|
561
|
+
await scope.session.set(`redirect-message:${scope.redirectMessageID}`, scope.redirectMessage);
|
|
547
562
|
}
|
|
548
563
|
this.writeRedirectHeaders(scope.httpEvent, scope.response);
|
|
549
564
|
} else {
|
|
@@ -47,6 +47,9 @@ export function renderHttpMessageInit(httpMessageInit) {
|
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
export async function parseHttpMessage(httpMessage) {
|
|
50
|
+
if (httpMessage.meta && 'body' in httpMessage.meta && httpMessage.meta.type === 'json') {
|
|
51
|
+
return httpMessage.meta.body;
|
|
52
|
+
}
|
|
50
53
|
let result;
|
|
51
54
|
const contentType = httpMessage.headers.get('Content-Type') || '';
|
|
52
55
|
if (contentType === 'application/x-www-form-urlencoded' || contentType.startsWith('multipart/form-data')) {
|
|
@@ -132,9 +135,19 @@ Object.defineProperties(Request, {
|
|
|
132
135
|
value: async function (request, init = {}) {
|
|
133
136
|
const requestInit = [
|
|
134
137
|
'method', 'headers', 'mode', 'credentials', 'cache', 'redirect', 'referrer', 'integrity',
|
|
135
|
-
].reduce((init, prop) => (
|
|
138
|
+
].reduce(($init, prop) => (
|
|
139
|
+
{ ...$init, [prop]: prop in init ? init[prop] : (prop === 'headers' ? new Headers(request[prop]) : request[prop]) }
|
|
140
|
+
), {});
|
|
136
141
|
if (!['GET', 'HEAD'].includes(init.method?.toUpperCase() || request.method)) {
|
|
137
|
-
|
|
142
|
+
if ('body' in init) {
|
|
143
|
+
requestInit.body = init.body
|
|
144
|
+
if (!('headers' in init)) {
|
|
145
|
+
requestInit.headers.delete('Content-Type');
|
|
146
|
+
requestInit.headers.delete('Content-Length');
|
|
147
|
+
}
|
|
148
|
+
} else {
|
|
149
|
+
requestInit.body = await request.clone().arrayBuffer();
|
|
150
|
+
}
|
|
138
151
|
}
|
|
139
152
|
if (requestInit.mode === 'navigate') {
|
|
140
153
|
requestInit.mode = 'cors';
|
package/src/services-pi/index.js
CHANGED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
|
|
2
|
+
/**
|
|
3
|
+
* imports
|
|
4
|
+
*/
|
|
5
|
+
import webpush from 'web-push';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* @description
|
|
9
|
+
*/
|
|
10
|
+
export const desc = {
|
|
11
|
+
generate: 'Generate a set of VAPID keys for push notifications.',
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Reads SSL from file.
|
|
16
|
+
*
|
|
17
|
+
* @return object
|
|
18
|
+
*/
|
|
19
|
+
export async function generate() {
|
|
20
|
+
const cx = this || {};
|
|
21
|
+
const vapidKeys = webpush.generateVAPIDKeys();
|
|
22
|
+
cx.logger.log(vapidKeys);
|
|
23
|
+
}
|