-
Notifications
You must be signed in to change notification settings - Fork 325
/
Copy pathhttp-proxy.js
155 lines (134 loc) · 5.95 KB
/
http-proxy.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
'use strict'
/* eslint-env browser, webextensions */
const browser = require('webextension-polyfill')
const { safeURL } = require('./options')
const debug = require('debug')
const log = debug('ipfs-companion:http-proxy')
log.error = debug('ipfs-companion:http-proxy:error')
// Preface:
//
// When go-ipfs runs on localhost, it exposes two types of gateway:
// 127.0.0.1:8080 - old school path gateway
// localhost:8080 - subdomain gateway supporting Origins like $cid.ipfs.localhost
// More: https://docs-beta.ipfs.io/how-to/address-ipfs-on-web/#subdomain-gateway
//
// In a web browser contexts we care about Origin per content root (CID)
// because entire web security model uses it as a basis for sandboxing and
// access controls:
// https://developer.mozilla.org/en-US/docs/Web/Security/Same-origin_policy
// registerSubdomainProxy is necessary wourkaround for supporting subdomains
// under 'localhost' (*.ipfs.localhost) because some operating systems do not
// resolve them to local IP and return NX error not found instead
//
// State in Q2 2020:
// - Chromium hardcodes `localhost` name to point at local IP and proxy is not
// really necessary. The code is here (inactivE) in case we need it in the future.
// - Firefox requires proxy to avoid DNS lookup, but there is an open issue
// that will remove that need at some point:
// https://bugzilla.mozilla.org/show_bug.cgi?id=1220810
async function registerSubdomainProxy (getState, runtime, notify) {
// At the moment only firefox requires proxy registration
if (!runtime.isFirefox) return
try {
const { active, useSubdomains, gwURLString } = getState()
const enable = active && useSubdomains
// HTTP Proxy feature is exposed on the gateway port
// Just ensure we use localhost IP to remove any dependency on DNS
const { hostname, port } = safeURL(gwURLString, { useLocalhostName: false })
// Firefox uses own APIs for selective proxying
if (runtime.isFirefox) {
return await registerSubdomainProxyFirefox(enable, hostname, port)
}
// At this point we would asume Chromium, but its not needed atm
// Uncomment below if ever needed (+ add 'proxy' permission to manifest.json)
// return await registerSubdomainProxyChromium(enable, hostname, port)
} catch (err) {
// registerSubdomainProxy is just a failsafe, not necessary in most cases,
// so we should not break init when it fails.
// For now we just log error and exit as NOOP
log.error('registerSubdomainProxy failed', err)
// Show pop-up only the first time, during init() when notify is passed
try {
if (notify) notify('notify_addonIssueTitle', 'notify_addonIssueMsg')
} catch (_) {
}
}
}
// storing listener for later
var onRequestProxyListener
// registerSubdomainProxyFirefox sets proxy using API available in Firefox
// https://developer.mozilla.org/en-US/docs/Mozilla/Add-ons/WebExtensions/API/proxy/onRequest
async function registerSubdomainProxyFirefox (enable, hostname, port) {
const { onRequest } = browser.proxy
// always remove the old listener (host and port could change)
const oldListener = onRequestProxyListener
if (oldListener && onRequest.hasListener(oldListener)) {
onRequest.removeListener(oldListener)
}
if (enable) {
// create new listener with the latest host:port note: the listener is
// handling requests made to all localhost ports (limitation of the API,
// port is ignored) that is why we manually check port inside of the listener
onRequestProxyListener = (request) => {
if (new URL(request.url).port === port) {
return { type: 'http', host: hostname, port }
}
return { type: 'direct' }
}
// register the listener
onRequest.addListener(onRequestProxyListener, {
urls: ['http://*.localhost/*'],
incognito: false
})
log(`enabled ${hostname}:${port} as HTTP proxy for *.localhost`)
return
}
// at this point we effectively disabled proxy
log('disabled HTTP proxy for *.localhost')
}
/*
* Chromium 80 does not need proxy, so below is not used.
* Uncomment below if ever needed (+ add 'proxy' permission to manifest.json)
// Helpers for converting callback chrome.* API to promises
const cb = (resolve, reject) => (result) => {
const err = chrome.runtime.lastError
if (err) return reject(err)
return resolve(result)
}
const get = async (opts) => new Promise((resolve, reject) => chrome.proxy.settings.get(opts, cb(resolve, reject)))
const set = async (opts) => new Promise((resolve, reject) => chrome.proxy.settings.set(opts, cb(resolve, reject)))
const clear = async (opts) => new Promise((resolve, reject) => chrome.proxy.settings.clear(opts, cb(resolve, reject)))
// registerSubdomainProxyChromium sets proxy using API available in Chromium
// https://developer.chrome.com/extensions/proxy
async function registerSubdomainProxyChromium (enable, hostname, port) {
const scope = 'regular_only'
// read current proxy settings
const settings = await get({ incognito: false })
// set or update, if enabled
if (enable) {
// PAC script enables selective routing to PROXY at host+port
// here, PROXY is the same as HTTP API endpoint
const pacConfig = {
mode: 'pac_script',
pacScript: {
data: 'function FindProxyForURL(url, host) {\n' +
` if (shExpMatch(host, '*.localhost:${port}'))\n` +
` return 'PROXY ${hostname}:${port}';\n` +
" return 'DIRECT';\n" +
'}'
}
}
await set({ value: pacConfig, scope })
log(`enabled ${hostname}:${port} as HTTP proxy for *.localhost`)
// log('updated chrome.proxy.settings', await get({ incognito: false }))
return
}
// else: remove any existing proxy settings
if (settings && settings.levelOfControl === 'controlled_by_this_extension') {
// remove any proxy settings ipfs-companion set up before
await clear({ scope })
log('disabled HTTP proxy for *.localhost')
}
}
*/
module.exports.registerSubdomainProxy = registerSubdomainProxy