1

I am using AWS API Gateway / EC2 instance to host my backend api services like this:

enter image description here

My Backend is Nodejs / Express JS, and I am using socket.io for real-time messaging:


const initializeExpress = (): void => {
  const app = express();
  let http = require("http").createServer(app);
  let io = require("socket.io")(http, {
    cors: {}
  });
  

  io.on("connection", function(socket: any) {
    socket.on("hello", (res) => {
      console.log('on hello: ', res); 
    });
  });

  const server = http.listen(parseInt(process.env.PORT) || 3001, function() {
    console.log("listening on *:3001");
  });


  // register some middlewares for web socket
  io.use((socket, next) => {
    const username = socket.handshake.auth.username;
    const organizationId = socket.handshake.auth.organizationId;
    if (!username || !organizationId) {
      return next(new Error("invalid username or organizationId"));
    }
    socket.username = username;
    socket.organizationId = organizationId;
    next();
  });


  app.use(httpLogger);
  app.use(cors());
  app.use(express.json());
  app.use(express.urlencoded());
  app.use(express.urlencoded({ extended: true }));
  app.use(bodyParser.urlencoded({ extended: true }));
  app.use(bodyParser.json());
  app.use(express.static(path.join(__dirname, 'build')));
  app.use(addRespondToResponse);

  attachPublicRoutes(app);

  // validate incoming request has valid username
  app.use('/', authenticateUser);

  // all api endpints;
  attachPrivateRoutes(app);
  app.use((req, _res, next) => next(new RouteNotFoundError(req.originalUrl)));

  // a place for all errors;
  app.use(handleError);
};

The above code works when testing in local environment. To be precise, when the backend is running on 127.0.0.1:3001, the frontend, also run locally, can establish WebSocket connection with it and things are working smoothly.

However, when I move to the AWS API Gateway configuration above, all other api endpoints are working (meaning that the API Gateway is configured with success), it is just the websocket request is failing with 404 Error:

error: User undefined got Error on NLB-myapp-internal-1234123.elb.ap-northeast-1.amazonaws.com/socket.io?EIO=4&t=Nv2sCMO&transport=polling: Error: Route '/socket.io?EIO=4&t=Nv2sCMO&transport=polling' does not exist.  Error: Route '/socket.io?EIO=4&t=Nv2sCMO&transport=polling' does not exist.

The message Error: Route '/socket.io?EIO=4&t=Nv2sCMO&transport=polling' does not exist. keeps appearing on and on...

Below is my Restful API created in AWS API Gateway: enter image description here

It is configured as a Proxy Integration. And we can see that the request is indeed proxied to the EC2 instance port running the Nodejs/Express service. It is just that the Route /socket.io is missing.

I have found some related answers: https://stackoverflow.com/a/24811468/3703783 https://stackoverflow.com/a/16982780/3703783

However, they are not helping because I have done what they suggested by setting the socket.io in Express as:


  const app = express();
  let http = require("http").createServer(app);
  let io = require("socket.io")(http, {
    cors: {}
  });
  const server = http.listen(parseInt(process.env.PORT) || 3001, function() {
    console.log("listening on *:3001");
  });

By the way, in the local dev environment (without api gateway), when the request is successful delivered to the target port, things are OK.

In the AWS API Gateway architecture, why is the request successful delivered to the target port but still complaining that Route /socket.io does not exist?

What am I missing here?

Kid_Learning_C
  • 2,605
  • 4
  • 39
  • 71

1 Answers1

2

socket io tries to connect to the backend using an endpoint socket.io like you figured out. thing is, aws socket gw takes away all the complications of sockets and transforms it to REST, which looks like this: sockets -> GW -> REST . what you are trying to do here is : sockets -> GW -> socket.io

that won't work for two reasons:

  1. there is no sockets support on the backend when using aws gw.
  2. it seems that socket io does not use standard protocol for web sockets so it is not supported with aws.

one more thing regarding directly to the question is that if you use custom domains for your gw, you can define the socket.io in the path and then the handshake goes through but it will drop and re-establish because of what i wrote in 2.

Liran
  • 591
  • 3
  • 13