From 943d4a8cf2d4c4ff5ecd4814c59cb0aae0cfa1fd Mon Sep 17 00:00:00 2001 From: Alex Potsides Date: Fri, 10 Apr 2020 13:57:39 +0100 Subject: [PATCH] fix: make http api only accept POST requests (#2977) * fix: make http api only accept POST requests This is to prevent people maliciously controlling your local node by injecting images into webpages with URLs of API endpoints. BREAKING CHANGE: Where we used to accept all and any HTTP methods, now only POST is accepted. The API client will now only send POST requests too. * test: add tests to make sure we are post-only * chore: upgrade ipfs-utils * fix: return 405 instead of 404 for bad methods * fix: reject browsers that do not send an origin Also fixes running interface tests over http in browsers against js-ipfs --- .travis.yml | 2 +- packages/interface-ipfs-core/package.json | 2 +- packages/ipfs-core-utils/package.json | 2 +- packages/ipfs-http-client/package.json | 2 +- packages/ipfs-http-client/src/add.js | 5 +- packages/ipfs-http-client/src/block/rm.js | 4 +- packages/ipfs-http-client/src/cat.js | 9 +- .../ipfs-http-client/src/dht/find-peer.js | 4 +- .../ipfs-http-client/src/dht/find-provs.js | 5 +- packages/ipfs-http-client/src/dht/get.js | 5 +- packages/ipfs-http-client/src/dht/provide.js | 5 +- packages/ipfs-http-client/src/dht/put.js | 4 +- packages/ipfs-http-client/src/dht/query.js | 4 +- packages/ipfs-http-client/src/files/ls.js | 5 +- packages/ipfs-http-client/src/get.js | 5 +- packages/ipfs-http-client/src/lib/core.js | 14 ++ packages/ipfs-http-client/src/log/tail.js | 4 +- packages/ipfs-http-client/src/ls.js | 5 +- packages/ipfs-http-client/src/name/resolve.js | 4 +- packages/ipfs-http-client/src/pin/ls.js | 5 +- packages/ipfs-http-client/src/ping.js | 7 +- .../ipfs-http-client/src/pubsub/subscribe.js | 6 +- packages/ipfs-http-client/src/refs/index.js | 7 +- packages/ipfs-http-client/src/refs/local.js | 7 +- packages/ipfs-http-client/src/repo/gc.js | 7 +- packages/ipfs-http-client/src/stats/bw.js | 7 +- packages/ipfs/package.json | 9 +- packages/ipfs/src/http/api/routes/bitswap.js | 6 +- packages/ipfs/src/http/api/routes/block.js | 8 +- .../ipfs/src/http/api/routes/bootstrap.js | 12 +- packages/ipfs/src/http/api/routes/config.js | 10 +- packages/ipfs/src/http/api/routes/debug.js | 2 +- packages/ipfs/src/http/api/routes/dht.js | 12 +- packages/ipfs/src/http/api/routes/dns.js | 2 +- .../ipfs/src/http/api/routes/files-regular.js | 18 +- packages/ipfs/src/http/api/routes/id.js | 2 +- packages/ipfs/src/http/api/routes/index.js | 30 ++- packages/ipfs/src/http/api/routes/key.js | 12 +- packages/ipfs/src/http/api/routes/name.js | 10 +- packages/ipfs/src/http/api/routes/object.js | 20 +- packages/ipfs/src/http/api/routes/pin.js | 6 +- packages/ipfs/src/http/api/routes/ping.js | 2 +- packages/ipfs/src/http/api/routes/pubsub.js | 8 +- packages/ipfs/src/http/api/routes/repo.js | 6 +- packages/ipfs/src/http/api/routes/resolve.js | 2 +- packages/ipfs/src/http/api/routes/shutdown.js | 2 +- packages/ipfs/src/http/api/routes/stats.js | 6 +- packages/ipfs/src/http/api/routes/swarm.js | 12 +- packages/ipfs/src/http/api/routes/version.js | 2 +- packages/ipfs/src/http/index.js | 35 ++++ packages/ipfs/test/http-api/inject/bitswap.js | 121 +++++++----- packages/ipfs/test/http-api/inject/block.js | 37 +++- .../ipfs/test/http-api/inject/bootstrap.js | 119 ++++++----- .../test/http-api/inject/browser-headers.js | 55 ++++++ packages/ipfs/test/http-api/inject/config.js | 185 ++++++++++-------- packages/ipfs/test/http-api/inject/dag.js | 21 +- packages/ipfs/test/http-api/inject/dht.js | 63 ++++-- packages/ipfs/test/http-api/inject/dns.js | 9 +- packages/ipfs/test/http-api/inject/files.js | 33 +++- .../ipfs/test/http-api/inject/files/index.js | 16 -- packages/ipfs/test/http-api/inject/id.js | 7 +- packages/ipfs/test/http-api/inject/mfs.js | 16 ++ .../http-api/inject/{files => mfs}/chmod.js | 7 +- .../test/http-api/inject/{files => mfs}/cp.js | 24 ++- .../http-api/inject/{files => mfs}/flush.js | 7 +- .../test/http-api/inject/{files => mfs}/ls.js | 7 +- .../http-api/inject/{files => mfs}/mkdir.js | 7 +- .../test/http-api/inject/{files => mfs}/mv.js | 7 +- .../http-api/inject/{files => mfs}/read.js | 7 +- .../test/http-api/inject/{files => mfs}/rm.js | 25 ++- .../http-api/inject/{files => mfs}/stat.js | 7 +- .../http-api/inject/{files => mfs}/touch.js | 7 +- .../http-api/inject/{files => mfs}/write.js | 2 +- packages/ipfs/test/http-api/inject/name.js | 95 +++++---- packages/ipfs/test/http-api/inject/object.js | 41 ++++ packages/ipfs/test/http-api/inject/pin.js | 31 ++- packages/ipfs/test/http-api/inject/ping.js | 11 +- packages/ipfs/test/http-api/inject/pubsub.js | 27 ++- packages/ipfs/test/http-api/inject/resolve.js | 7 +- packages/ipfs/test/http-api/inject/version.js | 7 +- packages/ipfs/test/http-api/interface.js | 15 +- packages/ipfs/test/http-api/routes.js | 2 + packages/ipfs/test/utils/http.js | 13 +- packages/ipfs/test/utils/test-http-method.js | 24 +++ 84 files changed, 966 insertions(+), 466 deletions(-) create mode 100644 packages/ipfs/test/http-api/inject/browser-headers.js delete mode 100644 packages/ipfs/test/http-api/inject/files/index.js create mode 100644 packages/ipfs/test/http-api/inject/mfs.js rename packages/ipfs/test/http-api/inject/{files => mfs}/chmod.js (92%) rename packages/ipfs/test/http-api/inject/{files => mfs}/cp.js (77%) rename packages/ipfs/test/http-api/inject/{files => mfs}/flush.js (88%) rename packages/ipfs/test/http-api/inject/{files => mfs}/ls.js (94%) rename packages/ipfs/test/http-api/inject/{files => mfs}/mkdir.js (94%) rename packages/ipfs/test/http-api/inject/{files => mfs}/mv.js (94%) rename packages/ipfs/test/http-api/inject/{files => mfs}/read.js (91%) rename packages/ipfs/test/http-api/inject/{files => mfs}/rm.js (63%) rename packages/ipfs/test/http-api/inject/{files => mfs}/stat.js (93%) rename packages/ipfs/test/http-api/inject/{files => mfs}/touch.js (92%) rename packages/ipfs/test/http-api/inject/{files => mfs}/write.js (99%) create mode 100644 packages/ipfs/test/utils/test-http-method.js diff --git a/.travis.yml b/.travis.yml index 69f916f8df..ff884728ff 100644 --- a/.travis.yml +++ b/.travis.yml @@ -64,7 +64,7 @@ jobs: - stage: check script: - npm run build -- $RUN_SINCE --scope={ipfs,ipfs-http-client} -- -- --bundlesize - - npm run dep-check -- $RUN_SINCE -- -- -- -i wrtc -i electron-webrtc + - npm run dep-check -- $RUN_SINCE -- -- -- -i electron-webrtc - npm run lint -- $RUN_SINCE --concurrency 1 - stage: test diff --git a/packages/interface-ipfs-core/package.json b/packages/interface-ipfs-core/package.json index 0300b68131..ab8a387bae 100644 --- a/packages/interface-ipfs-core/package.json +++ b/packages/interface-ipfs-core/package.json @@ -38,7 +38,7 @@ "dirty-chai": "^2.0.1", "ipfs-block": "^0.8.1", "ipfs-unixfs": "^1.0.1", - "ipfs-utils": "^1.2.4", + "ipfs-utils": "ipfs/js-ipfs-utils#fix/add-iterator-method-to-response", "ipld-dag-cbor": "^0.15.1", "ipld-dag-pb": "^0.18.3", "is-ipfs": "^1.0.0", diff --git a/packages/ipfs-core-utils/package.json b/packages/ipfs-core-utils/package.json index 7f6794ed6a..031e2fb486 100644 --- a/packages/ipfs-core-utils/package.json +++ b/packages/ipfs-core-utils/package.json @@ -30,7 +30,7 @@ "dependencies": { "buffer": "^5.4.2", "err-code": "^2.0.0", - "ipfs-utils": "^1.2.4" + "ipfs-utils": "ipfs/js-ipfs-utils#fix/add-iterator-method-to-response" }, "devDependencies": { "aegir": "21.4.5", diff --git a/packages/ipfs-http-client/package.json b/packages/ipfs-http-client/package.json index 326232d921..90ce79e086 100644 --- a/packages/ipfs-http-client/package.json +++ b/packages/ipfs-http-client/package.json @@ -47,7 +47,7 @@ "form-data": "^3.0.0", "ipfs-block": "^0.8.1", "ipfs-core-utils": "^0.1.1", - "ipfs-utils": "^1.2.4", + "ipfs-utils": "ipfs/js-ipfs-utils#fix/add-iterator-method-to-response", "ipld-dag-cbor": "^0.15.1", "ipld-dag-pb": "^0.18.3", "ipld-raw": "^4.0.1", diff --git a/packages/ipfs-http-client/src/add.js b/packages/ipfs-http-client/src/add.js index e2fb989fc1..06fdc24327 100644 --- a/packages/ipfs-http-client/src/add.js +++ b/packages/ipfs-http-client/src/add.js @@ -10,8 +10,7 @@ module.exports = configure((api) => { return async function * add (input, options = {}) { const progressFn = options.progress - const res = await api.ndjson('add', { - method: 'POST', + const res = await api.post('add', { searchParams: toUrlSearchParams(null, { ...options, 'stream-channels': true, @@ -24,7 +23,7 @@ module.exports = configure((api) => { ) }) - for await (let file of res) { + for await (let file of res.ndjson()) { file = toCamel(file) if (progressFn && file.bytes) { diff --git a/packages/ipfs-http-client/src/block/rm.js b/packages/ipfs-http-client/src/block/rm.js index 8e0a1d49da..f60e9f0278 100644 --- a/packages/ipfs-http-client/src/block/rm.js +++ b/packages/ipfs-http-client/src/block/rm.js @@ -23,13 +23,13 @@ module.exports = configure(api => { searchParams.append('arg', new CID(cid).toString()) }) - const res = await api.ndjson('block/rm', { + const res = await api.post('block/rm', { timeout: options.timeout, signal: options.signal, searchParams: searchParams }) - for await (const removed of res) { + for await (const removed of res.ndjson()) { yield toCoreInterface(removed) } } diff --git a/packages/ipfs-http-client/src/cat.js b/packages/ipfs-http-client/src/cat.js index bb73c63def..edea5410c6 100644 --- a/packages/ipfs-http-client/src/cat.js +++ b/packages/ipfs-http-client/src/cat.js @@ -1,7 +1,6 @@ 'use strict' const CID = require('cids') -const { Buffer } = require('buffer') const merge = require('merge-options') const configure = require('./lib/configure') @@ -13,15 +12,13 @@ module.exports = configure(api => { arg: typeof path === 'string' ? path : new CID(path).toString() } ) - const res = await api.iterator('cat', { - method: 'POST', + + const res = await api.post('cat', { timeout: options.timeout, signal: options.signal, searchParams: options }) - for await (const chunk of res) { - yield Buffer.from(chunk) - } + yield * res.iterator() } }) diff --git a/packages/ipfs-http-client/src/dht/find-peer.js b/packages/ipfs-http-client/src/dht/find-peer.js index 536a486cbf..26758ae182 100644 --- a/packages/ipfs-http-client/src/dht/find-peer.js +++ b/packages/ipfs-http-client/src/dht/find-peer.js @@ -9,13 +9,13 @@ module.exports = configure(api => { return async function findPeer (peerId, options = {}) { options.arg = `${Buffer.isBuffer(peerId) ? new CID(peerId) : peerId}` - const res = await api.ndjson('dht/findpeer', { + const res = await api.post('dht/findpeer', { timeout: options.timeout, signal: options.signal, searchParams: options }) - for await (const data of res) { + for await (const data of res.ndjson()) { if (data.Type === 3) { throw new Error(data.Extra) } diff --git a/packages/ipfs-http-client/src/dht/find-provs.js b/packages/ipfs-http-client/src/dht/find-provs.js index 9706106ae2..166428a2ef 100644 --- a/packages/ipfs-http-client/src/dht/find-provs.js +++ b/packages/ipfs-http-client/src/dht/find-provs.js @@ -7,14 +7,13 @@ const configure = require('../lib/configure') module.exports = configure(api => { return async function * findProvs (cid, options = {}) { options.arg = `${new CID(cid)}` - const res = await api.ndjson('dht/findprovs', { - method: 'POST', + const res = await api.post('dht/findprovs', { timeout: options.timeout, signal: options.signal, searchParams: options }) - for await (const message of res) { + for await (const message of res.ndjson()) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/libp2p/go-libp2p-kad-dht/blob/master/routing.go#L525-L526 diff --git a/packages/ipfs-http-client/src/dht/get.js b/packages/ipfs-http-client/src/dht/get.js index 88a36476f4..d8064550e2 100644 --- a/packages/ipfs-http-client/src/dht/get.js +++ b/packages/ipfs-http-client/src/dht/get.js @@ -11,14 +11,13 @@ module.exports = configure(api => { } options.key = encodeBufferURIComponent(key) - const res = await api.ndjson('dht/get', { - method: 'POST', + const res = await api.post('dht/get', { timeout: options.timeout, signal: options.signal, searchParams: options }) - for await (const message of res) { + for await (const message of res.ndjson()) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/ipfs/go-ipfs/blob/eb11f569b064b960d1aba4b5b8ca155a3bd2cb21/core/commands/dht.go#L472-L473 diff --git a/packages/ipfs-http-client/src/dht/provide.js b/packages/ipfs-http-client/src/dht/provide.js index 2e82868a12..a4b71f4dd1 100644 --- a/packages/ipfs-http-client/src/dht/provide.js +++ b/packages/ipfs-http-client/src/dht/provide.js @@ -12,14 +12,13 @@ module.exports = configure(api => { const searchParams = new URLSearchParams(options) cids.forEach(cid => searchParams.append('arg', `${new CID(cid)}`)) - const res = await api.ndjson('dht/provide', { - method: 'POST', + const res = await api.post('dht/provide', { timeout: options.timeout, signal: options.signal, searchParams }) - for await (let message of res) { + for await (let message of res.ndjson()) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/ipfs/go-ipfs/blob/eb11f569b064b960d1aba4b5b8ca155a3bd2cb21/core/commands/dht.go#L283-L284 diff --git a/packages/ipfs-http-client/src/dht/put.js b/packages/ipfs-http-client/src/dht/put.js index 4d4d1d647b..e49739efc7 100644 --- a/packages/ipfs-http-client/src/dht/put.js +++ b/packages/ipfs-http-client/src/dht/put.js @@ -11,13 +11,13 @@ module.exports = configure(api => { searchParams.append('arg', key) searchParams.append('arg', value) - const res = await api.ndjson('dht/put', { + const res = await api.post('dht/put', { timeout: options.timeout, signal: options.signal, searchParams }) - for await (let message of res) { + for await (let message of res.ndjson()) { // 3 = QueryError // https://github.com/libp2p/go-libp2p-core/blob/6e566d10f4a5447317a66d64c7459954b969bdab/routing/query.go#L18 // https://github.com/ipfs/go-ipfs/blob/eb11f569b064b960d1aba4b5b8ca155a3bd2cb21/core/commands/dht.go#L472-L473 diff --git a/packages/ipfs-http-client/src/dht/query.js b/packages/ipfs-http-client/src/dht/query.js index 786d621044..5e281125d5 100644 --- a/packages/ipfs-http-client/src/dht/query.js +++ b/packages/ipfs-http-client/src/dht/query.js @@ -8,13 +8,13 @@ const configure = require('../lib/configure') module.exports = configure(api => { return async function * query (peerId, options = {}) { options.arg = new CID(peerId) - const res = await api.ndjson('dht/query', { + const res = await api.post('dht/query', { timeout: options.timeout, signal: options.signal, searchParams: options }) - for await (let message of res) { + for await (let message of res.ndjson()) { message = toCamel(message) message.id = new CID(message.id) message.responses = (message.responses || []).map(({ ID, Addrs }) => ({ diff --git a/packages/ipfs-http-client/src/files/ls.js b/packages/ipfs-http-client/src/files/ls.js index 40b2a35b69..2f1a00fe36 100644 --- a/packages/ipfs-http-client/src/files/ls.js +++ b/packages/ipfs-http-client/src/files/ls.js @@ -12,8 +12,7 @@ module.exports = configure(api => { path = '/' } - const res = await api.ndjson('files/ls', { - method: 'POST', + const res = await api.post('files/ls', { timeout: options.timeout, signal: options.signal, searchParams: toUrlSearchParams( @@ -30,7 +29,7 @@ module.exports = configure(api => { ) }) - for await (const result of res) { + for await (const result of res.ndjson()) { // go-ipfs does not yet support the "stream" option if ('Entries' in result) { for (const entry of result.Entries || []) { diff --git a/packages/ipfs-http-client/src/get.js b/packages/ipfs-http-client/src/get.js index b8ff34ac03..a98218a82c 100644 --- a/packages/ipfs-http-client/src/get.js +++ b/packages/ipfs-http-client/src/get.js @@ -9,8 +9,7 @@ module.exports = configure(api => { return async function * get (path, options = {}) { options.arg = `${Buffer.isBuffer(path) ? new CID(path) : path}` - const res = await api.iterator('get', { - method: 'POST', + const res = await api.post('get', { timeout: options.timeout, signal: options.signal, searchParams: options @@ -18,7 +17,7 @@ module.exports = configure(api => { const extractor = Tar.extract() - for await (const { header, body } of extractor(res)) { + for await (const { header, body } of extractor(res.iterator())) { if (header.type === 'directory') { yield { path: header.name diff --git a/packages/ipfs-http-client/src/lib/core.js b/packages/ipfs-http-client/src/lib/core.js index bb38e5d5b4..d3cf6631c6 100644 --- a/packages/ipfs-http-client/src/lib/core.js +++ b/packages/ipfs-http-client/src/lib/core.js @@ -7,6 +7,7 @@ const { URL } = require('iso-url') const parseDuration = require('parse-duration') const log = require('debug')('ipfs-http-client:lib:error-handler') const HTTP = require('ipfs-utils/src/http') +const merge = require('merge-options') const isMultiaddr = (input) => { try { @@ -126,6 +127,19 @@ class Client extends HTTP { return out } }) + + delete this.get + delete this.put + delete this.delete + delete this.options + + const fetch = this.fetch + + this.fetch = (resource, options = {}) => { + return fetch.call(this, resource, merge(options, { + method: 'POST' + })) + } } } diff --git a/packages/ipfs-http-client/src/log/tail.js b/packages/ipfs-http-client/src/log/tail.js index 91d00d7132..c2868e13c3 100644 --- a/packages/ipfs-http-client/src/log/tail.js +++ b/packages/ipfs-http-client/src/log/tail.js @@ -4,12 +4,12 @@ const configure = require('../lib/configure') module.exports = configure(api => { return async function * tail (options = {}) { - const res = await api.ndjson('log/tail', { + const res = await api.post('log/tail', { timeout: options.timeout, signal: options.signal, searchParams: options }) - yield * res + yield * res.ndjson() } }) diff --git a/packages/ipfs-http-client/src/ls.js b/packages/ipfs-http-client/src/ls.js index f6619dccc6..f36f536a8e 100644 --- a/packages/ipfs-http-client/src/ls.js +++ b/packages/ipfs-http-client/src/ls.js @@ -9,14 +9,13 @@ module.exports = configure(api => { const searchParams = new URLSearchParams(options) searchParams.set('arg', `${Buffer.isBuffer(path) ? new CID(path) : path}`) - const res = await api.ndjson('ls', { - method: 'POST', + const res = await api.post('ls', { timeout: options.timeout, signal: options.signal, searchParams }) - for await (let result of res) { + for await (let result of res.ndjson()) { result = result.Objects if (!result) { diff --git a/packages/ipfs-http-client/src/name/resolve.js b/packages/ipfs-http-client/src/name/resolve.js index 16406bab22..753f6270fd 100644 --- a/packages/ipfs-http-client/src/name/resolve.js +++ b/packages/ipfs-http-client/src/name/resolve.js @@ -8,13 +8,13 @@ module.exports = configure(api => { searchParams.set('arg', path) searchParams.set('stream', options.stream || true) - const res = await api.ndjson('name/resolve', { + const res = await api.post('name/resolve', { timeout: options.timeout, signal: options.signal, searchParams }) - for await (const result of res) { + for await (const result of res.ndjson()) { yield result.Path } } diff --git a/packages/ipfs-http-client/src/pin/ls.js b/packages/ipfs-http-client/src/pin/ls.js index 26cc3bd36d..1ee7b7b4cb 100644 --- a/packages/ipfs-http-client/src/pin/ls.js +++ b/packages/ipfs-http-client/src/pin/ls.js @@ -16,14 +16,13 @@ module.exports = configure(api => { searchParams.set('stream', options.stream || true) path.forEach(p => searchParams.append('arg', `${p}`)) - const source = api.ndjson('pin/ls', { - method: 'POST', + const res = await api.post('pin/ls', { timeout: options.timeout, signal: options.signal, searchParams }) - for await (const pin of source) { + for await (const pin of res.ndjson()) { if (pin.Keys) { // non-streaming response // eslint-disable-next-line guard-for-in for (const key in pin.Keys) { diff --git a/packages/ipfs-http-client/src/ping.js b/packages/ipfs-http-client/src/ping.js index 373fdfe661..67ff3e9b19 100644 --- a/packages/ipfs-http-client/src/ping.js +++ b/packages/ipfs-http-client/src/ping.js @@ -4,16 +4,17 @@ const toCamel = require('./lib/object-to-camel') const configure = require('./lib/configure') module.exports = configure(api => { - return function ping (peerId, options = {}) { + return async function * ping (peerId, options = {}) { const searchParams = new URLSearchParams(options) searchParams.set('arg', `${peerId}`) - return api.ndjson('ping', { - method: 'POST', + const res = await api.post('ping', { timeout: options.timeout, signal: options.signal, searchParams, transform: toCamel }) + + yield * res.ndjson() } }) diff --git a/packages/ipfs-http-client/src/pubsub/subscribe.js b/packages/ipfs-http-client/src/pubsub/subscribe.js index d9a6368c98..bb4b8881f3 100644 --- a/packages/ipfs-http-client/src/pubsub/subscribe.js +++ b/packages/ipfs-http-client/src/pubsub/subscribe.js @@ -4,7 +4,6 @@ const bs58 = require('bs58') const { Buffer } = require('buffer') const log = require('debug')('ipfs-http-client:pubsub:subscribe') const SubscriptionTracker = require('./subscription-tracker') -const { streamToAsyncIterator, ndjson } = require('../lib/core') const configure = require('../lib/configure') module.exports = configure((api, options) => { @@ -32,8 +31,7 @@ module.exports = configure((api, options) => { }, 1000) try { - res = await api.stream('pubsub/sub', { - method: 'POST', + res = await api.post('pubsub/sub', { timeout: options.timeout, signal: options.signal, searchParams @@ -45,7 +43,7 @@ module.exports = configure((api, options) => { clearTimeout(ffWorkaround) - readMessages(ndjson(streamToAsyncIterator(res)), { + readMessages(res.ndjson(), { onMessage: handler, onEnd: () => subsTracker.unsubscribe(topic, handler), onError: options.onError diff --git a/packages/ipfs-http-client/src/refs/index.js b/packages/ipfs-http-client/src/refs/index.js index 06ddbb599a..97ac4ecc73 100644 --- a/packages/ipfs-http-client/src/refs/index.js +++ b/packages/ipfs-http-client/src/refs/index.js @@ -6,7 +6,7 @@ const toCamel = require('../lib/object-to-camel') const configure = require('../lib/configure') module.exports = configure((api, options) => { - const refs = (args, options = {}) => { + const refs = async function * (args, options = {}) { const searchParams = new URLSearchParams(options) if (!Array.isArray(args)) { @@ -17,13 +17,14 @@ module.exports = configure((api, options) => { searchParams.append('arg', `${Buffer.isBuffer(arg) ? new CID(arg) : arg}`) } - return api.ndjson('refs', { - method: 'POST', + const res = await api.post('refs', { timeout: options.timeout, signal: options.signal, searchParams, transform: toCamel }) + + yield * res.ndjson() } refs.local = require('./local')(options) diff --git a/packages/ipfs-http-client/src/refs/local.js b/packages/ipfs-http-client/src/refs/local.js index 1ddfbadfc4..74c749eb22 100644 --- a/packages/ipfs-http-client/src/refs/local.js +++ b/packages/ipfs-http-client/src/refs/local.js @@ -4,12 +4,13 @@ const toCamel = require('../lib/object-to-camel') const configure = require('../lib/configure') module.exports = configure(api => { - return function refsLocal (options = {}) { - return api.ndjson('refs/local', { - method: 'POST', + return async function * refsLocal (options = {}) { + const res = await api.post('refs/local', { timeout: options.timeout, signal: options.signal, transform: toCamel }) + + yield * res.ndjson() } }) diff --git a/packages/ipfs-http-client/src/repo/gc.js b/packages/ipfs-http-client/src/repo/gc.js index 230daa6fc2..fc8a685363 100644 --- a/packages/ipfs-http-client/src/repo/gc.js +++ b/packages/ipfs-http-client/src/repo/gc.js @@ -4,9 +4,8 @@ const CID = require('cids') const configure = require('../lib/configure') module.exports = configure(api => { - return function gc (options = {}) { - return api.ndjson('repo/gc', { - method: 'POST', + return async function * gc (options = {}) { + const res = await api.post('repo/gc', { timeout: options.timeout, signal: options.signal, searchParams: options, @@ -17,5 +16,7 @@ module.exports = configure(api => { } } }) + + yield res.ndjson() } }) diff --git a/packages/ipfs-http-client/src/stats/bw.js b/packages/ipfs-http-client/src/stats/bw.js index 9c6d081eec..918c10e4f1 100644 --- a/packages/ipfs-http-client/src/stats/bw.js +++ b/packages/ipfs-http-client/src/stats/bw.js @@ -4,9 +4,8 @@ const { BigNumber } = require('bignumber.js') const configure = require('../lib/configure') module.exports = configure(api => { - return function bw (options = {}) { - return api.ndjson('stats/bw', { - method: 'POST', + return async function * bw (options = {}) { + const res = await api.post('stats/bw', { timeout: options.timeout, signal: options.signal, searchParams: options, @@ -17,5 +16,7 @@ module.exports = configure(api => { rateOut: new BigNumber(stats.RateOut) }) }) + + yield * res.ndjson() } }) diff --git a/packages/ipfs/package.json b/packages/ipfs/package.json index 6274da4f3f..5e02f04eb0 100644 --- a/packages/ipfs/package.json +++ b/packages/ipfs/package.json @@ -106,7 +106,7 @@ "ipfs-unixfs": "^1.0.1", "ipfs-unixfs-exporter": "^1.0.3", "ipfs-unixfs-importer": "^1.0.3", - "ipfs-utils": "^1.2.4", + "ipfs-utils": "ipfs/js-ipfs-utils#fix/add-iterator-method-to-response", "ipld": "^0.25.0", "ipld-bitcoin": "^0.3.0", "ipld-dag-cbor": "^0.15.1", @@ -134,8 +134,8 @@ "libp2p": "^0.27.2", "libp2p-bootstrap": "^0.10.3", "libp2p-crypto": "^0.17.1", - "libp2p-delegated-content-routing": "^0.4.3", - "libp2p-delegated-peer-routing": "^0.4.1", + "libp2p-delegated-content-routing": "^0.4.4", + "libp2p-delegated-peer-routing": "^0.4.2", "libp2p-floodsub": "^0.20.0", "libp2p-gossipsub": "^0.2.3", "libp2p-kad-dht": "^0.18.3", @@ -198,7 +198,8 @@ "sinon": "^9.0.1", "stream-to-promise": "^2.2.0", "string-argv": "^0.3.1", - "temp-write": "^4.0.0" + "temp-write": "^4.0.0", + "wrtc": "^0.4.4" }, "optionalDependencies": { "prom-client": "^12.0.0", diff --git a/packages/ipfs/src/http/api/routes/bitswap.js b/packages/ipfs/src/http/api/routes/bitswap.js index 8a59fe6169..58ad173196 100644 --- a/packages/ipfs/src/http/api/routes/bitswap.js +++ b/packages/ipfs/src/http/api/routes/bitswap.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/bitswap/wantlist', options: { validate: resources.bitswap.wantlist.validate @@ -12,7 +12,7 @@ module.exports = [ handler: resources.bitswap.wantlist.handler }, { - method: '*', + method: 'POST', path: '/api/v0/bitswap/stat', options: { validate: resources.bitswap.stat.validate @@ -20,7 +20,7 @@ module.exports = [ handler: resources.bitswap.stat.handler }, { - method: '*', + method: 'POST', path: '/api/v0/bitswap/unwant', options: { pre: [ diff --git a/packages/ipfs/src/http/api/routes/block.js b/packages/ipfs/src/http/api/routes/block.js index ae45ce8bed..d44d919963 100644 --- a/packages/ipfs/src/http/api/routes/block.js +++ b/packages/ipfs/src/http/api/routes/block.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/block/get', options: { pre: [ @@ -14,7 +14,7 @@ module.exports = [ handler: resources.block.get.handler }, { - method: '*', + method: 'POST', path: '/api/v0/block/put', options: { payload: { @@ -29,7 +29,7 @@ module.exports = [ handler: resources.block.put.handler }, { - method: '*', + method: 'POST', path: '/api/v0/block/rm', options: { pre: [ @@ -40,7 +40,7 @@ module.exports = [ handler: resources.block.rm.handler }, { - method: '*', + method: 'POST', path: '/api/v0/block/stat', config: { pre: [ diff --git a/packages/ipfs/src/http/api/routes/bootstrap.js b/packages/ipfs/src/http/api/routes/bootstrap.js index ec37966895..edc46d5054 100644 --- a/packages/ipfs/src/http/api/routes/bootstrap.js +++ b/packages/ipfs/src/http/api/routes/bootstrap.js @@ -4,12 +4,12 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/bootstrap', handler: resources.bootstrap.list }, { - method: '*', + method: 'POST', path: '/api/v0/bootstrap/add', options: { pre: [ @@ -19,17 +19,17 @@ module.exports = [ handler: resources.bootstrap.add.handler }, { - method: '*', + method: 'POST', path: '/api/v0/bootstrap/add/default', handler: resources.bootstrap.addDefault }, { - method: '*', + method: 'POST', path: '/api/v0/bootstrap/list', handler: resources.bootstrap.list }, { - method: '*', + method: 'POST', path: '/api/v0/bootstrap/rm', options: { pre: [ @@ -39,7 +39,7 @@ module.exports = [ handler: resources.bootstrap.rm.handler }, { - method: '*', + method: 'POST', path: '/api/v0/bootstrap/rm/all', handler: resources.bootstrap.rmAll } diff --git a/packages/ipfs/src/http/api/routes/config.js b/packages/ipfs/src/http/api/routes/config.js index 0f3ea9ddcb..512aa89a60 100644 --- a/packages/ipfs/src/http/api/routes/config.js +++ b/packages/ipfs/src/http/api/routes/config.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/config/{key?}', options: { pre: [ @@ -14,12 +14,12 @@ module.exports = [ handler: resources.config.getOrSet.handler }, { - method: '*', + method: 'POST', path: '/api/v0/config/show', handler: resources.config.show }, { - method: '*', + method: 'POST', path: '/api/v0/config/replace', options: { payload: { @@ -33,7 +33,7 @@ module.exports = [ handler: resources.config.replace.handler }, { - method: '*', + method: 'POST', path: '/api/v0/config/profile/apply', options: { pre: [ @@ -44,7 +44,7 @@ module.exports = [ handler: resources.config.profiles.apply.handler }, { - method: '*', + method: 'POST', path: '/api/v0/config/profile/list', handler: resources.config.profiles.list.handler } diff --git a/packages/ipfs/src/http/api/routes/debug.js b/packages/ipfs/src/http/api/routes/debug.js index 24dc880a87..fe65eb2c8a 100644 --- a/packages/ipfs/src/http/api/routes/debug.js +++ b/packages/ipfs/src/http/api/routes/debug.js @@ -9,7 +9,7 @@ const gauge = new client.Gauge({ name: 'number_of_peers', help: 'the_number_of_c // Endpoint for handling debug metrics module.exports = { - method: 'GET', + method: 'POST', path: '/debug/metrics/prometheus', async handler (request, h) { if (!process.env.IPFS_MONITORING) { diff --git a/packages/ipfs/src/http/api/routes/dht.js b/packages/ipfs/src/http/api/routes/dht.js index 3dde96b9bd..3429bc48fb 100644 --- a/packages/ipfs/src/http/api/routes/dht.js +++ b/packages/ipfs/src/http/api/routes/dht.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/dht/findpeer', options: { validate: resources.dht.findPeer.validate @@ -12,7 +12,7 @@ module.exports = [ handler: resources.dht.findPeer.handler }, { - method: '*', + method: 'POST', path: '/api/v0/dht/findprovs', options: { validate: resources.dht.findProvs.validate @@ -20,7 +20,7 @@ module.exports = [ handler: resources.dht.findProvs.handler }, { - method: '*', + method: 'POST', path: '/api/v0/dht/get', options: { validate: resources.dht.get.validate @@ -28,7 +28,7 @@ module.exports = [ handler: resources.dht.get.handler }, { - method: '*', + method: 'POST', path: '/api/v0/dht/provide', options: { validate: resources.dht.provide.validate @@ -36,7 +36,7 @@ module.exports = [ handler: resources.dht.provide.handler }, { - method: '*', + method: 'POST', path: '/api/v0/dht/put', options: { pre: [ @@ -47,7 +47,7 @@ module.exports = [ handler: resources.dht.put.handler }, { - method: '*', + method: 'POST', path: '/api/v0/dht/query', options: { validate: resources.dht.query.validate diff --git a/packages/ipfs/src/http/api/routes/dns.js b/packages/ipfs/src/http/api/routes/dns.js index 4a3bbec3c9..1ecd0bc66a 100644 --- a/packages/ipfs/src/http/api/routes/dns.js +++ b/packages/ipfs/src/http/api/routes/dns.js @@ -3,7 +3,7 @@ const resources = require('../resources') module.exports = { - method: '*', + method: 'POST', path: '/api/v0/dns', handler: resources.dns } diff --git a/packages/ipfs/src/http/api/routes/files-regular.js b/packages/ipfs/src/http/api/routes/files-regular.js index 770e98ab01..310af8d5b2 100644 --- a/packages/ipfs/src/http/api/routes/files-regular.js +++ b/packages/ipfs/src/http/api/routes/files-regular.js @@ -4,8 +4,7 @@ const resources = require('../resources') module.exports = [ { - // TODO fix method - method: '*', + method: 'POST', path: '/api/v0/cat', options: { pre: [ @@ -15,8 +14,7 @@ module.exports = [ handler: resources.filesRegular.cat.handler }, { - // TODO fix method - method: '*', + method: 'POST', path: '/api/v0/get', options: { pre: [ @@ -26,8 +24,7 @@ module.exports = [ handler: resources.filesRegular.get.handler }, { - // TODO fix method - method: '*', + method: 'POST', path: '/api/v0/add', options: { payload: { @@ -40,8 +37,7 @@ module.exports = [ handler: resources.filesRegular.add.handler }, { - // TODO fix method - method: '*', + method: 'POST', path: '/api/v0/ls', options: { pre: [ @@ -51,8 +47,7 @@ module.exports = [ handler: resources.filesRegular.ls.handler }, { - // TODO fix method - method: '*', + method: 'POST', path: '/api/v0/refs', options: { pre: [ @@ -63,8 +58,7 @@ module.exports = [ handler: resources.filesRegular.refs.handler }, { - // TODO fix method - method: '*', + method: 'POST', path: '/api/v0/refs/local', handler: resources.filesRegular.refs.local.handler } diff --git a/packages/ipfs/src/http/api/routes/id.js b/packages/ipfs/src/http/api/routes/id.js index 3af3386301..06f8416a42 100644 --- a/packages/ipfs/src/http/api/routes/id.js +++ b/packages/ipfs/src/http/api/routes/id.js @@ -3,7 +3,7 @@ const resources = require('../resources') module.exports = { - method: '*', + method: 'POST', path: '/api/v0/id', handler: resources.id.get } diff --git a/packages/ipfs/src/http/api/routes/index.js b/packages/ipfs/src/http/api/routes/index.js index 94ceb86e10..b08d1c8369 100644 --- a/packages/ipfs/src/http/api/routes/index.js +++ b/packages/ipfs/src/http/api/routes/index.js @@ -1,6 +1,16 @@ 'use strict' -module.exports = [ +const Boom = require('@hapi/boom') + +const METHODS = [ + 'GET', + 'PUT', + 'PATCH', + 'DELETE', + 'OPTIONS' +] + +const routes = [ require('./version'), require('./shutdown'), require('./id'), @@ -26,3 +36,21 @@ module.exports = [ ...require('./name'), ...require('./dht') ] + +const extraRoutes = [] + +const handler = () => { + throw Boom.methodNotAllowed() +} + +METHODS.forEach(method => { + routes.forEach(route => { + extraRoutes.push({ + method, + handler, + path: route.path + }) + }) +}) + +module.exports = routes.concat(extraRoutes) diff --git a/packages/ipfs/src/http/api/routes/key.js b/packages/ipfs/src/http/api/routes/key.js index 036efd4637..09b71c8417 100644 --- a/packages/ipfs/src/http/api/routes/key.js +++ b/packages/ipfs/src/http/api/routes/key.js @@ -4,32 +4,32 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/key/list', handler: resources.key.list }, { - method: '*', + method: 'POST', path: '/api/v0/key/gen', handler: resources.key.gen }, { - method: '*', + method: 'POST', path: '/api/v0/key/rm', handler: resources.key.rm }, { - method: '*', + method: 'POST', path: '/api/v0/key/rename', handler: resources.key.rename }, { - method: '*', + method: 'POST', path: '/api/v0/key/export', handler: resources.key.export }, { - method: '*', + method: 'POST', path: '/api/v0/key/import', handler: resources.key.import } diff --git a/packages/ipfs/src/http/api/routes/name.js b/packages/ipfs/src/http/api/routes/name.js index a2a7a61e4c..bc95d47875 100644 --- a/packages/ipfs/src/http/api/routes/name.js +++ b/packages/ipfs/src/http/api/routes/name.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/name/resolve', options: { validate: resources.name.resolve.validate @@ -12,7 +12,7 @@ module.exports = [ handler: resources.name.resolve.handler }, { - method: '*', + method: 'POST', path: '/api/v0/name/publish', options: { validate: resources.name.publish.validate @@ -20,17 +20,17 @@ module.exports = [ handler: resources.name.publish.handler }, { - method: '*', + method: 'POST', path: '/api/v0/name/pubsub/state', handler: resources.name.pubsub.state.handler }, { - method: '*', + method: 'POST', path: '/api/v0/name/pubsub/subs', handler: resources.name.pubsub.subs.handler }, { - method: '*', + method: 'POST', path: '/api/v0/name/pubsub/cancel', options: { validate: resources.name.pubsub.cancel.validate diff --git a/packages/ipfs/src/http/api/routes/object.js b/packages/ipfs/src/http/api/routes/object.js index c7c0daae4b..34b692d9de 100644 --- a/packages/ipfs/src/http/api/routes/object.js +++ b/packages/ipfs/src/http/api/routes/object.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/object/new', options: { validate: resources.object.new.validate @@ -12,7 +12,7 @@ module.exports = [ handler: resources.object.new.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/get', options: { pre: [ @@ -23,7 +23,7 @@ module.exports = [ handler: resources.object.get.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/put', options: { payload: { @@ -38,7 +38,7 @@ module.exports = [ handler: resources.object.put.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/stat', options: { pre: [ @@ -49,7 +49,7 @@ module.exports = [ handler: resources.object.stat.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/data', options: { pre: [ @@ -59,7 +59,7 @@ module.exports = [ handler: resources.object.data.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/links', options: { pre: [ @@ -70,7 +70,7 @@ module.exports = [ handler: resources.object.links.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/patch/append-data', options: { payload: { @@ -85,7 +85,7 @@ module.exports = [ handler: resources.object.patchAppendData.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/patch/set-data', options: { payload: { @@ -100,7 +100,7 @@ module.exports = [ handler: resources.object.patchSetData.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/patch/add-link', options: { pre: [ @@ -111,7 +111,7 @@ module.exports = [ handler: resources.object.patchAddLink.handler }, { - method: '*', + method: 'POST', path: '/api/v0/object/patch/rm-link', options: { pre: [ diff --git a/packages/ipfs/src/http/api/routes/pin.js b/packages/ipfs/src/http/api/routes/pin.js index 6912fe42fc..7dc0cdab6c 100644 --- a/packages/ipfs/src/http/api/routes/pin.js +++ b/packages/ipfs/src/http/api/routes/pin.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/pin/add', options: { pre: [ @@ -15,7 +15,7 @@ module.exports = [ handler: resources.pin.add.handler }, { - method: '*', + method: 'POST', path: '/api/v0/pin/rm', options: { pre: [ @@ -26,7 +26,7 @@ module.exports = [ handler: resources.pin.rm.handler }, { - method: '*', + method: 'POST', path: '/api/v0/pin/ls', config: { pre: [ diff --git a/packages/ipfs/src/http/api/routes/ping.js b/packages/ipfs/src/http/api/routes/ping.js index a2eff08989..d21e5c9e57 100644 --- a/packages/ipfs/src/http/api/routes/ping.js +++ b/packages/ipfs/src/http/api/routes/ping.js @@ -3,7 +3,7 @@ const resources = require('../resources') module.exports = { - method: '*', + method: 'POST', path: '/api/v0/ping', config: { handler: resources.ping.handler, diff --git a/packages/ipfs/src/http/api/routes/pubsub.js b/packages/ipfs/src/http/api/routes/pubsub.js index 0bce311bf3..15c666ce8e 100644 --- a/packages/ipfs/src/http/api/routes/pubsub.js +++ b/packages/ipfs/src/http/api/routes/pubsub.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/pubsub/sub', handler: resources.pubsub.subscribe.handler, options: { @@ -14,17 +14,17 @@ module.exports = [ } }, { - method: '*', + method: 'POST', path: '/api/v0/pubsub/pub', handler: resources.pubsub.publish.handler }, { - method: '*', + method: 'POST', path: '/api/v0/pubsub/ls', handler: resources.pubsub.ls.handler }, { - method: '*', + method: 'POST', path: '/api/v0/pubsub/peers', handler: resources.pubsub.peers.handler } diff --git a/packages/ipfs/src/http/api/routes/repo.js b/packages/ipfs/src/http/api/routes/repo.js index e2722a93d6..b7f41b0f1f 100644 --- a/packages/ipfs/src/http/api/routes/repo.js +++ b/packages/ipfs/src/http/api/routes/repo.js @@ -4,17 +4,17 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/repo/version', handler: resources.repo.version }, { - method: '*', + method: 'POST', path: '/api/v0/repo/stat', handler: resources.repo.stat }, { - method: '*', + method: 'POST', path: '/api/v0/repo/gc', options: { validate: resources.repo.gc.validate diff --git a/packages/ipfs/src/http/api/routes/resolve.js b/packages/ipfs/src/http/api/routes/resolve.js index a95a2e2d62..937a2c5827 100644 --- a/packages/ipfs/src/http/api/routes/resolve.js +++ b/packages/ipfs/src/http/api/routes/resolve.js @@ -3,7 +3,7 @@ const resources = require('../resources') module.exports = { - method: '*', + method: 'POST', path: '/api/v0/resolve', options: { validate: resources.resolve.validate diff --git a/packages/ipfs/src/http/api/routes/shutdown.js b/packages/ipfs/src/http/api/routes/shutdown.js index f7201a40e6..b96db161a2 100644 --- a/packages/ipfs/src/http/api/routes/shutdown.js +++ b/packages/ipfs/src/http/api/routes/shutdown.js @@ -3,7 +3,7 @@ const resources = require('../resources') module.exports = { - method: '*', + method: 'POST', path: '/api/v0/shutdown', handler: resources.shutdown } diff --git a/packages/ipfs/src/http/api/routes/stats.js b/packages/ipfs/src/http/api/routes/stats.js index 86b259c2f0..d02cf4b555 100644 --- a/packages/ipfs/src/http/api/routes/stats.js +++ b/packages/ipfs/src/http/api/routes/stats.js @@ -4,7 +4,7 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/stats/bitswap', options: { validate: resources.stats.bitswap.validate @@ -12,12 +12,12 @@ module.exports = [ handler: resources.stats.bitswap.handler }, { - method: '*', + method: 'POST', path: '/api/v0/stats/repo', handler: resources.stats.repo }, { - method: '*', + method: 'POST', path: '/api/v0/stats/bw', handler: resources.stats.bw } diff --git a/packages/ipfs/src/http/api/routes/swarm.js b/packages/ipfs/src/http/api/routes/swarm.js index 73f3fad47b..ccf06a5383 100644 --- a/packages/ipfs/src/http/api/routes/swarm.js +++ b/packages/ipfs/src/http/api/routes/swarm.js @@ -4,22 +4,22 @@ const resources = require('../resources') module.exports = [ { - method: '*', + method: 'POST', path: '/api/v0/swarm/peers', handler: resources.swarm.peers.handler }, { - method: '*', + method: 'POST', path: '/api/v0/swarm/addrs', handler: resources.swarm.addrs.handler }, { - method: '*', + method: 'POST', path: '/api/v0/swarm/addrs/local', handler: resources.swarm.localAddrs.handler }, { - method: '*', + method: 'POST', path: '/api/v0/swarm/connect', options: { pre: [ @@ -29,7 +29,7 @@ module.exports = [ handler: resources.swarm.connect.handler }, { - method: '*', + method: 'POST', path: '/api/v0/swarm/disconnect', options: { pre: [ @@ -40,7 +40,7 @@ module.exports = [ } // TODO // { - // method: '*', + // method: 'POST', // path: '/api/v0/swarm/filters', // handler: resources.swarm.disconnect // } diff --git a/packages/ipfs/src/http/api/routes/version.js b/packages/ipfs/src/http/api/routes/version.js index 8333d40a80..2df9eec746 100644 --- a/packages/ipfs/src/http/api/routes/version.js +++ b/packages/ipfs/src/http/api/routes/version.js @@ -3,7 +3,7 @@ const resources = require('../resources') module.exports = { - method: '*', + method: 'POST', path: '/api/v0/version', handler: resources.version } diff --git a/packages/ipfs/src/http/index.js b/packages/ipfs/src/http/index.js index 2bb3abb20c..5e15162a80 100644 --- a/packages/ipfs/src/http/index.js +++ b/packages/ipfs/src/http/index.js @@ -5,6 +5,7 @@ const Pino = require('hapi-pino') const debug = require('debug') const multiaddr = require('multiaddr') const toMultiaddr = require('uri-to-multiaddr') +const Boom = require('@hapi/boom') const errorHandler = require('./error-handler') const LOG = 'ipfs:http-api' @@ -90,6 +91,40 @@ class HttpApi { } }) + // https://github.com/ipfs/go-ipfs-cmds/pull/193/files + server.ext({ + type: 'onRequest', + method: function (request, h) { + // This check affects POST as we should never get POST requests from a + // browser without Origin or Referer, but we might: + // https://bugzilla.mozilla.org/show_bug.cgi?id=429594 + if (request.method !== 'post') { + return h.continue + } + + const headers = request.headers || {} + const origin = headers.origin || '' + const referrer = headers.referrer || '' + const userAgent = headers['user-agent'] || '' + + // If these are set, we leave up to CORS and CSRF checks. + if (origin || referrer) { + return h.continue + } + + // Allow if the user agent does not start with Mozilla... (i.e. curl) + if (!userAgent.startsWith('Mozilla')) { + return h.continue + } + + // Disallow otherwise. + // + // This means the request probably came from a browser and thus, it + // should have included Origin or referer headers. + throw Boom.forbidden() + } + }) + const setHeader = (key, value) => { server.ext('onPreResponse', (request, h) => { const { response } = request diff --git a/packages/ipfs/test/http-api/inject/bitswap.js b/packages/ipfs/test/http-api/inject/bitswap.js index e5c1fe09a3..46409e312d 100644 --- a/packages/ipfs/test/http-api/inject/bitswap.js +++ b/packages/ipfs/test/http-api/inject/bitswap.js @@ -4,6 +4,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const CID = require('cids') const waitFor = require('../../utils/wait-for') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/bitswap', () => { @@ -19,12 +20,12 @@ module.exports = (http) => { this.timeout(120 * 1000) // Add a CID to the wantlist - api.inject({ method: 'GET', url: `/api/v0/block/get?arg=${wantedCid0}` }) - api.inject({ method: 'GET', url: `/api/v0/block/get?arg=${wantedCid1}` }) + api.inject({ method: 'POST', url: `/api/v0/block/get?arg=${wantedCid0}` }) + api.inject({ method: 'POST', url: `/api/v0/block/get?arg=${wantedCid1}` }) const test = async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/bitswap/wantlist' }) @@ -44,75 +45,87 @@ module.exports = (http) => { }) }) - it('/wantlist', async () => { - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bitswap/wantlist' + describe('/wantlist', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/bitswap/wantlist') }) - expect(res.statusCode).to.equal(200) - expect(res.result).to.have.property('Keys') - expect(res.result.Keys).to.deep.include({ '/': wantedCid0 }) - }) + it('/wantlist', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bitswap/wantlist' + }) - it('/wantlist?cid-base=base64', async () => { - const base64Cid = new CID(wantedCid1).toString('base64') - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bitswap/wantlist?cid-base=base64' + expect(res.statusCode).to.equal(200) + expect(res.result).to.have.property('Keys') + expect(res.result.Keys).to.deep.include({ '/': wantedCid0 }) }) - expect(res.statusCode).to.equal(200) - expect(res.result.Keys).to.deep.include({ '/': base64Cid }) - }) + it('/wantlist?cid-base=base64', async () => { + const base64Cid = new CID(wantedCid1).toString('base64') + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bitswap/wantlist?cid-base=base64' + }) - it('/wantlist?cid-base=invalid', async () => { - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bitswap/wantlist?cid-base=invalid' + expect(res.statusCode).to.equal(200) + expect(res.result.Keys).to.deep.include({ '/': base64Cid }) }) - expect(res.statusCode).to.equal(400) - expect(res.result.Message).to.include('Invalid request query input') + it('/wantlist?cid-base=invalid', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bitswap/wantlist?cid-base=invalid' + }) + + expect(res.statusCode).to.equal(400) + expect(res.result.Message).to.include('Invalid request query input') + }) }) - it('/stat', async () => { - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bitswap/stat' + describe('/stat', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/bitswap/stat') }) - expect(res.statusCode).to.equal(200) - expect(res.result).to.have.property('ProvideBufLen') - expect(res.result).to.have.property('BlocksReceived') - expect(res.result).to.have.property('Wantlist') - expect(res.result).to.have.property('Peers') - expect(res.result).to.have.property('DupBlksReceived') - expect(res.result).to.have.property('DupDataReceived') - expect(res.result).to.have.property('DataReceived') - expect(res.result).to.have.property('BlocksSent') - expect(res.result).to.have.property('DataSent') - }) + it('/stat', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bitswap/stat' + }) - it('/stat?cid-base=base64', async () => { - const base64Cid = new CID(wantedCid1).toString('base64') - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bitswap/stat?cid-base=base64' + expect(res.statusCode).to.equal(200) + expect(res.result).to.have.property('ProvideBufLen') + expect(res.result).to.have.property('BlocksReceived') + expect(res.result).to.have.property('Wantlist') + expect(res.result).to.have.property('Peers') + expect(res.result).to.have.property('DupBlksReceived') + expect(res.result).to.have.property('DupDataReceived') + expect(res.result).to.have.property('DataReceived') + expect(res.result).to.have.property('BlocksSent') + expect(res.result).to.have.property('DataSent') }) - expect(res.statusCode).to.equal(200) - expect(res.result.Wantlist).to.deep.include({ '/': base64Cid }) - }) + it('/stat?cid-base=base64', async () => { + const base64Cid = new CID(wantedCid1).toString('base64') + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bitswap/stat?cid-base=base64' + }) - it('/stat?cid-base=invalid', async () => { - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bitswap/stat?cid-base=invalid' + expect(res.statusCode).to.equal(200) + expect(res.result.Wantlist).to.deep.include({ '/': base64Cid }) }) - expect(res.statusCode).to.equal(400) - expect(res.result.Message).to.include('Invalid request query input') + it('/stat?cid-base=invalid', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bitswap/stat?cid-base=invalid' + }) + + expect(res.statusCode).to.equal(400) + expect(res.result.Message).to.include('Invalid request query input') + }) }) }) } diff --git a/packages/ipfs/test/http-api/inject/block.js b/packages/ipfs/test/http-api/inject/block.js index 794893ea3a..9b824f873d 100644 --- a/packages/ipfs/test/http-api/inject/block.js +++ b/packages/ipfs/test/http-api/inject/block.js @@ -7,6 +7,7 @@ const fs = require('fs') const FormData = require('form-data') const streamToPromise = require('stream-to-promise') const multibase = require('multibase') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/block', () => { @@ -16,7 +17,11 @@ module.exports = (http) => { api = http.api._httpApi._apiServers[0] }) - describe('/block/put', () => { + describe('/put', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/block/put') + }) + it('returns 400 if no node is provided', async () => { const form = new FormData() const headers = form.getHeaders() @@ -90,9 +95,13 @@ module.exports = (http) => { }) describe('/block/get', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/block/get') + }) + it('returns 400 for request without argument', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/get' }) @@ -102,7 +111,7 @@ module.exports = (http) => { it('returns 400 for request with invalid argument', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/get?arg=invalid' }) @@ -113,7 +122,7 @@ module.exports = (http) => { it('returns value', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/get?arg=QmZjTnYw2TFhn9Nn7tjmPSoTBoY7YRkwPzwSrSbabY24Kp' }) @@ -123,9 +132,13 @@ module.exports = (http) => { }) describe('/block/stat', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/block/stat') + }) + it('returns 400 for request without argument', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/stat' }) @@ -135,7 +148,7 @@ module.exports = (http) => { it('returns 400 for request with invalid argument', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/stat?arg=invalid' }) @@ -146,7 +159,7 @@ module.exports = (http) => { it('returns value', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/stat?arg=QmZjTnYw2TFhn9Nn7tjmPSoTBoY7YRkwPzwSrSbabY24Kp' }) @@ -191,9 +204,13 @@ module.exports = (http) => { }) describe('/block/rm', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/block/rm') + }) + it('returns 400 for request without argument', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/rm' }) @@ -203,7 +220,7 @@ module.exports = (http) => { it('returns 400 for request with invalid argument', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/rm?arg=invalid' }) @@ -214,7 +231,7 @@ module.exports = (http) => { it('returns 200', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/block/rm?arg=QmZjTnYw2TFhn9Nn7tjmPSoTBoY7YRkwPzwSrSbabY24Kp' }) diff --git a/packages/ipfs/test/http-api/inject/bootstrap.js b/packages/ipfs/test/http-api/inject/bootstrap.js index 7d9c6925e1..83cf490c86 100644 --- a/packages/ipfs/test/http-api/inject/bootstrap.js +++ b/packages/ipfs/test/http-api/inject/bootstrap.js @@ -4,6 +4,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const qs = require('qs') const defaultList = require('../../../src/core/runtime/config-nodejs.js')().Bootstrap +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/bootstrap', () => { @@ -13,77 +14,103 @@ module.exports = (http) => { before(() => { api = http.api._httpApi._apiServers[0] return api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/bootstrap/add/default' }) }) - it('/list', async () => { - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bootstrap/list' + describe('/list', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/bootstrap/list') }) - expect(res.statusCode).to.be.eql(200) - expect(res.result.Peers).to.deep.equal(defaultList) - }) + it('returns a list', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bootstrap/list' + }) - it('/list alias', async () => { - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bootstrap' + expect(res.statusCode).to.be.eql(200) + expect(res.result.Peers).to.deep.equal(defaultList) }) - expect(res.statusCode).to.be.eql(200) - expect(res.result.Peers).to.deep.equal(defaultList) + it('alias', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bootstrap' + }) + + expect(res.statusCode).to.be.eql(200) + expect(res.result.Peers).to.deep.equal(defaultList) + }) }) - it('/add', async () => { - const query = { - arg: validIp4 - } + describe('/add', () => { + it('only accepts POST', () => { + const query = { + arg: validIp4 + } - const res = await api.inject({ - method: 'GET', - url: `/api/v0/bootstrap/add?${qs.stringify(query)}` + return testHttpMethod(`/api/v0/bootstrap/add?${qs.stringify(query)}`) }) - expect(res.statusCode).to.be.eql(200) - expect(res.result.Peers).to.be.eql([validIp4]) - }) + it('adds a bootstrapper', async () => { + const query = { + arg: validIp4 + } - it('/add/default', async () => { - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bootstrap/add/default' + const res = await api.inject({ + method: 'POST', + url: `/api/v0/bootstrap/add?${qs.stringify(query)}` + }) + + expect(res.statusCode).to.be.eql(200) + expect(res.result.Peers).to.be.eql([validIp4]) }) - expect(res.statusCode).to.be.eql(200) - expect(res.result.Peers).to.be.eql(defaultList) + it('restores default', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bootstrap/add/default' + }) + + expect(res.statusCode).to.be.eql(200) + expect(res.result.Peers).to.be.eql(defaultList) + }) }) - it('/rm', async () => { - const query = { - arg: validIp4 - } + describe('/rm', () => { + it('only accepts POST', () => { + const query = { + arg: validIp4 + } - const res = await api.inject({ - method: 'GET', - url: `/api/v0/bootstrap/rm?${qs.stringify(query)}` + return testHttpMethod(`/api/v0/bootstrap/rm?${qs.stringify(query)}`) }) - expect(res.statusCode).to.be.eql(200) - expect(res.result.Peers).to.be.eql([validIp4]) - }) + it('removes a bootstrapper', async () => { + const query = { + arg: validIp4 + } - it('/rm/all', async () => { - const res = await api.inject({ - method: 'GET', - url: '/api/v0/bootstrap/rm/all' + const res = await api.inject({ + method: 'POST', + url: `/api/v0/bootstrap/rm?${qs.stringify(query)}` + }) + + expect(res.statusCode).to.be.eql(200) + expect(res.result.Peers).to.be.eql([validIp4]) }) - expect(res.statusCode).to.be.eql(200) - expect(res.result.Peers).to.be.eql(defaultList) + it('removes all bootstrappers', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/bootstrap/rm/all' + }) + + expect(res.statusCode).to.be.eql(200) + expect(res.result.Peers).to.be.eql(defaultList) + }) }) }) } diff --git a/packages/ipfs/test/http-api/inject/browser-headers.js b/packages/ipfs/test/http-api/inject/browser-headers.js new file mode 100644 index 0000000000..e914826a53 --- /dev/null +++ b/packages/ipfs/test/http-api/inject/browser-headers.js @@ -0,0 +1,55 @@ +/* eslint-env mocha */ +'use strict' + +const { expect } = require('interface-ipfs-core/src/utils/mocha') +const sinon = require('sinon') +const http = require('../../utils/http') + +module.exports = () => { + describe('browser headers', () => { + let ipfs + + before(() => { + ipfs = { + id: sinon.stub().returns({}) + } + }) + + it('should block Mozilla* browsers that do not provide origins', async () => { + const res = await http({ + method: 'POST', + url: '/api/v0/id', + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0' + } + }, { ipfs }) + + expect(res.statusCode).to.equal(403) + }) + + it('should not block on GETs', async () => { + const res = await http({ + method: 'GET', + url: '/api/v0/id', + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0' + } + }, { ipfs }) + + expect(res.statusCode).to.equal(405) + }) + + it('should not block a Mozilla* browser that provides an allowed Origin', async () => { + const res = await http({ + method: 'POST', + url: '/api/v0/id', + headers: { + 'User-Agent': 'Mozilla/5.0 (X11; Linux x86_64; rv:10.0) Gecko/20100101 Firefox/10.0', + Origin: 'null' + } + }, { ipfs }) + + expect(res.statusCode).to.equal(200) + }) + }) +} diff --git a/packages/ipfs/test/http-api/inject/config.js b/packages/ipfs/test/http-api/inject/config.js index 53eb9f8785..d11449ec63 100644 --- a/packages/ipfs/test/http-api/inject/config.js +++ b/packages/ipfs/test/http-api/inject/config.js @@ -7,6 +7,7 @@ const FormData = require('form-data') const streamToPromise = require('stream-to-promise') const path = require('path') const { profiles } = require('../../../src/core/components/config') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/config', () => { @@ -25,120 +26,132 @@ module.exports = (http) => { fs.writeFileSync(configPath, fs.readFileSync(originalConfigPath, 'utf8'), 'utf8') }) - describe('/config', () => { - it('returns 400 for request without arguments', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config' - }) + it('only accepts POST', () => { + return testHttpMethod('/api/v0/config') + }) - expect(res.statusCode).to.equal(400) + it('returns 400 for request without arguments', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config' }) - it('404 for request with missing args', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config?arg=kitten' - }) + expect(res.statusCode).to.equal(400) + }) - expect(res.statusCode).to.equal(404) - expect(res.result.Code).to.equal(3) - expect(res.result.Message).to.be.a('string') + it('404 for request with missing args', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config?arg=kitten' }) - it('returns value for request with valid arg', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config?arg=API.HTTPHeaders' - }) + expect(res.statusCode).to.equal(404) + expect(res.result.Code).to.equal(3) + expect(res.result.Message).to.be.a('string') + }) - expect(res.statusCode).to.equal(200) - expect(res.result.Key).to.equal('API.HTTPHeaders') - expect(res.result.Value).to.equal(null) + it('returns value for request with valid arg', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config?arg=API.HTTPHeaders' }) - it('returns value for request as subcommand', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config/API.HTTPHeaders' - }) + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('API.HTTPHeaders') + expect(res.result.Value).to.equal(null) + }) - expect(res.statusCode).to.equal(200) - expect(res.result.Key).to.equal('API.HTTPHeaders') - expect(res.result.Value).to.equal(null) + it('returns value for request as subcommand', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config/API.HTTPHeaders' }) - it('updates value for request with both args', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config?arg=Datastore.Path&arg=kitten' - }) + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('API.HTTPHeaders') + expect(res.result.Value).to.equal(null) + }) - expect(res.statusCode).to.equal(200) - expect(res.result.Key).to.equal('Datastore.Path') - expect(res.result.Value).to.equal('kitten') - expect(updatedConfig().Datastore.Path).to.equal('kitten') + it('updates value for request with both args', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg=kitten' }) - it('returns 400 value for request with both args and JSON flag with invalid JSON argument', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config?arg=Datastore.Path&arg=kitten&json' - }) + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('Datastore.Path') + expect(res.result.Value).to.equal('kitten') + expect(updatedConfig().Datastore.Path).to.equal('kitten') + }) - expect(res.statusCode).to.equal(400) - expect(res.result.Code).to.equal(1) - expect(res.result.Message).to.be.a('string') + it('returns 400 value for request with both args and JSON flag with invalid JSON argument', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg=kitten&json' }) - it('updates value for request with both args and JSON flag with valid JSON argument', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config?arg=Datastore.Path&arg={"kitten": true}&json' - }) + expect(res.statusCode).to.equal(400) + expect(res.result.Code).to.equal(1) + expect(res.result.Message).to.be.a('string') + }) - expect(res.statusCode).to.equal(200) - expect(res.result.Key).to.equal('Datastore.Path') - expect(res.result.Value).to.deep.equal({ kitten: true }) - expect(updatedConfig().Datastore.Path).to.deep.equal({ kitten: true }) + it('updates value for request with both args and JSON flag with valid JSON argument', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg={"kitten": true}&json' }) - it('updates value for request with both args and bool flag and true argument', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config?arg=Datastore.Path&arg=true&bool' - }) + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('Datastore.Path') + expect(res.result.Value).to.deep.equal({ kitten: true }) + expect(updatedConfig().Datastore.Path).to.deep.equal({ kitten: true }) + }) - expect(res.statusCode).to.equal(200) - expect(res.result.Key).to.equal('Datastore.Path') - expect(res.result.Value).to.deep.equal(true) - expect(updatedConfig().Datastore.Path).to.deep.equal(true) + it('updates value for request with both args and bool flag and true argument', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg=true&bool' + }) + + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('Datastore.Path') + expect(res.result.Value).to.deep.equal(true) + expect(updatedConfig().Datastore.Path).to.deep.equal(true) + }) + + it('updates value for request with both args and bool flag and false argument', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/config?arg=Datastore.Path&arg=false&bool' + }) + + expect(res.statusCode).to.equal(200) + expect(res.result.Key).to.equal('Datastore.Path') + expect(res.result.Value).to.deep.equal(false) + expect(updatedConfig().Datastore.Path).to.deep.equal(false) + }) + + describe('/show', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/config/show') }) - it('updates value for request with both args and bool flag and false argument', async () => { + it('shows config', async () => { const res = await api.inject({ method: 'POST', - url: '/api/v0/config?arg=Datastore.Path&arg=false&bool' + url: '/api/v0/config/show' }) expect(res.statusCode).to.equal(200) - expect(res.result.Key).to.equal('Datastore.Path') - expect(res.result.Value).to.deep.equal(false) - expect(updatedConfig().Datastore.Path).to.deep.equal(false) + expect(res.result).to.deep.equal(updatedConfig()) }) }) - it('/config/show', async () => { - const res = await api.inject({ - method: 'POST', - url: '/api/v0/config/show' + describe('/replace', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/config/replace') }) - expect(res.statusCode).to.equal(200) - expect(res.result).to.deep.equal(updatedConfig()) - }) - - describe('/config/replace', () => { it('returns 400 if no config is provided', async () => { const form = new FormData() const headers = form.getHeaders() @@ -191,13 +204,17 @@ module.exports = (http) => { }) }) - describe('/config/profile/apply', () => { + describe('/profile/apply', () => { let originalConfig beforeEach(() => { originalConfig = JSON.parse(fs.readFileSync(configPath, 'utf8')) }) + it('only accepts POST', () => { + return testHttpMethod('/api/v0/config/profile/apply') + }) + it('returns 400 if no config profile is provided', async () => { const res = await api.inject({ method: 'POST', @@ -239,7 +256,11 @@ module.exports = (http) => { }) }) - describe('/config/profile/list', () => { + describe('/profile/list', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/config/profile/list') + }) + it('lists available profiles', async () => { const res = await api.inject({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/dag.js b/packages/ipfs/test/http-api/inject/dag.js index c76b458465..8e80126f71 100644 --- a/packages/ipfs/test/http-api/inject/dag.js +++ b/packages/ipfs/test/http-api/inject/dag.js @@ -10,6 +10,7 @@ const FormData = require('form-data') const streamToPromise = require('stream-to-promise') const CID = require('cids') const all = require('it-all') +const testHttpMethod = require('../../utils/test-http-method') const toHeadersAndPayload = async (thing) => { const stream = new Readable() @@ -26,14 +27,18 @@ const toHeadersAndPayload = async (thing) => { } module.exports = (http) => { - describe('dag endpoint', () => { + describe('/dag', () => { let api before(() => { api = http.api._httpApi._apiServers[0] }) - describe('/dag/get', () => { + describe('/get', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/dag/get') + }) + it('returns error for request without argument', async () => { const res = await api.inject({ method: 'POST', @@ -179,7 +184,11 @@ module.exports = (http) => { }) }) - describe('/dag/put', () => { + describe('/put', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/dag/put') + }) + it('returns error for request without file argument', async () => { const res = await api.inject({ method: 'POST', @@ -297,7 +306,11 @@ module.exports = (http) => { }) }) - describe('/dag/resolve', () => { + describe('/resolve', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/dag/resolve') + }) + it('returns error for request without argument', async () => { const res = await api.inject({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/dht.js b/packages/ipfs/test/http-api/inject/dht.js index 039df2bee2..8e961cbc88 100644 --- a/packages/ipfs/test/http-api/inject/dht.js +++ b/packages/ipfs/test/http-api/inject/dht.js @@ -3,6 +3,7 @@ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { // TODO: unskip when DHT is enabled: https://github.com/ipfs/js-ipfs/pull/1994 @@ -14,9 +15,13 @@ module.exports = (http) => { }) describe('/findpeer', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/dht/findpeer') + }) + it('returns 400 if no peerId is provided', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/dht/findpeer' }) @@ -27,7 +32,7 @@ module.exports = (http) => { it('returns 404 if peerId is provided as there is no peers in the routing table', async () => { const peerId = 'QmQ2zigjQikYnyYUSXZydNXrDRhBut2mubwJBaLXobMt3A' const res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/dht/findpeer?arg=${peerId}` }) @@ -36,12 +41,21 @@ module.exports = (http) => { }) describe('/findprovs', () => { - it('returns 400 if no key is provided', async () => { + it('only accepts POST', async () => { const res = await api.inject({ method: 'GET', url: '/api/v0/dht/findprovs' }) + expect(res.statusCode).to.equal(404) + }) + + it('returns 400 if no key is provided', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/dht/findprovs' + }) + expect(res.statusCode).to.equal(400) expect(res.result.Code).to.be.eql(1) }) @@ -49,7 +63,7 @@ module.exports = (http) => { it('returns 200 if key is provided', async () => { const key = 'Qmc77hSNykXJ6Jxp1C6RpD8VENV7RK6JD7eAcWpc7nEZx2' const res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/dht/findprovs?arg=${key}` }) @@ -59,12 +73,21 @@ module.exports = (http) => { }) describe('/get', () => { - it('returns 400 if no key is provided', async () => { + it('only accepts POST', async () => { const res = await api.inject({ method: 'GET', url: '/api/v0/dht/get' }) + expect(res.statusCode).to.equal(404) + }) + + it('returns 400 if no key is provided', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/dht/get' + }) + expect(res.statusCode).to.equal(400) expect(res.result.Code).to.be.eql(1) }) @@ -74,14 +97,14 @@ module.exports = (http) => { const value = 'value' let res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/dht/put?arg=${key}&arg=${value}` }) expect(res.statusCode).to.equal(200) res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/dht/get?arg=${key}` }) @@ -92,12 +115,21 @@ module.exports = (http) => { }) describe('/provide', () => { - it('returns 400 if no key is provided', async () => { + it('only accepts POST', async () => { const res = await api.inject({ method: 'GET', url: '/api/v0/dht/provide' }) + expect(res.statusCode).to.equal(404) + }) + + it('returns 400 if no key is provided', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/dht/provide' + }) + expect(res.statusCode).to.equal(400) expect(res.result.Code).to.be.eql(1) }) @@ -105,7 +137,7 @@ module.exports = (http) => { it('returns 500 if key is provided as the file was not added', async () => { const key = 'Qmc77hSNykXJ6Jxp1C6RpD8VENV7RK6JD7eAcWpc7nEZx2' const res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/dht/provide?arg=${key}` }) @@ -114,12 +146,21 @@ module.exports = (http) => { }) describe('/put', () => { - it('returns 400 if no key or value is provided', async () => { + it('only accepts POST', async () => { const res = await api.inject({ method: 'GET', url: '/api/v0/dht/put' }) + expect(res.statusCode).to.equal(404) + }) + + it('returns 400 if no key or value is provided', async () => { + const res = await api.inject({ + method: 'POST', + url: '/api/v0/dht/put' + }) + expect(res.statusCode).to.equal(400) expect(res.result.Code).to.be.eql(1) }) @@ -131,7 +172,7 @@ module.exports = (http) => { const value = 'value' const res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/dht/put?arg=${key}&arg=${value}` }) diff --git a/packages/ipfs/test/http-api/inject/dns.js b/packages/ipfs/test/http-api/inject/dns.js index 0e01879e54..8d86a3addb 100644 --- a/packages/ipfs/test/http-api/inject/dns.js +++ b/packages/ipfs/test/http-api/inject/dns.js @@ -2,6 +2,7 @@ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/dns', () => { @@ -11,9 +12,13 @@ module.exports = (http) => { api = http.api._httpApi._apiServers[0] }) + it('only accepts POST', () => { + return testHttpMethod('/api/v0/dns?arg=ipfs.io') + }) + it('resolve ipfs.io DNS', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/dns?arg=ipfs.io' }) @@ -22,7 +27,7 @@ module.exports = (http) => { it('resolve ipfs.enstest.eth ENS', async function () { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/dns?arg=ipfs.enstest.eth' }) diff --git a/packages/ipfs/test/http-api/inject/files.js b/packages/ipfs/test/http-api/inject/files.js index a90930f354..44f53327db 100644 --- a/packages/ipfs/test/http-api/inject/files.js +++ b/packages/ipfs/test/http-api/inject/files.js @@ -7,6 +7,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const FormData = require('form-data') const streamToPromise = require('stream-to-promise') const multibase = require('multibase') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/files', () => { @@ -17,6 +18,10 @@ module.exports = (http) => { }) describe('/add', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/add') + }) + it('should add buffer bigger than Hapi default max bytes (1024 * 1024)', async () => { const payload = Buffer.from([ '', @@ -111,9 +116,13 @@ module.exports = (http) => { }) describe('/cat', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/cat') + }) + it('returns 400 for request without argument', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/cat' }) @@ -123,7 +132,7 @@ module.exports = (http) => { it('returns 400 for request with invalid argument', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/cat?arg=invalid' }) @@ -151,7 +160,7 @@ module.exports = (http) => { const cid = JSON.parse(res.result).Hash res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/cat?arg=' + cid }) @@ -161,9 +170,17 @@ module.exports = (http) => { }) }) - describe('/get', () => {}) // TODO + describe('/get', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/get') + }) + }) describe('/ls', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/ls') + }) + it('should list directory contents and return a base64 encoded CIDs', async () => { const form = new FormData() form.append('file', Buffer.from('TEST' + Date.now()), { filename: 'data.txt' }) @@ -193,6 +210,10 @@ module.exports = (http) => { }) describe('/refs', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/refs') + }) + it('should list refs', async () => { const form = new FormData() form.append('file', Buffer.from('TEST' + Date.now()), { filename: 'data.txt' }) @@ -220,6 +241,10 @@ module.exports = (http) => { }) describe('/refs/local', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/refs/local') + }) + it('should list local refs', async () => { const form = new FormData() form.append('file', Buffer.from('TEST' + Date.now()), { filename: 'data.txt' }) diff --git a/packages/ipfs/test/http-api/inject/files/index.js b/packages/ipfs/test/http-api/inject/files/index.js deleted file mode 100644 index 53d4c3c955..0000000000 --- a/packages/ipfs/test/http-api/inject/files/index.js +++ /dev/null @@ -1,16 +0,0 @@ -/* eslint-env mocha */ -'use strict' - -describe('files', () => { - require('./chmod') - require('./cp') - require('./flush') - require('./ls') - require('./mkdir') - require('./mv') - require('./read') - require('./rm') - require('./stat') - require('./touch') - require('./write') -}) diff --git a/packages/ipfs/test/http-api/inject/id.js b/packages/ipfs/test/http-api/inject/id.js index 4022535e70..6105a2c766 100644 --- a/packages/ipfs/test/http-api/inject/id.js +++ b/packages/ipfs/test/http-api/inject/id.js @@ -2,6 +2,7 @@ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/id', () => { @@ -11,9 +12,13 @@ module.exports = (http) => { api = http.api._httpApi._apiServers[0] }) + it('only accepts POST', () => { + return testHttpMethod('/api/v0/id') + }) + it('get the id', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/id' }) diff --git a/packages/ipfs/test/http-api/inject/mfs.js b/packages/ipfs/test/http-api/inject/mfs.js new file mode 100644 index 0000000000..4997e885c1 --- /dev/null +++ b/packages/ipfs/test/http-api/inject/mfs.js @@ -0,0 +1,16 @@ +/* eslint-env mocha */ +'use strict' + +module.exports = () => { + require('./mfs/chmod') + require('./mfs/cp') + require('./mfs/flush') + require('./mfs/ls') + require('./mfs/mkdir') + require('./mfs/mv') + require('./mfs/read') + require('./mfs/rm') + require('./mfs/stat') + require('./mfs/touch') + require('./mfs/write') +} diff --git a/packages/ipfs/test/http-api/inject/files/chmod.js b/packages/ipfs/test/http-api/inject/mfs/chmod.js similarity index 92% rename from packages/ipfs/test/http-api/inject/files/chmod.js rename to packages/ipfs/test/http-api/inject/mfs/chmod.js index f5090b139e..849051cf38 100644 --- a/packages/ipfs/test/http-api/inject/files/chmod.js +++ b/packages/ipfs/test/http-api/inject/mfs/chmod.js @@ -4,6 +4,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const http = require('../../../utils/http') const sinon = require('sinon') +const testHttpMethod = require('../../../utils/test-http-method') function defaultOptions (modification = {}) { const options = { @@ -20,7 +21,7 @@ function defaultOptions (modification = {}) { return options } -describe('chmod', () => { +describe('/files/chmod', () => { const path = '/foo' const mode = '0654' let ipfs @@ -33,6 +34,10 @@ describe('chmod', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/chmod?arg=${path}&mode=${mode}`, ipfs) + }) + it('should update the mode for a file', async () => { await http({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/files/cp.js b/packages/ipfs/test/http-api/inject/mfs/cp.js similarity index 77% rename from packages/ipfs/test/http-api/inject/files/cp.js rename to packages/ipfs/test/http-api/inject/mfs/cp.js index ac5a1c7309..e1786b64b7 100644 --- a/packages/ipfs/test/http-api/inject/files/cp.js +++ b/packages/ipfs/test/http-api/inject/mfs/cp.js @@ -4,9 +4,11 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const http = require('../../../utils/http') const sinon = require('sinon') +const testHttpMethod = require('../../../utils/test-http-method') function defaultOptions (modification = {}) { const options = { + cidVersion: 0, parents: false, hashAlg: 'sha2-256', flush: true, @@ -20,7 +22,7 @@ function defaultOptions (modification = {}) { return options } -describe('cp', () => { +describe('/files/cp', () => { const source = 'source' const dest = 'dest' let ipfs @@ -33,6 +35,10 @@ describe('cp', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/cp?arg=${source}&arg=${dest}`, ipfs) + }) + it('should copy files', async () => { await http({ method: 'POST', @@ -63,6 +69,22 @@ describe('cp', () => { ]) }) + it('should copy files with a different cid version', async () => { + await http({ + method: 'POST', + url: `/api/v0/files/cp?arg=${source}&arg=${dest}&cidVersion=1` + }, { ipfs }) + + expect(ipfs.files.cp.callCount).to.equal(1) + expect(ipfs.files.cp.getCall(0).args).to.deep.equal([ + source, + dest, + defaultOptions({ + cidVersion: 1 + }) + ]) + }) + it('should copy files with a different hash algorithm', async () => { await http({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/files/flush.js b/packages/ipfs/test/http-api/inject/mfs/flush.js similarity index 88% rename from packages/ipfs/test/http-api/inject/files/flush.js rename to packages/ipfs/test/http-api/inject/mfs/flush.js index b340c34399..fa4695ed83 100644 --- a/packages/ipfs/test/http-api/inject/files/flush.js +++ b/packages/ipfs/test/http-api/inject/mfs/flush.js @@ -6,8 +6,9 @@ const http = require('../../../utils/http') const sinon = require('sinon') const CID = require('cids') const cid = new CID('QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn') +const testHttpMethod = require('../../../utils/test-http-method') -describe('flush', () => { +describe('/files/flush', () => { const path = '/foo' let ipfs @@ -19,6 +20,10 @@ describe('flush', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/flush?arg=${path}`, ipfs) + }) + it('should flush a path', async () => { const response = await http({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/files/ls.js b/packages/ipfs/test/http-api/inject/mfs/ls.js similarity index 94% rename from packages/ipfs/test/http-api/inject/files/ls.js rename to packages/ipfs/test/http-api/inject/mfs/ls.js index a8a7f4fac0..6cdbfc8c03 100644 --- a/packages/ipfs/test/http-api/inject/files/ls.js +++ b/packages/ipfs/test/http-api/inject/mfs/ls.js @@ -6,8 +6,9 @@ const http = require('../../../utils/http') const sinon = require('sinon') const CID = require('cids') const fileCid = new CID('bafybeigyov3nzxrqjismjpq7ghkkjorcmozy5rgaikvyieakoqpxfc3rvu') +const testHttpMethod = require('../../../utils/test-http-method') -describe('ls', () => { +describe('/files/ls', () => { const path = '/foo' const file = { name: 'file-name', @@ -30,6 +31,10 @@ describe('ls', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/ls?arg=${path}`, ipfs) + }) + it('should list a path', async () => { ipfs.files.ls = sinon.stub().returns([file]) diff --git a/packages/ipfs/test/http-api/inject/files/mkdir.js b/packages/ipfs/test/http-api/inject/mfs/mkdir.js similarity index 94% rename from packages/ipfs/test/http-api/inject/files/mkdir.js rename to packages/ipfs/test/http-api/inject/mfs/mkdir.js index 34f29aee2d..e7a6029877 100644 --- a/packages/ipfs/test/http-api/inject/files/mkdir.js +++ b/packages/ipfs/test/http-api/inject/mfs/mkdir.js @@ -4,6 +4,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const http = require('../../../utils/http') const sinon = require('sinon') +const testHttpMethod = require('../../../utils/test-http-method') function defaultOptions (modification = {}) { const options = { @@ -23,7 +24,7 @@ function defaultOptions (modification = {}) { return options } -describe('mkdir', () => { +describe('/files/mkdir', () => { const path = '/foo' let ipfs @@ -35,6 +36,10 @@ describe('mkdir', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/mkdir?arg=${path}`, ipfs) + }) + it('should make a directory', async () => { await http({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/files/mv.js b/packages/ipfs/test/http-api/inject/mfs/mv.js similarity index 94% rename from packages/ipfs/test/http-api/inject/files/mv.js rename to packages/ipfs/test/http-api/inject/mfs/mv.js index ed2d42d775..a66cbdbba3 100644 --- a/packages/ipfs/test/http-api/inject/files/mv.js +++ b/packages/ipfs/test/http-api/inject/mfs/mv.js @@ -4,6 +4,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const http = require('../../../utils/http') const sinon = require('sinon') +const testHttpMethod = require('../../../utils/test-http-method') function defaultOptions (modification = {}) { const options = { @@ -22,7 +23,7 @@ function defaultOptions (modification = {}) { return options } -describe('mv', () => { +describe('/files/mv', () => { const source = '/src' const dest = '/dest' let ipfs @@ -35,6 +36,10 @@ describe('mv', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/mv?arg=${source}&arg=${dest}`, ipfs) + }) + it('should move an entry', async () => { await http({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/files/read.js b/packages/ipfs/test/http-api/inject/mfs/read.js similarity index 91% rename from packages/ipfs/test/http-api/inject/files/read.js rename to packages/ipfs/test/http-api/inject/mfs/read.js index e9588ba32a..207ed57fe6 100644 --- a/packages/ipfs/test/http-api/inject/files/read.js +++ b/packages/ipfs/test/http-api/inject/mfs/read.js @@ -4,6 +4,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const http = require('../../../utils/http') const sinon = require('sinon') +const testHttpMethod = require('../../../utils/test-http-method') function defaultOptions (modification = {}) { const options = { @@ -18,7 +19,7 @@ function defaultOptions (modification = {}) { return options } -describe('read', () => { +describe('/files/read', () => { const path = '/foo' let ipfs @@ -30,6 +31,10 @@ describe('read', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/read?arg=${path}`, ipfs) + }) + it('should read a path', async () => { const response = await http({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/files/rm.js b/packages/ipfs/test/http-api/inject/mfs/rm.js similarity index 63% rename from packages/ipfs/test/http-api/inject/files/rm.js rename to packages/ipfs/test/http-api/inject/mfs/rm.js index b799eb50ce..0a354078b1 100644 --- a/packages/ipfs/test/http-api/inject/files/rm.js +++ b/packages/ipfs/test/http-api/inject/mfs/rm.js @@ -4,10 +4,12 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const http = require('../../../utils/http') const sinon = require('sinon') +const testHttpMethod = require('../../../utils/test-http-method') function defaultOptions (modification = {}) { const options = { - recursive: false + recursive: false, + shardSplitThreshold: 1000 } Object.keys(modification).forEach(key => { @@ -17,7 +19,7 @@ function defaultOptions (modification = {}) { return options } -describe('rm', () => { +describe('/files/rm', () => { const path = '/foo' let ipfs @@ -29,6 +31,10 @@ describe('rm', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/rm?arg=${path}`, ipfs) + }) + it('should remove a path', async () => { await http({ method: 'POST', @@ -56,4 +62,19 @@ describe('rm', () => { }) ]) }) + + it('should remove a path with a different shard split threshold', async () => { + await http({ + method: 'POST', + url: `/api/v0/files/rm?arg=${path}&shardSplitThreshold=10` + }, { ipfs }) + + expect(ipfs.files.rm.callCount).to.equal(1) + expect(ipfs.files.rm.getCall(0).args).to.deep.equal([ + path, + defaultOptions({ + shardSplitThreshold: 10 + }) + ]) + }) }) diff --git a/packages/ipfs/test/http-api/inject/files/stat.js b/packages/ipfs/test/http-api/inject/mfs/stat.js similarity index 93% rename from packages/ipfs/test/http-api/inject/files/stat.js rename to packages/ipfs/test/http-api/inject/mfs/stat.js index ca5e3c14b3..0092f0287b 100644 --- a/packages/ipfs/test/http-api/inject/files/stat.js +++ b/packages/ipfs/test/http-api/inject/mfs/stat.js @@ -6,6 +6,7 @@ const http = require('../../../utils/http') const sinon = require('sinon') const CID = require('cids') const fileCid = new CID('bafybeigyov3nzxrqjismjpq7ghkkjorcmozy5rgaikvyieakoqpxfc3rvu') +const testHttpMethod = require('../../../utils/test-http-method') function defaultOptions (modification = {}) { const options = { @@ -21,7 +22,7 @@ function defaultOptions (modification = {}) { return options } -describe('stat', () => { +describe('/files/stat', () => { const path = '/foo' const stats = { cid: fileCid, @@ -42,6 +43,10 @@ describe('stat', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/stat?arg=${path}`, ipfs) + }) + it('should stat a path', async () => { const response = await http({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/files/touch.js b/packages/ipfs/test/http-api/inject/mfs/touch.js similarity index 92% rename from packages/ipfs/test/http-api/inject/files/touch.js rename to packages/ipfs/test/http-api/inject/mfs/touch.js index c8d3c7de66..65ee904032 100644 --- a/packages/ipfs/test/http-api/inject/files/touch.js +++ b/packages/ipfs/test/http-api/inject/mfs/touch.js @@ -4,6 +4,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const http = require('../../../utils/http') const sinon = require('sinon') +const testHttpMethod = require('../../../utils/test-http-method') function defaultOptions (modification = {}) { const options = { @@ -21,7 +22,7 @@ function defaultOptions (modification = {}) { return options } -describe('touch', () => { +describe('/files/touch', () => { const path = '/foo' const mtime = new Date(1000000) let ipfs @@ -34,6 +35,10 @@ describe('touch', () => { } }) + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/files/touch?arg=${path}&mtime=${mtime.getTime() / 1000}`, ipfs) + }) + it('should update the mtime for a file', async () => { await http({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/files/write.js b/packages/ipfs/test/http-api/inject/mfs/write.js similarity index 99% rename from packages/ipfs/test/http-api/inject/files/write.js rename to packages/ipfs/test/http-api/inject/mfs/write.js index 104b93a6ff..771a5dcc22 100644 --- a/packages/ipfs/test/http-api/inject/files/write.js +++ b/packages/ipfs/test/http-api/inject/mfs/write.js @@ -45,7 +45,7 @@ async function send (text, headers = {}) { } } -describe('write', () => { +describe('/files/write', () => { const path = '/foo' let ipfs let content diff --git a/packages/ipfs/test/http-api/inject/name.js b/packages/ipfs/test/http-api/inject/name.js index db53423a5a..0855e39a11 100644 --- a/packages/ipfs/test/http-api/inject/name.js +++ b/packages/ipfs/test/http-api/inject/name.js @@ -5,6 +5,7 @@ const CID = require('cids') const { expect } = require('interface-ipfs-core/src/utils/mocha') const checkAll = (bits) => string => bits.every(bit => string.includes(bit)) +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/name', function () { @@ -15,62 +16,74 @@ module.exports = (http) => { api = http.api._httpApi._apiServers[0] }) - it('should publish a record', async function () { - this.timeout(80 * 1000) - - const res = await api.inject({ - method: 'GET', - url: `/api/v0/name/publish?arg=${cid}&resolve=false` + describe('/publish', () => { + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/name/publish?arg=${cid}&resolve=false`) }) - expect(res).to.exist() - expect(res.result.Value).to.equal(`/ipfs/${cid}`) - }) + it('should publish a record', async function () { + this.timeout(80 * 1000) - it('should publish and resolve a record', async function () { - this.timeout(160 * 1000) + const res = await api.inject({ + method: 'POST', + url: `/api/v0/name/publish?arg=${cid}&resolve=false` + }) - let res = await api.inject({ - method: 'GET', - url: `/api/v0/name/publish?arg=${cid}&resolve=false` + expect(res).to.exist() + expect(res.result.Value).to.equal(`/ipfs/${cid}`) }) + }) - expect(res).to.exist() - expect(res.result.Value).to.equal(`/ipfs/${cid}`) - - res = await api.inject({ - method: 'GET', - url: '/api/v0/name/resolve' + describe('/resolve', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/name/resolve') }) - expect(res).to.exist() - expect(res.result.Path).to.satisfy(checkAll([`/ipfs/${cid}`])) - }) + it('should publish and resolve a record', async function () { + this.timeout(160 * 1000) + + let res = await api.inject({ + method: 'POST', + url: `/api/v0/name/publish?arg=${cid}&resolve=false` + }) - it('should publish and resolve a record with explicit CIDv1 in Base32', async function () { - this.timeout(160 * 1000) + expect(res).to.exist() + expect(res.result.Value).to.equal(`/ipfs/${cid}`) - // ensure PeerID is represented as CIDv1 in Base32 - const { id } = await http.api._ipfs.id() - let cidv1 = new CID(id) - if (cidv1.version === 0) cidv1 = cidv1.toV1() // future-proofing - const peerIdAsCidv1b32 = cidv1.toString('base32') + res = await api.inject({ + method: 'POST', + url: '/api/v0/name/resolve' + }) - let res = await api.inject({ - method: 'GET', - url: `/api/v0/name/publish?arg=${cid}&resolve=false` + expect(res).to.exist() + expect(res.result.Path).to.satisfy(checkAll([`/ipfs/${cid}`])) }) - expect(res).to.exist() - expect(res.result.Value).to.equal(`/ipfs/${cid}`) + it('should publish and resolve a record with explicit CIDv1 in Base32', async function () { + this.timeout(160 * 1000) - res = await api.inject({ - method: 'GET', - url: `/api/v0/name/resolve?arg=${peerIdAsCidv1b32}` - }) + // ensure PeerID is represented as CIDv1 in Base32 + const { id } = await http.api._ipfs.id() + let cidv1 = new CID(id) + if (cidv1.version === 0) cidv1 = cidv1.toV1() // future-proofing + const peerIdAsCidv1b32 = cidv1.toString('base32') + + let res = await api.inject({ + method: 'POST', + url: `/api/v0/name/publish?arg=${cid}&resolve=false` + }) + + expect(res).to.exist() + expect(res.result.Value).to.equal(`/ipfs/${cid}`) - expect(res).to.exist() - expect(res.result.Path).to.satisfy(checkAll([`/ipfs/${cid}`])) + res = await api.inject({ + method: 'POST', + url: `/api/v0/name/resolve?arg=${peerIdAsCidv1b32}` + }) + + expect(res).to.exist() + expect(res.result.Path).to.satisfy(checkAll([`/ipfs/${cid}`])) + }) }) }) } diff --git a/packages/ipfs/test/http-api/inject/object.js b/packages/ipfs/test/http-api/inject/object.js index 77370b2620..da400ae0da 100644 --- a/packages/ipfs/test/http-api/inject/object.js +++ b/packages/ipfs/test/http-api/inject/object.js @@ -7,6 +7,7 @@ const fs = require('fs') const FormData = require('form-data') const streamToPromise = require('stream-to-promise') const multibase = require('multibase') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/object', () => { @@ -17,6 +18,10 @@ module.exports = (http) => { }) describe('/new', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/new') + }) + it('returns value', async () => { const res = await api.inject({ method: 'POST', @@ -52,6 +57,10 @@ module.exports = (http) => { }) describe('/get', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/get') + }) + it('returns 400 for request without argument', async () => { const res = await api.inject({ method: 'POST', @@ -120,6 +129,10 @@ module.exports = (http) => { }) describe('/put', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/put') + }) + it('returns 400 if no node is provided', async () => { const form = new FormData() const headers = form.getHeaders() @@ -218,6 +231,10 @@ module.exports = (http) => { }) describe('/stat', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/stat') + }) + it('returns 400 for request without argument', async () => { const res = await api.inject({ method: 'POST', @@ -291,6 +308,10 @@ module.exports = (http) => { }) describe('/data', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/data') + }) + it('returns 400 for request without argument', async () => { const res = await api.inject({ method: 'POST', @@ -324,6 +345,10 @@ module.exports = (http) => { }) describe('/links', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/links') + }) + it('returns 400 for request without argument', async () => { const res = await api.inject({ method: 'POST', @@ -439,6 +464,10 @@ module.exports = (http) => { }) describe('/patch/append-data', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/patch/append-data') + }) + it('returns 400 for request without key', async () => { const res = await api.inject({ method: 'POST', @@ -556,6 +585,10 @@ module.exports = (http) => { }) describe('/patch/set-data', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/patch/set-data') + }) + it('returns 400 for request without key', async () => { const res = await api.inject({ method: 'POST', @@ -670,6 +703,10 @@ module.exports = (http) => { }) describe('/patch/add-link', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/patch/add-link') + }) + it('returns 400 for request without arguments', async () => { const res = await api.inject({ method: 'POST', @@ -764,6 +801,10 @@ module.exports = (http) => { }) describe('/patch/rm-link', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/object/patch/rm-link') + }) + it('returns 400 for request without arguments', async () => { const res = await api.inject({ method: 'POST', diff --git a/packages/ipfs/test/http-api/inject/pin.js b/packages/ipfs/test/http-api/inject/pin.js index f4a77ab1d6..37b4a2f63b 100644 --- a/packages/ipfs/test/http-api/inject/pin.js +++ b/packages/ipfs/test/http-api/inject/pin.js @@ -6,6 +6,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const FormData = require('form-data') const streamToPromise = require('stream-to-promise') const multibase = require('multibase') +const testHttpMethod = require('../../utils/test-http-method') // We use existing pin structure in the go-ipfs-repo fixture // so that we don't have to stream a bunch of object/put operations @@ -32,7 +33,7 @@ const pins = { } module.exports = (http) => { - describe('pin', function () { + describe('/pin', function () { this.timeout(20 * 1000) let api @@ -40,7 +41,11 @@ module.exports = (http) => { api = http.api._httpApi._apiServers[0] }) - describe('rm', () => { + describe('/rm', () => { + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/pin/rm?arg=${pins.root1}`) + }) + it('fails on invalid args', async () => { const res = await api.inject({ method: 'POST', @@ -131,7 +136,11 @@ module.exports = (http) => { }) }) - describe('add', () => { + describe('/add', () => { + it('only accepts POST', () => { + return testHttpMethod(`/api/v0/pin/add?arg=${pins.root1}`) + }) + it('fails on invalid args', async () => { const res = await api.inject({ method: 'POST', @@ -216,10 +225,14 @@ module.exports = (http) => { }) }) - describe('ls', () => { + describe('/ls', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/pin/ls') + }) + it('fails on invalid args', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/pin/ls?arg=invalid' }) @@ -229,7 +242,7 @@ module.exports = (http) => { it('finds all pinned objects', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/pin/ls' }) @@ -239,7 +252,7 @@ module.exports = (http) => { it('finds all pinned objects streaming', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/pin/ls?stream=true' }) @@ -250,7 +263,7 @@ module.exports = (http) => { it('finds specific pinned objects', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/pin/ls?arg=${pins.c1}` }) @@ -261,7 +274,7 @@ module.exports = (http) => { it('finds pins of type', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/pin/ls?type=recursive' }) diff --git a/packages/ipfs/test/http-api/inject/ping.js b/packages/ipfs/test/http-api/inject/ping.js index ca1429afe8..c4dc0c3d6b 100644 --- a/packages/ipfs/test/http-api/inject/ping.js +++ b/packages/ipfs/test/http-api/inject/ping.js @@ -3,6 +3,7 @@ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/ping', function () { @@ -12,9 +13,13 @@ module.exports = (http) => { api = http.api._httpApi._apiServers[0] }) + it('only accepts POST', () => { + return testHttpMethod('/api/v0/ping') + }) + it('returns 400 if both n and count are provided', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/ping?arg=peerid&n=1&count=1' }) @@ -23,7 +28,7 @@ module.exports = (http) => { it('returns 400 if arg is not provided', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/ping?count=1' }) @@ -34,7 +39,7 @@ module.exports = (http) => { this.timeout(90 * 1000) const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/ping?arg=peerid' }) diff --git a/packages/ipfs/test/http-api/inject/pubsub.js b/packages/ipfs/test/http-api/inject/pubsub.js index bb8198636b..6a5b9c6ebc 100644 --- a/packages/ipfs/test/http-api/inject/pubsub.js +++ b/packages/ipfs/test/http-api/inject/pubsub.js @@ -3,6 +3,7 @@ 'use strict' const { expect } = require('interface-ipfs-core/src/utils/mocha') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/pubsub', () => { @@ -17,9 +18,13 @@ module.exports = (http) => { }) describe('/sub', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/pubsub/sub') + }) + it('returns 400 if no topic is provided', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/pubsub/sub' }) @@ -43,7 +48,7 @@ module.exports = (http) => { }, 100) }) // const res = await api.inject({ - // method: 'GET', + // method: 'POST', // url: `/api/v0/pubsub/sub/${topic}` // }) // console.log(res.result) @@ -54,6 +59,10 @@ module.exports = (http) => { }) describe('/pub', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/pubsub/pub') + }) + it('returns 400 if no buffer is provided', async () => { const res = await api.inject({ method: 'POST', @@ -75,9 +84,13 @@ module.exports = (http) => { }) describe.skip('/ls', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/pubsub/ls') + }) + it('returns 200', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/pubsub/ls' }) expect(res.statusCode).to.equal(200) @@ -86,9 +99,13 @@ module.exports = (http) => { }) describe('/peers', () => { + it('only accepts POST', () => { + return testHttpMethod('/api/v0/pubsub/peers') + }) + it('returns 200 if not subscribed to a topic', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/pubsub/peers?arg=${topicNotSubscribed}` }) @@ -98,7 +115,7 @@ module.exports = (http) => { it('returns 200 with topic', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: `/api/v0/pubsub/peers?arg=${topic}` }) diff --git a/packages/ipfs/test/http-api/inject/resolve.js b/packages/ipfs/test/http-api/inject/resolve.js index e2f3f390e0..16f3d534bf 100644 --- a/packages/ipfs/test/http-api/inject/resolve.js +++ b/packages/ipfs/test/http-api/inject/resolve.js @@ -4,15 +4,20 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const FormData = require('form-data') const streamToPromise = require('stream-to-promise') +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { - describe('resolve', () => { + describe('/resolve', () => { let api before(() => { api = http.api._httpApi._apiServers[0] }) + it('only accepts POST', () => { + return testHttpMethod('/api/v0/resolve') + }) + it('should not resolve a path for invalid cid-base option', async () => { const form = new FormData() form.append('data', Buffer.from('TEST' + Date.now())) diff --git a/packages/ipfs/test/http-api/inject/version.js b/packages/ipfs/test/http-api/inject/version.js index fb6ec8f3ef..02a8b4003c 100644 --- a/packages/ipfs/test/http-api/inject/version.js +++ b/packages/ipfs/test/http-api/inject/version.js @@ -3,6 +3,7 @@ const { expect } = require('interface-ipfs-core/src/utils/mocha') const pkgversion = require('./../../../package.json').version +const testHttpMethod = require('../../utils/test-http-method') module.exports = (http) => { describe('/version', () => { @@ -12,9 +13,13 @@ module.exports = (http) => { api = http.api._httpApi._apiServers[0] }) + it('only accepts POST', () => { + return testHttpMethod('/api/v0/version') + }) + it('get the version', async () => { const res = await api.inject({ - method: 'GET', + method: 'POST', url: '/api/v0/version' }) diff --git a/packages/ipfs/test/http-api/interface.js b/packages/ipfs/test/http-api/interface.js index 0905467848..5554ea25c6 100644 --- a/packages/ipfs/test/http-api/interface.js +++ b/packages/ipfs/test/http-api/interface.js @@ -3,7 +3,7 @@ const tests = require('interface-ipfs-core') const factory = require('../utils/factory') -const { isNode } = require('ipfs-utils/src/env') +const { isNode, isBrowser } = require('ipfs-utils/src/env') /** @typedef { import("ipfsd-ctl").ControllerOptions } ControllerOptions */ describe('interface-ipfs-core over ipfs-http-client tests', function () { @@ -60,7 +60,18 @@ describe('interface-ipfs-core over ipfs-http-client tests', function () { sharding: true } } - })) + }), { + skip: isBrowser ? [{ + name: 'should make directory and specify mtime as hrtime', + reason: 'Not designed to run in the browser' + }, { + name: 'should write file and specify mtime as hrtime', + reason: 'Not designed to run in the browser' + }, { + name: 'should set mtime as hrtime', + reason: 'Not designed to run in the browser' + }] : [] + }) tests.key(commonFactory) diff --git a/packages/ipfs/test/http-api/routes.js b/packages/ipfs/test/http-api/routes.js index f952815ff8..6faeff31aa 100644 --- a/packages/ipfs/test/http-api/routes.js +++ b/packages/ipfs/test/http-api/routes.js @@ -9,6 +9,8 @@ const ncp = promisify(require('ncp').ncp) const path = require('path') const clean = require('../utils/clean') +require('./inject/mfs') + describe('HTTP API', () => { const repoExample = path.join(__dirname, '../fixtures/go-ipfs-repo') const repoTests = path.join(__dirname, '../repo-tests-run') diff --git a/packages/ipfs/test/utils/http.js b/packages/ipfs/test/utils/http.js index e3af496e60..4ee8c66363 100644 --- a/packages/ipfs/test/utils/http.js +++ b/packages/ipfs/test/utils/http.js @@ -1,15 +1,10 @@ 'use strict' -const Hapi = require('@hapi/hapi') -const routes = require('../../src/http/api/routes') +const HttpApi = require('../../src/http') -module.exports = (request, { ipfs }) => { - const server = Hapi.server() - server.app.ipfs = ipfs - - routes.forEach(route => { - server.route(route) - }) +module.exports = async (request, { ipfs } = {}) => { + const api = new HttpApi(ipfs) + const server = await api._createApiServer('127.0.0.1', 8080, ipfs) return server.inject(request) } diff --git a/packages/ipfs/test/utils/test-http-method.js b/packages/ipfs/test/utils/test-http-method.js new file mode 100644 index 0000000000..49d1d226a8 --- /dev/null +++ b/packages/ipfs/test/utils/test-http-method.js @@ -0,0 +1,24 @@ +'use strict' + +const { expect } = require('interface-ipfs-core/src/utils/mocha') +const http = require('./http') + +const METHODS = [ + 'GET', + 'PUT', + 'PATCH', + 'DELETE', + 'OPTIONS', + 'HEAD' +] + +module.exports = async (url, ipfs) => { + for (let i = 0; i < METHODS.length; i++) { + const res = await http({ + method: METHODS[i], + url + }, { ipfs }) + + expect(res.statusCode).to.equal(405) + } +}