Skip to content

Commit

Permalink
refactor: decouple HttpApi from cli/commands/daemon
Browse files Browse the repository at this point in the history
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 <lidel@lidel.org>
  • Loading branch information
lidel committed Apr 11, 2019
1 parent 04bbd24 commit a3e0bd4
Show file tree
Hide file tree
Showing 4 changed files with 100 additions and 51 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ docs
# Logs
logs
*.log
# npm pack
*.tgz

coverage

Expand Down
8 changes: 4 additions & 4 deletions src/cli/commands/daemon.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,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,
Expand All @@ -60,7 +60,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)
Expand All @@ -74,7 +74,7 @@ module.exports = {

const cleanup = async () => {
print(`Received interrupt signal, shutting down...`)
await api.stop()
await daemon.stop()
process.exit(0)
}

Expand Down
90 changes: 90 additions & 0 deletions src/cli/standalone-daemon.js
Original file line number Diff line number Diff line change
@@ -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
51 changes: 4 additions & 47 deletions src/http/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -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')
const LOG = 'ipfs:http-api'
const LOG_ERROR = 'ipfs:http-api:error'
Expand Down Expand Up @@ -48,7 +42,8 @@ function serverCreator (serverAddrs, createServer, ipfs) {
}

class HttpApi {
constructor (options) {
constructor (ipfs, options) {
this._ipfs = ipfs
this._options = options || {}
this._log = debug(LOG)
this._log.error = debug(LOG_ERROR)
Expand All @@ -66,44 +61,7 @@ class HttpApi {
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 || {}
Expand Down Expand Up @@ -208,8 +166,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
Expand Down

0 comments on commit a3e0bd4

Please sign in to comment.