Skip to main content
edited title
Link
jcubic
  • 248
  • 2
  • 9

async Promise constructor that handles Handling pure in browser HTTP request in Service Worker fetch event

edited title
Link
jcubic
  • 248
  • 2
  • 9

Refactor async Promise constructor that handles HTTP request in Service Worker fetch event

added 89 characters in body
Source Link
jcubic
  • 248
  • 2
  • 9
export class HTTPResponse {
    constructor(resolve, reject) {
        this._resolve = resolve;
        this._reject = reject;
    }
    html(data, init) {
        this.send(data, { type: 'text/html', ...init });
    }
    text(data, init) {
        this.send(data, init);
    }
    json(data, init) {
        this.send(JSON.stringify(data), { type: 'application/json', ...init });
    }
    blob(blob, init = {}) {
        this._resolve(new Response(blob, init));
    }
    send(data, { type = 'text/plain', ...init } = {}) {
        if (![undefined, null].includes(data)) {
            data = new Blob([data], {
                type
            });
        }
        this.blob(data, init);
    }
    // .. there are more code that are irrelevant
}

function chain_handlers(handlers, callback) {
    if (handlers.length) {
        return new Promise((resolve, reject) => {
            let i = 0;
            (async function recur() {
                const handler = handlers[i];
                if (!handler) {
                    return resolve();
                }
                try {
                    await callback(handler, function next() {
                        i++
                        recur();
                    });
                } catch(error) {
                    reject(error);
                }
            })();
        });
    }
}

export class Wayne {
    constructor() {
        this._er_handlers = [];
        this._middlewares = [];
        this._routes = {};
        this._timeout = 5 * 60 * 1000; // 5 minutes
        this._parser = new RouteParser();
        self.addEventListener('fetch', (event) => {
            const promise = new Promise(async (resolve, reject) => {
                const req = event.request;
                try {
                    const res = new HTTPResponse(resolve, reject);
                    await chain_handlers(this._middlewares, function(fn, next) {
                        return fn(req, res, next);
                    });
                    const method = req.method;
                    const url = new URL(req.url);
                    const path = normalize_url(url.pathname);
                    const routes = this._routes[method];
                    if (routes) {
                        const match = this._parser.pick(routes, path);
                        if (match.length) {
                            const [first_match] = match;
                            const fns = [...this._middlewares, ...routes[first_match.pattern]];
                            req.params = first_match.data;
                            setTimeout(function() {
                                reject('Timeout Error');
                            }, this._timeout);
                            await chain_handlers(fns, (fn, next) => {
                                return fn(req, res, next);
                            });
                            return;
                        }
                    }
                    if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
                        return;
                    }
                    //request = credentials: 'include'
                    fetch(event.request).then(resolve).catch(reject);
                } catch(error) {
                    this._handle_error(resolve, req, error);
                }
            });
            event.respondWith(promise.catch(() => {}));
        });
        ['GET', 'POST', 'DELETE', 'PATCH', 'PUT'].forEach(method => {
            this[method.toLowerCase()] = this.method(method);
        });
    }
    _handle_error(resolve, req, error) {
        const res = new HTTPResponse(resolve);
        if (this._er_handlers.length) {
            chain_handlers(this._er_handlers, function(handler, next) {
                handler(error, req, res, next);
            }, function(error) {
                res.html(...error500(error));
            });
        } else {
            res.html(...error500(error));
        }
    }
    use(...fns) {
        fns.forEach(fn => {
            if (typeof fn === 'function') {
                if (fn.length === 4) {
                    this._er_handlers.push(fn);
                } else if (fn.length === 3) {
                    this._middlewares.push(fn);
                }
            }
        });
    }
    method(method) {
        return function(url, fn) {
            if (!this._routes[method]) {
                this._routes[method] = {};
            }
            const routes = this._routes[method];
            if (!routes[url]) {
                routes[url] = [];
            }
            routes[url].push(fn);
            return this;
        };
    }
}

export class HTTPResponse {
    constructor(resolve, reject) {
        this._resolve = resolve;
        this._reject = reject;
    }
    text(data, init) {
        this.send(data, init);
    }
    json(data, init) {
        this.send(JSON.stringify(data), { type: 'application/json', ...init });
    }
    blob(blob, init = {}) {
        this._resolve(new Response(blob, init));
    }
    send(data, { type = 'text/plain', ...init } = {}) {
        if (![undefined, null].includes(data)) {
            data = new Blob([data], {
                type
            });
        }
        this.blob(data, init);
    }
    // .. there are more code that are irrelevant
}

function chain_handlers(handlers, callback) {
    if (handlers.length) {
        return new Promise((resolve, reject) => {
            let i = 0;
            (async function recur() {
                const handler = handlers[i];
                if (!handler) {
                    return resolve();
                }
                try {
                    await callback(handler, function next() {
                        i++
                        recur();
                    });
                } catch(error) {
                    reject(error);
                }
            })();
        });
    }
}

