Skip to content

Commit

Permalink
fix: catch listen error (#34)
Browse files Browse the repository at this point in the history
The WSServer re-emits all events from the underlying http(s) server so we need to listen for the error event on it.

Fixes libp2p/js-libp2p-websockets#184

Co-authored-by: Alex Potsides <[email protected]>
  • Loading branch information
mpetrunic and achingbrain committed Dec 8, 2022
1 parent 488fe49 commit 7a96e45
Show file tree
Hide file tree
Showing 2 changed files with 45 additions and 35 deletions.
72 changes: 37 additions & 35 deletions src/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,24 @@ export interface WebSocketServer extends EventEmitter {

class Server extends EventEmitter {
private readonly server: http.Server | https.Server
private readonly wsServer: WSServer

constructor (server: http.Server | https.Server) {
constructor (server: http.Server | https.Server, opts?: ServerOptions) {
super()

opts = opts ?? {}
this.server = server
this.wsServer = new WSServer({
server: server,
perMessageDeflate: false,
verifyClient: opts.verifyClient
})
this.wsServer.on('connection', this.onWsServerConnection.bind(this))
}

async listen (addrInfo: { port: number } | number) {
return await new Promise<WebSocketServer>(resolve => {
this.once('listening', () => resolve(this))
return await new Promise<WebSocketServer>((resolve, reject) => {
this.wsServer.once('error', (e) => reject(e))
this.wsServer.once('listening', () => resolve(this))
this.server.listen(typeof addrInfo === 'number' ? addrInfo : addrInfo.port)
})
}
Expand All @@ -51,6 +59,31 @@ class Server extends EventEmitter {
address () {
return this.server.address()
}

onWsServerConnection (socket: WebSocket, req: http.IncomingMessage) {
const addr = this.wsServer.address()

if (typeof addr === 'string') {
this.emit('error', new Error('Cannot listen on unix sockets'))
return
}

if (req.socket.remoteAddress == null || req.socket.remotePort == null) {
this.emit('error', new Error('Remote connection did not have address and/or port'))
return
}

const stream: DuplexWebSocket = {
...duplex(socket, {
remoteAddress: req.socket.remoteAddress,
remotePort: req.socket.remotePort
}),
localAddress: addr.address,
localPort: addr.port
}

this.emit('connection', stream, req)
}
}

export function createServer (opts?: ServerOptions): WebSocketServer {
Expand All @@ -69,40 +102,9 @@ export function createServer (opts?: ServerOptions): WebSocketServer {
})
}

const wsServer = new WSServer({
server: server,
perMessageDeflate: false,
verifyClient: opts.verifyClient
})

proxy(server, 'listening')
proxy(server, 'request')
proxy(server, 'close')

wsServer.on('connection', function (socket: WebSocket, req: http.IncomingMessage) {
const addr = wsServer.address()

if (typeof addr === 'string') {
wss.emit('error', new Error('Cannot listen on unix sockets'))
return
}

if (req.socket.remoteAddress == null || req.socket.remotePort == null) {
wss.emit('error', new Error('Remote connection did not have address and/or port'))
return
}

const stream: DuplexWebSocket = {
...duplex(socket, {
remoteAddress: req.socket.remoteAddress,
remotePort: req.socket.remotePort
}),
localAddress: addr.address,
localPort: addr.port
}

wss.emit('connection', stream, req)
})

return wss
}
8 changes: 8 additions & 0 deletions test/server-address.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,12 @@ describe('address', () => {
expect(server.address()).to.have.property('port', 55214, 'return address should match')
await server.close()
})

it('server listen error should be catchable', async () => {
const server1 = WS.createServer()
await server1.listen(55215)
const server2 = WS.createServer()
await expect(server2.listen(55215)).to.be.eventually.rejectedWith('listen EADDRINUSE')
await server1.close()
})
})

0 comments on commit 7a96e45

Please sign in to comment.