mirror of
https://github.com/zero-peak/ZeroOmega.git
synced 2025-01-22 15:08:12 -05:00
Support browser.proxy.onRequest. Fix #1456.
This commit also refactors other implementations and moves them to dedicated files, using feature detection to select one on runtime.
This commit is contained in:
parent
7a6cc98ff7
commit
465c98f78a
@ -179,7 +179,10 @@ if chrome?.storage?.sync or browser?.storage?.sync
|
||||
sync.enabled = false
|
||||
sync.transformValue = OmegaTargetCurrent.Options.transformValueForSync
|
||||
|
||||
options = new OmegaTargetCurrent.Options(null, storage, state, Log, sync)
|
||||
proxyImpl = OmegaTargetCurrent.proxy.getProxyImpl(Log)
|
||||
state.set({proxyImplFeatures: proxyImpl.features})
|
||||
options = new OmegaTargetCurrent.Options(null, storage, state, Log, sync,
|
||||
proxyImpl)
|
||||
options.externalApi = new OmegaTargetCurrent.ExternalApi(options)
|
||||
options.externalApi.listen()
|
||||
|
||||
@ -218,7 +221,7 @@ options._inspect = new OmegaTargetCurrent.Inspect (url, tab) ->
|
||||
options.setProxyNotControllable(null)
|
||||
timeout = null
|
||||
|
||||
options.watchProxyChange (details) ->
|
||||
proxyImpl.watchProxyChange (details) ->
|
||||
return if options.externalApi.disabled
|
||||
return unless details
|
||||
notControllableBefore = options.proxyNotControllable()
|
||||
|
@ -7,6 +7,7 @@ module.exports =
|
||||
WebRequestMonitor: require('./web_request_monitor')
|
||||
Inspect: require('./inspect')
|
||||
Url: require('url')
|
||||
proxy: require('./proxy')
|
||||
|
||||
for name, value of require('omega-target')
|
||||
module.exports[name] ?= value
|
||||
|
@ -2,9 +2,7 @@ OmegaTarget = require('omega-target')
|
||||
OmegaPac = OmegaTarget.OmegaPac
|
||||
Promise = OmegaTarget.Promise
|
||||
querystring = require('querystring')
|
||||
chromeApiPromisify = require('./chrome_api').chromeApiPromisify
|
||||
parseExternalProfile = require('./parse_external_profile')
|
||||
ProxyAuth = require('./proxy_auth')
|
||||
WebRequestMonitor = require('./web_request_monitor')
|
||||
ChromePort = require('./chrome_port')
|
||||
fetchUrl = require('./fetch_url')
|
||||
@ -73,202 +71,6 @@ class ChromeOptions extends OmegaTarget.Options
|
||||
chrome.browserAction.setBadgeText?(text: '')
|
||||
return
|
||||
|
||||
_formatBypassItem: (condition) ->
|
||||
str = OmegaPac.Conditions.str(condition)
|
||||
i = str.indexOf(' ')
|
||||
return str.substr(i + 1)
|
||||
_fixedProfileConfig: (profile) ->
|
||||
config = {}
|
||||
config['mode'] = 'fixed_servers'
|
||||
rules = {}
|
||||
protocols = ['proxyForHttp', 'proxyForHttps', 'proxyForFtp']
|
||||
protocolProxySet = false
|
||||
for protocol in protocols when profile[protocol]?
|
||||
rules[protocol] = profile[protocol]
|
||||
protocolProxySet = true
|
||||
|
||||
if profile.fallbackProxy
|
||||
if profile.fallbackProxy.scheme == 'http'
|
||||
# Chromium does not allow HTTP proxies in 'fallbackProxy'.
|
||||
if not protocolProxySet
|
||||
# Use 'singleProxy' if no proxy is configured for other protocols.
|
||||
rules['singleProxy'] = profile.fallbackProxy
|
||||
else
|
||||
# Try to set the proxies of all possible protocols.
|
||||
for protocol in protocols
|
||||
rules[protocol] ?= JSON.parse(JSON.stringify(profile.fallbackProxy))
|
||||
else
|
||||
rules['fallbackProxy'] = profile.fallbackProxy
|
||||
else if not protocolProxySet
|
||||
config['mode'] = 'direct'
|
||||
|
||||
if config['mode'] != 'direct'
|
||||
rules['bypassList'] = bypassList = []
|
||||
for condition in profile.bypassList
|
||||
bypassList.push(@_formatBypassItem(condition))
|
||||
config['rules'] = rules
|
||||
return config
|
||||
|
||||
_proxyChangeWatchers: null
|
||||
_proxyChangeListener: null
|
||||
watchProxyChange: (callback) ->
|
||||
@_proxyChangeWatchers = []
|
||||
if not @_proxyChangeListener?
|
||||
@_proxyChangeListener = (details) =>
|
||||
for watcher in @_proxyChangeWatchers
|
||||
watcher(details)
|
||||
if chrome?.proxy?.settings?.onChange?
|
||||
chrome.proxy.settings.onChange.addListener @_proxyChangeListener
|
||||
@_proxyChangeWatchers.push(callback)
|
||||
applyProfileProxy: (profile, meta) ->
|
||||
if browser?.proxy?.register? or browser?.proxy?.registerProxyScript?
|
||||
return @applyProfileProxyScript(profile, meta)
|
||||
else if chrome?.proxy?.settings?
|
||||
return @applyProfileProxySettings(profile, meta)
|
||||
else
|
||||
ex = new Error('Your browser does not support proxy settings!')
|
||||
return Promise.reject ex
|
||||
applyProfileProxySettings: (profile, meta) ->
|
||||
meta ?= profile
|
||||
if profile.profileType == 'SystemProfile'
|
||||
# Clear proxy settings, returning proxy control to Chromium.
|
||||
return chromeApiPromisify(chrome.proxy.settings, 'clear')({}).then =>
|
||||
chrome.proxy.settings.get {}, @_proxyChangeListener
|
||||
return
|
||||
config = {}
|
||||
if profile.profileType == 'DirectProfile'
|
||||
config['mode'] = 'direct'
|
||||
else if profile.profileType == 'PacProfile'
|
||||
config['mode'] = 'pac_script'
|
||||
|
||||
config['pacScript'] =
|
||||
if !profile.pacScript || OmegaPac.Profiles.isFileUrl(profile.pacUrl)
|
||||
url: profile.pacUrl
|
||||
mandatory: true
|
||||
else
|
||||
data: OmegaPac.PacGenerator.ascii(profile.pacScript)
|
||||
mandatory: true
|
||||
else if profile.profileType == 'FixedProfile'
|
||||
config = @_fixedProfileConfig(profile)
|
||||
else
|
||||
config['mode'] = 'pac_script'
|
||||
config['pacScript'] =
|
||||
data: null
|
||||
mandatory: true
|
||||
setPacScript = @pacForProfile(profile).then (script) ->
|
||||
profileName = OmegaPac.PacGenerator.ascii(JSON.stringify(meta.name))
|
||||
profileName = profileName.replace(/\*/g, '\\u002a')
|
||||
profileName = profileName.replace(/\\/g, '\\u002f')
|
||||
prefix = "/*OmegaProfile*#{profileName}*#{meta.revision}*/"
|
||||
config['pacScript'].data = prefix + script
|
||||
return
|
||||
setPacScript ?= Promise.resolve()
|
||||
setPacScript.then(=>
|
||||
@_proxyAuth ?= new ProxyAuth(this)
|
||||
@_proxyAuth.listen()
|
||||
@_proxyAuth.setProxies(@_watchingProfiles)
|
||||
chromeApiPromisify(chrome.proxy.settings, 'set')({value: config})
|
||||
).then =>
|
||||
chrome.proxy.settings.get {}, @_proxyChangeListener
|
||||
return
|
||||
|
||||
_proxyScriptUrl: 'js/omega_webext_proxy_script.min.js'
|
||||
_proxyScriptDisabled: false
|
||||
applyProfileProxyScript: (profile, state) ->
|
||||
state = state ? {}
|
||||
state.currentProfileName = profile.name
|
||||
if profile.name == ''
|
||||
state.tempProfile = @_tempProfile
|
||||
if profile.profileType == 'SystemProfile'
|
||||
# MOZ: SystemProfile cannot be done now due to lack of "PASS" support.
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1319634
|
||||
# In the mean time, let's just unregister the script.
|
||||
if browser.proxy.unregister?
|
||||
browser.proxy.unregister()
|
||||
else
|
||||
# Some older browers may not ship with .unregister API.
|
||||
# In that case, let's just set an invalid script to unregister it.
|
||||
browser.proxy.registerProxyScript('js/omega_invalid_proxy_script.js')
|
||||
@_proxyScriptDisabled = true
|
||||
else
|
||||
@_proxyScriptState = state
|
||||
Promise.all([
|
||||
browser.runtime.getBrowserInfo(),
|
||||
@_initWebextProxyScript(),
|
||||
]).then ([info]) =>
|
||||
if info.vendor == 'Mozilla' and info.buildID < '20170918220054'
|
||||
# MOZ: Legacy proxy support expects PAC-like string return type.
|
||||
# TODO(catus): Remove support for string return type.
|
||||
@log.error(
|
||||
'Your browser is outdated! SOCKS5 DNS/Auth unsupported! ' +
|
||||
"Please update your browser ASAP! (Current Build #{info.buildID})")
|
||||
@_proxyScriptState.useLegacyStringReturn = true
|
||||
@_proxyScriptStateChanged()
|
||||
@_proxyAuth ?= new ProxyAuth(this)
|
||||
@_proxyAuth.listen()
|
||||
@_proxyAuth.setProxies(@_watchingProfiles)
|
||||
return Promise.resolve()
|
||||
|
||||
_proxyScriptInitialized: false
|
||||
_proxyScriptState: {}
|
||||
_initWebextProxyScript: ->
|
||||
if not @_proxyScriptInitialized
|
||||
browser.proxy.onProxyError.addListener (err) =>
|
||||
if err?.message?
|
||||
if err.message.indexOf('Invalid Proxy Rule: DIRECT') >= 0
|
||||
# DIRECT cannot be parsed in Mozilla earlier due to a bug. Even
|
||||
# though it throws, it actually falls back to direct connection
|
||||
# so it works.
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1355198
|
||||
return
|
||||
if err.message.indexOf('Return type must be a string') >= 0
|
||||
# MOZ: Legacy proxy support expects PAC-like string return type.
|
||||
# TODO(catus): Remove support for string return type.
|
||||
#
|
||||
@log.error(
|
||||
'Your browser is outdated! SOCKS5 DNS/Auth unsupported! ' +
|
||||
'Please update your browser ASAP!')
|
||||
@_proxyScriptState.useLegacyStringReturn = true
|
||||
@_proxyScriptStateChanged()
|
||||
return
|
||||
@log.error(err)
|
||||
browser.runtime.onMessage.addListener (message) =>
|
||||
return unless message.event == 'proxyScriptLog'
|
||||
if message.level == 'error'
|
||||
@log.error(message)
|
||||
else if message.level == 'warn'
|
||||
@log.error(message)
|
||||
else
|
||||
@log.log(message)
|
||||
|
||||
if not @_proxyScriptInitialized or @_proxyScriptDisabled
|
||||
promise = new Promise (resolve) ->
|
||||
onMessage = (message) ->
|
||||
return unless message.event == 'proxyScriptLoaded'
|
||||
resolve()
|
||||
browser.runtime.onMessage.removeListener onMessage
|
||||
return
|
||||
browser.runtime.onMessage.addListener onMessage
|
||||
# The API has been renamed to .register but for some old browsers' sake:
|
||||
if browser.proxy.register?
|
||||
browser.proxy.register(@_proxyScriptUrl)
|
||||
else
|
||||
browser.proxy.registerProxyScript(@_proxyScriptUrl)
|
||||
@_proxyScriptDisabled = false
|
||||
else
|
||||
promise = Promise.resolve()
|
||||
@_proxyScriptInitialized = true
|
||||
return promise
|
||||
|
||||
_proxyScriptStateChanged: ->
|
||||
browser.runtime.sendMessage({
|
||||
event: 'proxyScriptStateChanged'
|
||||
state: @_proxyScriptState
|
||||
options: @_options
|
||||
}, {
|
||||
toProxyScript: true
|
||||
})
|
||||
|
||||
_quickSwitchInit: false
|
||||
_quickSwitchHandlerReady: false
|
||||
_quickSwitchCanEnable: false
|
||||
@ -478,4 +280,3 @@ class ChromeOptions extends OmegaTarget.Options
|
||||
}
|
||||
|
||||
module.exports = ChromeOptions
|
||||
|
||||
|
@ -0,0 +1,10 @@
|
||||
ListenerProxyImpl = require('./proxy_impl_listener')
|
||||
SettingsProxyImpl = require('./proxy_impl_settings')
|
||||
ScriptProxyImpl = require('./proxy_impl_script')
|
||||
|
||||
exports.proxyImpls = [ListenerProxyImpl, ScriptProxyImpl, SettingsProxyImpl]
|
||||
exports.getProxyImpl = (log) ->
|
||||
for Impl in exports.proxyImpls
|
||||
if Impl.isSupported()
|
||||
return new Impl(log)
|
||||
throw new Error('Your browser does not support proxy settings!')
|
@ -3,18 +3,18 @@ OmegaPac = OmegaTarget.OmegaPac
|
||||
Promise = OmegaTarget.Promise
|
||||
|
||||
module.exports = class ProxyAuth
|
||||
constructor: (options) ->
|
||||
constructor: (log) ->
|
||||
@_requests = {}
|
||||
@options = options
|
||||
@log = log
|
||||
|
||||
listening: false
|
||||
listen: ->
|
||||
return if @listening
|
||||
if not chrome.webRequest
|
||||
@options.log.error('Proxy auth disabled! No webRequest permission.')
|
||||
@log.error('Proxy auth disabled! No webRequest permission.')
|
||||
return
|
||||
if not chrome.webRequest.onAuthRequired
|
||||
@options.log.error('Proxy auth disabled! onAuthRequired not available.')
|
||||
@log.error('Proxy auth disabled! onAuthRequired not available.')
|
||||
return
|
||||
chrome.webRequest.onAuthRequired.addListener(
|
||||
@authHandler.bind(this)
|
||||
@ -35,9 +35,7 @@ module.exports = class ProxyAuth
|
||||
setProxies: (profiles) ->
|
||||
@_proxies = {}
|
||||
@_fallbacks = []
|
||||
processProfile = (profile) =>
|
||||
profile = @options.profile(profile)
|
||||
return unless profile?.auth
|
||||
for profile in profiles when profile.auth
|
||||
for scheme in OmegaPac.Profiles.schemes when profile[scheme.prop]
|
||||
auth = profile.auth?[scheme.prop]
|
||||
continue unless auth
|
||||
@ -59,13 +57,6 @@ module.exports = class ProxyAuth
|
||||
name: profile.name + '.' + 'all'
|
||||
})
|
||||
|
||||
if Array.isArray(profiles)
|
||||
for profile in profiles
|
||||
processProfile(profile)
|
||||
else
|
||||
for _, profile of profiles
|
||||
processProfile(profile)
|
||||
|
||||
_proxies: {}
|
||||
_fallbacks: []
|
||||
_requests: null
|
||||
@ -86,7 +77,7 @@ module.exports = class ProxyAuth
|
||||
proxy = list[req.authTries]
|
||||
else
|
||||
proxy = @_fallbacks[req.authTries - listLen]
|
||||
@options.log.log('ProxyAuth', key, req.authTries, proxy?.name)
|
||||
@log.log('ProxyAuth', key, req.authTries, proxy?.name)
|
||||
|
||||
return {} unless proxy?
|
||||
req.authTries++
|
@ -0,0 +1,41 @@
|
||||
OmegaTarget = require('omega-target')
|
||||
Promise = OmegaTarget.Promise
|
||||
ProxyAuth = require('./proxy_auth')
|
||||
|
||||
class ProxyImpl
|
||||
constructor: (log) ->
|
||||
@log = log
|
||||
@isSupported: -> false
|
||||
applyProfile: (profile, meta) -> Promise.reject()
|
||||
watchProxyChange: (callback) -> null
|
||||
_profileNotFound: (name) ->
|
||||
@log.error("Profile #{name} not found! Things may go very, very wrong.")
|
||||
return OmegaPac.Profiles.create({
|
||||
name: name
|
||||
profileType: 'VirtualProfile'
|
||||
defaultProfileName: 'direct'
|
||||
})
|
||||
setProxyAuth: (profile, options) ->
|
||||
return Promise.try(=>
|
||||
@_proxyAuth ?= new ProxyAuth(@log)
|
||||
@_proxyAuth.listen()
|
||||
referenced_profiles = []
|
||||
ref_set = OmegaPac.Profiles.allReferenceSet(profile,
|
||||
options, profileNotFound: @_profileNotFound.bind(this))
|
||||
for own _, name of ref_set
|
||||
referenced_profiles.push(OmegaPac.Profiles.byName(name, options))
|
||||
@_proxyAuth.setProxies(referenced_profiles)
|
||||
)
|
||||
getProfilePacScript: (profile, meta, options) ->
|
||||
meta ?= profile
|
||||
ast = OmegaPac.PacGenerator.script(options, profile,
|
||||
profileNotFound: @_profileNotFound.bind(this))
|
||||
ast = OmegaPac.PacGenerator.compress(ast)
|
||||
script = OmegaPac.PacGenerator.ascii(ast.print_to_string())
|
||||
profileName = OmegaPac.PacGenerator.ascii(JSON.stringify(meta.name))
|
||||
profileName = profileName.replace(/\*/g, '\\u002a')
|
||||
profileName = profileName.replace(/\\/g, '\\u002f')
|
||||
prefix = "/*OmegaProfile*#{profileName}*#{meta.revision}*/"
|
||||
return prefix + script
|
||||
|
||||
module.exports = ProxyImpl
|
@ -0,0 +1,81 @@
|
||||
OmegaTarget = require('omega-target')
|
||||
# The browser only accepts native promises as onRequest return values.
|
||||
# DO NOT USE Bluebird Promises here!
|
||||
NativePromise = Promise
|
||||
ProxyImpl = require('./proxy_impl')
|
||||
|
||||
class ListenerProxyImpl extends ProxyImpl
|
||||
@isSupported: -> browser?.proxy?.onRequest?
|
||||
features: ['fullUrl', 'socks5Auth']
|
||||
constructor: ->
|
||||
super(arguments...)
|
||||
@_optionsReady = new NativePromise (resolve) =>
|
||||
@_optionsReadyCallback = resolve
|
||||
# We want to register listeners early so that it can start blocking requests
|
||||
# when starting the browser & extension, returning correct results later.
|
||||
@_initRequestListeners()
|
||||
_initRequestListeners: ->
|
||||
browser.proxy.onRequest.addListener(@onRequest.bind(this),
|
||||
{urls: ["<all_urls>"]})
|
||||
browser.proxy.onError.addListener(@onError.bind(this))
|
||||
watchProxyChange: (callback) -> null
|
||||
applyProfile: (profile, state, options) ->
|
||||
@_options = options
|
||||
@_profile = profile
|
||||
@_optionsReadyCallback?()
|
||||
@_optionsReadyCallback = null
|
||||
return @setProxyAuth(profile, options)
|
||||
onRequest: (requestDetails) ->
|
||||
# The browser only recognizes native promises return values, not Bluebird.
|
||||
return NativePromise.resolve(@_optionsReady.then(=>
|
||||
request = OmegaPac.Conditions.requestFromUrl(requestDetails.url)
|
||||
profile = @_profile
|
||||
while profile
|
||||
result = OmegaPac.Profiles.match(profile, request)
|
||||
if not result
|
||||
switch profile.profileType
|
||||
when 'DirectProfile'
|
||||
return {type: 'direct'}
|
||||
when 'SystemProfile'
|
||||
# Returning undefined means using the default proxy from previous.
|
||||
# https://hg.mozilla.org/mozilla-central/rev/9f0ee2f582a2#l1.337
|
||||
return undefined
|
||||
else
|
||||
throw new Error('Unsupported profile: ' + profile.profileType)
|
||||
if Array.isArray(result)
|
||||
proxy = result[2]
|
||||
auth = result[3]
|
||||
return @proxyInfo(proxy, auth) if proxy
|
||||
next = result[0]
|
||||
else if result.profileName
|
||||
next = OmegaPac.Profiles.nameAsKey(result.profileName)
|
||||
else
|
||||
break
|
||||
profile = OmegaPac.Profiles.byKey(next, @_options)
|
||||
|
||||
throw new Error('Profile not found: ' + next)
|
||||
))
|
||||
onError: (error) ->
|
||||
@log.error(error)
|
||||
proxyInfo: (proxy, auth) ->
|
||||
proxyInfo =
|
||||
type: proxy.scheme
|
||||
host: proxy.host
|
||||
port: proxy.port
|
||||
if proxyInfo.type == 'socks5'
|
||||
# MOZ: SOCKS5 proxies should be specified as "type": "socks".
|
||||
# https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/proxy/ProxyInfo
|
||||
proxyInfo.type = 'socks'
|
||||
if auth
|
||||
# Username & password here are only available for SOCKS5.
|
||||
# https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/proxy/ProxyInfo
|
||||
# HTTP proxy auth must be handled via webRequest.onAuthRequired.
|
||||
proxyInfo.username = auth.username
|
||||
proxyInfo.password = auth.password
|
||||
if proxyInfo.type == 'socks' or proxyInfo.type == 'socks4'
|
||||
# Enable SOCKS remote DNS.
|
||||
# TODO(catus): Maybe allow the users to configure this?
|
||||
proxyInfo.proxyDNS = true
|
||||
return [proxyInfo]
|
||||
|
||||
module.exports = ListenerProxyImpl
|
@ -0,0 +1,107 @@
|
||||
OmegaTarget = require('omega-target')
|
||||
Promise = OmegaTarget.Promise
|
||||
ProxyImpl = require('./proxy_impl')
|
||||
|
||||
class ScriptProxyImpl extends ProxyImpl
|
||||
@isSupported: ->
|
||||
return browser?.proxy?.register? or browser?.proxy?.registerProxyScript?
|
||||
features: ['socks5Auth']
|
||||
_proxyScriptUrl: 'js/omega_webext_proxy_script.min.js'
|
||||
_proxyScriptDisabled: false
|
||||
_proxyScriptInitialized: false
|
||||
_proxyScriptState: {}
|
||||
watchProxyChange: (callback) -> null
|
||||
applyProfile: (profile, state, options) ->
|
||||
@log.error(
|
||||
'Your browser is outdated! Full-URL based matching, etc. unsupported! ' +
|
||||
"Please update your browser ASAP!")
|
||||
state = state ? {}
|
||||
@_options = options
|
||||
state.currentProfileName = profile.name
|
||||
if profile.name == ''
|
||||
state.tempProfile = profile
|
||||
if profile.profileType == 'SystemProfile'
|
||||
# MOZ: SystemProfile cannot be done now due to lack of "PASS" support.
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1319634
|
||||
# In the mean time, let's just unregister the script.
|
||||
if browser.proxy.unregister?
|
||||
browser.proxy.unregister()
|
||||
else
|
||||
# Some older browers may not ship with .unregister API.
|
||||
# In that case, let's just set an invalid script to unregister it.
|
||||
browser.proxy.registerProxyScript('js/omega_invalid_proxy_script.js')
|
||||
@_proxyScriptDisabled = true
|
||||
else
|
||||
@_proxyScriptState = state
|
||||
Promise.all([
|
||||
browser.runtime.getBrowserInfo(),
|
||||
@_initWebextProxyScript(),
|
||||
]).then ([info]) =>
|
||||
if info.vendor == 'Mozilla' and info.buildID < '20170918220054'
|
||||
# MOZ: Legacy proxy support expects PAC-like string return type.
|
||||
# TODO(catus): Remove support for string return type.
|
||||
@log.error(
|
||||
'Your browser is outdated! SOCKS5 DNS/Auth unsupported! ' +
|
||||
"Please update your browser ASAP! (Current Build #{info.buildID})")
|
||||
@_proxyScriptState.useLegacyStringReturn = true
|
||||
@_proxyScriptStateChanged()
|
||||
return @setProxyAuth(profile, options)
|
||||
_initWebextProxyScript: ->
|
||||
if not @_proxyScriptInitialized
|
||||
browser.proxy.onProxyError.addListener (err) =>
|
||||
if err?.message?
|
||||
if err.message.indexOf('Invalid Proxy Rule: DIRECT') >= 0
|
||||
# DIRECT cannot be parsed in Mozilla earlier due to a bug. Even
|
||||
# though it throws, it actually falls back to direct connection
|
||||
# so it works.
|
||||
# https://bugzilla.mozilla.org/show_bug.cgi?id=1355198
|
||||
return
|
||||
if err.message.indexOf('Return type must be a string') >= 0
|
||||
# MOZ: Legacy proxy support expects PAC-like string return type.
|
||||
# TODO(catus): Remove support for string return type.
|
||||
#
|
||||
@log.error(
|
||||
'Your browser is outdated! SOCKS5 DNS/Auth unsupported! ' +
|
||||
'Please update your browser ASAP!')
|
||||
@_proxyScriptState.useLegacyStringReturn = true
|
||||
@_proxyScriptStateChanged()
|
||||
return
|
||||
@log.error(err)
|
||||
browser.runtime.onMessage.addListener (message) =>
|
||||
return unless message.event == 'proxyScriptLog'
|
||||
if message.level == 'error'
|
||||
@log.error(message)
|
||||
else if message.level == 'warn'
|
||||
@log.error(message)
|
||||
else
|
||||
@log.log(message)
|
||||
|
||||
if not @_proxyScriptInitialized or @_proxyScriptDisabled
|
||||
promise = new Promise (resolve) ->
|
||||
onMessage = (message) ->
|
||||
return unless message.event == 'proxyScriptLoaded'
|
||||
resolve()
|
||||
browser.runtime.onMessage.removeListener onMessage
|
||||
return
|
||||
browser.runtime.onMessage.addListener onMessage
|
||||
# The API has been renamed to .register but for some old browsers' sake:
|
||||
if browser.proxy.register?
|
||||
browser.proxy.register(@_proxyScriptUrl)
|
||||
else
|
||||
browser.proxy.registerProxyScript(@_proxyScriptUrl)
|
||||
@_proxyScriptDisabled = false
|
||||
else
|
||||
promise = Promise.resolve()
|
||||
@_proxyScriptInitialized = true
|
||||
return promise
|
||||
|
||||
_proxyScriptStateChanged: ->
|
||||
browser.runtime.sendMessage({
|
||||
event: 'proxyScriptStateChanged'
|
||||
state: @_proxyScriptState
|
||||
options: @_options
|
||||
}, {
|
||||
toProxyScript: true
|
||||
})
|
||||
|
||||
module.exports = ScriptProxyImpl
|
@ -0,0 +1,90 @@
|
||||
sOmegaTarget = require('omega-target')
|
||||
Promise = OmegaTarget.Promise
|
||||
chromeApiPromisify = require('../chrome_api').chromeApiPromisify
|
||||
ProxyImpl = require('./proxy_impl')
|
||||
|
||||
class SettingsProxyImpl extends ProxyImpl
|
||||
@isSupported: -> chrome?.proxy?.settings?
|
||||
features: ['fullUrlHttp', 'pacScript', 'watchProxyChange']
|
||||
applyProfile: (profile, meta, options) ->
|
||||
meta ?= profile
|
||||
if profile.profileType == 'SystemProfile'
|
||||
# Clear proxy settings, returning proxy control to Chromium.
|
||||
return chromeApiPromisify(chrome.proxy.settings, 'clear')({}).then =>
|
||||
chrome.proxy.settings.get {}, @_proxyChangeListener
|
||||
return
|
||||
config = {}
|
||||
if profile.profileType == 'DirectProfile'
|
||||
config['mode'] = 'direct'
|
||||
else if profile.profileType == 'PacProfile'
|
||||
config['mode'] = 'pac_script'
|
||||
|
||||
config['pacScript'] =
|
||||
if !profile.pacScript || OmegaPac.Profiles.isFileUrl(profile.pacUrl)
|
||||
url: profile.pacUrl
|
||||
mandatory: true
|
||||
else
|
||||
data: OmegaPac.PacGenerator.ascii(profile.pacScript)
|
||||
mandatory: true
|
||||
else if profile.profileType == 'FixedProfile'
|
||||
config = @_fixedProfileConfig(profile)
|
||||
else
|
||||
config['mode'] = 'pac_script'
|
||||
config['pacScript'] =
|
||||
mandatory: true
|
||||
data: @getProfilePacScript(profile, meta, options)
|
||||
return @setProxyAuth(profile, options).then(->
|
||||
return chromeApiPromisify(chrome.proxy.settings, 'set')({value: config})
|
||||
).then(=>
|
||||
chrome.proxy.settings.get {}, @_proxyChangeListener
|
||||
return
|
||||
)
|
||||
_fixedProfileConfig: (profile) ->
|
||||
config = {}
|
||||
config['mode'] = 'fixed_servers'
|
||||
rules = {}
|
||||
protocols = ['proxyForHttp', 'proxyForHttps', 'proxyForFtp']
|
||||
protocolProxySet = false
|
||||
for protocol in protocols when profile[protocol]?
|
||||
rules[protocol] = profile[protocol]
|
||||
protocolProxySet = true
|
||||
|
||||
if profile.fallbackProxy
|
||||
if profile.fallbackProxy.scheme == 'http'
|
||||
# Chromium does not allow HTTP proxies in 'fallbackProxy'.
|
||||
if not protocolProxySet
|
||||
# Use 'singleProxy' if no proxy is configured for other protocols.
|
||||
rules['singleProxy'] = profile.fallbackProxy
|
||||
else
|
||||
# Try to set the proxies of all possible protocols.
|
||||
for protocol in protocols
|
||||
rules[protocol] ?= JSON.parse(JSON.stringify(profile.fallbackProxy))
|
||||
else
|
||||
rules['fallbackProxy'] = profile.fallbackProxy
|
||||
else if not protocolProxySet
|
||||
config['mode'] = 'direct'
|
||||
|
||||
if config['mode'] != 'direct'
|
||||
rules['bypassList'] = bypassList = []
|
||||
for condition in profile.bypassList
|
||||
bypassList.push(@_formatBypassItem(condition))
|
||||
config['rules'] = rules
|
||||
return config
|
||||
_formatBypassItem: (condition) ->
|
||||
str = OmegaPac.Conditions.str(condition)
|
||||
i = str.indexOf(' ')
|
||||
return str.substr(i + 1)
|
||||
|
||||
_proxyChangeWatchers: null
|
||||
_proxyChangeListener: (details) ->
|
||||
for watcher in (@_proxyChangeWatchers ? [])
|
||||
watcher(details)
|
||||
watchProxyChange: (callback) ->
|
||||
if not @_proxyChangeWatchers?
|
||||
@_proxyChangeWatchers = []
|
||||
if chrome?.proxy?.settings?.onChange?
|
||||
chrome.proxy.settings.onChange.addListener @_proxyChangeListener
|
||||
@_proxyChangeWatchers.push(callback)
|
||||
return
|
||||
|
||||
module.exports = SettingsProxyImpl
|
@ -54,7 +54,7 @@ class Options
|
||||
value = profile
|
||||
return value
|
||||
|
||||
constructor: (options, @_storage, @_state, @log, @sync) ->
|
||||
constructor: (options, @_storage, @_state, @log, @sync, @proxyImpl) ->
|
||||
@_options = {}
|
||||
@_tempProfileRules = {}
|
||||
@_tempProfileRulesByProfile = {}
|
||||
@ -566,9 +566,10 @@ class Options
|
||||
|
||||
@_watchingProfiles = OmegaPac.Profiles.allReferenceSet(@_tempProfile,
|
||||
@_options, profileNotFound: @_profileNotFound.bind(this))
|
||||
applyProxy = @applyProfileProxy(@_tempProfile, profile)
|
||||
|
||||
applyProxy = @proxyImpl.applyProfile(@_tempProfile, profile, @_options)
|
||||
else
|
||||
applyProxy = @applyProfileProxy(profile)
|
||||
applyProxy = @proxyImpl.applyProfile(profile, profile, @_options)
|
||||
|
||||
return applyProxy if options? and options.update == false
|
||||
|
||||
@ -598,16 +599,6 @@ class Options
|
||||
###
|
||||
isSystem: -> @_isSystem
|
||||
|
||||
###*
|
||||
# Set proxy settings based on the given profile.
|
||||
# In base class, this method is not implemented and will always reject.
|
||||
# @param {{}} profile The profile to apply
|
||||
# @param {{}=profile} meta The metadata of the profile, like name and revision
|
||||
# @returns {Promise} A promise which is fulfilled when the proxy is set.
|
||||
###
|
||||
applyProfileProxy: (profile, meta) ->
|
||||
Promise.reject new Error('not implemented')
|
||||
|
||||
###*
|
||||
# Called when current profile has changed.
|
||||
# In base class, this method is not implemented and will not do anything.
|
||||
|
Loading…
Reference in New Issue
Block a user