ProxyPerUserContextManager.sys.mjs (5040B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 import { XPCOMUtils } from "resource://gre/modules/XPCOMUtils.sys.mjs"; 6 7 const lazy = {}; 8 9 ChromeUtils.defineESModuleGetters(lazy, { 10 ProxyTypes: "chrome://remote/content/shared/webdriver/Capabilities.sys.mjs", 11 }); 12 13 XPCOMUtils.defineLazyServiceGetter( 14 lazy, 15 "protocolProxyService", 16 "@mozilla.org/network/protocol-proxy-service;1", 17 Ci.nsIProtocolProxyService 18 ); 19 20 const nsIProtocolProxyChannelFilter = ChromeUtils.generateQI([ 21 Ci.nsIProtocolProxyChannelFilter, 22 ]); 23 24 // The maximum uint32 value. 25 const PR_UINT32_MAX = 4294967295; 26 27 /** 28 * A ProxyPerUserContextManager class keeps track of user contexts and their proxy configuration. 29 */ 30 export class ProxyPerUserContextManager { 31 #channelProxyFilter; 32 #proxyFilterRegistered; 33 #userContextToProxyConfiguration; 34 35 constructor() { 36 this.#proxyFilterRegistered = false; 37 38 // A map between internal user context ids and proxy configurations. 39 this.#userContextToProxyConfiguration = new Map(); 40 } 41 42 destroy() { 43 this.#userContextToProxyConfiguration = new Map(); 44 45 this.#unregisterProxyFilter(); 46 } 47 48 /** 49 * Add proxy configuration for a provided user context id. 50 * 51 * @param {string} userContextId 52 * Internal user context id. 53 * @param {Proxy} proxy 54 * Proxy configuration. 55 */ 56 addConfiguration(userContextId, proxy) { 57 this.#userContextToProxyConfiguration.set(userContextId, proxy); 58 59 this.#registerProxyFilter(); 60 } 61 62 /** 63 * Delete proxy configuration for a provided user context id. 64 * 65 * @param {string} userContextId 66 * Internal user context id. 67 */ 68 deleteConfiguration(userContextId) { 69 this.#userContextToProxyConfiguration.delete(userContextId); 70 71 this.#unregisterProxyFilter(); 72 } 73 74 #addProxyFilter(proxyFilter, proxySettings) { 75 const { host, port, type } = proxySettings; 76 77 proxyFilter.onProxyFilterResult( 78 lazy.protocolProxyService.newProxyInfo( 79 type, 80 host, 81 port, 82 "" /* aProxyAuthorizationHeader */, 83 "" /* aConnectionIsolationKey */, 84 Ci.nsIProxyInfo.TRANSPARENT_PROXY_RESOLVES_HOST /* aFlags */, 85 PR_UINT32_MAX /* aFailoverTimeout */, 86 null /* failover proxy */ 87 ) 88 ); 89 } 90 91 #applyFilter = (channel, defaultProxyInfo, proxyFilter) => { 92 const originAttributes = 93 channel.loadInfo && channel.loadInfo.originAttributes; 94 95 if ( 96 this.#userContextToProxyConfiguration.has(originAttributes.userContextId) 97 ) { 98 const proxyInfo = this.#userContextToProxyConfiguration.get( 99 originAttributes.userContextId 100 ); 101 102 if (proxyInfo.proxyType === lazy.ProxyTypes.Direct) { 103 proxyFilter.onProxyFilterResult(null); 104 105 return; 106 } 107 108 if (proxyInfo.proxyType === lazy.ProxyTypes.Manual) { 109 const channelURI = channel.originalURI; 110 for (const url of proxyInfo.noProxy ?? []) { 111 if ( 112 (url.startsWith(".") && channelURI.host.endsWith(url)) || 113 (!url.startsWith(".") && channelURI.host === url) 114 ) { 115 proxyFilter.onProxyFilterResult(defaultProxyInfo); 116 117 // If at least one element from noProxy matches the channel URL no need to check further. 118 return; 119 } 120 } 121 122 if (proxyInfo.httpProxy) { 123 this.#addProxyFilter(proxyFilter, { 124 host: proxyInfo.httpProxy, 125 port: proxyInfo.httpProxyPort, 126 type: "http", 127 }); 128 } 129 130 if (proxyInfo.sslProxy) { 131 this.#addProxyFilter(proxyFilter, { 132 host: proxyInfo.sslProxy, 133 port: proxyInfo.sslProxyPort, 134 type: "https", 135 }); 136 } 137 138 if (proxyInfo.socksProxy) { 139 this.#addProxyFilter(proxyFilter, { 140 host: proxyInfo.socksProxy, 141 port: proxyInfo.socksProxyPort, 142 type: 143 proxyInfo.socksVersion === 5 144 ? "socks" 145 : `socks${proxyInfo.socksVersion}`, 146 }); 147 } 148 149 return; 150 } 151 } 152 153 proxyFilter.onProxyFilterResult(defaultProxyInfo); 154 }; 155 156 #registerProxyFilter() { 157 if (!this.#proxyFilterRegistered) { 158 this.#proxyFilterRegistered = true; 159 160 this.#channelProxyFilter = { 161 QueryInterface: nsIProtocolProxyChannelFilter, 162 applyFilter: this.#applyFilter, 163 }; 164 165 lazy.protocolProxyService.registerChannelFilter( 166 this.#channelProxyFilter, 167 0 /* set position `0` to override global filters */ 168 ); 169 } 170 } 171 172 #unregisterProxyFilter() { 173 if ( 174 this.#proxyFilterRegistered && 175 this.#userContextToProxyConfiguration.size === 0 176 ) { 177 this.#proxyFilterRegistered = false; 178 lazy.protocolProxyService.unregisterChannelFilter( 179 this.#channelProxyFilter 180 ); 181 this.#channelProxyFilter = null; 182 } 183 } 184 }