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

Decouple HTTP Servers from cli/commands/daemon #1950

Merged
merged 4 commits into from
Apr 12, 2019
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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')
lidel marked this conversation as resolved.
Show resolved Hide resolved
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)
})
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Argh, any chance you can help @pkafei writing a test for #1878 so we don't need to do this?

(completely optional and I won't block merging this!)

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we pair program and work on the test together?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@alanshaw I'd prefer to merge this PR as-is and improve it later.

@pkafei Sure! I am booked today, but feel free to grab time slot next week: https://calendly.com/pl-lidel/ :)


this._ipfs = ipfs

// start HTTP servers (if API or Gateway is enabled in options)
const httpApi = new HttpApi(ipfs, Object.assign({ announceListeners: true }, 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
58 changes: 8 additions & 50 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 All @@ -119,14 +77,15 @@ class HttpApi {
const gatewayAddrs = config.Addresses.Gateway
this._gatewayServers = await serverCreator(gatewayAddrs, this._createGatewayServer, ipfs)

const announce = this._options.announceListeners ? ipfs._print : this._log
this._apiServers.forEach(apiServer => {
ipfs._print('API listening on %s', apiServer.info.ma)
announce('API listening on %s', apiServer.info.ma.toString())
})
this._gatewayServers.forEach(gatewayServer => {
ipfs._print('Gateway (read only) listening on %s', gatewayServer.info.ma)
announce('Gateway (read only) listening on %s', gatewayServer.info.ma.toString())
})
this._apiServers.forEach(apiServer => {
ipfs._print('Web UI available at %s', toUri(apiServer.info.ma) + '/webui')
announce('Web UI available at %s', toUri(apiServer.info.ma) + '/webui')
lidel marked this conversation as resolved.
Show resolved Hide resolved
})
this._log('started')
return this
Expand Down Expand Up @@ -208,8 +167,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