Skip to content
This repository was archived by the owner on Mar 10, 2020. It is now read-only.

Commit 3764d06

Browse files
author
Alan Shaw
authored
feat: browser pubsub (#1059)
This PR enabled pubsub in the browser and paves the way for a switch to using `fetch` by default and allowing for cancelable requests via the use of `AbortController`. It's mostly the work done in ipfs-shipyard/js-ipfs-http-client-lite#1 but adapted a bit for use here. If approved, we can start work moving the other commands to use `fetch`. The work in https://github.com/ipfs-shipyard/js-ipfs-http-client-lite has proven the hard parts (uploading files) are all possible using the `fetch` API. Since `fetch` is promise based, when moving the other commands it makes sense to just switch to async/await as per ipfs/js-ipfs#1670 (and callbackify instead of promisify). Depends on: * [x] ipfs-inactive/interface-js-ipfs-core#505 resolves #518 refs ipfs/js-ipfs#2093 resolves #932 License: MIT Signed-off-by: Alan Shaw <alan.shaw@protocol.ai>
1 parent 07e6841 commit 3764d06

22 files changed

+618
-467
lines changed

package.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
"coverage": "npx nyc -r html npm run test:node -- --bail"
3434
},
3535
"dependencies": {
36+
"abort-controller": "^3.0.0",
3637
"async": "^2.6.1",
3738
"bignumber.js": "^9.0.0",
3839
"bl": "^3.0.0",
@@ -44,6 +45,7 @@
4445
"detect-node": "^2.0.4",
4546
"end-of-stream": "^1.4.1",
4647
"err-code": "^1.1.2",
48+
"explain-error": "^1.0.4",
4749
"flatmap": "0.0.3",
4850
"glob": "^7.1.3",
4951
"ipfs-block": "~0.8.1",
@@ -56,9 +58,12 @@
5658
"is-stream": "^2.0.0",
5759
"iso-stream-http": "~0.1.2",
5860
"iso-url": "~0.4.6",
61+
"iterable-ndjson": "^1.1.0",
5962
"just-kebab-case": "^1.1.0",
6063
"just-map-keys": "^1.1.0",
6164
"kind-of": "^6.0.2",
65+
"ky": "^0.11.2",
66+
"ky-universal": "^0.2.2",
6267
"lru-cache": "^5.1.1",
6368
"multiaddr": "^6.0.6",
6469
"multibase": "~0.6.0",
@@ -68,6 +73,7 @@
6873
"once": "^1.4.0",
6974
"peer-id": "~0.12.3",
7075
"peer-info": "~0.15.1",
76+
"promise-nodeify": "^3.0.1",
7177
"promisify-es6": "^1.0.3",
7278
"pull-defer": "~0.2.3",
7379
"pull-stream": "^3.6.9",
@@ -86,7 +92,7 @@
8692
"cross-env": "^5.2.0",
8793
"dirty-chai": "^2.0.1",
8894
"go-ipfs-dep": "0.4.21",
89-
"interface-ipfs-core": "^0.109.0",
95+
"interface-ipfs-core": "^0.111.0",
9096
"ipfsd-ctl": "~0.43.0",
9197
"nock": "^10.0.2",
9298
"stream-equal": "^1.1.1"

src/lib/configure.js

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
'use strict'
2+
/* eslint-env browser */
3+
4+
const ky = require('ky-universal').default
5+
const { isBrowser, isWebWorker } = require('ipfs-utils/src/env')
6+
const { toUri } = require('./multiaddr')
7+
const errorHandler = require('./error-handler')
8+
9+
// Set default configuration and call create function with them
10+
module.exports = create => config => {
11+
config = config || {}
12+
13+
if (typeof config === 'string') {
14+
config = { apiAddr: config }
15+
} else if (config.constructor && config.constructor.isMultiaddr) {
16+
config = { apiAddr: config }
17+
} else {
18+
config = { ...config }
19+
}
20+
21+
config.apiAddr = (config.apiAddr || getDefaultApiAddr(config)).toString()
22+
config.apiAddr = config.apiAddr.startsWith('/') ? toUri(config.apiAddr) : config.apiAddr
23+
config.apiPath = config.apiPath || config['api-path'] || '/api/v0'
24+
25+
return create({
26+
// TODO configure ky to use config.fetch when this is released:
27+
// https://github.com/sindresorhus/ky/pull/153
28+
ky: ky.extend({
29+
prefixUrl: config.apiAddr + config.apiPath,
30+
timeout: config.timeout || 60 * 1000,
31+
headers: config.headers,
32+
hooks: {
33+
afterResponse: [errorHandler]
34+
}
35+
}),
36+
...config
37+
})
38+
}
39+
40+
function getDefaultApiAddr ({ protocol, host, port }) {
41+
if (isBrowser || isWebWorker) {
42+
if (!protocol && !host && !port) { // Use current origin
43+
return ''
44+
}
45+
46+
if (!protocol) {
47+
protocol = location.protocol.startsWith('http')
48+
? location.protocol.split(':')[0]
49+
: 'http'
50+
}
51+
52+
host = host || location.hostname
53+
port = port || location.port
54+
55+
return `${protocol}://${host}${port ? ':' + port : ''}`
56+
}
57+
58+
return `${protocol || 'http'}://${host || 'localhost'}:${port || 5001}`
59+
}

src/lib/error-handler.js

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
'use strict'
2+
3+
const { HTTPError } = require('ky-universal')
4+
const log = require('debug')('ipfs-http-client:lib:error-handler')
5+
6+
function isJsonResponse (res) {
7+
return (res.headers.get('Content-Type') || '').startsWith('application/json')
8+
}
9+
10+
module.exports = async function errorHandler (response) {
11+
if (response.ok) return
12+
13+
let msg
14+
15+
try {
16+
if (isJsonResponse(response)) {
17+
const data = await response.json()
18+
log(data)
19+
msg = data.Message || data.message
20+
} else {
21+
msg = await response.text()
22+
}
23+
} catch (err) {
24+
log('Failed to parse error response', err)
25+
// Failed to extract/parse error message from response
26+
throw new HTTPError(response)
27+
}
28+
29+
if (!msg) throw new HTTPError(response)
30+
throw Object.assign(new Error(msg), { status: response.status })
31+
}

src/lib/multiaddr.js

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
'use strict'
2+
3+
// Convert a multiaddr to a URI
4+
// Assumes multiaddr is in a format that can be converted to a HTTP(s) URI
5+
exports.toUri = ma => {
6+
const parts = `${ma}`.split('/')
7+
const port = getPort(parts)
8+
return `${getProtocol(parts)}://${parts[2]}${port == null ? '' : ':' + port}`
9+
}
10+
11+
function getProtocol (maParts) {
12+
return maParts.indexOf('https') === -1 ? 'http' : 'https'
13+
}
14+
15+
function getPort (maParts) {
16+
const tcpIndex = maParts.indexOf('tcp')
17+
return tcpIndex === -1 ? null : maParts[tcpIndex + 1]
18+
}

src/lib/stream-to-iterable.js

+25
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
'use strict'
2+
3+
module.exports = function toIterable (body) {
4+
// Node.js stream
5+
if (body[Symbol.asyncIterator]) return body
6+
7+
// Browser ReadableStream
8+
if (body.getReader) {
9+
return (async function * () {
10+
const reader = body.getReader()
11+
12+
try {
13+
while (true) {
14+
const { done, value } = await reader.read()
15+
if (done) return
16+
yield value
17+
}
18+
} finally {
19+
reader.releaseLock()
20+
}
21+
})()
22+
}
23+
24+
throw new Error('unknown stream')
25+
}

src/pubsub.js

-212
This file was deleted.

0 commit comments

Comments
 (0)