gecko-dev/toolkit/modules/ServiceRequest.jsm

184 строки
4.8 KiB
JavaScript

/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
"use strict";
/**
* This module consolidates various code and data update requests, so flags
* can be set, Telemetry collected, etc. in a central place.
*/
const { Services } = ChromeUtils.import("resource://gre/modules/Services.jsm");
const { XPCOMUtils } = ChromeUtils.import(
"resource://gre/modules/XPCOMUtils.jsm"
);
XPCOMUtils.defineLazyGlobalGetters(this, ["XMLHttpRequest"]);
XPCOMUtils.defineLazyServiceGetter(
this,
"ProxyService",
"@mozilla.org/network/protocol-proxy-service;1",
"nsIProtocolProxyService"
);
XPCOMUtils.defineLazyModuleGetters(this, {
ExtensionPreferencesManager:
"resource://gre/modules/ExtensionPreferencesManager.jsm",
});
XPCOMUtils.defineLazyServiceGetter(
this,
"CaptivePortalService",
"@mozilla.org/network/captive-portal-service;1",
"nsICaptivePortalService"
);
XPCOMUtils.defineLazyServiceGetter(
this,
"gNetworkLinkService",
"@mozilla.org/network/network-link-service;1",
"nsINetworkLinkService"
);
var EXPORTED_SYMBOLS = ["ServiceRequest"];
const PROXY_CONFIG_TYPES = [
"direct",
"manual",
"pac",
"unused", // nsIProtocolProxyService.idl skips index 3.
"wpad",
"system",
];
function recordEvent(service, source = {}) {
try {
Services.telemetry.setEventRecordingEnabled("service_request", true);
Services.telemetry.recordEvent(
"service_request",
"bypass",
"proxy_info",
service,
source
);
} catch (err) {
// If the telemetry throws just log the error so it doesn't break any
// functionality.
Cu.reportError(err);
}
}
// If proxy.settings is used to change the proxy, an extension will
// be "in control". This returns the id of that extension.
async function getControllingExtension() {
if (
!WebExtensionPolicy.getActiveExtensions().some(p =>
p.permissions.includes("proxy")
)
) {
return undefined;
}
// Is this proxied by an extension that set proxy prefs?
let setting = await ExtensionPreferencesManager.getSetting("proxy.settings");
return setting?.id;
}
async function getProxySource(proxyInfo) {
// sourceId is set when using proxy.onRequest
if (proxyInfo.sourceId) {
return {
source: proxyInfo.sourceId,
type: "api",
};
}
let type = PROXY_CONFIG_TYPES[ProxyService.proxyConfigType] || "unknown";
// If we have a policy it will have set the prefs.
if (
Services.policies &&
Services.policies.status === Services.policies.ACTIVE
) {
let policies = Services.policies.getActivePolicies()?.filter(p => p.Proxy);
if (policies?.length) {
return {
source: "policy",
type,
};
}
}
let source = await getControllingExtension();
return {
source: source || "prefs",
type,
};
}
/**
* ServiceRequest is intended to be a drop-in replacement for current users
* of XMLHttpRequest.
*
* @param {Object} options - Options for underlying XHR, e.g. { mozAnon: bool }
*/
class ServiceRequest extends XMLHttpRequest {
constructor(options) {
super(options);
}
/**
* Opens an XMLHttpRequest, and sets the NSS "beConservative" flag.
* Requests are always async.
*
* @param {String} method - HTTP method to use, e.g. "GET".
* @param {String} url - URL to open.
* @param {Object} options - Additional options { bypassProxy: bool }.
*/
open(method, url, options) {
super.open(method, url, true);
if (super.channel instanceof Ci.nsIHttpChannelInternal) {
let internal = super.channel.QueryInterface(Ci.nsIHttpChannelInternal);
// Disable cutting edge features, like TLS 1.3, where middleboxes might brick us
internal.beConservative = true;
// Disable use of proxy for this request if necessary.
if (options?.bypassProxy && this.bypassProxyEnabled) {
internal.bypassProxy = true;
}
}
}
get bypassProxy() {
let { channel } = this;
return channel.QueryInterface(Ci.nsIHttpChannelInternal).bypassProxy;
}
get isProxied() {
let { channel } = this;
return !!(channel instanceof Ci.nsIProxiedChannel && channel.proxyInfo);
}
get bypassProxyEnabled() {
return Services.prefs.getBoolPref("network.proxy.allow_bypass", true);
}
static async logProxySource(channel, service) {
if (channel.proxyInfo) {
let source = await getProxySource(channel.proxyInfo);
recordEvent(service, source);
}
}
static get isOffline() {
try {
return (
Services.io.offline ||
CaptivePortalService.state == CaptivePortalService.LOCKED_PORTAL ||
!gNetworkLinkService.isLinkUp
);
} catch (ex) {
// we cannot get state, assume the best.
}
return false;
}
}