UNPKG

ipfs

Version:

JavaScript implementation of the IPFS specification

161 lines (132 loc) 4.52 kB
'use strict' const Hapi = require('@hapi/hapi') const Pino = require('hapi-pino') const debug = require('debug') const multiaddr = require('multiaddr') const toMultiaddr = require('uri-to-multiaddr') const errorHandler = require('./error-handler') const LOG = 'ipfs:http-api' const LOG_ERROR = 'ipfs:http-api:error' function hapiInfoToMultiaddr (info) { let hostname = info.host let uri = info.uri // ipv6 fix if (hostname.includes(':') && !hostname.startsWith('[')) { // hapi 16 produces invalid URI for ipv6 // we fix it here by restoring missing square brackets hostname = `[${hostname}]` uri = uri.replace(`://${info.host}`, `://${hostname}`) } return toMultiaddr(uri) } function serverCreator (serverAddrs, createServer, ipfs) { serverAddrs = serverAddrs || [] // just in case the address is just string serverAddrs = Array.isArray(serverAddrs) ? serverAddrs : [serverAddrs] const processServer = async address => { const addrParts = address.split('/') const server = await createServer(addrParts[2], addrParts[4], ipfs) await server.start() server.info.ma = hapiInfoToMultiaddr(server.info) return server } return Promise.all(serverAddrs.map(processServer)) } class HttpApi { constructor (ipfs, options) { this._ipfs = ipfs this._options = options || {} this._log = debug(LOG) this._log.error = debug(LOG_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 ipfs = this._ipfs const config = await ipfs.config.get() config.Addresses = config.Addresses || {} const apiAddrs = config.Addresses.API this._apiServers = await serverCreator(apiAddrs, this._createApiServer, ipfs) const gatewayAddrs = config.Addresses.Gateway this._gatewayServers = await serverCreator(gatewayAddrs, this._createGatewayServer, ipfs) this._log('started') return this } async _createApiServer (host, port, ipfs) { const server = Hapi.server({ host, port, // CORS is enabled by default // TODO: shouldn't, fix this routes: { cors: true } }) server.app.ipfs = ipfs await server.register({ plugin: Pino, options: { prettyPrint: process.env.NODE_ENV !== 'production', logEvents: ['onPostStart', 'onPostStop', 'response', 'request-error'], level: debug.enabled(LOG) ? 'debug' : (debug.enabled(LOG_ERROR) ? 'error' : 'fatal') } }) const setHeader = (key, value) => { server.ext('onPreResponse', (request, h) => { const { response } = request if (response.isBoom) { response.output.headers[key] = value } else { response.header(key, value) } return h.continue }) } // Set default headers setHeader('Access-Control-Allow-Headers', 'X-Stream-Output, X-Chunked-Output, X-Content-Length') setHeader('Access-Control-Expose-Headers', 'X-Stream-Output, X-Chunked-Output, X-Content-Length') server.route(require('./api/routes')) errorHandler(server) return server } async _createGatewayServer (host, port, ipfs) { const server = Hapi.server({ host, port }) server.app.ipfs = ipfs await server.register({ plugin: Pino, options: { prettyPrint: Boolean(debug.enabled(LOG)), logEvents: ['onPostStart', 'onPostStop', 'response', 'request-error'], level: debug.enabled(LOG) ? 'debug' : (debug.enabled(LOG_ERROR) ? 'error' : 'fatal') } }) server.route(require('./gateway/routes')) return server } get apiAddr () { if (!this._apiServers || !this._apiServers.length) { throw new Error('API address unavailable - server is not started') } return multiaddr('/ip4/127.0.0.1/tcp/' + this._apiServers[0].info.port) } async stop () { this._log('stopping') const stopServers = servers => Promise.all((servers || []).map(s => s.stop())) await Promise.all([ stopServers(this._apiServers), stopServers(this._gatewayServers) ]) this._log('stopped') return this } } module.exports = HttpApi