ultraviolet/client/requests/fetch.js
2022-02-14 00:25:17 -05:00

166 lines
No EOL
5.8 KiB
JavaScript

import EventEmitter from "../events.js";
import HookEvent from "../hook.js";
class Fetch extends EventEmitter {
constructor(ctx) {
super();
this.ctx = ctx;
this.window = ctx.window;
this.fetch = this.window.fetch;
this.Request = this.window.Request;
this.Response = this.window.Response;
this.Headers = this.window.Headers;
this.reqProto = this.Request ? this.Request.prototype : {};
this.resProto = this.Response ? this.Response.prototype : {};
this.headersProto = this.Headers ? this.Headers.prototype : {};
this.reqUrl = ctx.nativeMethods.getOwnPropertyDescriptor(this.reqProto, 'url');
this.resUrl = ctx.nativeMethods.getOwnPropertyDescriptor(this.resProto, 'url');
this.reqHeaders = ctx.nativeMethods.getOwnPropertyDescriptor(this.reqProto, 'headers');
this.resHeaders = ctx.nativeMethods.getOwnPropertyDescriptor(this.resProto, 'headers');
};
override() {
this.overrideRequest();
this.overrideUrl();
this.overrideHeaders();
return true;
};
overrideRequest() {
if (!this.fetch) return false;
this.ctx.override(this.window, 'fetch', (target, that, args) => {
if (!args.length || args[0] instanceof this.Request) return target.apply(that, args);
let [ input, options = {} ] = args;
const event = new HookEvent({ input, options }, target, that);
this.emit('request', event);
if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.input, event.data.options);
});
this.ctx.override(this.window, 'Request', (target, that, args) => {
if (!args.length) return new target(...args);
let [ input, options = {} ] = args;
const event = new HookEvent({ input, options }, target);
this.emit('request', event);
if (event.intercepted) return event.returnValue;
return new event.target(event.data.input, event.data.options);
}, true);
return true;
};
overrideUrl() {
this.ctx.overrideDescriptor(this.reqProto, 'url', {
get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that);
this.emit('requestUrl', event);
if (event.intercepted) return event.returnValue;
return event.data.value;
},
});
this.ctx.overrideDescriptor(this.resProto, 'url', {
get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that);
this.emit('responseUrl', event);
if (event.intercepted) return event.returnValue;
return event.data.value;
},
});
return true;
};
overrideHeaders() {
if (!this.Headers) return false;
this.ctx.overrideDescriptor(this.reqProto, 'headers', {
get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that);
this.emit('requestHeaders', event);
if (event.intercepted) return event.returnValue;
return event.data.value;
},
});
this.ctx.overrideDescriptor(this.resProto, 'headers', {
get: (target, that) => {
const event = new HookEvent({ value: target.call(that) }, target, that);
this.emit('responseHeaders', event);
if (event.intercepted) return event.returnValue;
return event.data.value;
},
});
this.ctx.override(this.headersProto, 'get', (target, that, [ name ]) => {
if (!name) return target.call(that);
const event = new HookEvent({ name, value: target.call(that, name) }, target, that);
this.emit('getHeader', event);
if (event.intercepted) return event.returnValue;
return event.data.value;
});
this.ctx.override(this.headersProto, 'set', (target, that, args) => {
if (2 > args.length) return target.apply(that, args);
let [ name, value ] = args;
const event = new HookEvent({ name, value }, target, that);
this.emit('setHeader', event);
if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name, event.data.value);
});
this.ctx.override(this.headersProto, 'has', (target, that, args) => {
if (!args.length) return target.call(that);
let [ name ] = args;
const event = new HookEvent({ name, value: target.call(that, name) }, target, that);
this.emit('hasHeader', event);
if (event.intercepted) return event.returnValue;
return event.data;
});
this.ctx.override(this.headersProto, 'append', (target, that, args) => {
if (2 > args.length) return target.apply(that, args);
let [ name, value ] = args;
const event = new HookEvent({ name, value }, target, that);
this.emit('appendHeader', event);
if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name, event.data.value);
});
this.ctx.override(this.headersProto, 'delete', (target, that, args) => {
if (!args.length) return target.apply(that, args);
let [ name ] = args;
const event = new HookEvent({ name }, target, that);
this.emit('deleteHeader', event);
if (event.intercepted) return event.returnValue;
return event.target.call(event.that, event.data.name);
});
return true;
};
};
export default Fetch;