diff --git a/add-on/manifest.firefox.json b/add-on/manifest.firefox.json index 59a279c11..0865fb696 100644 --- a/add-on/manifest.firefox.json +++ b/add-on/manifest.firefox.json @@ -8,7 +8,7 @@ "applications": { "gecko": { "id": "ipfs-firefox-addon@lidel.org", - "strict_min_version": "58.0" + "strict_min_version": "59.0" } }, "page_action": { @@ -21,18 +21,33 @@ "protocol_handlers": [ { "protocol": "web+dweb", - "name": "IPFS Add-On: DWEB protocol handler", - "uriTemplate": "https://ipfs.io/%s" + "name": "IPFS Companion: DWEB Protocol Handler", + "uriTemplate": "https://ipfs.io/#redirect/%s" }, { "protocol": "web+ipns", - "name": "IPFS Add-On: IPNS protocol handler", - "uriTemplate": "https://ipfs.io/%s" + "name": "IPFS Companion: IPNS Protocol Handler", + "uriTemplate": "https://ipfs.io/#redirect/%s" }, { "protocol": "web+ipfs", - "name": "IPFS Add-On: IPFS protocol handler", - "uriTemplate": "https://ipfs.io/%s" + "name": "IPFS Companion: IPFS Protocol Handler", + "uriTemplate": "https://ipfs.io/#redirect/%s" + }, + { + "protocol": "dweb", + "name": "IPFS Companion: DWEB Protocol Handler", + "uriTemplate": "https://ipfs.io/#redirect/%s" + }, + { + "protocol": "ipns", + "name": "IPFS Companion: IPNS Protocol Handler", + "uriTemplate": "https://ipfs.io/#redirect/%s" + }, + { + "protocol": "ipfs", + "name": "IPFS Companion: IPFS Protocol Handler", + "uriTemplate": "https://ipfs.io/#redirect/%s" } ] } diff --git a/add-on/src/lib/ipfs-request.js b/add-on/src/lib/ipfs-request.js index 8846cfb31..319c028fa 100644 --- a/add-on/src/lib/ipfs-request.js +++ b/add-on/src/lib/ipfs-request.js @@ -27,9 +27,9 @@ function createRequestModifier (getState, dnsLink, ipfsPathValidator) { } // handler for protocol_handlers from manifest.json - if (webPlusProtocolRequest(request)) { + if (redirectingProtocolRequest(request)) { // fix path passed via custom protocol - const fix = normalizedWebPlusRequest(request, state.pubGwURLString) + const fix = normalizedRedirectingProtocolRequest(request, state.pubGwURLString) if (fix) { return fix } @@ -71,28 +71,40 @@ function redirectToGateway (requestUrl, state) { return { redirectUrl: url.toString() } } -// PROTOCOL HANDLERS: web+ in Firefox (protocol_handlers from manifest.json) +// REDIRECT-BASED PROTOCOL HANDLERS +// This API is available only Firefox (protocol_handlers from manifest.json) +// Background: https://github.com/ipfs-shipyard/ipfs-companion/issues/164#issuecomment-282513891 +// Notes on removal of web+ in Firefox 59: https://github.com/ipfs-shipyard/ipfs-companion/issues/164#issuecomment-355708883 // =================================================================== -const webPlusProtocolHandler = 'https://ipfs.io/web%2B' +// This is just a placeholder that we had to provide -- removed in normalizedRedirectingProtocolRequest() +const redirectingProtocolHandler = 'https://ipfs.io/#redirect/' -function webPlusProtocolRequest (request) { - return request.url.startsWith(webPlusProtocolHandler) +function redirectingProtocolRequest (request) { + return request.url.startsWith(redirectingProtocolHandler) } -function normalizedWebPlusRequest (request, pubGwUrl) { - const oldPath = decodeURIComponent(new URL(request.url).pathname) +function normalizedRedirectingProtocolRequest (request, pubGwUrl) { + const oldPath = decodeURIComponent(new URL(request.url).hash) let path = oldPath - path = path.replace(/^\/web\+dweb:\//i, '/') // web+dweb:/ipfs/Qm → /ipfs/Qm - path = path.replace(/^\/web\+ipfs:\/\//i, '/ipfs/') // web+ipfs://Qm → /ipfs/Qm - path = path.replace(/^\/web\+ipns:\/\//i, '/ipns/') // web+ipns://Qm → /ipns/Qm + // prefixed (Firefox < 59) + path = path.replace(/^#redirect\/web\+dweb:\//i, '/') // web+dweb:/ipfs/Qm → /ipfs/Qm + path = path.replace(/^#redirect\/web\+ipfs:\/\//i, '/ipfs/') // web+ipfs://Qm → /ipfs/Qm + path = path.replace(/^#redirect\/web\+ipns:\/\//i, '/ipns/') // web+ipns://Qm → /ipns/Qm + // without prefix (Firefox >= 59) + path = path.replace(/^#redirect\/dweb:\//i, '/') // dweb:/ipfs/Qm → /ipfs/Qm + path = path.replace(/^#redirect\/ipfs:\/\//i, '/ipfs/') // ipfs://Qm → /ipfs/Qm + path = path.replace(/^#redirect\/ipns:\/\//i, '/ipns/') // ipns://Qm → /ipns/Qm + // console.log(`oldPath: '${oldPath}' new: '${path}'`) if (oldPath !== path && IsIpfs.path(path)) { return { redirectUrl: urlAtPublicGw(path, pubGwUrl) } } return null } -// PROTOCOL HANDLERS: UNIVERSAL FALLBACK FOR UNHANDLED PROTOCOLS +// SEARCH-HIJACK HANDLERS: UNIVERSAL FALLBACK FOR UNHANDLED PROTOCOLS +// (Used in Chrome and other browsers that do not provide better alternatives) +// Background: https://github.com/ipfs-shipyard/ipfs-companion/issues/164#issuecomment-328374052 // =================================================================== const unhandledIpfsRE = /=(?:web%2B|)(ipfs(?=%3A%2F%2F)|ipns(?=%3A%2F%2F)|dweb(?=%3A%2Fip[f|n]s))%3A(?:%2F%2F|%2F)([^&]+)/ diff --git a/test/functional/lib/ipfs-request.test.js b/test/functional/lib/ipfs-request.test.js index 0435bba09..951b7a6d4 100644 --- a/test/functional/lib/ipfs-request.test.js +++ b/test/functional/lib/ipfs-request.test.js @@ -153,47 +153,83 @@ describe('modifyRequest', function () { state.ipfsNodeType = nodeType }) describe(`with ${nodeType} node:`, function () { - describe('request made via "web+" handler from manifest.json/protocol_handlers', function () { - // requests done with custom protocol handler are always normalized to public gateway + describe('request made via redirect-based protocol handler from manifest.json/protocol_handlers', function () { + // Note: requests done with custom protocol handler are always normalized to public gateway // (if external node is enabled, redirect will happen in next iteration of modifyRequest) + + // 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) + }) + 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') + }) + 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) + }) + 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') + }) + 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') + }) + 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) + }) + 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') + }) + 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) + }) + + // web+ prefixed versions (Firefox < 59 and Chrome) it('should not be normalized if web+ipfs:/{CID}', function () { - const request = url2request('https://ipfs.io/web%2Bipfs%3A%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') + const request = url2request('https://ipfs.io/#redirect/web%2Bipfs%3A%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') expect(modifyRequest(request)).to.equal(undefined) }) it('should be normalized if web+ipfs://{CID}', function () { - const request = url2request('https://ipfs.io/web%2Bipfs%3A%2F%2FQmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') + 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') }) it('should not be normalized if web+ipns:/{foo}', function () { - const request = url2request('https://ipfs.io/web%2Bipns%3A%2Fipfs.io%3FargTest%23hashTest') + const request = url2request('https://ipfs.io/#redirect/web%2Bipns%3A%2Fipfs.io%3FargTest%23hashTest') expect(modifyRequest(request)).to.equal(undefined) }) it('should be normalized if web+ipns://{foo}', function () { - const request = url2request('https://ipfs.io/web%2Bipns%3A%2F%2Fipfs.io%3FargTest%23hashTest') + 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') }) it('should be normalized if web+dweb:/ipfs/{CID}', function () { - const request = url2request('https://ipfs.io/web%2Bdweb%3A%2Fipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') + 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') }) it('should not be normalized if web+dweb://ipfs/{CID}', function () { - const request = url2request('https://ipfs.io/web%2Bdweb%3A%2F%2Fipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') + const request = url2request('https://ipfs.io/#redirect/web%2Bdweb%3A%2F%2Fipfs/QmbWqxBEKC3P8tqsKc98xmWNzrzDtRLMiMPL8wBuTGsMnR%3FargTest%23hashTest') expect(modifyRequest(request)).to.equal(undefined) }) it('should be normalized if web+dweb:/ipns/{foo}', function () { - const request = url2request('https://ipfs.io/web%2Bdweb%3A%2Fipns/ipfs.io%3FargTest%23hashTest') + 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') }) it('should not be normalized if web+dweb://ipns/{foo}', function () { - const request = url2request('https://ipfs.io/web%2Bdweb%3A%2F%2Fipns/ipfs.io%3FargTest%23hashTest') + const request = url2request('https://ipfs.io/#redirect/web%2Bdweb%3A%2F%2Fipns/ipfs.io%3FargTest%23hashTest') expect(modifyRequest(request)).to.equal(undefined) }) it('should not be normalized if web+{foo}:/bar', function () { - const request = url2request('https://ipfs.io/web%2Bfoo%3A%2Fbar%3FargTest%23hashTest') + const request = url2request('https://ipfs.io/#redirect/web%2Bfoo%3A%2Fbar%3FargTest%23hashTest') expect(modifyRequest(request)).to.equal(undefined) }) it('should not be normalized if web+{foo}://bar', function () { - const request = url2request('https://ipfs.io/web%2Bfoo%3A%2F%2Fbar%3FargTest%23hashTest') + const request = url2request('https://ipfs.io/#redirect/web%2Bfoo%3A%2F%2Fbar%3FargTest%23hashTest') expect(modifyRequest(request)).to.equal(undefined) }) }) diff --git a/yarn.lock b/yarn.lock index 3765c5ab6..c7603fecd 100644 --- a/yarn.lock +++ b/yarn.lock @@ -112,10 +112,14 @@ acorn@^4.0.3: version "4.0.13" resolved "https://registry.yarnpkg.com/acorn/-/acorn-4.0.13.tgz#105495ae5361d697bd195c825192e1ad7f253787" -acorn@^5.0.0, acorn@^5.2.1, acorn@^5.4.0, acorn@^5.4.1: +acorn@^5.0.0, acorn@^5.4.0, acorn@^5.4.1: version "5.4.1" resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.4.1.tgz#fdc58d9d17f4a4e98d102ded826a9b9759125102" +acorn@^5.2.1: + version "5.3.0" + resolved "https://registry.yarnpkg.com/acorn/-/acorn-5.3.0.tgz#7446d39459c54fb49a80e6ee6478149b940ec822" + adbkit-logcat@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/adbkit-logcat/-/adbkit-logcat-1.1.0.tgz#01d7f9b0cef9093a30bcb3b007efff301508962f" @@ -2343,8 +2347,8 @@ epimetheus@^1.0.55: prom-client "^10.0.0" errno@^0.1.3, errno@~0.1.1: - version "0.1.7" - resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.7.tgz#4684d71779ad39af177e3f007996f7c67c852618" + version "0.1.6" + resolved "https://registry.yarnpkg.com/errno/-/errno-0.1.6.tgz#c386ce8a6283f14fc09563b71560908c9bf53026" dependencies: prr "~1.0.1" @@ -9069,11 +9073,11 @@ trough@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/trough/-/trough-1.0.1.tgz#a9fd8b0394b0ae8fff82e0633a0a36ccad5b5f86" -tty-browserify@0.0.0: +tty-browserify@0.0.0, tty-browserify@~0.0.0: version "0.0.0" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.0.tgz#a157ba402da24e9bf957f9aa69d524eed42901a6" -tty-browserify@0.0.1, tty-browserify@~0.0.0: +tty-browserify@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/tty-browserify/-/tty-browserify-0.0.1.tgz#3f05251ee17904dfd0677546670db9651682b811"