Skip to content

When you enable websocket in proxyMiddleware Why Invalid frame header came in some microservices #1076

@abhishek73magar

Description

@abhishek73magar

Checks

Describe the bug (be clear and concise)

I don't know why but we we use route as path like /forestfire/socket.io it will throw invalid header in some of my socket.io microservice but not other why i am not sure?
server.ts

import express, { Application, NextFunction, Request, Response } from 'express'
import { createProxyMiddleware } from 'http-proxy-middleware'
import cors from 'cors'
import cookieParser from 'cookie-parser'
import setHeaders from 'libs/set-headers'
import { PORT } from 'config/config'
import {  ClientRequest, IncomingMessage, Server } from 'http'
import routes from 'config/routes'
import accessConfig, { verifyToken } from 'libs/access-config'
import EventEmitter from 'events'

// default is 12 is not enough for this project cause we use a lot of proxy
EventEmitter.defaultMaxListeners = 20 

const proxyReq = (openAccess: boolean | Array<string> | undefined) => (proxyReq: ClientRequest, req: Request, res: Response) => {
  // accessConfig(openAccess)(req, res) // check if user has access to the route
  if(openAccess === true) return;
  if(Array.isArray(openAccess)){
    if(openAccess.includes(req.path)) return;
  }

  // check auth token;
  const token = req.cookies.alert_token
  if(token){
    const data = verifyToken(token)
    if(data) proxyReq.setHeader('data', JSON.stringify({ user: data}))
  }
 
}

const proxyRes = (proxyRes: IncomingMessage, req: Request, res: Response) => {
  // for(let oldKey in proxyRes.headers){
  //   if(oldKey === 'data') continue;
  //   let newkey = oldKey.replace(/((?:^|-)[a-z])/g, function(val) { return val.toUpperCase() });
  //   newkey = newkey.replace(/(-Os-)/g, function (val) { return val.toUpperCase() });
  //   proxyRes.headers[newkey] = proxyRes.headers[oldKey];
  //   delete proxyRes.headers[oldKey];
  // }
}

const start = (): Promise<Server> => {
  return new Promise((resolve, reject) => {
    const app: Application = express();
    app.use(cors({
      origin: ['http://localhost:3000', 'http://localhost:3001', 'http://192.168.4.161:3000'],
      credentials: true,
    }))

    app.use(cookieParser())
    app.use(setHeaders)

    routes.forEach(({ route, target, pathRewrite, openAccess }) => {
      let rewrite_path: Record<string, string> | undefined = undefined
      // HPM (HTTP PROXY MIDDLEWARE)
      if(!target) return console.log(`[HPM] Proxy target not found for route: ${route} -> ${target}`)
      // add logs for each proxy
      console.log(`[HPM] Proxy created: ${route} -> ${target}`)
      if(pathRewrite) {
        const [key, value] = pathRewrite
        rewrite_path = { [key]: value }
        // console.log(`[HPM] Proxy rewrite rule created: "${key}" ~> "${value}"`)
      }
      
      // if(route === '/'){
      //   // use this / routes for the main frontend app if we not use like this it will thorow Invalid frame header
      //   app.use(route, createProxyMiddleware({ target, changeOrigin: true, logger: console, ws: false, on: { proxyReq: proxyReq(openAccess), proxyRes } }))
      //   return;
      // }

      const proxyMiddleware = createProxyMiddleware({
        target: target,
        pathFilter: route,
        changeOrigin: true,
        pathRewrite: rewrite_path,
        // timeout: 1000 * 60, // 1 minute
        // proxyTimeout: 1000 * 60 * 2, //  default 2 minute
        ws: route === '/' ? true : true, // for websocket proxy 
        logger: console, 
        on: { 
          proxyReq: proxyReq(openAccess), 
          proxyRes 
        }
      })

      app.use(proxyMiddleware)
    })

    const server = app.listen(PORT, () => resolve(server))
  })
}

export { start }

routes.ts

import services from 'config/services'

const authNotAllow = [
  '/auth/login', '/auth/create', '/auth/verify-recaptcha', '/auth/user-access',
  '/auth/forgot-password', '/auth/change-password', "/auth/update-user", "/auth/refresh-page",
  "/auth/socket.io"
]
const helperAuthNotAllow = [
  '/helper/location',
  '/helper/test', '/helper/wsm-date', '/helper/basin', '/helper/district', '/helper/province', '/helper/municipality', '/helper/nepal-boundary', '/helper/seiscomp-station', '/helper/earthquake-event',
  '/helper/wms-forecast', '/helper/wms-forecast2', '/helper/wms-satellite',
  '/helper/river_layer', '/helper/boundaries/province', '/helper/boundaries/district', '/helper/boundaries/municipality', '/helper/geoglows/get-comid', '/helper/geoglows/comid-list'
]

