From cd2ccb008c1b4f8e5b8982d008546766dcdef6e2 Mon Sep 17 00:00:00 2001 From: Marcin Rataj Date: Tue, 19 Mar 2019 14:58:53 +0100 Subject: [PATCH] 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. https://github.com/ipfs-shipyard/ipfs-companion/issues/664 License: MIT Signed-off-by: Marcin Rataj --- .gitignore | 2 + src/cli/commands/daemon.js | 8 ++-- src/cli/standalone-daemon.js | 90 ++++++++++++++++++++++++++++++++++++ src/http/index.js | 60 ++---------------------- 4 files changed, 100 insertions(+), 60 deletions(-) create mode 100644 src/cli/standalone-daemon.js diff --git a/.gitignore b/.gitignore index c548c9f1d6..d7223c6b87 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,8 @@ docs # Logs logs *.log +# npm pack +*.tgz coverage diff --git a/src/cli/commands/daemon.js b/src/cli/commands/daemon.js index 8e254164bb..8ace62fdbe 100644 --- a/src/cli/commands/daemon.js +++ b/src/cli/commands/daemon.js @@ -43,8 +43,8 @@ module.exports = { const repoPath = getRepoPath() // Required inline to reduce startup time - const HttpApi = require('../../http') - const api = new HttpApi({ + const StandaloneDaemon = require('../../cli/standalone-daemon') + const daemon = new StandaloneDaemon({ silent: argv.silent, repo: process.env.IPFS_PATH, offline: argv.offline, @@ -59,7 +59,7 @@ module.exports = { }) try { - await api.start() + await daemon.start() } catch (err) { if (err.code === 'ENOENT' && err.message.match(/uninitialized/i)) { print('Error: no initialized ipfs repo found in ' + repoPath) @@ -73,7 +73,7 @@ module.exports = { const cleanup = async () => { print(`Received interrupt signal, shutting down...`) - await api.stop() + await daemon.stop() process.exit(0) } diff --git a/src/cli/standalone-daemon.js b/src/cli/standalone-daemon.js new file mode 100644 index 0000000000..fde604241e --- /dev/null +++ b/src/cli/standalone-daemon.js @@ -0,0 +1,90 @@ +'use strict' + +const debug = require('debug') + +const IPFS = require('../core') +const HttpApi = require('../http') +const WStar = require('libp2p-webrtc-star') +const TCP = require('libp2p-tcp') +const MulticastDNS = require('libp2p-mdns') +const WS = require('libp2p-websockets') +const Bootstrap = require('libp2p-bootstrap') + +class StandaloneDaemon { + constructor (options) { + this._options = options || {} + this._log = debug('ipfs:daemon') + this._log.error = debug('ipfs:daemon:error') + + if (process.env.IPFS_MONITORING) { + // Setup debug metrics collection + const prometheusClient = require('prom-client') + const prometheusGcStats = require('prometheus-gc-stats') + const collectDefaultMetrics = prometheusClient.collectDefaultMetrics + collectDefaultMetrics({ timeout: 5000 }) + prometheusGcStats(prometheusClient.register)() + } + } + + async start () { + this._log('starting') + + const libp2p = { modules: {} } + + // Attempt to use any of the WebRTC versions available globally + let electronWebRTC + let wrtc + try { + electronWebRTC = require('electron-webrtc')() + } catch (err) { + this._log('failed to load optional electron-webrtc dependency') + } + try { + wrtc = require('wrtc') + } catch (err) { + this._log('failed to load optional webrtc dependency') + } + + if (wrtc || electronWebRTC) { + const using = wrtc ? 'wrtc' : 'electron-webrtc' + this._log(`Using ${using} for webrtc support`) + const wstar = new WStar({ wrtc: (wrtc || electronWebRTC) }) + libp2p.modules.transport = [TCP, WS, wstar] + libp2p.modules.peerDiscovery = [MulticastDNS, Bootstrap, wstar.discovery] + } + + // start the daemon + const ipfsOpts = Object.assign({ init: false }, this._options, { start: true, libp2p }) + const ipfs = new IPFS(ipfsOpts) + + await new Promise((resolve, reject) => { + ipfs.once('error', err => { + this._log('error starting core', err) + err.code = 'ENOENT' + reject(err) + }) + ipfs.once('start', resolve) + }) + + this._ipfs = ipfs + + // start HTTP servers (if API or Gateway is enabled in options) + const httpApi = new HttpApi(ipfs, ipfsOpts) + this._httpApi = await httpApi.start() + + this._log('started') + return this + } + + async stop () { + this._log('stopping') + await Promise.all([ + this._httpApi && this._httpApi.stop(), + this._ipfs && this._ipfs.stop() + ]) + this._log('stopped') + return this + } +} + +module.exports = StandaloneDaemon diff --git a/src/http/index.js b/src/http/index.js index 0692f4785c..b8dab7c10d 100644 --- a/src/http/index.js +++ b/src/http/index.js @@ -8,12 +8,6 @@ const promisify = require('promisify-es6') const toUri = require('multiaddr-to-uri') const toMultiaddr = require('uri-to-multiaddr') -const IPFS = require('../core') -const WStar = require('libp2p-webrtc-star') -const TCP = require('libp2p-tcp') -const MulticastDNS = require('libp2p-mdns') -const WS = require('libp2p-websockets') -const Bootstrap = require('libp2p-bootstrap') const errorHandler = require('./error-handler') function hapiInfoToMultiaddr (info) { @@ -46,62 +40,17 @@ function serverCreator (serverAddrs, createServer, ipfs) { } class HttpApi { - constructor (options) { + constructor (ipfs, options) { + this._ipfs = ipfs this._options = options || {} this._log = debug('ipfs:http-api') this._log.error = debug('ipfs:http-api:error') - - if (process.env.IPFS_MONITORING) { - // Setup debug metrics collection - const prometheusClient = require('prom-client') - const prometheusGcStats = require('prometheus-gc-stats') - const collectDefaultMetrics = prometheusClient.collectDefaultMetrics - collectDefaultMetrics({ timeout: 5000 }) - prometheusGcStats(prometheusClient.register)() - } } async start () { this._log('starting') - const libp2p = { modules: {} } - - // Attempt to use any of the WebRTC versions available globally - let electronWebRTC - let wrtc - try { - electronWebRTC = require('electron-webrtc')() - } catch (err) { - this._log('failed to load optional electron-webrtc dependency') - } - try { - wrtc = require('wrtc') - } catch (err) { - this._log('failed to load optional webrtc dependency') - } - - if (wrtc || electronWebRTC) { - const using = wrtc ? 'wrtc' : 'electron-webrtc' - this._log(`Using ${using} for webrtc support`) - const wstar = new WStar({ wrtc: (wrtc || electronWebRTC) }) - libp2p.modules.transport = [TCP, WS, wstar] - libp2p.modules.peerDiscovery = [MulticastDNS, Bootstrap, wstar.discovery] - } - - // start the daemon - const ipfsOpts = Object.assign({ init: false }, this._options, { start: true, libp2p }) - const ipfs = new IPFS(ipfsOpts) - - await new Promise((resolve, reject) => { - ipfs.once('error', err => { - this._log('error starting core', err) - err.code = 'ENOENT' - reject(err) - }) - ipfs.once('start', resolve) - }) - - this._ipfs = ipfs + const ipfs = this._ipfs const config = await ipfs.config.get() config.Addresses = config.Addresses || {} @@ -206,8 +155,7 @@ class HttpApi { const stopServers = servers => Promise.all((servers || []).map(s => s.stop())) await Promise.all([ stopServers(this._apiServers), - stopServers(this._gatewayServers), - this._ipfs && this._ipfs.stop() + stopServers(this._gatewayServers) ]) this._log('stopped') return this