aboutsummaryrefslogtreecommitdiff
path: root/ext/js/background/request-builder.js
diff options
context:
space:
mode:
Diffstat (limited to 'ext/js/background/request-builder.js')
-rw-r--r--ext/js/background/request-builder.js294
1 files changed, 85 insertions, 209 deletions
diff --git a/ext/js/background/request-builder.js b/ext/js/background/request-builder.js
index 663e242b..7ee89539 100644
--- a/ext/js/background/request-builder.js
+++ b/ext/js/background/request-builder.js
@@ -31,7 +31,6 @@ class RequestBuilder {
* Creates a new instance.
*/
constructor() {
- this._onBeforeSendHeadersExtraInfoSpec = ['blocking', 'requestHeaders', 'extraHeaders'];
this._textEncoder = new TextEncoder();
this._ruleIds = new Set();
}
@@ -42,6 +41,7 @@ class RequestBuilder {
async prepare() {
try {
await this._clearDynamicRules();
+ await this._clearSessionRules();
} catch (e) {
// NOP
}
@@ -54,15 +54,50 @@ class RequestBuilder {
* @returns {Promise<Response>} The response of the `fetch` call.
*/
async fetchAnonymous(url, init) {
- if (isObject(chrome.declarativeNetRequest)) {
- return await this._fetchAnonymousDeclarative(url, init);
+ const id = this._getNewRuleId();
+ const originUrl = this._getOriginURL(url);
+ url = encodeURI(decodeURI(url));
+
+ this._ruleIds.add(id);
+ try {
+ const addRules = [{
+ id,
+ priority: 1,
+ condition: {
+ urlFilter: `|${this._escapeDnrUrl(url)}|`,
+ resourceTypes: ['xmlhttprequest']
+ },
+ action: {
+ type: 'modifyHeaders',
+ requestHeaders: [
+ {
+ operation: 'remove',
+ header: 'Cookie'
+ },
+ {
+ operation: 'set',
+ header: 'Origin',
+ value: originUrl
+ }
+ ],
+ responseHeaders: [
+ {
+ operation: 'remove',
+ header: 'Set-Cookie'
+ }
+ ]
+ }
+ }];
+
+ await this._updateSessionRules({addRules});
+ try {
+ return await fetch(url, init);
+ } finally {
+ await this._tryUpdateSessionRules({removeRuleIds: [id]});
+ }
+ } finally {
+ this._ruleIds.delete(id);
}
- const originURL = this._getOriginURL(url);
- const headerModifications = [
- ['cookie', null],
- ['origin', {name: 'Origin', value: originURL}]
- ];
- return await this._fetchInternal(url, init, headerModifications);
}
/**
@@ -125,145 +160,56 @@ class RequestBuilder {
// Private
- async _fetchInternal(url, init, headerModifications) {
- const filter = {
- urls: [this._getMatchURL(url)],
- types: ['xmlhttprequest']
- };
-
- let requestId = null;
- const onBeforeSendHeadersCallback = (details) => {
- if (requestId !== null || details.url !== url) { return {}; }
- ({requestId} = details);
-
- if (headerModifications === null) { return {}; }
-
- const requestHeaders = details.requestHeaders;
- this._modifyHeaders(requestHeaders, headerModifications);
- return {requestHeaders};
- };
-
- let errorDetailsTimer = null;
- let {promise: errorDetailsPromise, resolve: errorDetailsResolve} = deferPromise();
- const onErrorOccurredCallback = (details) => {
- if (errorDetailsResolve === null || details.requestId !== requestId) { return; }
- if (errorDetailsTimer !== null) {
- clearTimeout(errorDetailsTimer);
- errorDetailsTimer = null;
- }
- errorDetailsResolve(details);
- errorDetailsResolve = null;
- };
-
- const eventListeners = [];
- const onBeforeSendHeadersExtraInfoSpec = (headerModifications !== null ? this._onBeforeSendHeadersExtraInfoSpec : []);
- this._addWebRequestEventListener(chrome.webRequest.onBeforeSendHeaders, onBeforeSendHeadersCallback, filter, onBeforeSendHeadersExtraInfoSpec, eventListeners);
- this._addWebRequestEventListener(chrome.webRequest.onErrorOccurred, onErrorOccurredCallback, filter, void 0, eventListeners);
+ async _clearSessionRules() {
+ const rules = await this._getSessionRules();
- try {
- return await fetch(url, init);
- } catch (e) {
- // onErrorOccurred is not always invoked by this point, so a delay is needed
- if (errorDetailsResolve !== null) {
- errorDetailsTimer = setTimeout(() => {
- errorDetailsTimer = null;
- if (errorDetailsResolve === null) { return; }
- errorDetailsResolve(null);
- errorDetailsResolve = null;
- }, 100);
- }
- const details = await errorDetailsPromise;
- if (details !== null) {
- const data = {details};
- this._assignErrorData(e, data);
- }
- throw e;
- } finally {
- this._removeWebRequestEventListeners(eventListeners);
- }
- }
+ if (rules.length === 0) { return; }
- _addWebRequestEventListener(target, callback, filter, extraInfoSpec, eventListeners) {
- try {
- for (let i = 0; i < 2; ++i) {
- try {
- if (typeof extraInfoSpec === 'undefined') {
- target.addListener(callback, filter);
- } else {
- target.addListener(callback, filter, extraInfoSpec);
- }
- break;
- } catch (e) {
- // Firefox doesn't support the 'extraHeaders' option and will throw the following error:
- // Type error for parameter extraInfoSpec (Error processing 2: Invalid enumeration value "extraHeaders") for [target].
- if (i === 0 && `${e.message}`.includes('extraHeaders') && Array.isArray(extraInfoSpec)) {
- const index = extraInfoSpec.indexOf('extraHeaders');
- if (index >= 0) {
- extraInfoSpec.splice(index, 1);
- continue;
- }
- }
- throw e;
- }
- }
- } catch (e) {
- console.log(e);
- return;
+ const removeRuleIds = [];
+ for (const {id} of rules) {
+ removeRuleIds.push(id);
}
- eventListeners.push({target, callback});
- }
- _removeWebRequestEventListeners(eventListeners) {
- for (const {target, callback} of eventListeners) {
- try {
- target.removeListener(callback);
- } catch (e) {
- console.log(e);
- }
- }
+ await this._updateSessionRules({removeRuleIds});
}
- _getMatchURL(url) {
- const url2 = new URL(url);
- return `${url2.protocol}//${url2.host}${url2.pathname}${url2.search}`.replace(/\*/g, '%2a');
+ _getSessionRules() {
+ return new Promise((resolve, reject) => {
+ chrome.declarativeNetRequest.getSessionRules((result) => {
+ const e = chrome.runtime.lastError;
+ if (e) {
+ reject(new Error(e.message));
+ } else {
+ resolve(result);
+ }
+ });
+ });
}
- _getOriginURL(url) {
- const url2 = new URL(url);
- return `${url2.protocol}//${url2.host}`;
+ _updateSessionRules(options) {
+ return new Promise((resolve, reject) => {
+ chrome.declarativeNetRequest.updateSessionRules(options, () => {
+ const e = chrome.runtime.lastError;
+ if (e) {
+ reject(new Error(e.message));
+ } else {
+ resolve();
+ }
+ });
+ });
}
- _modifyHeaders(headers, modifications) {
- modifications = new Map(modifications);
-
- for (let i = 0, ii = headers.length; i < ii; ++i) {
- const header = headers[i];
- const name = header.name.toLowerCase();
- const modification = modifications.get(name);
- if (typeof modification === 'undefined') { continue; }
-
- modifications.delete(name);
-
- if (modification === null) {
- headers.splice(i, 1);
- --i;
- --ii;
- } else {
- headers[i] = modification;
- }
- }
-
- for (const header of modifications.values()) {
- if (header !== null) {
- headers.push(header);
- }
+ async _tryUpdateSessionRules(options) {
+ try {
+ await this._updateSessionRules(options);
+ return true;
+ } catch (e) {
+ return false;
}
}
async _clearDynamicRules() {
- if (!isObject(chrome.declarativeNetRequest)) { return; }
-
- const rules = this._getDynamicRules();
+ const rules = await this._getDynamicRules();
if (rules.length === 0) { return; }
@@ -275,53 +221,6 @@ class RequestBuilder {
await this._updateDynamicRules({removeRuleIds});
}
- async _fetchAnonymousDeclarative(url, init) {
- const id = this._getNewRuleId();
- const originUrl = this._getOriginURL(url);
- url = encodeURI(decodeURI(url));
-
- this._ruleIds.add(id);
- try {
- const addRules = [{
- id,
- priority: 1,
- condition: {
- urlFilter: `|${this._escapeDnrUrl(url)}|`,
- resourceTypes: ['xmlhttprequest']
- },
- action: {
- type: 'modifyHeaders',
- requestHeaders: [
- {
- operation: 'remove',
- header: 'Cookie'
- },
- {
- operation: 'set',
- header: 'Origin',
- value: originUrl
- }
- ],
- responseHeaders: [
- {
- operation: 'remove',
- header: 'Set-Cookie'
- }
- ]
- }
- }];
-
- await this._updateDynamicRules({addRules});
- try {
- return await this._fetchInternal(url, init, null);
- } finally {
- await this._tryUpdateDynamicRules({removeRuleIds: [id]});
- }
- } finally {
- this._ruleIds.delete(id);
- }
- }
-
_getDynamicRules() {
return new Promise((resolve, reject) => {
chrome.declarativeNetRequest.getDynamicRules((result) => {
@@ -348,15 +247,6 @@ class RequestBuilder {
});
}
- async _tryUpdateDynamicRules(options) {
- try {
- await this._updateDynamicRules(options);
- return true;
- } catch (e) {
- return false;
- }
- }
-
_getNewRuleId() {
let id = 1;
while (this._ruleIds.has(id)) {
@@ -367,6 +257,11 @@ class RequestBuilder {
return id;
}
+ _getOriginURL(url) {
+ const url2 = new URL(url);
+ return `${url2.protocol}//${url2.host}`;
+ }
+
_escapeDnrUrl(url) {
return url.replace(/[|*^]/g, (char) => this._urlEncodeUtf8(char));
}
@@ -380,25 +275,6 @@ class RequestBuilder {
return result;
}
- _assignErrorData(error, data) {
- try {
- error.data = data;
- } catch (e) {
- // On Firefox, assigning DOMException.data can fail in certain contexts.
- // https://bugzilla.mozilla.org/show_bug.cgi?id=1776555
- try {
- Object.defineProperty(error, 'data', {
- configurable: true,
- enumerable: true,
- writable: true,
- value: data
- });
- } catch (e2) {
- // NOP
- }
- }
- }
-
static _joinUint8Arrays(items, totalLength) {
if (items.length === 1) {
const {array, length} = items[0];