diff --git a/README.md b/README.md index 80d21ed74..239797ba6 100644 --- a/README.md +++ b/README.md @@ -38,10 +38,14 @@ Learn more at [ipfs.io](https://ipfs.io) (it is really cool, we promise!) #### Automagical Detection of IPFS Resources - Requests for IPFS-like paths (`/ipfs/$cid` or `/ipns/$peerid_or_fqdn-with-dnslink`) are detected on any website. - If tested path is a valid IPFS address it gets redirected and loaded from a local gateway, e.g: - `https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR` - → `http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR` +Requests for IPFS-like paths (`/ipfs/$cid` or `/ipns/$peerid_or_fqdn-with-dnslink`) are detected on any website. +If tested path is a valid IPFS address it gets redirected and loaded from a local gateway, e.g: +> `https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR` +> → `http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR` + +It is possible to opt-out from redirect by +a) suspending extension via global toggle +b) including `x-ipfs-no-redirect` in the URL ([as a hash or query parameter](https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?x-ipfs-no-redirect#x-ipfs-no-redirect)). #### IPFS API as `window.ipfs` diff --git a/add-on/src/lib/ipfs-companion.js b/add-on/src/lib/ipfs-companion.js index c48a61ff4..2458c22a2 100644 --- a/add-on/src/lib/ipfs-companion.js +++ b/add-on/src/lib/ipfs-companion.js @@ -6,7 +6,7 @@ const { optionDefaults, storeMissingOptions } = require('./options') const { initState, offlinePeerCount } = require('./state') const { createIpfsPathValidator, urlAtPublicGw } = require('./ipfs-path') const createDnsLink = require('./dns-link') -const { createRequestModifier } = require('./ipfs-request') +const { createRequestModifier, redirectOptOutHint } = require('./ipfs-request') const { initIpfsClient, destroyIpfsClient } = require('./ipfs-client') const { createIpfsUrlProtocolHandler } = require('./ipfs-protocol') const createNotifier = require('./notifier') @@ -136,42 +136,11 @@ module.exports = async function init () { // =================================================================== function onBeforeSendHeaders (request) { - // skip websocket handshake (not supported by HTTP2IPFS gateways) - if (request.type === 'websocket') { - return - } - if (request.url.startsWith(state.apiURLString)) { - // There is a bug in go-ipfs related to keep-alive connections - // that results in partial response for ipfs.files.add - // mangled by error "http: invalid Read on closed Body" - // More info: https://github.com/ipfs/go-ipfs/issues/5168 - if (request.url.includes('api/v0/add')) { - for (let header of request.requestHeaders) { - if (header.name === 'Connection') { - console.log('[ipfs-companion] Executing "Connection: close" workaround for https://github.com/ipfs/go-ipfs/issues/5168') - header.value = 'close' - break - } - } - } - // For some reason js-ipfs-api sent requests with "Origin: null" under Chrome - // which produced '403 - Forbidden' error. - // This workaround removes bogus header from API requests - for (let i = 0; i < request.requestHeaders.length; i++) { - let header = request.requestHeaders[i] - if (header.name === 'Origin' && (header.value == null || header.value === 'null')) { - request.requestHeaders.splice(i, 1) - break - } - } - } - return { - requestHeaders: request.requestHeaders - } + return modifyRequest.onBeforeSendHeaders(request) } function onBeforeRequest (request) { - return modifyRequest(request) + return modifyRequest.onBeforeRequest(request) } // RUNTIME MESSAGES (one-off messaging) @@ -253,7 +222,9 @@ module.exports = async function init () { // asynchronous HTTP HEAD request preloads triggers content without downloading it return new Promise((resolve, reject) => { const http = new XMLHttpRequest() - http.open('HEAD', urlAtPublicGw(path, state.pubGwURLString)) + // Make sure preload request is excluded from global redirect + const preloadUrl = urlAtPublicGw(`${path}#${redirectOptOutHint}`, state.pubGwURLString) + http.open('HEAD', preloadUrl) http.onreadystatechange = function () { if (this.readyState === this.DONE) { console.info(`[ipfs-companion] preloadAtPublicGateway(${path}):`, this.statusText) diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js index ab3ed7ed9..51cbbc9d6 100644 --- a/add-on/src/lib/ipfs-request.js +++ b/add-on/src/lib/ipfs-request.js @@ -3,69 +3,127 @@ const IsIpfs = require('is-ipfs') const { urlAtPublicGw } = require('./ipfs-path') +const redirectOptOutHint = 'x-ipfs-no-redirect' function createRequestModifier (getState, dnsLink, ipfsPathValidator, runtime) { - return function modifyRequest (request) { - const state = getState() - - // skip requests to the custom gateway or API (otherwise we have too much recursion) - if (request.url.startsWith(state.gwURLString) || request.url.startsWith(state.apiURLString)) { - return - } - - // skip websocket handshake (not supported by HTTP2IPFS gateways) - if (request.type === 'websocket') { - return - } - - // skip all local requests - if (request.url.startsWith('http://127.0.0.1:') || request.url.startsWith('http://localhost:') || request.url.startsWith('http://[::1]:')) { - return - } - - // poor-mans protocol handlers - https://github.com/ipfs/ipfs-companion/issues/164#issuecomment-328374052 - if (state.catchUnhandledProtocols && mayContainUnhandledIpfsProtocol(request)) { - const fix = normalizedUnhandledIpfsProtocol(request, state.pubGwURLString) - if (fix) { - return fix + // Request modifier provides event listeners for the various stages of making an HTTP request + // API Details: https://developer.mozilla.org/en-US/Add-ons/WebExtensions/API/webRequest + return { + onBeforeRequest (request) { + // This event is triggered when a request is about to be made, and before headers are available. + // This is a good place to listen if you want to cancel or redirect the request. + const state = getState() + // early sanity checks + if (preNormalizationSkip(state, request)) { + return } - } - - // handler for protocol_handlers from manifest.json - if (redirectingProtocolRequest(request)) { - // fix path passed via custom protocol - const fix = normalizedRedirectingProtocolRequest(request, state.pubGwURLString) - if (fix) { - return fix + // poor-mans protocol handlers - https://github.com/ipfs/ipfs-companion/issues/164#issuecomment-328374052 + if (state.catchUnhandledProtocols && mayContainUnhandledIpfsProtocol(request)) { + const fix = normalizedUnhandledIpfsProtocol(request, state.pubGwURLString) + if (fix) { + return fix + } } - } - - // skip requests to the public gateway if embedded node is running (otherwise we have too much recursion) - if (state.ipfsNodeType === 'embedded' && request.url.startsWith(state.pubGwURLString)) { - return - // TODO: do not skip and redirect to `ipfs://` and `ipns://` if hasNativeProtocolHandler === true - } - - // handle redirects to custom gateway - if (state.active && state.redirect) { - // Ignore preload requests - if (request.method === 'HEAD') { + // handler for protocol_handlers from manifest.json + if (redirectingProtocolRequest(request)) { + // fix path passed via custom protocol + const fix = normalizedRedirectingProtocolRequest(request, state.pubGwURLString) + if (fix) { + return fix + } + } + // handle redirects to custom gateway + if (state.active && state.redirect) { + // late sanity checks + if (postNormalizationSkip(state, request)) { + return + } + // Detect valid /ipfs/ and /ipns/ on any site + if (ipfsPathValidator.publicIpfsOrIpnsResource(request.url) && isSafeToRedirect(request, runtime)) { + return redirectToGateway(request.url, state) + } + // Look for dnslink in TXT records of visited sites + if (state.dnslink && dnsLink.isDnslookupSafeForURL(request.url) && isSafeToRedirect(request, runtime)) { + return dnsLink.dnslinkLookupAndOptionalRedirect(request.url) + } + } + }, + + onBeforeSendHeaders (request) { + // This event is triggered before sending any HTTP data, but after all HTTP headers are available. + // This is a good place to listen if you want to modify HTTP request headers. + const state = getState() + // ignore websocket handshake (not supported by HTTP2IPFS gateways) + if (request.type === 'websocket') { return } - // Detect valid /ipfs/ and /ipns/ on any site - if (ipfsPathValidator.publicIpfsOrIpnsResource(request.url) && isSafeToRedirect(request, runtime)) { - return redirectToGateway(request.url, state) + if (request.url.startsWith(state.apiURLString)) { + // There is a bug in go-ipfs related to keep-alive connections + // that results in partial response for ipfs.files.add + // mangled by error "http: invalid Read on closed Body" + // More info: https://github.com/ipfs/go-ipfs/issues/5168 + if (request.url.includes('/api/v0/add')) { + for (let header of request.requestHeaders) { + if (header.name === 'Connection') { + console.log('[ipfs-companion] Executing "Connection: close" workaround for https://github.com/ipfs/go-ipfs/issues/5168') + header.value = 'close' + break + } + } + } + // For some reason js-ipfs-api sent requests with "Origin: null" under Chrome + // which produced '403 - Forbidden' error. + // This workaround removes bogus header from API requests + for (let i = 0; i < request.requestHeaders.length; i++) { + let header = request.requestHeaders[i] + if (header.name === 'Origin' && (header.value == null || header.value === 'null')) { + request.requestHeaders.splice(i, 1) + break + } + } } - // Look for dnslink in TXT records of visited sites - if (state.dnslink && dnsLink.isDnslookupSafeForURL(request.url)) { - return dnsLink.dnslinkLookupAndOptionalRedirect(request.url) + return { + requestHeaders: request.requestHeaders } } + } } +exports.redirectOptOutHint = redirectOptOutHint exports.createRequestModifier = createRequestModifier +// types of requests to be skipped before any normalization happens +function preNormalizationSkip (state, request) { + // skip requests to the custom gateway or API (otherwise we have too much recursion) + if (request.url.startsWith(state.gwURLString) || request.url.startsWith(state.apiURLString)) { + return true + } + + // skip websocket handshake (not supported by HTTP2IPFS gateways) + if (request.type === 'websocket') { + return true + } + + // skip all local requests + if (request.url.startsWith('http://127.0.0.1:') || request.url.startsWith('http://localhost:') || request.url.startsWith('http://[::1]:')) { + return true + } + + return false +} + +// types of requests to be skipped after expensive normalization happens +function postNormalizationSkip (state, request) { + // skip requests to the public gateway if embedded node is running (otherwise we have too much recursion) + if (state.ipfsNodeType === 'embedded' && request.url.startsWith(state.pubGwURLString)) { + return true + // TODO: do not skip and redirect to `ipfs://` and `ipns://` if hasNativeProtocolHandler === true + } + + return false +} + function redirectToGateway (requestUrl, state) { // TODO: redirect to `ipfs://` if hasNativeProtocolHandler === true const gwUrl = state.ipfsNodeType === 'embedded' ? state.pubGwURL : state.gwURL @@ -77,6 +135,11 @@ function redirectToGateway (requestUrl, state) { } function isSafeToRedirect (request, runtime) { + // Do not redirect if URL includes opt-out hint + if (request.url.includes('x-ipfs-no-redirect')) { + return false + } + // Ignore XHR requests for which redirect would fail due to CORS bug in Firefox // See: https://github.com/ipfs-shipyard/ipfs-companion/issues/436 // TODO: revisit when upstream bug is addressed diff --git a/test/functional/lib/ipfs-request.test.js b/test/functional/lib/ipfs-request.test.js index 47bb18fff..b9db670a3 100644 --- a/test/functional/lib/ipfs-request.test.js +++ b/test/functional/lib/ipfs-request.test.js @@ -6,7 +6,7 @@ const { URL } = require('url') const browser = require('sinon-chrome') const { initState } = require('../../../add-on/src/lib/state') const createRuntimeChecks = require('../../../add-on/src/lib/runtime-checks') -const { createRequestModifier } = require('../../../add-on/src/lib/ipfs-request') +const { createRequestModifier, redirectOptOutHint } = require('../../../add-on/src/lib/ipfs-request') const createDnsLink = require('../../../add-on/src/lib/dns-link') const { createIpfsPathValidator } = require('../../../add-on/src/lib/ipfs-path') const { optionDefaults } = require('../../../add-on/src/lib/options') @@ -17,7 +17,7 @@ const url2request = (string) => { const nodeTypes = ['external', 'embedded'] -describe('modifyRequest', function () { +describe('modifyRequest.onBeforeRequest', function () { let state, dnsLink, ipfsPathValidator, modifyRequest, runtime before(function () { @@ -48,7 +48,7 @@ describe('modifyRequest', function () { }) it('should be served from custom gateway if redirect is enabled', function () { const request = url2request('https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) }) describe('with embedded node', function () { @@ -57,7 +57,7 @@ describe('modifyRequest', function () { }) it('should be served from public gateway if redirect is enabled', function () { const request = url2request('https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) }) describe('with every node type', function () { @@ -69,22 +69,28 @@ describe('modifyRequest', function () { it(`should be left untouched if redirect is disabled (${nodeType} node)`, function () { state.redirect = false const request = url2request('https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it(`should be left untouched if redirect is enabled but global active flag is OFF (${nodeType} node)`, function () { state.active = false state.redirect = true const request = url2request('https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) + }) + it(`should be left untouched if URL includes opt-out hint (${nodeType} node)`, function () { + // A safe way for preloading data at arbitrary gateways - it should arrive at original destination + const request = url2request('https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?x-ipfs-no-redirect#hashTest') + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) + expect(redirectOptOutHint).to.equal('x-ipfs-no-redirect') }) it(`should be left untouched if CID is invalid (${nodeType} node)`, function () { const request = url2request('https://google.com/ipfs/notacid?argTest#hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) - it(`should be left untouched if its is a preload request (${nodeType} node)`, function () { - // HTTP HEAD is often used for preloading data at arbitrary gateways - it should arrive at original destination - const headRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest', method: 'HEAD'} - expect(modifyRequest(headRequest)).to.equal(undefined) + it(`should be left untouched if its is a HEAD preload with explicit opt-out in URL hash (${nodeType} node)`, function () { + // HTTP HEAD is a popular way for preloading data at arbitrary gateways, so we have a dedicated test to make sure it works as expected + const headRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#x-ipfs-no-redirect', method: 'HEAD'} + expect(modifyRequest.onBeforeRequest(headRequest)).to.equal(undefined) }) }) }) @@ -98,17 +104,17 @@ describe('modifyRequest', function () { it('should be served from custom gateway if fetched from the same origin and redirect is enabled in Firefox', function () { runtime.isFirefox = true const xhrRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest', type: 'xmlhttprequest', originUrl: 'https://google.com/'} - expect(modifyRequest(xhrRequest).redirectUrl).to.equal('http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(xhrRequest).redirectUrl).to.equal('http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should be served from custom gateway if fetched from the same origin and redirect is enabled in non-Firefox', function () { runtime.isFirefox = false const xhrRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest', type: 'xmlhttprequest', initiator: 'https://google.com/'} - expect(modifyRequest(xhrRequest).redirectUrl).to.equal('http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(xhrRequest).redirectUrl).to.equal('http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should be served from custom gateway if fetch is cross-origin and redirect is enabled in non-Firefox', function () { runtime.isFirefox = false const xhrRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest', type: 'xmlhttprequest', initiator: 'https://www.nasa.gov/foo.html'} - expect(modifyRequest(xhrRequest).redirectUrl).to.equal('http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(xhrRequest).redirectUrl).to.equal('http://127.0.0.1:8080/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) }) describe('with embedded node', function () { @@ -118,17 +124,17 @@ describe('modifyRequest', function () { it('should be served from public gateway if fetched from the same origin and redirect is enabled in Firefox', function () { runtime.isFirefox = true const xhrRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest', type: 'xmlhttprequest', originUrl: 'https://google.com/'} - expect(modifyRequest(xhrRequest).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(xhrRequest).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should be served from public gateway if fetched from the same origin and redirect is enabled in non-Firefox', function () { runtime.isFirefox = false const xhrRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest', type: 'xmlhttprequest', initiator: 'https://google.com/'} - expect(modifyRequest(xhrRequest).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(xhrRequest).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should be served from public gateway if fetch is cross-origin and redirect is enabled in non-Firefox', function () { runtime.isFirefox = false const xhrRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest', type: 'xmlhttprequest', initiator: 'https://www.nasa.gov/foo.html'} - expect(modifyRequest(xhrRequest).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(xhrRequest).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) }) describe('with every node type', function () { @@ -142,7 +148,7 @@ describe('modifyRequest', function () { // Context for XHR & bogus CORS problems in Firefox: https://github.com/ipfs-shipyard/ipfs-companion/issues/436 runtime.isFirefox = true const xhrRequest = {url: 'https://google.com/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest', type: 'xmlhttprequest', originUrl: 'https://www.nasa.gov/foo.html'} - expect(modifyRequest(xhrRequest)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(xhrRequest)).to.equal(undefined) }) }) }) @@ -160,12 +166,12 @@ describe('modifyRequest', function () { dnsLink.readDnslinkFromTxtRecord = sinon.stub().withArgs(fqdn).returns('/ipfs/Qmazvovg6Sic3m9igZMKoAPjkiVZsvbWWc8ZvgjjK1qMss') // pretend API is online and we can do dns lookups with it state.peerCount = 1 - expect(modifyRequest(request).redirectUrl).to.equal('http://127.0.0.1:8080/ipns/ipfs.git.sexy/index.html?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('http://127.0.0.1:8080/ipns/ipfs.git.sexy/index.html?argTest#hashTest') }) it('should be served from custom gateway if {path} starts with a valid CID', function () { const request = url2request('https://google.com/ipns/QmSWnBwMKZ28tcgMFdihD8XS7p6QzdRSGf71cCybaETSsU/index.html?argTest#hashTest') dnsLink.readDnslinkFromTxtRecord = sinon.stub().returns(false) - expect(modifyRequest(request).redirectUrl).to.equal('http://127.0.0.1:8080/ipns/QmSWnBwMKZ28tcgMFdihD8XS7p6QzdRSGf71cCybaETSsU/index.html?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('http://127.0.0.1:8080/ipns/QmSWnBwMKZ28tcgMFdihD8XS7p6QzdRSGf71cCybaETSsU/index.html?argTest#hashTest') }) }) @@ -180,12 +186,12 @@ describe('modifyRequest', function () { dnsLink.readDnslinkFromTxtRecord = sinon.stub().withArgs(fqdn).returns('/ipfs/Qmazvovg6Sic3m9igZMKoAPjkiVZsvbWWc8ZvgjjK1qMss') // pretend API is online and we can do dns lookups with it state.peerCount = 1 - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.git.sexy/index.html?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.git.sexy/index.html?argTest#hashTest') }) it('should be served from public gateway if {path} starts with a valid CID', function () { const request = url2request('https://google.com/ipns/QmSWnBwMKZ28tcgMFdihD8XS7p6QzdRSGf71cCybaETSsU/index.html?argTest#hashTest') dnsLink.readDnslinkFromTxtRecord = sinon.stub().returns(false) - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/QmSWnBwMKZ28tcgMFdihD8XS7p6QzdRSGf71cCybaETSsU/index.html?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/QmSWnBwMKZ28tcgMFdihD8XS7p6QzdRSGf71cCybaETSsU/index.html?argTest#hashTest') }) }) @@ -198,12 +204,12 @@ describe('modifyRequest', function () { it(`should be left untouched if redirect is disabled' (${nodeType} node)`, function () { state.redirect = false const request = url2request('https://google.com/ipns/ipfs.io?argTest#hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it(`should be left untouched if FQDN is not a real domain nor a valid CID (${nodeType} node)`, function () { const request = url2request('https://google.com/ipns/notafqdnorcid?argTest#hashTest') dnsLink.readDnslinkFromTxtRecord = sinon.stub().returns(false) - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it(`should be left untouched if {path} points to a FQDN but API is offline (${nodeType} node)`, function () { const request = url2request('https://google.com/ipns/ipfs.git.sexy/index.html?argTest#hashTest') @@ -212,7 +218,7 @@ describe('modifyRequest', function () { dnsLink.readDnslinkFromTxtRecord = sinon.stub().withArgs(fqdn).returns('/ipfs/Qmazvovg6Sic3m9igZMKoAPjkiVZsvbWWc8ZvgjjK1qMss') // pretend API is offline and we can do dns lookups with it state.peerCount = 0 - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) }) }) @@ -231,165 +237,165 @@ describe('modifyRequest', function () { // without web+ prefix (Firefox > 59: https://github.com/ipfs-shipyard/ipfs-companion/issues/164#issuecomment-356301174) it('should not be normalized if ipfs:/{CID}', function () { const request = url2request('https://ipfs.io/#redirect/ipfs%3A%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if ipfs://{CID}', function () { const request = url2request('https://ipfs.io/#redirect/ipfs%3A%2F%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should not be normalized if ipns:/{foo}', function () { const request = url2request('https://ipfs.io/#redirect/ipns%3A%2Fipfs.io%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if ipns://{foo}', function () { const request = url2request('https://ipfs.io/#redirect/ipns%3A%2F%2Fipfs.io%3FargTest%23hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.io?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.io?argTest#hashTest') }) it('should be normalized if dweb:/ipfs/{CID}', function () { const request = url2request('https://ipfs.io/#redirect/dweb%3A%2Fipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should not be normalized if dweb://ipfs/{CID}', function () { const request = url2request('https://ipfs.io/#redirect/dweb%3A%2F%2Fipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if dweb:/ipns/{foo}', function () { const request = url2request('https://ipfs.io/#redirect/dweb%3A%2Fipns/ipfs.io%3FargTest%23hashTest') - expect(modifyRequest(request).redirectUrl).equal('https://ipfs.io/ipns/ipfs.io?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).equal('https://ipfs.io/ipns/ipfs.io?argTest#hashTest') }) it('should not be normalized if dweb://ipns/{foo}', function () { const request = url2request('https://ipfs.io/#redirect/dweb%3A%2F%2Fipns/ipfs.io%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) // web+ prefixed versions (Firefox < 59 and Chrome) it('should not be normalized if web+ipfs:/{CID}', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bipfs%3A%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if web+ipfs://{CID}', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bipfs%3A%2F%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should not be normalized if web+ipns:/{foo}', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bipns%3A%2Fipfs.io%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if web+ipns://{foo}', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bipns%3A%2F%2Fipfs.io%3FargTest%23hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.io?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.io?argTest#hashTest') }) it('should be normalized if web+dweb:/ipfs/{CID}', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bdweb%3A%2Fipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should not be normalized if web+dweb://ipfs/{CID}', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bdweb%3A%2F%2Fipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if web+dweb:/ipns/{foo}', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bdweb%3A%2Fipns/ipfs.io%3FargTest%23hashTest') - expect(modifyRequest(request).redirectUrl).equal('https://ipfs.io/ipns/ipfs.io?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).equal('https://ipfs.io/ipns/ipfs.io?argTest#hashTest') }) it('should not be normalized if web+dweb://ipns/{foo}', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bdweb%3A%2F%2Fipns/ipfs.io%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should not be normalized if web+{foo}:/bar', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bfoo%3A%2Fbar%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should not be normalized if web+{foo}://bar', function () { const request = url2request('https://ipfs.io/#redirect/web%2Bfoo%3A%2F%2Fbar%3FargTest%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) }) describe('catching unhandled custom protocol request', function () { it('should not be normalized if ipfs:/{CID}', function () { const request = url2request('https://duckduckgo.com/?q=ipfs%3A%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest&foo=bar') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if ipfs://{CID}', function () { const request = url2request('https://duckduckgo.com/?q=ipfs%3A%2F%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest&foo=bar') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should not be normalized if ipns:/{foo}', function () { const request = url2request('https://duckduckgo.com/?q=ipns%3A%2Fipns.io%2Findex.html%3Farg%3Dfoo%26bar%3Dbuzz%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if ipns://{foo}', function () { const request = url2request('https://duckduckgo.com/?q=ipns%3A%2F%2Fipns.io%2Findex.html%3Farg%3Dfoo%26bar%3Dbuzz%23hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipns.io/index.html?arg=foo&bar=buzz#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipns.io/index.html?arg=foo&bar=buzz#hashTest') }) it('should be normalized if dweb:/ipfs/{CID}', function () { const request = url2request('https://duckduckgo.com/?q=dweb%3A%2Fipfs%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3Farg%3Dfoo%26bar%3Dbuzz%23hash&ia=software') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?arg=foo&bar=buzz#hash') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?arg=foo&bar=buzz#hash') }) it('should not be normalized if dweb://ipfs/{CID}', function () { const request = url2request('https://duckduckgo.com/?q=dweb%3A%2F%2Fipfs%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3Farg%3Dfoo%26bar%3Dbuzz%23hash&ia=software') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if dweb:/ipns/{foo}', function () { const request = url2request('https://duckduckgo.com/?q=dweb%3A%2Fipns%2Fipfs.io%2Findex.html%3Farg%3Dfoo%26bar%3Dbuzz%23hash&ia=web') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.io/index.html?arg=foo&bar=buzz#hash') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.io/index.html?arg=foo&bar=buzz#hash') }) it('should not be normalized if dweb://ipns/{foo}', function () { const request = url2request('https://duckduckgo.com/?q=dweb%3A%2F%2Fipns%2Fipfs.io%2Findex.html%3Farg%3Dfoo%26bar%3Dbuzz%23hash&ia=web') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should not be normalized if web+ipfs:/{CID}', function () { const request = url2request('https://duckduckgo.com/?q=web%2Bipfs%3A%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest&foo=bar') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if web+ipfs://{CID}', function () { const request = url2request('https://duckduckgo.com/?q=web%2Bipfs%3A%2F%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest&foo=bar') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?argTest#hashTest') }) it('should not be normalized if web+ipns:/{foo}', function () { const request = url2request('https://duckduckgo.com/?q=web%2Bipns%3A%2Fipns.io%2Findex.html%3Farg%3Dfoo%26bar%3Dbuzz%23hashTest') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if web+ipns://{foo}', function () { const request = url2request('https://duckduckgo.com/?q=web%2Bipns%3A%2F%2Fipns.io%2Findex.html%3Farg%3Dfoo%26bar%3Dbuzz%23hashTest') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipns.io/index.html?arg=foo&bar=buzz#hashTest') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipns.io/index.html?arg=foo&bar=buzz#hashTest') }) it('should be normalized if web+dweb:/ipfs/{CID}', function () { const request = url2request('https://duckduckgo.com/?q=web%2Bdweb%3A%2Fipfs%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3Farg%3Dfoo%26bar%3Dbuzz%23hash&ia=software') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?arg=foo&bar=buzz#hash') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR?arg=foo&bar=buzz#hash') }) it('should not be normalized if web+dweb://ipfs/{CID}', function () { const request = url2request('https://duckduckgo.com/?q=web%2Bdweb%3A%2F%2Fipfs%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3Farg%3Dfoo%26bar%3Dbuzz%23hash&ia=software') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be normalized if web+dweb:/ipns/{foo}', function () { const request = url2request('https://duckduckgo.com/?q=web%2Bdweb%3A%2Fipns%2Fipfs.io%2Findex.html%3Farg%3Dfoo%26bar%3Dbuzz%23hash&ia=web') - expect(modifyRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.io/index.html?arg=foo&bar=buzz#hash') + expect(modifyRequest.onBeforeRequest(request).redirectUrl).to.equal('https://ipfs.io/ipns/ipfs.io/index.html?arg=foo&bar=buzz#hash') }) it('should not be normalized if web+dweb://ipns/{foo}', function () { const request = url2request('https://duckduckgo.com/?q=web%2Bdweb%3A%2F%2Fipns%2Fipfs.io%2Findex.html%3Farg%3Dfoo%26bar%3Dbuzz%23hash&ia=web') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should not be normalized if disabled in Preferences', function () { state.catchUnhandledProtocols = false const request = url2request('https://duckduckgo.com/?q=ipfs%3A%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest&foo=bar') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should not be normalized if CID is invalid', function () { state.catchUnhandledProtocols = false const request = url2request('https://duckduckgo.com/?q=ipfs%3A%2FnotARealIpfsPathWithCid%3FargTest%23hashTest&foo=bar') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should not be normalized if presence of %3A%2F is a false-positive', function () { state.catchUnhandledProtocols = false const request = url2request('https://duckduckgo.com/?q=foo%3A%2Fbar%3FargTest%23hashTest&foo=bar') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should not be normalized if request.type != main_frame', function () { const mediaRequest = {url: 'https://duckduckgo.com/?q=ipfs%3A%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest&foo=bar', type: 'media'} - expect(modifyRequest(mediaRequest)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(mediaRequest)).to.equal(undefined) }) }) @@ -399,19 +405,19 @@ describe('modifyRequest', function () { it('should be left untouched if 127.0.0.1 is used', function () { state.redirect = true const request = url2request('http://127.0.0.1:5001/ipfs/QmPhnvn747LqwPYMJmQVorMaGbMSgA7mRRoyyZYz3DoZRQ/') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be left untouched if localhost is used', function () { // https://github.com/ipfs/ipfs-companion/issues/291 state.redirect = true const request = url2request('http://localhost:5001/ipfs/QmPhnvn747LqwPYMJmQVorMaGbMSgA7mRRoyyZYz3DoZRQ/') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) it('should be left untouched if [::1] is used', function () { // https://github.com/ipfs/ipfs-companion/issues/291 state.redirect = true const request = url2request('http://[::1]:5001/ipfs/QmPhnvn747LqwPYMJmQVorMaGbMSgA7mRRoyyZYz3DoZRQ/') - expect(modifyRequest(request)).to.equal(undefined) + expect(modifyRequest.onBeforeRequest(request)).to.equal(undefined) }) }) })