WebSocket (Socket.io) connection Error - failed: Connection closed before receiving a handshake response

Viewed 2004

I have spent all day figuring out and searching for solution online for error

WebSocket connection to 'wss://localhost/myapp/peerjs?key=peerjs&id=5c70da87-62c1-41dd-b1b1-e7aea5acc09b&token=j4n0nprnu6' failed: Connection closed before receiving a handshake response

I am developing this nodejs app on express server. My express server where I initialize PeerServer along with it:

// Dependencies
const express = require('express')
const app = express()
const httpPort = process.env.PORT || 80
const httpsPort = 443
const { ExpressPeerServer } = require('peer')
const path = require('path')
const http = require('http')
const https = require('https')
const fs = require('fs')

// Certificate & credentials
const privateKey = fs.readFileSync(path.join(__dirname, 'certs', 'key.pem'))
const certificate = fs.readFileSync(path.join(__dirname, 'certs', 'cert.pem'))
const credentials = {
        key: privateKey,
        cert: certificate
}

const mainServer = http.createServer(app).listen(httpPort, () => { console.log('Main Server listening to port ' + httpPort) })
const httpsServer = https.createServer(credentials, app).listen(httpsPort, () => { console.log('Peer Server listening to port ' + httpsPort) })

const peerServer = ExpressPeerServer(mainServer, {
        debug: true,
        path: '/myapp'
})

//app.use('peerjs', peerServer)
app.use(peerServer)

const io = require('socket.io')(httpsServer)
const { v4: uuidV4 } = require('uuid')

app.set('view engine', 'ejs')
app.use(express.static('public'))

app.get('/', (req, res) => {
  res.redirect(`/${uuidV4()}`)
})

app.get('/:room', (req, res) => {
  res.render('room', { roomId: req.params.room })
  //console.log('Room Created / Joined')
})

io.on('connection', (socket) => {
    //console.log('IO Connectedd')
    socket.on('join-room', (roomId, userId) => {
        console.log(roomId, userId)
        socket.join(roomId)
        socket.to(roomId).emit('user-connected', userId)

        socket.on('disconnect', () => {
            socket.to(roomId).emit('user-disconnected', userId)
        })
    })
})

And below is my client side code

const socket = io('/')
const videoGrid = document.getElementById('video-grid')
const myPeer = new Peer(undefined, {
    host: '/',
    port: '443',
    path: '/myapp'
})
const ownVideo = document.createElement('video') // Own Video
ownVideo.muted = true
const peers = {}
navigator.mediaDevices.getUserMedia({
    video: true,
    audio: true
}).then(stream => {
    addVideoStream(ownVideo, stream)

    myPeer.on('call', call => { // On Receiving Other Persons Call
        call.answer(stream) // Send Video Stream On Answer
        const video = document.createElement('video')
        call.on('stream', userVideoStream => { // Send Back Video Stream On Stream
            addVideoStream(video, userVideoStream)
        })
    })

    socket.on('user-connected', userId => { // Allow Self To Be Connected To Others
        console.log('User Connected: ' + userId)
        connectToNewUser(userId, stream)
    })
})

socket.on('user-disconnected', userId => {
    if (peers[userId]) peers[userId].close()
})

myPeer.on('open', id => {
    socket.emit('join-room', ROOM_ID, id)
})

function connectToNewUser(userId, stream) {
    const call = myPeer.call(userId, stream) // We Connect To Other User
    const video = document.createElement('video')
    call.on('stream', userVideoStream => { // Other User Connects To Us
        addVideoStream(video, userVideoStream)
    })
    call.on('close', () => { // Other User Disconnects
        video.remove()
    })

    peers[userId] = call
}

function addVideoStream(video, stream) {
    video.srcObject = stream
    video.addEventListener('loadedmetadata', () => {
        video.play()
    })
    videoGrid.append(video)
}

I am new to nodejs and couldn't find any solution online for it.

Please help!!

2 Answers