const _createTuple = (key: string, value: string): [string, string] => [key, value]
interface Routes {
  service: string,
  route: string,
  target?: string
  pathRewrite?: [string, string],
  openAccess?: boolean | string[],
  // ws?: boolean
}


const routes: Routes[] = [
  // { service: 'meroalert', route: '/mero-alert', target: services.meroalert, pathRewrite: _createTuple('/mero_alert', ''), openAccess: true },
  // { service: 'meteoblue', route: '/meteoblue', target: services.meteoblue, openAccess: true },
  // { service: 'wrf-forecast', route: '/wrf-forecast', target: services.wrfForecast, pathRewrite: _createTuple('/wrf-forecast', ''), openAccess: true },
  // { service: 'geoserver', route: '/geoserver', target: services.geoserver, openAccess: true },
  // { service: 'seiscomp-history', route: '/seiscomp-history', target: services.seiscomp_history, openAccess: true},
  // { service: "seiscomp-xml", route: "/seiscomp-xml", target: services.seiscomp_xml, openAccess: true, pathRewrite: _createTuple('/seiscomp-xml', '') },
  // { service: 'seiscomp', route: '/seiscomp/socket.io', target: services.seiscomp, openAccess: true },
  // { service: 'lightning-threats', route: '/lightning-threats/socket.io', target: services.lightning_threats, openAccess: true },
  // { service: 'lightning', route: '/lightning/socket.io', target: services.lightning, openAccess: true },
  { service: 'helper', route: '/helper', target: services.helper, pathRewrite: _createTuple('/helper', ''), openAccess: helperAuthNotAllow},
  { service: 'personal-alert', route: '/personal-alert', target: services.personal_alert, openAccess: false },
  // { service: 'forestfire', route: '/forestfire/socket.io', target: services.forestfire, openAccess: true },
  // { service: 'cap', route: '/cap/socket.io', target: services.cap, openAccess: true },
  // { service: 'auth', route: '/auth/socket.io', target: services.auth, openAccess: true },
  { service: 'auth', route: '/auth/google', target: services.auth, openAccess: true },
  { service: 'auth', route: '/auth', target: services.auth, openAccess: authNotAllow },
  { service: 'personal-dashboard', route: '/personal-dashboard', target: services.personal_dashbarod, pathRewrite: _createTuple('/personal_dashboard', ''), openAccess: true },
  { service: 'aws_dashboard', route: '/aws_dashboard', target: services.aws_dashboard, pathRewrite: _createTuple("/aws_dashboard", ''), openAccess: true },
  { service: 'hs_dashboard', route: '/hs_dashboard', target: services.hs_dashboard, pathRewrite: _createTuple("/hs_dashboard", ''), openAccess: true },
  { service: 'watershed_dashboard', route: '/watershed_dashboard', target: services.watershed_dashboard, pathRewrite: _createTuple("/watershed_dashboard", ''), openAccess: true },
  { service: 'alertnepal', route: '/', target: services.alertnepal, openAccess: true },
]


export default routes;

this is my code

when i disable websocket in / path it, the invalid headers error resolved but i like to tell you this why its happening i don't know please review once

or tell me the if i am doing somthing wrong

its happen to /auth/socket.io only i don't know why but in /auth microservice credentials is true.
but when i directly connect that invalid header error is not coming

Step-by-step reproduction instructions

1. use the following code which is provided in decription.
2. ...

Expected behavior (be clear and concise)

http proxy is worked fine & other socket.io microservice is working also good but, which /auth/socket.io it will throw warning when you enable ws in / routes

How is http-proxy-middleware used in your project?

i have personal project which have more then 12 microservices so i used for centralized the services.

What http-proxy-middleware configuration are you using?

latest http-proxy-middleware (V3)

 const proxyMiddleware = createProxyMiddleware({
        target: target,
        pathFilter: route,
        changeOrigin: true,
        pathRewrite: rewrite_path,
        // timeout: 1000 * 60, // 1 minute
        // proxyTimeout: 1000 * 60 * 2, //  default 2 minute
        ws: route === '/' ? true : true, // for websocket proxy 
        logger: console, 
        on: { 
          proxyReq: proxyReq(openAccess), 
          proxyRes 
        }
      })

What OS/version and node/version are you seeing the problem?

Ubuntu 24.04.2 LTS x86_64 & node 23

Additional context (optional)

No response

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions