Express middleware not capturing request events for socket.io

Issue

I’m using Express (v4.17.3), Socket.io, and Node.Js’s http module. I’m adding a middleware for express to capture all incoming requests but that’s failing.

I’ll first show the code I’m using and the output then explain my understanding/expectation of the output (I’m new to Node and all the mentioned libraries so perhaps I’m missing something)

First of all, below is the code I’m referring to. Using Express‘s middleware I’m trying to capture all the incoming requests and log them, and doing the same for the http on("request"). However, requests going to socket.io aren’t captured by the middleware.

// Express
const express = require("express");
const app = express()
// Socket
const server = require('http').createServer(app);
const {Server} = require("socket.io");
const io = new Server(server)

// Want to listen to all incoming requests using the middleware (this doesn't work)
app.use((req,res,next)=>{
    console.log(`Express request = ${req.url}`)
    next()
})

// Listening to all incoming requests (this works)
server.on("request", (req, res)=>{
    console.log(`Http request = ${req.url}`) 
})

server.listen(8080, () => {
    console.log(`Listening on port 8080`)
})

output when I GET /

Express request = /

Http request = /
Http request = /socket.io/socket.io.js
Http request = /socket.io/?EIO=4&transport=polling&t=O0va...
Http request = /socket.io/?EIO=4&transport=polling&t=O0va24A&sid=c...
Http request = /socket.io/?EIO=4&transport=polling&t=O0va24F&sid=c...
Http request = /socket.io/?EIO=4&transport=polling&t=O0va27x&sid=c...

My expected output is to have equal logs for the middleware app.use() and on("request") ("Express request = " & "Http request = ")

My understanding:

1- When I add a middleware for express as in the code below, any incoming requests should be captured here first before going anywhere else. (correct?)

app.use((req,res,next)=>{...})

2- When I’m passing the express app as an argument to http’screateServer, that the express app will be treated as a listener and any request events will be passed to it. (correct?)

const server = require('http').createServer(app);

So if my understanding is correct, why aren’t all the requests captured by the request event passed to the middleware as well?

Solution

This is normal. Socket.io puts itself in front of express (or any other listener for incoming requests on the http server) so that it takes the request before Express sees it. Thus, Express (or it’s middleware) never see any socket.io connection requests.

Socket.io has its own middleware layer that you can use to participate in the initialization of socket.io requests.

Or, you can register for incoming socket.io connections (to be called after they are already connected) with the io.on('connection', ...) event handler.

When I add a middleware for express as in the code below, any incoming requests should be captured here first before going anywhere else. (correct?)

That is true except for code that registers directly request handlers right on the http server and inserts itself before Express in the listener chain, thus preventing Express from seeing any requests that are destined for socket.io.

When I’m passing the express app as an argument to http’screateServer, that the express app will be treated as a listener and any request events will be passed to it. (correct?)

That is true. But socket.io jumps in front of Express and takes/hides any requests it wants so that Express never sees them.


If you’re curious, here’s the socket.io code that jumps in line to the front of all listeners for the http server thus bypassing the express listener:

attachServe(srv) {
    debug("attaching client serving req handler");
    const evs = srv.listeners("request").slice(0);
    srv.removeAllListeners("request");
    srv.on("request", (req, res) => {
        if (this.clientPathRegex.test(req.url)) {
            this.serve(req, res);
        }
        else {
            for (let i = 0; i < evs.length; i++) {
                evs[i].call(srv, req, res);
            }
        }
    });
}

It grabs all the existing listeners into an array. Then, it removes them all. Then, it registers for the request event itself and, if it is a socket.io request, then it does not call the prior listeners. If it is not a socket.io prefix, then it manually calls the prior listeners.

Answered By – jfriend00

Answer Checked By – Mildred Charles (AngularFixing Admin)

Leave a Reply

Your email address will not be published.