Hi Render team and community!
I am using render hobby to host a Node JS websocket server (using ws lib), that is used for some ESP8266 clients to connect. I found an issue that I can’t figure out what is going on, because there’s no error on the server side, just a behavior occurring only when the app is deployed on render, not reproducible locally. The issue is that my clients, sometimes, enters in a loop when connecting to my server. I have an authentication function and the client reaches the point it is authenticated with success but something happens on the way, that is causing an error to establish the connection. I can see a protocols error
on client side (ESP8266) and this keeps trying to connect until it gets success (and I dont understand why sometimes they can connect with success…). No errors from Node JS side, I can only follow what is happening by the logs and it seems to be fine… The client is authenticated but the connection crashes without any reason from Node side. This behavior is even worse if I add more than one async
function inside my upgrade handler and if I have multiple clients connecting to it…
Technical description:
1 - On websocket server I do a simple authentication based on a header received from the ESP8266 client. I am using the server.on('upgrade')
handler to do that:
The minimal node JS code is:
const app = express()
app.use(express.json())
app.use(bodyParser.urlencoded({ extended: false }))
app.use(cors())
const server = http.createServer(app)
const wss = new WebSocketServer({
noServer: true
})
server.on('upgrade', function upgrade(request, socket, head) {
socket.on('error', (err) => console.error(err))
authenticateWsClient(request).then((data) => {
const { err, req } = data
if (err) {
socket.write('HTTP/1.1 401 Unauthorized\r\n\r\n')
socket.destroy()
return
}
socket.removeListener('error', (err) => console.error(err))
wss.handleUpgrade(request, socket, head, function done(ws) {
wss.emit('connection', ws, request)
})
})
})
This async authenticateClients
do a simple query on MongoDB database to check if the incoming id received from header exists
async function authenticateWsClient(req) {
const url = req.url
const splittedUrl = url.split('/')
const deviceId = splittedUrl[1]
console.log('------verify connected client------')
if (!deviceId ) {
return { err: 403, status: 403, message: 'Invalid token'}
}
const isDeviceValid= await Device.findOne({ deviceId })
if (!isDeviceValid) {
return { err: 403, status: 403, message: 'Not authorized'}
}
console.log(`Serial key ${deviceSerialKey} authorized. Connection Opened.`)
return { req, message: 'Authorized' }
}
I tried to open spcecific topics on GitHub for each library I am using, and it has some additional details in my previous investigations:
Arduino Websockets: Cannot add async functions to server.upgrade. Causes client connection close during the authentication. · Issue #2235 · websockets/ws · GitHub
node js ws: Getting my connection closed when on node js wss server · Issue #163 · gilmaimon/ArduinoWebsockets · GitHub
Is there a way to track from render side what is happening?