-
Notifications
You must be signed in to change notification settings - Fork 325
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: *.localhost subdomain gateway support with http proxy #853
Merged
Merged
Changes from all commits
Commits
Show all changes
12 commits
Select commit
Hold shift + click to select a range
51671da
chore: web-ext update
lidel 4bf9d09
chore: [email protected]
lidel a736a5f
feat: subdomain gateway via HTTP proxy
lidel e5889f2
fix: toggle per website on <fqdn>.ipfs.localhost
lidel 4ce869e
fix: proxy only the gateway port
lidel 1acf899
fix: decode content paths with decodeURI
lidel 2a6c67e
fix: disable http-proxy when extension is inactive
lidel 207fd76
fix: async setApiStatusUpdateInterval
lidel 3e6708b
fix: mixed-content on HTTP pages
lidel eb66dfa
refactor: use HTTP proxy for subdomains only on Firefox
lidel 4a81bf1
refactor: remove code for "blessing" custom webui
lidel 5bce2a2
test: findDNSLinkHostname without match
lidel File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ build | |
npm-debug.log | ||
yarn-error.log | ||
crowdin.yml | ||
.connect-deps* | ||
.*~ | ||
add-on/dist | ||
add-on/webui/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,4 +1,17 @@ | ||
{ | ||
"minimum_chrome_version": "72", | ||
"permissions": [ | ||
"<all_urls>", | ||
"idle", | ||
"tabs", | ||
"notifications", | ||
"storage", | ||
"unlimitedStorage", | ||
"contextMenus", | ||
"clipboardWrite", | ||
"webNavigation", | ||
"webRequest", | ||
"webRequestBlocking" | ||
], | ||
"incognito": "not_allowed" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,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 |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
On Preferences screen the above labels look like this:
@jessicaschilling @autonome I suspect this could be phrased in a more human way, but this is the best I could do for next Beta release. Suggestions welcome before we ship to Stable.
This is a description of a toggle that lets people to switch to old path-based gateway on
127.0.0.1
if they choose to do so for some reason. (disabling it stops HTTP proxy and swaps Custom Gateway URL to use raw IP)