Your code needed some rearrangement, once done, it worked fine for me on W10, VSC and Chrome Version 91.0.4472.164 (Official Build) (64-bit). It is simpler to include my working code rather than trying to explain each change. I will include here the script.js and server.js files (working) and the whole code working workspace in github here for anybody to use. I want to emphasize that you do want to call the peer.on('call'.... event handler ahead of waiting for any promises, to make sure your remote client has this handler ready before receiving a call from the originator client, that was not part of the issue that created the error you asked about.

CLIENT SIDE:

const socket = io('/')
const videoGrid = document.getElementById('video-grid')
let Peer = window.Peer;
const peer = new Peer({
    host: '/',
    path: '/peerjs',
    debug: 3,
    port: 80,
    secure: false,
});

console.log('***Created peer instance, userId: ' + peer.id)

// Function to obtain stream and then await until after it is obtained to go into video chat call and answer code. Critical to start the event listener ahead of everything to ensure not to miss an incoming call.

const ownVideo = document.createElement('video') // Own Video
ownVideo.muted = true
const peers = {}
// On Receiving Other Persons Call
peer.on("call", async (call) => {
    try {
        let stream = null;
        stream = await navigator.mediaDevices.getUserMedia({
            video: true,
            audio: true
        });
        call.answer(stream) // Send Video Stream On Answer
        const video = document.createElement('video');
        // Send Back Video Stream On Stream
        call.on('stream', userVideoStream => {
            addVideoStream(video, userVideoStream);
        });
    }
    catch (err) {
        /* handle the error */
        console.log('*** ERROR returning the stream: ' + err);
    }
});

    (async () => {
        try {
            let stream = null;
            stream = await navigator.mediaDevices.getUserMedia(
                {
                    audio: true,
                    video: true,
                });
            if (stream != undefined) {
                addVideoStream(ownVideo, stream);
                console.log('added own Video stream');
            } else {
                console.log('You can only access your audio/video media streams over https');
                alert('Sorry retry using https, for security reasons Google Media blocks access to your video stream over unsecure http connections');
            }
        } catch (err) {
            /* handle the error */
            console.log('*** ERROR returning the stream: ' + err);
            alert('Sorry retry using https, for security reasons Google Media blocks access to your video stream over unsecure http connections');
        }
    })();


socket.on('user-connected', userId => { // Allow Self To Be Connected To Others
    console.log('User Connected: ' + userId)
    navigator.mediaDevices.getUserMedia({
        video: true,
        audio: true
    }).then(stream => {
        connectToNewUser(userId, stream)
    })
})

socket.on('user-disconnected', userId => {
    if (peers[userId]) peers[userId].close()
})

peer.on('open', id => {
    socket.emit('join-room', ROOM_ID, id)
})

function connectToNewUser(userId, stream) {
    const call = peer.call(userId, stream) // We Connect To Other User
    const video = document.createElement('video')
    call.on('stream', userVideoStream => { // Other User Connects To Us
        addVideoStream(video, userVideoStream)
    })
    call.on('close', () => { // Other User Disconnects
        video.remove()
    })

    peers[userId] = call
}

function addVideoStream(video, stream) {
    video.srcObject = stream
    video.addEventListener('loadedmetadata', () => {
        video.play()
    })
    videoGrid.append(video)
}

SERVER SIDE:

// Dependencies
const express = require('express')
const app = express()
const httpPort = process.env.PORT || 80
const httpsPort = 443
const { ExpressPeerServer } = require('peer')
const path = require('path')
const http = require('http')
const https = require('https')
const fs = require('fs')
const certs = `C:/Users/spine/Documents/Personal Files/ArduinoData/packages/esp8266/hardware/esp8266/2.7.4/libraries/ESP8266WiFi/examples/BearSSL_Server/`


// Certificate & credentials
const privateKey = fs.readFileSync(path.join(certs, 'key.pem'))
const certificate = fs.readFileSync(path.join(certs, 'cert.pem'))
const credentials = {
        key: privateKey,
        cert: certificate
}

const mainServer = http.createServer(app).listen(httpPort, () => { console.log('Main Server listening to port ' + httpPort) })
const httpsServer = https.createServer(credentials, app).listen(httpsPort, () => { console.log('Peer Server listening to port ' + httpsPort) })

const peerServer = ExpressPeerServer(mainServer, {
        debug: true,
        ssl: {},
})

app.use('/peerjs', peerServer)
// app.use(peerServer)

const io = require('socket.io')(httpsServer,{
// const io = require('socket.io')(mainServer, {
  cors: {
      origin: "*",
  },
});
const { v4: uuidV4 } = require('uuid')

app.set('view engine', 'ejs')
app.use(express.static('public'))

app.get('/', (req, res) => {
  res.redirect(`/${uuidV4()}`)
})

app.get('/:room', (req, res) => {
  res.render('room', { roomId: req.params.room })
  //console.log('Room Created / Joined')
})

io.on('connection', (socket) => {
    //console.log('IO Connectedd')
    socket.on('join-room', (roomId, userId) => {
        console.log(roomId, userId)
        socket.join(roomId)
        socket.to(roomId).emit('user-connected', userId)

        socket.on('disconnect', () => {
            socket.to(roomId).emit('user-disconnected', userId)
        })
    })
})

I got it working with following cod on server side. Though I was able to get it work over SSL only.

const express = require('express')
const app = express()
const httpPort = process.env.PORT || 80
const httpsPort = 443
const { ExpressPeerServer } = require('peer')
const path = require('path')
const http = require('http')
const https = require('https')
const fs = require('fs')

// Certificate & credentials
const privateKey = fs.readFileSync(path.join(__dirname, 'certs', 'key.pem'))
const certificate = fs.readFileSync(path.join(__dirname, 'certs', 'cert.pem'))
const credentials = {
    key: privateKey,
    cert: certificate
}

const httpsServer = https.createServer(credentials, app).listen(httpsPort, () => { console.log('Peer Server listening to port ' + httpsPort) })

const peerServer = ExpressPeerServer(httpsServer, {
        debug: true,
        path: '/myapp'
})

app.use(peerServer)

const io = require('socket.io')(httpsServer, {
   forceNew: true,
   transports: ["polling"],
})
const { v4: uuidV4 } = require('uuid')

app.set('view engine', 'ejs')
app.use(express.static('public'))

app.get('/', (req, res) => {
  res.redirect(`/${uuidV4()}`)
})

app.get('/:room', (req, res) => {
  res.render('room', { roomId: req.params.room })
})

io.on('connection', (socket) => {
    socket.on('join-room', (roomId, userId) => {
        socket.join(roomId)
        socket.broadcast.to(roomId).emit('user-connected', userId)

        socket.on('disconnect', () => {
            socket.broadcast.to(roomId).emit('user-disconnected', userId)
        })
        socket.on('text-message', message => {
            socket.broadcast.to(roomId).emit('text-message-received', message)
        })
        socket.on('system-stream-updated', remoteUserId => {
            socket.broadcast.to(roomId).emit('new-remote-stream', remoteUserId)
        })
    })
})
Related