@@ -16,32 +16,16 @@ const Ipfs = require('ipfs')
16
16
const HttpApi = require ( 'ipfs/src/http' )
17
17
const multiaddr = require ( 'multiaddr' )
18
18
const maToUri = require ( 'multiaddr-to-uri' )
19
+ const getPort = require ( 'get-port' )
19
20
20
21
const { optionDefaults } = require ( '../options' )
21
22
22
23
// js-ipfs with embedded hapi HTTP server
23
24
let node = null
24
25
let nodeHttpApi = null
25
26
26
- // additional servers for smoke-tests
27
- // let httpServer = null
28
- // let hapiServer = null
29
-
30
- exports . init = function init ( opts ) {
31
- /*
32
- // TEST RAW require('http') SERVER
33
- if (!httpServer) {
34
- httpServer = startRawHttpServer(9091)
35
- }
36
- // TEST require('@hapi/hapi') HTTP SERVER (same as in js-ipfs)
37
- if (!hapiServer) {
38
- hapiServer = startRawHapiServer(9092)
39
- }
40
- */
41
- log ( 'init embedded:chromesockets' )
42
-
27
+ async function buildConfig ( opts ) {
43
28
const defaultOpts = JSON . parse ( optionDefaults . ipfsNodeConfig )
44
-
45
29
defaultOpts . libp2p = {
46
30
config : {
47
31
dht : {
@@ -50,9 +34,31 @@ exports.init = function init (opts) {
50
34
}
51
35
}
52
36
}
53
-
54
37
const userOpts = JSON . parse ( opts . ipfsNodeConfig )
55
- const ipfsOpts = mergeOptions . call ( { concatArrays : true } , defaultOpts , userOpts , { start : false } )
38
+ const ipfsNodeConfig = mergeOptions . call ( { concatArrays : true } , defaultOpts , userOpts , { start : false } )
39
+
40
+ // Detect when API or Gateway port is not available (taken by something else)
41
+ // We find the next free port and update configuration to use it instead
42
+ const multiaddr2port = ( ma ) => parseInt ( new URL ( multiaddr2httpUrl ( ma ) ) . port , 10 )
43
+ const gatewayPort = multiaddr2port ( ipfsNodeConfig . config . Addresses . Gateway )
44
+ const apiPort = multiaddr2port ( ipfsNodeConfig . config . Addresses . API )
45
+ log ( `checking if ports are available: api: ${ apiPort } , gateway: ${ gatewayPort } ` )
46
+ const freeGatewayPort = await getPort ( { port : getPort . makeRange ( gatewayPort , gatewayPort + 100 ) } )
47
+ const freeApiPort = await getPort ( { port : getPort . makeRange ( apiPort , apiPort + 100 ) } )
48
+ if ( gatewayPort !== freeGatewayPort || apiPort !== freeApiPort ) {
49
+ log ( `updating config to available ports: api: ${ freeApiPort } , gateway: ${ freeGatewayPort } ` )
50
+ const addrs = ipfsNodeConfig . config . Addresses
51
+ addrs . Gateway = addrs . Gateway . replace ( gatewayPort . toString ( ) , freeGatewayPort . toString ( ) )
52
+ addrs . API = addrs . API . replace ( apiPort . toString ( ) , freeApiPort . toString ( ) )
53
+ }
54
+
55
+ return ipfsNodeConfig
56
+ }
57
+
58
+ exports . init = async function init ( opts ) {
59
+ log ( 'init embedded:chromesockets' )
60
+
61
+ const ipfsOpts = await buildConfig ( opts )
56
62
log ( 'creating js-ipfs with opts: ' , ipfsOpts )
57
63
node = new Ipfs ( ipfsOpts )
58
64
@@ -95,7 +101,6 @@ async function updateConfigWithHttpEndpoints (ipfs, opts) {
95
101
const apiMa = await ipfs . config . get ( 'Addresses.API' )
96
102
const httpGateway = multiaddr2httpUrl ( gwMa )
97
103
const httpApi = multiaddr2httpUrl ( apiMa )
98
- log ( `updating extension configuration to Gateway=${ httpGateway } and API=${ httpApi } ` )
99
104
// update ports in JSON configuration for embedded js-ipfs
100
105
const ipfsNodeConfig = JSON . parse ( localConfig . ipfsNodeConfig )
101
106
ipfsNodeConfig . config . Addresses . Gateway = gwMa
@@ -105,87 +110,44 @@ async function updateConfigWithHttpEndpoints (ipfs, opts) {
105
110
ipfsApiUrl : httpApi ,
106
111
ipfsNodeConfig : JSON . stringify ( ipfsNodeConfig , null , 2 )
107
112
}
108
- // update current runtime config (in place, effective without restart )
113
+ // update current runtime config (in place)
109
114
Object . assign ( opts , configChanges )
110
- // update user config in storage (effective on next run)
115
+ // update user config in storage (triggers async client restart if ports changed)
116
+ log ( `synchronizing ipfsNodeConfig with customGatewayUrl (${ configChanges . customGatewayUrl } ) and ipfsApiUrl (${ configChanges . ipfsApiUrl } )` )
111
117
await browser . storage . local . set ( configChanges )
112
118
}
113
119
}
114
120
115
121
exports . destroy = async function ( ) {
116
122
log ( 'destroy: embedded:chromesockets' )
117
123
118
- /*
119
- if (httpServer) {
120
- httpServer.close()
121
- httpServer = null
122
- }
123
- if (hapiServer) {
124
- try {
125
- await hapiServer.stop({ timeout: 1000 })
126
- } catch (err) {
127
- if (err) {
128
- console.error(`[ipfs-companion] failed to stop hapi`, err)
129
- } else {
130
- console.log('[ipfs-companion] hapi server stopped')
131
- }
132
- }
133
- hapiServer = null
134
- }
135
- */
136
-
137
124
if ( nodeHttpApi ) {
138
125
try {
139
126
await nodeHttpApi . stop ( )
140
127
} catch ( err ) {
141
- log . error ( 'failed to stop HttpApi' , err )
128
+ // TODO: needs upstream fix like https://github.com/ipfs/js-ipfs/issues/2257
129
+ if ( err . message !== 'Cannot stop server while in stopping phase' ) {
130
+ log . error ( 'failed to stop HttpApi' , err )
131
+ }
142
132
}
143
133
nodeHttpApi = null
144
134
}
145
135
if ( node ) {
146
- await node . stop ( )
147
- node = null
148
- }
149
- }
150
-
151
- /*
152
- // Quick smoke-test to confirm require('http') works for MVP
153
- function startRawHttpServer (port) {
154
- const http = require('http') // courtesy of chrome-net
155
- const httpServer = http.createServer(function (req, res) {
156
- res.writeHead(200, { 'Content-Type': 'text/plain' })
157
- res.end('Hello from ipfs-companion exposing HTTP via chrome.sockets in Brave :-)\n')
158
- })
159
- httpServer.listen(port, '127.0.0.1')
160
- console.log(`[ipfs-companion] require('http') HTTP server on http://127.0.0.1:${port}`)
161
- return httpServer
162
- }
163
-
164
- function startRawHapiServer (port) {
165
- let options = {
166
- host: '127.0.0.1',
167
- port,
168
- debug: {
169
- log: ['*'],
170
- request: ['*']
171
- }
172
- }
173
- const initHapi = async () => {
174
- // hapi v18 (js-ipfs >=v0.35.0-pre.0)
175
- const Hapi = require('@hapi /hapi') // courtesy of js-ipfs
176
- const hapiServer = new Hapi.Server(options)
177
- await hapiServer.route({
178
- method: 'GET',
179
- path: '/',
180
- handler: (request, h) => {
181
- console.log('[ipfs-companion] hapiServer processing request', request)
182
- return 'Hello from ipfs-companion+Hapi.js exposing HTTP via chrome.sockets in Brave :-)'
183
- }
136
+ const stopped = new Promise ( ( resolve , reject ) => {
137
+ node . on ( 'stop' , resolve )
138
+ node . on ( 'error' , reject )
184
139
} )
185
- await hapiServer.start()
186
- console.log(`[ipfs-companion] require('@hapi/hapi') HTTP server running at: ${hapiServer.info.uri}`)
140
+ try {
141
+ await node . stop ( )
142
+ } catch ( err ) {
143
+ // TODO: remove when fixed upstream: https://github.com/ipfs/js-ipfs/issues/2257
144
+ if ( err . message === 'Not able to stop from state: stopping' ) {
145
+ log ( 'destroy: embedded:chromesockets waiting for node.stop()' )
146
+ await stopped
147
+ } else {
148
+ throw err
149
+ }
150
+ }
151
+ node = null
187
152
}
188
- initHapi()
189
- return hapiServer
190
153
}
191
- */
0 commit comments