Skip to content
This repository was archived by the owner on Feb 12, 2024. It is now read-only.

Commit 5f62e99

Browse files
lidelAlan Shaw
authored and
Alan Shaw
committed
refactor: decouple HTTP servers from cli/commands/daemon (#1950)
* refactor: decouple HttpApi from cli/commands/daemon In the past API was exposed via HTTP Server only when jsipfs daemon was run from the commandline, so src/http/index.js was also responsible for orchestration that is not related to HTTP itself. This refactor moves code that is not related to HTTP Servers into standalone-daemon.js, which is easier to reason about, and unlocks use of HttpApi in contexts other than commandline jsipfs daemon, such as Firefox with libdweb or Chromium-based web browser with chrome.sockets APIs. Refs. ipfs/ipfs-companion#664 License: MIT Signed-off-by: Marcin Rataj <[email protected]> * fix: print HTTP listeners only when run as daemon This changes behavior in web browser. Instead of printing to console.log, it uses proper debug-based logger. Old behavior in terminal (when run via `jsipfs daemon`) does not change. License: MIT Signed-off-by: Marcin Rataj <[email protected]> * test: use StandaloneDaemon in test/http-api,gateway This replaces durect use of HttpApi with StandaloneDaemon, restoring all existing tests to operational state. License: MIT Signed-off-by: Marcin Rataj <[email protected]> * refactor: rename StandaloneDaemon to Daemon License: MIT Signed-off-by: Marcin Rataj <[email protected]>
1 parent 2b1095a commit 5f62e99

22 files changed

+137
-88
lines changed

.gitignore

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,8 @@ docs
66
# Logs
77
logs
88
*.log
9+
# npm pack
10+
*.tgz
911

1012
coverage
1113

src/cli/commands/daemon.js

+14-4
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
'use strict'
22

33
const os = require('os')
4+
const toUri = require('multiaddr-to-uri')
45
const { getRepoPath, print, ipfsPathHelp } = require('../utils')
56

67
module.exports = {
@@ -44,8 +45,8 @@ module.exports = {
4445
const repoPath = getRepoPath()
4546

4647
// Required inline to reduce startup time
47-
const HttpApi = require('../../http')
48-
const api = new HttpApi({
48+
const Daemon = require('../../cli/daemon')
49+
const daemon = new Daemon({
4950
silent: argv.silent,
5051
repo: process.env.IPFS_PATH,
5152
offline: argv.offline,
@@ -60,7 +61,16 @@ module.exports = {
6061
})
6162

6263
try {
63-
await api.start()
64+
await daemon.start()
65+
daemon._httpApi._apiServers.forEach(apiServer => {
66+
print(`API listening on ${apiServer.info.ma.toString()}`)
67+
})
68+
daemon._httpApi._gatewayServers.forEach(gatewayServer => {
69+
print(`Gateway (read only) listening on ${gatewayServer.info.ma.toString()}`)
70+
})
71+
daemon._httpApi._apiServers.forEach(apiServer => {
72+
print(`Web UI available at ${toUri(apiServer.info.ma)}/webui`)
73+
})
6474
} catch (err) {
6575
if (err.code === 'ENOENT' && err.message.match(/uninitialized/i)) {
6676
print('Error: no initialized ipfs repo found in ' + repoPath)
@@ -74,7 +84,7 @@ module.exports = {
7484

7585
const cleanup = async () => {
7686
print(`Received interrupt signal, shutting down...`)
77-
await api.stop()
87+
await daemon.stop()
7888
process.exit(0)
7989
}
8090

src/cli/daemon.js

+96
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
'use strict'
2+
3+
const debug = require('debug')
4+
5+
const IPFS = require('../core')
6+
const HttpApi = require('../http')
7+
const WStar = require('libp2p-webrtc-star')
8+
const TCP = require('libp2p-tcp')
9+
const MulticastDNS = require('libp2p-mdns')
10+
const WS = require('libp2p-websockets')
11+
const Bootstrap = require('libp2p-bootstrap')
12+
const promisify = require('promisify-es6')
13+
14+
class Daemon {
15+
constructor (options) {
16+
this._options = options || {}
17+
this._log = debug('ipfs:daemon')
18+
this._log.error = debug('ipfs:daemon:error')
19+
20+
if (process.env.IPFS_MONITORING) {
21+
// Setup debug metrics collection
22+
const prometheusClient = require('prom-client')
23+
const prometheusGcStats = require('prometheus-gc-stats')
24+
const collectDefaultMetrics = prometheusClient.collectDefaultMetrics
25+
collectDefaultMetrics({ timeout: 5000 })
26+
prometheusGcStats(prometheusClient.register)()
27+
}
28+
}
29+
30+
async start () {
31+
this._log('starting')
32+
33+
const libp2p = { modules: {} }
34+
35+
// Attempt to use any of the WebRTC versions available globally
36+
let electronWebRTC
37+
let wrtc
38+
try {
39+
electronWebRTC = require('electron-webrtc')()
40+
} catch (err) {
41+
this._log('failed to load optional electron-webrtc dependency')
42+
}
43+
try {
44+
wrtc = require('wrtc')
45+
} catch (err) {
46+
this._log('failed to load optional webrtc dependency')
47+
}
48+
49+
if (wrtc || electronWebRTC) {
50+
const using = wrtc ? 'wrtc' : 'electron-webrtc'
51+
this._log(`Using ${using} for webrtc support`)
52+
const wstar = new WStar({ wrtc: (wrtc || electronWebRTC) })
53+
libp2p.modules.transport = [TCP, WS, wstar]
54+
libp2p.modules.peerDiscovery = [MulticastDNS, Bootstrap, wstar.discovery]
55+
}
56+
57+
// start the daemon
58+
const ipfsOpts = Object.assign({ init: false }, this._options, { start: true, libp2p })
59+
const ipfs = new IPFS(ipfsOpts)
60+
61+
await new Promise((resolve, reject) => {
62+
ipfs.once('error', err => {
63+
this._log('error starting core', err)
64+
err.code = 'ENOENT'
65+
reject(err)
66+
})
67+
ipfs.once('start', resolve)
68+
})
69+
70+
this._ipfs = ipfs
71+
72+
// start HTTP servers (if API or Gateway is enabled in options)
73+
const httpApi = new HttpApi(ipfs, ipfsOpts)
74+
this._httpApi = await httpApi.start()
75+
76+
// for the CLI to know the where abouts of the API
77+
if (this._httpApi._apiServers.length) {
78+
await promisify(ipfs._repo.apiAddr.set)(this._httpApi._apiServers[0].info.ma)
79+
}
80+
81+
this._log('started')
82+
return this
83+
}
84+
85+
async stop () {
86+
this._log('stopping')
87+
await Promise.all([
88+
this._httpApi && this._httpApi.stop(),
89+
this._ipfs && this._ipfs.stop()
90+
])
91+
this._log('stopped')
92+
return this
93+
}
94+
}
95+
96+
module.exports = Daemon

src/http/index.js

+4-63
Original file line numberDiff line numberDiff line change
@@ -4,16 +4,8 @@ const Hapi = require('hapi')
44
const Pino = require('hapi-pino')
55
const debug = require('debug')
66
const multiaddr = require('multiaddr')
7-
const promisify = require('promisify-es6')
8-
const toUri = require('multiaddr-to-uri')
97
const toMultiaddr = require('uri-to-multiaddr')
108

11-
const IPFS = require('../core')
12-
const WStar = require('libp2p-webrtc-star')
13-
const TCP = require('libp2p-tcp')
14-
const MulticastDNS = require('libp2p-mdns')
15-
const WS = require('libp2p-websockets')
16-
const Bootstrap = require('libp2p-bootstrap')
179
const errorHandler = require('./error-handler')
1810
const LOG = 'ipfs:http-api'
1911
const LOG_ERROR = 'ipfs:http-api:error'
@@ -48,7 +40,8 @@ function serverCreator (serverAddrs, createServer, ipfs) {
4840
}
4941

5042
class HttpApi {
51-
constructor (options) {
43+
constructor (ipfs, options) {
44+
this._ipfs = ipfs
5245
this._options = options || {}
5346
this._log = debug(LOG)
5447
this._log.error = debug(LOG_ERROR)
@@ -66,68 +59,17 @@ class HttpApi {
6659
async start () {
6760
this._log('starting')
6861

69-
const libp2p = { modules: {} }
70-
71-
// Attempt to use any of the WebRTC versions available globally
72-
let electronWebRTC
73-
let wrtc
74-
try {
75-
electronWebRTC = require('electron-webrtc')()
76-
} catch (err) {
77-
this._log('failed to load optional electron-webrtc dependency')
78-
}
79-
try {
80-
wrtc = require('wrtc')
81-
} catch (err) {
82-
this._log('failed to load optional webrtc dependency')
83-
}
84-
85-
if (wrtc || electronWebRTC) {
86-
const using = wrtc ? 'wrtc' : 'electron-webrtc'
87-
this._log(`Using ${using} for webrtc support`)
88-
const wstar = new WStar({ wrtc: (wrtc || electronWebRTC) })
89-
libp2p.modules.transport = [TCP, WS, wstar]
90-
libp2p.modules.peerDiscovery = [MulticastDNS, Bootstrap, wstar.discovery]
91-
}
92-
93-
// start the daemon
94-
const ipfsOpts = Object.assign({ init: false }, this._options, { start: true, libp2p })
95-
const ipfs = new IPFS(ipfsOpts)
96-
97-
await new Promise((resolve, reject) => {
98-
ipfs.once('error', err => {
99-
this._log('error starting core', err)
100-
err.code = 'ENOENT'
101-
reject(err)
102-
})
103-
ipfs.once('start', resolve)
104-
})
105-
106-
this._ipfs = ipfs
62+
const ipfs = this._ipfs
10763

10864
const config = await ipfs.config.get()
10965
config.Addresses = config.Addresses || {}
11066

11167
const apiAddrs = config.Addresses.API
11268
this._apiServers = await serverCreator(apiAddrs, this._createApiServer, ipfs)
11369

114-
// for the CLI to know the where abouts of the API
115-
if (this._apiServers.length) {
116-
await promisify(ipfs._repo.apiAddr.set)(this._apiServers[0].info.ma)
117-
}
118-
11970
const gatewayAddrs = config.Addresses.Gateway
12071
this._gatewayServers = await serverCreator(gatewayAddrs, this._createGatewayServer, ipfs)
12172

122-
this._apiServers.forEach(apiServer => {
123-
ipfs._print('API listening on %s', apiServer.info.ma)
124-
})
125-
this._gatewayServers.forEach(gatewayServer => {
126-
ipfs._print('Gateway (read only) listening on %s', gatewayServer.info.ma)
127-
})
128-
this._apiServers.forEach(apiServer => {
129-
ipfs._print('Web UI available at %s', toUri(apiServer.info.ma) + '/webui')
130-
})
13173
this._log('started')
13274
return this
13375
}
@@ -208,8 +150,7 @@ class HttpApi {
208150
const stopServers = servers => Promise.all((servers || []).map(s => s.stop()))
209151
await Promise.all([
210152
stopServers(this._apiServers),
211-
stopServers(this._gatewayServers),
212-
this._ipfs && this._ipfs.stop()
153+
stopServers(this._gatewayServers)
213154
])
214155
this._log('stopped')
215156
return this

test/gateway/index.js

+3-3
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const chai = require('chai')
55
const dirtyChai = require('dirty-chai')
66
const expect = chai.expect
77
chai.use(dirtyChai)
8-
const API = require('../../src/http')
8+
const Daemon = require('../../src/cli/daemon')
99
const loadFixture = require('aegir/fixtures')
1010
const os = require('os')
1111
const path = require('path')
@@ -33,7 +33,7 @@ describe('HTTP Gateway', function () {
3333
this.timeout(60 * 1000)
3434
const repoPath = path.join(os.tmpdir(), '/ipfs-' + hat())
3535

36-
http.api = new API({
36+
http.api = new Daemon({
3737
repo: repoPath,
3838
init: true,
3939
config: {
@@ -60,7 +60,7 @@ describe('HTTP Gateway', function () {
6060

6161
await http.api.start()
6262

63-
gateway = http.api._gatewayServers[0]
63+
gateway = http.api._httpApi._gatewayServers[0]
6464

6565
// QmbQD7EMEL1zeebwBsWEfA3ndgSS6F7S6iTuwuqasPgVRi
6666
await http.api._ipfs.add([

test/http-api/inject/bitswap.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = (http) => {
1212
let api
1313

1414
before(() => {
15-
api = http.api._apiServers[0]
15+
api = http.api._httpApi._apiServers[0]
1616
})
1717

1818
before(async function () {

test/http-api/inject/block.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = (http) => {
1313
let api
1414

1515
before(() => {
16-
api = http.api._apiServers[0]
16+
api = http.api._httpApi._apiServers[0]
1717
})
1818

1919
describe('/block/put', () => {

test/http-api/inject/bootstrap.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ module.exports = (http) => {
1111
let api
1212

1313
before(() => {
14-
api = http.api._apiServers[0]
14+
api = http.api._httpApi._apiServers[0]
1515
return api.inject({
1616
method: 'GET',
1717
url: '/api/v0/bootstrap/add/default'

test/http-api/inject/config.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module.exports = (http) => {
1717

1818
before(() => {
1919
updatedConfig = () => JSON.parse(fs.readFileSync(configPath, 'utf8'))
20-
api = http.api._apiServers[0]
20+
api = http.api._httpApi._apiServers[0]
2121
})
2222

2323
after(() => {

test/http-api/inject/dag.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,7 @@ module.exports = (http) => {
3333
let api
3434

3535
before(() => {
36-
api = http.api._apiServers[0]
36+
api = http.api._httpApi._apiServers[0]
3737
})
3838

3939
describe('/dag/get', () => {

test/http-api/inject/dht.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ module.exports = (http) => {
1212
let api
1313

1414
before(() => {
15-
api = http.api._apiServers[0]
15+
api = http.api._httpApi._apiServers[0]
1616
})
1717

1818
describe('/findpeer', () => {

test/http-api/inject/dns.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = (http) => {
88
let api
99

1010
before(() => {
11-
api = http.api._apiServers[0]
11+
api = http.api._httpApi._apiServers[0]
1212
})
1313

1414
it('resolve ipfs.io dns', async () => {

test/http-api/inject/files.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ module.exports = (http) => {
1313
let api
1414

1515
before(() => {
16-
api = http.api._apiServers[0]
16+
api = http.api._httpApi._apiServers[0]
1717
})
1818

1919
describe('/add', () => {

test/http-api/inject/id.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ module.exports = (http) => {
88
let api
99

1010
before(() => {
11-
api = http.api._apiServers[0]
11+
api = http.api._httpApi._apiServers[0]
1212
})
1313

1414
it('get the id', async () => {

test/http-api/inject/name.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ module.exports = (http) => {
1616
let api
1717

1818
before(() => {
19-
api = http.api._apiServers[0]
19+
api = http.api._httpApi._apiServers[0]
2020
})
2121

2222
it('should publish a record', async function () {

test/http-api/inject/object.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ module.exports = (http) => {
1717
let api
1818

1919
before('api', () => {
20-
api = http.api._apiServers[0]
20+
api = http.api._httpApi._apiServers[0]
2121
})
2222

2323
describe('/new', () => {

test/http-api/inject/pin.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ module.exports = (http) => {
3737
let api
3838

3939
before(() => {
40-
api = http.api._apiServers[0]
40+
api = http.api._httpApi._apiServers[0]
4141
})
4242

4343
describe('rm', () => {

0 commit comments

Comments
 (0)