diff options
Diffstat (limited to 'ext/bg/js/request-builder.js')
-rw-r--r-- | ext/bg/js/request-builder.js | 266 |
1 files changed, 0 insertions, 266 deletions
diff --git a/ext/bg/js/request-builder.js b/ext/bg/js/request-builder.js deleted file mode 100644 index dda5825d..00000000 --- a/ext/bg/js/request-builder.js +++ /dev/null @@ -1,266 +0,0 @@ -/* - * Copyright (C) 2020-2021 Yomichan Authors - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see <https://www.gnu.org/licenses/>. - */ - -class RequestBuilder { - constructor() { - this._extraHeadersSupported = null; - this._onBeforeSendHeadersExtraInfoSpec = ['blocking', 'requestHeaders', 'extraHeaders']; - this._textEncoder = new TextEncoder(); - this._ruleIds = new Set(); - } - - async prepare() { - try { - await this._clearDynamicRules(); - } catch (e) { - // NOP - } - } - - async fetchAnonymous(url, init) { - if (isObject(chrome.declarativeNetRequest)) { - return await this._fetchAnonymousDeclarative(url, init); - } - const originURL = this._getOriginURL(url); - const modifications = [ - ['cookie', null], - ['origin', {name: 'Origin', value: originURL}] - ]; - return await this._fetchModifyHeaders(url, init, modifications); - } - - // Private - - async _fetchModifyHeaders(url, init, modifications) { - const matchURL = this._getMatchURL(url); - - let done = false; - const callback = (details) => { - if (done || details.url !== url) { return {}; } - done = true; - - const requestHeaders = details.requestHeaders; - this._modifyHeaders(requestHeaders, modifications); - return {requestHeaders}; - }; - const filter = { - urls: [matchURL], - types: ['xmlhttprequest'] - }; - - let needsCleanup = false; - try { - this._onBeforeSendHeadersAddListener(callback, filter); - needsCleanup = true; - } catch (e) { - // NOP - } - - try { - return await fetch(url, init); - } finally { - if (needsCleanup) { - try { - chrome.webRequest.onBeforeSendHeaders.removeListener(callback); - } catch (e) { - // NOP - } - } - } - } - - _onBeforeSendHeadersAddListener(callback, filter) { - const extraInfoSpec = this._onBeforeSendHeadersExtraInfoSpec; - for (let i = 0; i < 2; ++i) { - try { - chrome.webRequest.onBeforeSendHeaders.addListener(callback, filter, extraInfoSpec); - if (this._extraHeadersSupported === null) { - this._extraHeadersSupported = true; - } - 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 webRequest.onBeforeSendHeaders. - if (this._extraHeadersSupported !== null || !`${e.message}`.includes('extraHeaders')) { - throw e; - } - } - - // addListener failed; remove 'extraHeaders' from extraInfoSpec. - this._extraHeadersSupported = false; - const index = extraInfoSpec.indexOf('extraHeaders'); - if (index >= 0) { extraInfoSpec.splice(index, 1); } - } - } - - _getMatchURL(url) { - const url2 = new URL(url); - return `${url2.protocol}//${url2.host}${url2.pathname}`; - } - - _getOriginURL(url) { - const url2 = new URL(url); - return `${url2.protocol}//${url2.host}`; - } - - _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 _clearDynamicRules() { - if (!isObject(chrome.declarativeNetRequest)) { return; } - - const rules = this._getDynamicRules(); - - if (rules.length === 0) { return; } - - const removeRuleIds = []; - for (const {id} of rules) { - removeRuleIds.push(id); - } - - 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 fetch(url, init); - } finally { - await this._tryUpdateDynamicRules({removeRuleIds: [id]}); - } - } finally { - this._ruleIds.delete(id); - } - } - - _getDynamicRules() { - return new Promise((resolve, reject) => { - chrome.declarativeNetRequest.getDynamicRules((result) => { - const e = chrome.runtime.lastError; - if (e) { - reject(new Error(e.message)); - } else { - resolve(result); - } - }); - }); - } - - _updateDynamicRules(options) { - return new Promise((resolve, reject) => { - chrome.declarativeNetRequest.updateDynamicRules(options, () => { - const e = chrome.runtime.lastError; - if (e) { - reject(new Error(e.message)); - } else { - resolve(); - } - }); - }); - } - - async _tryUpdateDynamicRules(options) { - try { - await this._updateDynamicRules(options); - return true; - } catch (e) { - return false; - } - } - - _getNewRuleId() { - let id = 1; - while (this._ruleIds.has(id)) { - const pre = id; - ++id; - if (id === pre) { throw new Error('Could not generate an id'); } - } - return id; - } - - _escapeDnrUrl(url) { - return url.replace(/[|*^]/g, (char) => this._urlEncodeUtf8(char)); - } - - _urlEncodeUtf8(text) { - const array = this._textEncoder.encode(text); - let result = ''; - for (const byte of array) { - result += `%${byte.toString(16).toUpperCase().padStart(2, '0')}`; - } - return result; - } -} |