0
votes

My angular chat application is served on port 4200, my backend API (which serves both REST and websocket traffic), is running on port 3000.

To avoid messing with CORS issues, I am proxying everything through port 4200, and using path /api for all my REST API calls. This REST API proxying is working perfectly. The websocket proxying is almost working, but I am pretty sure it's failing when trying to 'upgrade' to WS, and after reading everything on the web and SO on this topic I am still stuck.

I am using angular-cli and socket.io, starting my API running on port 3000, and then starting my app with "ng serve --proxy-config proxy.conf.json" (this SO post helped me alot at the start)...

Here is my proxy.conf.json:

{
    "/api/*": {
      "target": "http://localhost:3000",
      "secure": false,
      "logLevel": "debug",
      "changeOrigin": true
    },
    "/sock/*": {
      "target": "http://localhost:3000/socket.io/",
      "secure": false,
      "logLevel": "debug",
      "ws": true,
      "changeOrigin": true
    }
}

The relevant client side code reside in an angular service where I am creating a single websocket in the constructor for the client to talk to the back end

const SERVER_URL = 'http://localhost:4200';

@Injectable()
export class WebSocketService {
    private socket;

    constructor() {
        console.log(`WebSocketService: initializing (URL ${SERVER_URL})`);
        this.socket = socketIo(SERVER_URL, {
            path: '/sock',
            autoConnect: true,
            transportOptions: {
              polling: {
                extraHeaders: {
                  'Authorization': 'Bearer abc'
                }
              }
            }
        });
    }
...
}

Here's what I am doing on the server side

import * as socketIo from 'socket.io';

export default class WebSocketServer {
    private server: Server;
    private io: SocketIO.Server;

    constructor(server: Server) {
        this.server = server; 
        this.io = socketIo(this.server,{ 
          serveClient: false, 
          pingInterval: 10000, 
          pingTimeout: 5000, 
          cookie: false 
        });
        this.io.use((socket, next) => {
          let header = socket.handshake.headers['authorization'];
          console.log(`WebSocketServer: socket ${socket.id}, auth: ${header}`);
          if (true) {
            return next();
          }
          //return next(new Error('authentication error'));
        });
    }
...
}

Here's what happens when I fire up the application on the client side

enter image description here

On the Server side, the proxying to port 3000 is clearly working for the long polling portion of the websocket protocol, but then after it tries to upgrade it's failing and falling back to long polling...(I think the upgrade attempt below is what results in the error message above):

enter image description here

I am thinking that the problem here is that the 'upgrade' message in the websocket protocol is not being handled properly by my proxy configuration, but I am not completely sure.

1
Could you please check on the wire (e.g. using Wireshark) whether the Angular CLI proxy is forwarding the "upgrade" header to your server? I got a similar situation and on my side I can see that the "upgrade" header is dropped causing the server to reject the connection attempt. - Marc-Christian Schulze
Marc-Christian, you're exactly right, that's what was going on! - sjtitus

1 Answers

0
votes

Thanks to Marc-Christian, who figured out what was going on with the forwarding not working correctly. I ended up fixing this by doing 2 things: 1) breaking the websocket traffic onto a different port (8080), and 2) changing the path in the socket.io and the forwarding path in proxy.conf.json to 'mysock' (not sure if both were needed).