Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

improve: reliability of the library when windows reload #7

Merged
merged 2 commits into from
Apr 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2,052 changes: 1,232 additions & 820 deletions package-lock.json

Large diffs are not rendered by default.

30 changes: 15 additions & 15 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "fakettp",
"version": "1.9.0",
"version": "1.9.1",
"description": "sandbox node core http module in a service worker.",
"main": "dist/fakettp.js",
"scripts": {
Expand All @@ -26,33 +26,33 @@
"author": "Sepehr Laal",
"license": "MIT",
"devDependencies": {
"@types/debug": "^4.1.11",
"@types/node": "^20.9.0",
"@types/debug": "^4.1.12",
"@types/node": "^20.12.7",
"@types/webpack": "^5.28.5",
"@wdio/cli": "^8.21.0",
"@wdio/local-runner": "^8.21.0",
"@wdio/mocha-framework": "^8.21.0",
"@wdio/spec-reporter": "^8.21.0",
"@wdio/cli": "^8.35.1",
"@wdio/local-runner": "^8.35.1",
"@wdio/mocha-framework": "^8.35.0",
"@wdio/spec-reporter": "^8.32.4",
"assert": "*",
"buffer": "*",
"copy-webpack-plugin": "^11.0.0",
"copy-webpack-plugin": "^12.0.2",
"cors": "^2.8.5",
"debug": "*",
"events": "*",
"html-webpack-plugin": "^5.5.3",
"html-webpack-plugin": "^5.6.0",
"null-loader": "^4.0.1",
"process": "*",
"stream-browserify": "*",
"stream-http": "*",
"terser-webpack-plugin": "^5.3.9",
"ts-loader": "^9.5.0",
"ts-node": "^10.9.1",
"typescript": "^5.2.2",
"terser-webpack-plugin": "^5.3.10",
"ts-loader": "^9.5.1",
"ts-node": "^10.9.2",
"typescript": "^5.4.5",
"url": "*",
"util": "*",
"webpack": "^5.89.0",
"webpack": "^5.91.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^4.15.1",
"webpack-dev-server": "^5.0.4",
"webpack-shell-plugin-next": "^2.3.1"
},
"peerDependencies": {
Expand Down
95 changes: 44 additions & 51 deletions src/common.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,29 @@ import debug from "debug";
const log = debug("fakettp:sw");

export const FIN = "\x00" as const;
export const ARM = "\x01" as const;

export function getBundledWorkerFileName() {
return process.env.WEBPACK_FILENAME || "fakettp.js";
return process.env.FAKETTP_MAIN || "fakettp.js";
}

export function getExcludedPaths() {
const paths = [getBundledWorkerFileName()];
return [
...paths,
"nosw.js",
"app.html",
"favicon.ico",
"auxillary.js",
"inspector.js",
"bundle.webgme.js",
"bundle.memory.zip",
"sample-express.js",
"sample-express.html",
"sample-express-static.js",
"sample-express-static.html",
"sample-socket-io.js",
"sample-socket-io.html",
];
}

export function isRunningInBrowserWindow() {
Expand Down Expand Up @@ -42,7 +61,11 @@ export function MessagePortToReadableStream(port: MessagePort, onClose?: () => v
log("message received from message port: %d", portId);
if (StringOrBufferToString(data) === FIN) {
log("fin received from message port for readable stream: %d", portId);
controller?.close();
try {
controller?.close();
} catch (e) {
log(e);
}
controller = null;
onClose?.();
} else {
Expand Down Expand Up @@ -84,15 +107,19 @@ export function ReadableStreamToMessagePort(stream: ReadableStream<StringOrBuffe
}

export async function RequestBodyToReadableStream(request: Request): Promise<ReadableStream> {
if (request.body) return request.body;
return (await Promise.all([
request.clone().blob().then((b) => b && b.stream()),
request.clone().text().then((t) => t && (new Blob([t], { type: 'text/plain' })).stream()),
request.clone().arrayBuffer().then((b) => b && (new Blob([b], { type: 'application/octet-stream' })).stream()),
])).filter(Boolean)?.[0];
}

export type SerializedResponse = ResponseInit & { id: number; };
const clone = request.clone();
if (clone.body) return clone.body;
if (clone.bodyUsed) return new ReadableStream();
return (
await Promise.all([
clone.blob().then((b) => b && b.stream()),
clone.text().then((t) => t && new Blob([t], { type: "text/plain" }).stream()),
clone.arrayBuffer().then((b) => b && new Blob([b], { type: "application/octet-stream" }).stream()),
])
).filter(Boolean)?.[0];
}

export type SerializedResponse = ResponseInit & { id: number };
export type SerializedRequest = Awaited<ReturnType<typeof serializeRequest>>;

export async function serializeRequest(request: Request) {
Expand All @@ -105,7 +132,10 @@ export async function serializeRequest(request: Request) {
headers[key] = value;
});
const mode = request.mode;
const body = method === "GET" || method === "HEAD" ? null : ReadableStreamToMessagePort(await RequestBodyToReadableStream(request));
const body =
method === "GET" || method === "HEAD"
? null
: ReadableStreamToMessagePort(await RequestBodyToReadableStream(request));
const credentials = request.credentials;
const cache = request.cache;
const redirect = request.redirect;
Expand All @@ -130,7 +160,7 @@ export async function serializeRequest(request: Request) {
};
}

export function deserializeRequest(request: SerializedRequest): Request & { id?: number; } {
export function deserializeRequest(request: SerializedRequest): Request & { id?: number } {
const {
id,
url,
Expand Down Expand Up @@ -180,40 +210,3 @@ function _defaultPort() {

export const defaultPort = _defaultPort();
export const defaultHost = _defaultHost();

interface ProxyInstanceEventTargetSW {
readonly sw: ServiceWorkerGlobalScope;
readonly mt: Promise<Client | null>;
readonly listeners: Map<number, (event: MessageEvent<SerializedResponse>) => void>;
host: string;
port: string;
}

interface ProxyInstanceEventTargetMT {
readonly sw: Promise<ServiceWorker | null>;
readonly mt: Window;
}

type ProxyInstanceEventClients<T extends ServiceWorkerGlobalScope | Window> = T extends ServiceWorkerGlobalScope
? ProxyInstanceEventTargetSW
: ProxyInstanceEventTargetMT;

interface ProxyInstanceCommon<T extends ServiceWorkerGlobalScope | Window> {
readonly armed: boolean;
arm(): Promise<void>;
disarm(): Promise<void>;
}

type ProxyInstance<T extends ServiceWorkerGlobalScope | Window> = ProxyInstanceCommon<T> & ProxyInstanceEventClients<T>;

export type ProxyWindowInstance = ProxyInstance<Window>;
export type ProxyWorkerInstance = ProxyInstance<ServiceWorkerGlobalScope>;

export class Singleton<T> {
private instance: T | null = null;
constructor(private readonly factory: () => T) { }
get get() {
if (this.instance === null) this.instance = this.factory();
return this.instance;
}
}
7 changes: 4 additions & 3 deletions src/index.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import debug from "debug";

import { createProxyClient } from "./sw";
import { createProxyServer, IncomingMessage, ServerResponse } from "./mt";
import { isRunningInBrowserWindow, isRunningInServiceWorker } from "./common";
import { createProxyServer, IncomingMessage, ServerResponse, unload } from "./mt";

import type { RequestListener } from "http";

Expand All @@ -15,13 +15,14 @@ const _http = (() => {
}
})();

log("built with webpack mode: %s", process.env.WEBPACK_MODE);
log("webpack bundle filename: %s", process.env.WEBPACK_FILENAME);
log("built with webpack mode: %s", process.env.FAKETTP_MODE);
log("webpack bundle filename: %s", process.env.FAKETTP_MAIN);

if (isRunningInServiceWorker()) createProxyClient();

const http = {
..._http,
unload,
ServerResponse,
IncomingMessage,
createServer: isRunningInBrowserWindow()
Expand Down
Loading
Loading