From aabb7848e2bcea4a750eccd76803aae5eed03fbf Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 28 Aug 2019 00:45:46 +0200 Subject: [PATCH 1/4] fix: tests of addFromURL in browsers Old version was Node-centric and did not work in web browser contexts. This change: - moves the HTTP server to separate file to enable spawning it from aegir hook (see .aegir.js/hooks/browser/pre|post in js-ipfs) - removes overhead of starting and stopping HTTP servers multiple times - simplifies test code License: MIT Signed-off-by: Marcin Rataj --- src/files-regular/add-from-url.js | 329 ++++++++++-------------------- src/files-regular/utils.js | 4 - src/utils/echo-http-server.js | 88 ++++++++ 3 files changed, 197 insertions(+), 224 deletions(-) create mode 100644 src/utils/echo-http-server.js diff --git a/src/files-regular/add-from-url.js b/src/files-regular/add-from-url.js index 47b5b62f5..34b8de18f 100644 --- a/src/files-regular/add-from-url.js +++ b/src/files-regular/add-from-url.js @@ -1,13 +1,13 @@ /* eslint-env mocha */ 'use strict' -const { fixtures } = require('./utils') const { getDescribe, getIt, expect } = require('../utils/mocha') -const http = require('http') -const https = require('https') -const each = require('async/each') -const waterfall = require('async/waterfall') const parallel = require('async/parallel') +const EchoHttpServer = require('../utils/echo-http-server') +const { isBrowser, isWebWorker } = require('ipfs-utils/src/env') + +const httpServer = EchoHttpServer.createServer() +const httpsServer = EchoHttpServer.createServer({ secure: true }) module.exports = (createCommon, options) => { const describe = getDescribe(options) @@ -23,274 +23,163 @@ module.exports = (createCommon, options) => { // CI takes longer to instantiate the daemon, so we need to increase the // timeout for the before step this.timeout(60 * 1000) - - common.setup((err, factory) => { - expect(err).to.not.exist() - - factory.spawnNode((err, node) => { - expect(err).to.not.exist() - ipfs = node - done() - }) - }) - }) - - after((done) => common.teardown(done)) - - let testServers = [] - - const sslOpts = fixtures.sslOpts - - const startTestServer = (handler, opts, cb) => { - if (typeof opts === 'function') { - cb = opts - opts = {} - } - - const server = opts.secure - ? https.createServer(sslOpts, handler) - : http.createServer(handler) - - server.listen((err) => { - if (err) return cb(err) - testServers.push(server) - cb(null, server) - }) - } - - beforeEach(() => { // Instructs node to not reject our snake oil SSL certificate when it // can't verify the certificate authority process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 + parallel([ + cb => common.setup((err, factory) => { + expect(err).to.not.exist() + factory.spawnNode((err, node) => { + expect(err).to.not.exist() + ipfs = node + cb() + }) + }), + cb => httpServer.start(cb), + cb => httpsServer.start(cb) + ], done) }) - afterEach((done) => { + after((done) => { // Reinstate unauthorised SSL cert rejection process.env.NODE_TLS_REJECT_UNAUTHORIZED = 1 - - each(testServers, (server, cb) => server.close(cb), (err) => { - testServers = [] - done(err) - }) + parallel([ + cb => httpServer.stop(cb), + cb => httpsServer.stop(cb), + cb => common.teardown(cb) + ], done) }) it('should add from a HTTP URL', (done) => { - const data = Buffer.from(`TEST${Date.now()}`) - + const text = `TEST${Date.now()}` + const url = httpServer.echoUrl(text) parallel({ - server: (cb) => { - const handler = (req, res) => { - res.write(data) - res.end() - } - startTestServer(handler, cb) - }, - expectedResult: (cb) => ipfs.add(data, cb) - }, (err, taskResult) => { + result: (cb) => ipfs.addFromURL(url, cb), + expectedResult: (cb) => ipfs.add(Buffer.from(text), cb) + }, (err, { result, expectedResult }) => { expect(err).to.not.exist() - const { server, expectedResult } = taskResult - - const url = `http://127.0.0.1:${server.address().port}/` - ipfs.addFromURL(url, (err, result) => { - expect(err).to.not.exist() - expect(result).to.deep.equal(expectedResult) - done() - }) + expect(result.err).to.not.exist() + expect(expectedResult.err).to.not.exist() + expect(result[0].hash).to.equal(expectedResult[0].hash) + expect(result[0].size).to.equal(expectedResult[0].size) + expect(result[0].path).to.equal(text) + done() }) }) - it('should add from a HTTPS URL', (done) => { - const data = Buffer.from(`TEST${Date.now()}`) - + it('should add from a HTTPS URL', function (done) { + if (isBrowser || isWebWorker) return this.skip() // unable to do self-signed HTTPS in browser + const text = `TEST${Date.now()}` + const url = httpsServer.echoUrl(text) parallel({ - server: (cb) => { - const handler = (req, res) => { - res.write(data) - res.end() - } - startTestServer(handler, { secure: true }, cb) - }, - expectedResult: (cb) => ipfs.add(data, cb) - }, (err, taskResult) => { + result: (cb) => ipfs.addFromURL(url, cb), + expectedResult: (cb) => ipfs.add(Buffer.from(text), cb) + }, (err, { result, expectedResult }) => { expect(err).to.not.exist() - const { server, expectedResult } = taskResult - - const url = `https://127.0.0.1:${server.address().port}/` - ipfs.addFromURL(url, (err, result) => { - expect(err).to.not.exist() - expect(result).to.deep.equal(expectedResult) - done() - }) + expect(result.err).to.not.exist() + expect(expectedResult.err).to.not.exist() + expect(result[0].hash).to.equal(expectedResult[0].hash) + expect(result[0].size).to.equal(expectedResult[0].size) + expect(result[0].path).to.equal(text) + done() }) }) it('should add from a HTTP URL with redirection', (done) => { - const data = Buffer.from(`TEST${Date.now()}`) + const text = `TEST${Date.now()}` + const url = httpServer.echoUrl(text) + '?foo=bar#buzz' + const redirectUrl = httpServer.redirectUrl(url) - waterfall([ - (cb) => { - const handler = (req, res) => { - res.write(data) - res.end() - } - startTestServer(handler, cb) - }, - (serverA, cb) => { - const url = `http://127.0.0.1:${serverA.address().port}` - const handler = (req, res) => { - res.statusCode = 302 - res.setHeader('Location', url) - res.end() - } - startTestServer(handler, (err, serverB) => { - if (err) return cb(err) - cb(null, { a: serverA, b: serverB }) - }) - } - ], (err, servers) => { + parallel({ + result: (cb) => ipfs.addFromURL(redirectUrl, cb), + expectedResult: (cb) => ipfs.add(Buffer.from(text), cb) + }, (err, { result, expectedResult }) => { expect(err).to.not.exist() - - ipfs.add(data, (err, res) => { - expect(err).to.not.exist() - - const expectedHash = res[0].hash - const url = `http://127.0.0.1:${servers.b.address().port}` - - ipfs.addFromURL(url, (err, result) => { - expect(err).to.not.exist() - expect(result[0].hash).to.equal(expectedHash) - done() - }) - }) + expect(result.err).to.not.exist() + expect(expectedResult.err).to.not.exist() + expect(result[0].hash).to.equal(expectedResult[0].hash) + expect(result[0].size).to.equal(expectedResult[0].size) + expect(result[0].path).to.equal(text) + done() }) }) - it('should add from a HTTPS URL with redirection', (done) => { - const data = Buffer.from(`TEST${Date.now()}`) + it('should add from a HTTPS URL with redirection', function (done) { + if (isBrowser || isWebWorker) return this.skip() // unable to do self-signed HTTPS in browser + const text = `TEST${Date.now()}` + const url = httpsServer.echoUrl(text) + '?foo=bar#buzz' + const redirectUrl = httpsServer.redirectUrl(url) - waterfall([ - (cb) => { - const handler = (req, res) => { - res.write(data) - res.end() - } - startTestServer(handler, { secure: true }, cb) - }, - (serverA, cb) => { - const url = `https://127.0.0.1:${serverA.address().port}` - const handler = (req, res) => { - res.statusCode = 302 - res.setHeader('Location', url) - res.end() - } - startTestServer(handler, { secure: true }, (err, serverB) => { - if (err) return cb(err) - cb(null, { a: serverA, b: serverB }) - }) - } - ], (err, servers) => { + parallel({ + result: (cb) => ipfs.addFromURL(redirectUrl, cb), + expectedResult: (cb) => ipfs.add(Buffer.from(text), cb) + }, (err, { result, expectedResult }) => { expect(err).to.not.exist() - - ipfs.add(data, (err, res) => { - expect(err).to.not.exist() - - const expectedHash = res[0].hash - const url = `https://127.0.0.1:${servers.b.address().port}` - - ipfs.addFromURL(url, (err, result) => { - expect(err).to.not.exist() - expect(result[0].hash).to.equal(expectedHash) - done() - }) - }) + expect(result.err).to.not.exist() + expect(expectedResult.err).to.not.exist() + expect(result[0].hash).to.equal(expectedResult[0].hash) + expect(result[0].size).to.equal(expectedResult[0].size) + expect(result[0].path).to.equal(text) + done() }) }) it('should add from a URL with only-hash=true', (done) => { - const handler = (req, res) => { - res.write(`TEST${Date.now()}`) - res.end() - } - - startTestServer(handler, (err, server) => { + const text = `TEST${Date.now()}` + const url = httpServer.echoUrl(text) + ipfs.addFromURL(url, { onlyHash: true }, (err, res) => { expect(err).to.not.exist() - const url = `http://127.0.0.1:${server.address().port}/` - - ipfs.addFromURL(url, { onlyHash: true }, (err, res) => { - expect(err).to.not.exist() - - // A successful object.get for this size data took my laptop ~14ms - let didTimeout = false - const timeoutId = setTimeout(() => { - didTimeout = true - done() - }, 500) + // A successful object.get for this size data took my laptop ~14ms + let didTimeout = false + const timeoutId = setTimeout(() => { + didTimeout = true + done() + }, 500) - ipfs.object.get(res[0].hash, () => { - clearTimeout(timeoutId) - if (didTimeout) return - expect(new Error('did not timeout')).to.not.exist() - }) + ipfs.object.get(res[0].hash, () => { + clearTimeout(timeoutId) + if (didTimeout) return + expect(new Error('did not timeout')).to.not.exist() }) }) }) it('should add from a URL with wrap-with-directory=true', (done) => { - const filename = `TEST${Date.now()}.txt` - const data = Buffer.from(`TEST${Date.now()}`) - + const filename = `TEST${Date.now()}.txt` // also acts as data + const url = httpServer.echoUrl(filename) + '?foo=bar#buzz' + const addOpts = { wrapWithDirectory: true } parallel({ - server: (cb) => startTestServer((req, res) => { - res.write(data) - res.end() - }, cb), - expectedResult: (cb) => { - ipfs.add([{ path: filename, content: data }], { wrapWithDirectory: true }, cb) - } - }, (err, taskResult) => { + result: (cb) => ipfs.addFromURL(url, addOpts, cb), + expectedResult: (cb) => ipfs.add([{ path: filename, content: Buffer.from(filename) }], addOpts, cb) + }, (err, { result, expectedResult }) => { expect(err).to.not.exist() - - const { server, expectedResult } = taskResult - const url = `http://127.0.0.1:${server.address().port}/${filename}?foo=bar#buzz` - - ipfs.addFromURL(url, { wrapWithDirectory: true }, (err, result) => { - expect(err).to.not.exist() - expect(result).to.deep.equal(expectedResult) - done() - }) + expect(result.err).to.not.exist() + expect(expectedResult.err).to.not.exist() + expect(result).to.deep.equal(expectedResult) + done() }) }) it('should add from a URL with wrap-with-directory=true and URL-escaped file name', (done) => { - const filename = '320px-Domažlice,_Jiráskova_43_(9102).jpg' - const data = Buffer.from(`TEST${Date.now()}`) - + const filename = `320px-Domažlice,_Jiráskova_43_(${Date.now()}).jpg` // also acts as data + const url = httpServer.echoUrl(filename) + '?foo=bar#buzz' + const addOpts = { wrapWithDirectory: true } parallel({ - server: (cb) => startTestServer((req, res) => { - res.write(data) - res.end() - }, cb), - expectedResult: (cb) => { - ipfs.add([{ path: filename, content: data }], { wrapWithDirectory: true }, cb) - } - }, (err, taskResult) => { + result: (cb) => ipfs.addFromURL(url, addOpts, cb), + expectedResult: (cb) => ipfs.add([{ path: filename, content: Buffer.from(filename) }], addOpts, cb) + }, (err, { result, expectedResult }) => { expect(err).to.not.exist() - - const { server, expectedResult } = taskResult - const url = `http://127.0.0.1:${server.address().port}/${encodeURIComponent(filename)}?foo=bar#buzz` - - ipfs.addFromURL(url, { wrapWithDirectory: true }, (err, result) => { - expect(err).to.not.exist() - expect(result).to.deep.equal(expectedResult) - done() - }) + expect(result.err).to.not.exist() + expect(expectedResult.err).to.not.exist() + expect(result).to.deep.equal(expectedResult) + done() }) }) it('should not add from an invalid url', (done) => { ipfs.addFromURL('http://invalid', (err, result) => { - expect(err.code).to.equal('ENOTFOUND') + expect(err).to.exist() expect(result).to.not.exist() done() }) diff --git a/src/files-regular/utils.js b/src/files-regular/utils.js index 8f6bf04d9..8ebfb229e 100644 --- a/src/files-regular/utils.js +++ b/src/files-regular/utils.js @@ -21,9 +21,5 @@ exports.fixtures = Object.freeze({ bigFile: Object.freeze({ cid: 'Qme79tX2bViL26vNjPsF3DP1R9rMKMvnPYJiKTTKPrXJjq', data: loadFixture('test/fixtures/15mb.random', 'interface-ipfs-core') - }), - sslOpts: Object.freeze({ - key: loadFixture('test/fixtures/ssl/privkey.pem', 'interface-ipfs-core'), - cert: loadFixture('test/fixtures/ssl/cert.pem', 'interface-ipfs-core') }) }) diff --git a/src/utils/echo-http-server.js b/src/utils/echo-http-server.js new file mode 100644 index 000000000..fa1698bbc --- /dev/null +++ b/src/utils/echo-http-server.js @@ -0,0 +1,88 @@ +/* eslint-env browser */ +'use strict' + +const http = require('http') +const https = require('https') +const URL = require('url').URL || self.URL +const { isBrowser, isWebWorker } = require('ipfs-utils/src/env') + +const loadFixture = require('aegir/fixtures') +const sslOpts = Object.freeze({ + key: loadFixture('test/fixtures/ssl/privkey.pem', 'interface-ipfs-core'), + cert: loadFixture('test/fixtures/ssl/cert.pem', 'interface-ipfs-core') +}) + +const httpPort = 11080 +const httpsPort = 11443 +module.exports.httpPort = httpPort +module.exports.httpsPort = httpsPort + +// Create a mock of remote HTTP server that can return arbitrary text in response +// or redirect to other URL. Used in tests of ipfs.addFromURL etc +module.exports.createServer = (opts) => { + const secure = opts && opts.secure + const defaultPort = secure ? httpsPort : httpPort + + // Web browser is not able to start HTTP server + // We return noop here and start it from Node via .aegir.js/hooks/browser/pre|post instead (eg. in js-ipfs) + if (isBrowser || isWebWorker) { + const noopServer = { + start: (cb) => cb(), + stop: (cb) => cb(), + url: () => `${secure ? 'https' : 'http'}://127.0.0.1:${defaultPort}`, + echoUrl: (text) => `${noopServer.url()}/echo/${encodeURIComponent(text)}`, + redirectUrl: (url) => `${noopServer.url()}/302/${encodeURI(url)}` + } + return Object.freeze(noopServer) + } + + const handler = (req, res) => { + // Relaxed CORS to enable use in tests in web browser with fetch + res.setHeader('Access-Control-Allow-Origin', '*') + res.setHeader('Access-Control-Request-Method', '*') + res.setHeader('Access-Control-Allow-Methods', 'OPTIONS, GET, DELETE') + res.setHeader('Access-Control-Allow-Headers', '*') + if (req.method === 'OPTIONS') { + res.writeHead(200) + res.end() + return + } + // get the path without query or hash + const { pathname } = new URL(`https://127.0.0.1${req.url}`) + if (pathname.startsWith('/echo/')) { + // Respond with text passed in URL after /echo/ + const [ , text ] = pathname.split('/echo/') + res.setHeader('Content-Type', 'text/plain') + res.write(decodeURIComponent(text)) + } else if (req.url.startsWith('/302/')) { + // Return a redirect to a passed URL + const [ , location ] = pathname.split('/302/') + const url = decodeURI(location) + res.statusCode = 302 + res.setHeader('Location', url) + } else { + res.statusCode = 500 + } + res.end() + } + + const server = secure + ? https.createServer(sslOpts, handler) + : http.createServer(handler) + + server.start = (opts, cb) => { + if (typeof opts === 'function') { + cb = opts + opts = {} + } + return server.listen(Object.assign({ port: defaultPort }, opts), cb) + } + + server.stop = (cb) => server.close(cb) + + server.url = () => `${secure ? 'https' : 'http'}://127.0.0.1:${server.address().port}` + server.echoUrl = (text) => `${server.url()}/echo/${encodeURIComponent(text)}` + server.redirectUrl = (url) => `${server.url()}/302/${encodeURI(url)}` + + return server +} From 865893e637e0b4d033ca53ef7763ed1d88a04e8e Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 28 Aug 2019 02:36:58 +0200 Subject: [PATCH 2/4] fix: skip HTTPS tests when !isNode License: MIT Signed-off-by: Marcin Rataj --- src/files-regular/add-from-url.js | 6 +++--- src/utils/echo-http-server.js | 8 +++----- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/files-regular/add-from-url.js b/src/files-regular/add-from-url.js index 34b8de18f..84acc07be 100644 --- a/src/files-regular/add-from-url.js +++ b/src/files-regular/add-from-url.js @@ -4,7 +4,7 @@ const { getDescribe, getIt, expect } = require('../utils/mocha') const parallel = require('async/parallel') const EchoHttpServer = require('../utils/echo-http-server') -const { isBrowser, isWebWorker } = require('ipfs-utils/src/env') +const { isNode } = require('ipfs-utils/src/env') const httpServer = EchoHttpServer.createServer() const httpsServer = EchoHttpServer.createServer({ secure: true }) @@ -68,7 +68,7 @@ module.exports = (createCommon, options) => { }) it('should add from a HTTPS URL', function (done) { - if (isBrowser || isWebWorker) return this.skip() // unable to do self-signed HTTPS in browser + if (!isNode) return this.skip() // unable to do self-signed HTTPS in browser const text = `TEST${Date.now()}` const url = httpsServer.echoUrl(text) parallel({ @@ -105,7 +105,7 @@ module.exports = (createCommon, options) => { }) it('should add from a HTTPS URL with redirection', function (done) { - if (isBrowser || isWebWorker) return this.skip() // unable to do self-signed HTTPS in browser + if (!isNode) return this.skip() // unable to do self-signed HTTPS in browser const text = `TEST${Date.now()}` const url = httpsServer.echoUrl(text) + '?foo=bar#buzz' const redirectUrl = httpsServer.redirectUrl(url) diff --git a/src/utils/echo-http-server.js b/src/utils/echo-http-server.js index fa1698bbc..9a0375f3c 100644 --- a/src/utils/echo-http-server.js +++ b/src/utils/echo-http-server.js @@ -4,7 +4,7 @@ const http = require('http') const https = require('https') const URL = require('url').URL || self.URL -const { isBrowser, isWebWorker } = require('ipfs-utils/src/env') +const { isNode } = require('ipfs-utils/src/env') const loadFixture = require('aegir/fixtures') const sslOpts = Object.freeze({ @@ -14,8 +14,6 @@ const sslOpts = Object.freeze({ const httpPort = 11080 const httpsPort = 11443 -module.exports.httpPort = httpPort -module.exports.httpsPort = httpsPort // Create a mock of remote HTTP server that can return arbitrary text in response // or redirect to other URL. Used in tests of ipfs.addFromURL etc @@ -25,7 +23,7 @@ module.exports.createServer = (opts) => { // Web browser is not able to start HTTP server // We return noop here and start it from Node via .aegir.js/hooks/browser/pre|post instead (eg. in js-ipfs) - if (isBrowser || isWebWorker) { + if (!isNode) { const noopServer = { start: (cb) => cb(), stop: (cb) => cb(), @@ -75,7 +73,7 @@ module.exports.createServer = (opts) => { cb = opts opts = {} } - return server.listen(Object.assign({ port: defaultPort }, opts), cb) + return server.listen(Object.assign({ port: defaultPort, host: '127.0.0.1' }, opts), cb) } server.stop = (cb) => server.close(cb) From 837579bba420b71d02739aafc98cd4ab3cf62598 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Wed, 28 Aug 2019 02:42:53 +0200 Subject: [PATCH 3/4] chore: fix lint License: MIT Signed-off-by: Marcin Rataj --- src/utils/echo-http-server.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/utils/echo-http-server.js b/src/utils/echo-http-server.js index 9a0375f3c..0e9d2a295 100644 --- a/src/utils/echo-http-server.js +++ b/src/utils/echo-http-server.js @@ -49,12 +49,12 @@ module.exports.createServer = (opts) => { const { pathname } = new URL(`https://127.0.0.1${req.url}`) if (pathname.startsWith('/echo/')) { // Respond with text passed in URL after /echo/ - const [ , text ] = pathname.split('/echo/') + const [, text] = pathname.split('/echo/') res.setHeader('Content-Type', 'text/plain') res.write(decodeURIComponent(text)) } else if (req.url.startsWith('/302/')) { // Return a redirect to a passed URL - const [ , location ] = pathname.split('/302/') + const [, location] = pathname.split('/302/') const url = decodeURI(location) res.statusCode = 302 res.setHeader('Location', url) From fd2892b6a44f31e0b5e61a6012f98853abe66dbf Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Fri, 30 Aug 2019 12:11:58 +0200 Subject: [PATCH 4/4] refactor: remove HTTPS server This simplifies the way we use HTTP Echo Server by moving it to aegir hooks and removing HTTPS version. Context: https://github.com/ipfs/interface-js-ipfs-core/pull/514#issuecomment-526225635 License: MIT Signed-off-by: Marcin Rataj --- src/files-regular/add-from-url.js | 88 ++++++------------------------- src/utils/echo-http-server.js | 44 ++++------------ 2 files changed, 25 insertions(+), 107 deletions(-) diff --git a/src/files-regular/add-from-url.js b/src/files-regular/add-from-url.js index 84acc07be..49bb5119c 100644 --- a/src/files-regular/add-from-url.js +++ b/src/files-regular/add-from-url.js @@ -3,11 +3,7 @@ const { getDescribe, getIt, expect } = require('../utils/mocha') const parallel = require('async/parallel') -const EchoHttpServer = require('../utils/echo-http-server') -const { isNode } = require('ipfs-utils/src/env') - -const httpServer = EchoHttpServer.createServer() -const httpsServer = EchoHttpServer.createServer({ secure: true }) +const { echoUrl, redirectUrl } = require('../utils/echo-http-server') module.exports = (createCommon, options) => { const describe = getDescribe(options) @@ -23,54 +19,21 @@ module.exports = (createCommon, options) => { // CI takes longer to instantiate the daemon, so we need to increase the // timeout for the before step this.timeout(60 * 1000) - // Instructs node to not reject our snake oil SSL certificate when it - // can't verify the certificate authority - process.env.NODE_TLS_REJECT_UNAUTHORIZED = 0 - parallel([ - cb => common.setup((err, factory) => { + common.setup((err, factory) => { + expect(err).to.not.exist() + factory.spawnNode((err, node) => { expect(err).to.not.exist() - factory.spawnNode((err, node) => { - expect(err).to.not.exist() - ipfs = node - cb() - }) - }), - cb => httpServer.start(cb), - cb => httpsServer.start(cb) - ], done) + ipfs = node + done() + }) + }) }) - after((done) => { - // Reinstate unauthorised SSL cert rejection - process.env.NODE_TLS_REJECT_UNAUTHORIZED = 1 - parallel([ - cb => httpServer.stop(cb), - cb => httpsServer.stop(cb), - cb => common.teardown(cb) - ], done) - }) + after((done) => common.teardown(done)) it('should add from a HTTP URL', (done) => { const text = `TEST${Date.now()}` - const url = httpServer.echoUrl(text) - parallel({ - result: (cb) => ipfs.addFromURL(url, cb), - expectedResult: (cb) => ipfs.add(Buffer.from(text), cb) - }, (err, { result, expectedResult }) => { - expect(err).to.not.exist() - expect(result.err).to.not.exist() - expect(expectedResult.err).to.not.exist() - expect(result[0].hash).to.equal(expectedResult[0].hash) - expect(result[0].size).to.equal(expectedResult[0].size) - expect(result[0].path).to.equal(text) - done() - }) - }) - - it('should add from a HTTPS URL', function (done) { - if (!isNode) return this.skip() // unable to do self-signed HTTPS in browser - const text = `TEST${Date.now()}` - const url = httpsServer.echoUrl(text) + const url = echoUrl(text) parallel({ result: (cb) => ipfs.addFromURL(url, cb), expectedResult: (cb) => ipfs.add(Buffer.from(text), cb) @@ -87,31 +50,10 @@ module.exports = (createCommon, options) => { it('should add from a HTTP URL with redirection', (done) => { const text = `TEST${Date.now()}` - const url = httpServer.echoUrl(text) + '?foo=bar#buzz' - const redirectUrl = httpServer.redirectUrl(url) - - parallel({ - result: (cb) => ipfs.addFromURL(redirectUrl, cb), - expectedResult: (cb) => ipfs.add(Buffer.from(text), cb) - }, (err, { result, expectedResult }) => { - expect(err).to.not.exist() - expect(result.err).to.not.exist() - expect(expectedResult.err).to.not.exist() - expect(result[0].hash).to.equal(expectedResult[0].hash) - expect(result[0].size).to.equal(expectedResult[0].size) - expect(result[0].path).to.equal(text) - done() - }) - }) - - it('should add from a HTTPS URL with redirection', function (done) { - if (!isNode) return this.skip() // unable to do self-signed HTTPS in browser - const text = `TEST${Date.now()}` - const url = httpsServer.echoUrl(text) + '?foo=bar#buzz' - const redirectUrl = httpsServer.redirectUrl(url) + const url = echoUrl(text) + '?foo=bar#buzz' parallel({ - result: (cb) => ipfs.addFromURL(redirectUrl, cb), + result: (cb) => ipfs.addFromURL(redirectUrl(url), cb), expectedResult: (cb) => ipfs.add(Buffer.from(text), cb) }, (err, { result, expectedResult }) => { expect(err).to.not.exist() @@ -126,7 +68,7 @@ module.exports = (createCommon, options) => { it('should add from a URL with only-hash=true', (done) => { const text = `TEST${Date.now()}` - const url = httpServer.echoUrl(text) + const url = echoUrl(text) ipfs.addFromURL(url, { onlyHash: true }, (err, res) => { expect(err).to.not.exist() @@ -147,7 +89,7 @@ module.exports = (createCommon, options) => { it('should add from a URL with wrap-with-directory=true', (done) => { const filename = `TEST${Date.now()}.txt` // also acts as data - const url = httpServer.echoUrl(filename) + '?foo=bar#buzz' + const url = echoUrl(filename) + '?foo=bar#buzz' const addOpts = { wrapWithDirectory: true } parallel({ result: (cb) => ipfs.addFromURL(url, addOpts, cb), @@ -163,7 +105,7 @@ module.exports = (createCommon, options) => { it('should add from a URL with wrap-with-directory=true and URL-escaped file name', (done) => { const filename = `320px-Domažlice,_Jiráskova_43_(${Date.now()}).jpg` // also acts as data - const url = httpServer.echoUrl(filename) + '?foo=bar#buzz' + const url = echoUrl(filename) + '?foo=bar#buzz' const addOpts = { wrapWithDirectory: true } parallel({ result: (cb) => ipfs.addFromURL(url, addOpts, cb), diff --git a/src/utils/echo-http-server.js b/src/utils/echo-http-server.js index 0e9d2a295..0554e18bd 100644 --- a/src/utils/echo-http-server.js +++ b/src/utils/echo-http-server.js @@ -2,38 +2,15 @@ 'use strict' const http = require('http') -const https = require('https') const URL = require('url').URL || self.URL -const { isNode } = require('ipfs-utils/src/env') -const loadFixture = require('aegir/fixtures') -const sslOpts = Object.freeze({ - key: loadFixture('test/fixtures/ssl/privkey.pem', 'interface-ipfs-core'), - cert: loadFixture('test/fixtures/ssl/cert.pem', 'interface-ipfs-core') -}) - -const httpPort = 11080 -const httpsPort = 11443 +const defaultPort = 11080 // Create a mock of remote HTTP server that can return arbitrary text in response // or redirect to other URL. Used in tests of ipfs.addFromURL etc -module.exports.createServer = (opts) => { - const secure = opts && opts.secure - const defaultPort = secure ? httpsPort : httpPort - - // Web browser is not able to start HTTP server - // We return noop here and start it from Node via .aegir.js/hooks/browser/pre|post instead (eg. in js-ipfs) - if (!isNode) { - const noopServer = { - start: (cb) => cb(), - stop: (cb) => cb(), - url: () => `${secure ? 'https' : 'http'}://127.0.0.1:${defaultPort}`, - echoUrl: (text) => `${noopServer.url()}/echo/${encodeURIComponent(text)}`, - redirectUrl: (url) => `${noopServer.url()}/302/${encodeURI(url)}` - } - return Object.freeze(noopServer) - } - +// It needs to be available to tests run in browsers: +// start it from Node via .aegir.js/hooks/browser/pre|post (example in js-ipfs) +module.exports.createServer = () => { const handler = (req, res) => { // Relaxed CORS to enable use in tests in web browser with fetch res.setHeader('Access-Control-Allow-Origin', '*') @@ -64,9 +41,7 @@ module.exports.createServer = (opts) => { res.end() } - const server = secure - ? https.createServer(sslOpts, handler) - : http.createServer(handler) + const server = http.createServer(handler) server.start = (opts, cb) => { if (typeof opts === 'function') { @@ -78,9 +53,10 @@ module.exports.createServer = (opts) => { server.stop = (cb) => server.close(cb) - server.url = () => `${secure ? 'https' : 'http'}://127.0.0.1:${server.address().port}` - server.echoUrl = (text) => `${server.url()}/echo/${encodeURIComponent(text)}` - server.redirectUrl = (url) => `${server.url()}/302/${encodeURI(url)}` - return server } + +module.exports.defaultPort = defaultPort +module.exports.url = `http://127.0.0.1:${module.exports.defaultPort}` +module.exports.echoUrl = (text) => `${module.exports.url}/echo/${encodeURIComponent(text)}` +module.exports.redirectUrl = (url) => `${module.exports.url}/302/${encodeURI(url)}`