export class Wayne {
    constructor() {
        this._er_handlers = [];
        this._middlewares = [];
        this._routes = {};
        this._timeout = 5 * 60 * 1000; // 5 minutes
        this._parser = new RouteParser();
        self.addEventListener('fetch', (event) => {
            const promise = new Promise(async (resolve, reject) => {
                const req = event.request;
                try {
                    const res = new HTTPResponse(resolve, reject);
                    await chain_handlers(this._middlewares, function(fn, next) {
                        return fn(req, res, next);
                    });
                    const method = req.method;
                    const url = new URL(req.url);
                    const path = normalize_url(url.pathname);
                    const routes = this._routes[method];
                    if (routes) {
                        const match = this._parser.pick(routes, path);
                        if (match.length) {
                            const [first_match] = match;
                            const fns = [...this._middlewares, ...routes[first_match.pattern]];
                            req.params = first_match.data;
                            setTimeout(function() {
                                reject('Timeout Error');
                            }, this._timeout);
                            await chain_handlers(fns, (fn, next) => {
                                return fn(req, res, next);
                            });
                            return;
                        }
                    }
                    if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
                        return;
                    }
                    //request = credentials: 'include'
                    fetch(event.request).then(resolve).catch(reject);
                } catch(error) {
                    this._handle_error(resolve, req, error);
                }
            });
            event.respondWith(promise.catch(() => {}));
        });
        ['GET', 'POST', 'DELETE', 'PATCH', 'PUT'].forEach(method => {
            this[method.toLowerCase()] = this.method(method);
        });
    }
    _handle_error(resolve, req, error) {
        const res = new HTTPResponse(resolve);
        if (this._er_handlers.length) {
            chain_handlers(this._er_handlers, function(handler, next) {
                handler(error, req, res, next);
            }, function(error) {
                res.html(...error500(error));
            });
        } else {
            res.html(...error500(error));
        }
    }
    use(...fns) {
        fns.forEach(fn => {
            if (typeof fn === 'function') {
                if (fn.length === 4) {
                    this._er_handlers.push(fn);
                } else if (fn.length === 3) {
                    this._middlewares.push(fn);
                }
            }
        });
    }
    method(method) {
        return function(url, fn) {
            if (!this._routes[method]) {
                this._routes[method] = {};
            }
            const routes = this._routes[method];
            if (!routes[url]) {
                routes[url] = [];
            }
            routes[url].push(fn);
            return this;
        };
    }
}

export class HTTPResponse {
    constructor(resolve, reject) {
        this._resolve = resolve;
        this._reject = reject;
    }
    html(data, init) {
        this.send(data, { type: 'text/html', ...init });
    }
    text(data, init) {
        this.send(data, init);
    }
    json(data, init) {
        this.send(JSON.stringify(data), { type: 'application/json', ...init });
    }
    blob(blob, init = {}) {
        this._resolve(new Response(blob, init));
    }
    send(data, { type = 'text/plain', ...init } = {}) {
        if (![undefined, null].includes(data)) {
            data = new Blob([data], {
                type
            });
        }
        this.blob(data, init);
    }
    // .. there are more code that are irrelevant
}

function chain_handlers(handlers, callback) {
    if (handlers.length) {
        return new Promise((resolve, reject) => {
            let i = 0;
            (async function recur() {
                const handler = handlers[i];
                if (!handler) {
                    return resolve();
                }
                try {
                    await callback(handler, function next() {
                        i++
                        recur();
                    });
                } catch(error) {
                    reject(error);
                }
            })();
        });
    }
}

export class Wayne {
    constructor() {
        this._er_handlers = [];
        this._middlewares = [];
        this._routes = {};
        this._timeout = 5 * 60 * 1000; // 5 minutes
        this._parser = new RouteParser();
        self.addEventListener('fetch', (event) => {
            const promise = new Promise(async (resolve, reject) => {
                const req = event.request;
                try {
                    const res = new HTTPResponse(resolve, reject);
                    await chain_handlers(this._middlewares, function(fn, next) {
                        return fn(req, res, next);
                    });
                    const method = req.method;
                    const url = new URL(req.url);
                    const path = normalize_url(url.pathname);
                    const routes = this._routes[method];
                    if (routes) {
                        const match = this._parser.pick(routes, path);
                        if (match.length) {
                            const [first_match] = match;
                            const fns = [...this._middlewares, ...routes[first_match.pattern]];
                            req.params = first_match.data;
                            setTimeout(function() {
                                reject('Timeout Error');
                            }, this._timeout);
                            await chain_handlers(fns, (fn, next) => {
                                return fn(req, res, next);
                            });
                            return;
                        }
                    }
                    if (event.request.cache === 'only-if-cached' && event.request.mode !== 'same-origin') {
                        return;
                    }
                    //request = credentials: 'include'
                    fetch(event.request).then(resolve).catch(reject);
                } catch(error) {
                    this._handle_error(resolve, req, error);
                }
            });
            event.respondWith(promise.catch(() => {}));
        });
        ['GET', 'POST', 'DELETE', 'PATCH', 'PUT'].forEach(method => {
            this[method.toLowerCase()] = this.method(method);
        });
    }
    _handle_error(resolve, req, error) {
        const res = new HTTPResponse(resolve);
        if (this._er_handlers.length) {
            chain_handlers(this._er_handlers, function(handler, next) {
                handler(error, req, res, next);
            }, function(error) {
                res.html(...error500(error));
            });
        } else {
            res.html(...error500(error));
        }
    }
    use(...fns) {
        fns.forEach(fn => {
            if (typeof fn === 'function') {
                if (fn.length === 4) {
                    this._er_handlers.push(fn);
                } else if (fn.length === 3) {
                    this._middlewares.push(fn);
                }
            }
        });
    }
    method(method) {
        return function(url, fn) {
            if (!this._routes[method]) {
                this._routes[method] = {};
            }
            const routes = this._routes[method];
            if (!routes[url]) {
                routes[url] = [];
            }
            routes[url].push(fn);
            return this;
        };
    }
}

Source Link
jcubic
  • 248
  • 2
  • 9
Loading