Search code examples
webpack-dev-servervue-cliwebpack-hmr

Webpack dev server sockjs-node returns 404 error


I am running a simple Vue app with webpack that I created with the vue-cli. When I run the dev server wtih npm run serve, it shows several errors in the client console when using sockjs-node. I believe this module is used by webpack for hot reloading (HMR).

The first error is:

Access to XMLHttpRequest at 'http://192.168.1.4:8080/sockjs-node/info?t=1615330207390' from origin 'http://localhost:8080' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.

I can solve this in two ways by editing the devServer in my vue.config.js. The first method is by setting public: 'localhost:8080'; and the second is by setting headers: {'Access-Control-Allow-Origin': 'http://192.168.1.4:8080', 'Access-Control-Allow-Credentials': 'true'}.

In both cases, I then see the following two errors:

POST http://localhost:8080/sockjs-node/690/qvgqwbdo/xhr_streaming?t=1615330686134 404 (Not Found)

GET http://localhost:8080/sockjs-node/690/zympwfsc/eventsource 404 (Not Found)

How do I resolve these errors so that the hot reloader will connect?


Solution

  • In the function I set to devServer.before in my vue.config.js file, I created my own websockets using Socket.io on the same port as my devSever. When the function returned, the devServer could not use that port for websockets, so it failed to launch sockjs-node. Therefore, when the frontend client tried to connect to the devServer, the requests were going to my sockets, instead of the devServer sockets, and it was ignoring them. Hence the 404 errors.

    Here is my original code:

    // server.js
    const { createServer } = require('http')
    const io = require('socket.io')
    const addSocketEvents = require('./socket-api')
    
    const port = process.env.PORT || 8080
    
    module.exports = {
        port,
        configure(app) {
            // `app` is an instance of express
    
            // add the websockets
            const httpServer = createServer(app)
            socket = io(httpServer, {
                path: '/socket-api'
            })
            addSocketEvents(socket)
    
            // starts the server
            // cannot use app.listen() because that will not start the websockets
            httpServer.listen(port)
        }
    }
    
    // vue.config.js
    const { port, configure } = require('./server')
    
    module.exports = {
        devServer: {
            before: configure,
            public: `localhost:${port}`,
        },
    }
    

    To fix this issue, I needed to allow the devServer to use the original port for sockjs-node, and launch my sockets on a different port. However, because I need to use the same port in production (due to restrictions by my current hosting provider), I only want my sockets to use a different port when running the devServer. To do this, I simply created a different httpServer and launched it on a different port, then created a proxy in the devServer config for that port. In my configure function, I just check to see if it is running in dev or prod, and act accordingly.

    My production server is a simple express instance which calls the same configure function after it is created. This allows me to put all my startup code in one place.

    Here is my new code:

    // server.js
    const { createServer } = require('http')
    const io = require('socket.io')
    const addSocketEvents = require('./socket-api')
    
    const port = process.env.PORT || 8080
    const proxyPort = 8081
    
    module.exports = {
        port,
        proxyPort,
        configure(app) {
            // `app` is an instance of express
    
            // add the websockets
            let httpServer, socketPort
            if (process.env.NODE_ENV === 'development') {
                httpServer = createServer()
                socketPort = proxyPort
            } else {
                httpServer = createServer(app)
                socketPort = port
            }
    
            // adds the socket-api to be used via websockets
            socket = io(httpServer, {
                path: '/socket-api'
            })
            addSocketEvents(socket)
            
            // starts the server
            httpServer.listen(socketPort)
        }
    }
    
    // vue.config.js
    const { port, configure } = require('./server')
    
    module.exports = {
        devServer: {
            before: configure,
            public: `localhost:${port}`,
            proxy: {
                '/socket-api': {
                    target: `http://localhost:${proxyPort}`,
                    ws: true
                }
            }
        },
    }