spring-cloud-gateway icon indicating copy to clipboard operation
spring-cloud-gateway copied to clipboard

Websocket handshake response missing added headers

Open andrewkcarter opened this issue 4 years ago • 4 comments

Describe the bug spring-boot-dependencies:2.5.2 spring-cloud-dependencies:2020.0.4 (spring-cloud-gateway-server:3.0.4)

We are using SCG to front a set of web applications implemented in Streamlit. Streamlit applications establish a websocket connection which is used to dynamically return data to the page. When the websocket connection is estabished, the streamlit server returns a Set-Cookie header that contains an XSRF token. This token is then used for operations such as file upload.

Screen Shot 2021-11-09 at 10 16 59 AM

After placing this app behind SCG, we noticed that we are no longer receiving the "additional" response headers. They are being lost as the response transits through the gateway.

I have put together a basic setup that replicates the issue using a simple Node.js websocket server, a client HTML page, and the SCG setup.

Steps to Reproduce

  1. Run the node server.mjs file:
// install the one dependency:
// npm install ws

import { WebSocketServer } from 'ws';

const wss = new WebSocketServer({ port: 5678 });

wss.on('headers', (headers) => {
    headers.push('Set-Cookie: _xsrf=1234599');
    headers.push('X-Other-Header: hello');
});
wss.on('connection', function connection(ws) {
    console.log('Connection')
    ws.send('hi from the server');
});
  1. Open the client.html in your browser. It should establish a connection to the websocket server and display hi from the server on the page.
<!DOCTYPE html>
<html lang="en">
<head>
    <title>WebSocket demo</title>
</head>
<body>
<script>
    port = "5678" // Go direct to server
    //port = "6600" // Go through gateway

    let ws = new WebSocket("ws://127.0.0.1:" + port + "/"), messages = document.createElement('ul');

    ws.onmessage = function (event) {
        let messages = document.getElementsByTagName('ul')[0],
            message = document.createElement('li'),
            content = document.createTextNode(event.data);
        message.appendChild(content);
        messages.appendChild(message);
    };
    document.body.appendChild(messages);
</script>
</body>
</html>
  1. Open up the network tab, and you should also observe that the browser received two response headers (Set-Cookie and X-Other-Header).
Screen Shot 2021-11-09 at 10 33 20 AM
  1. Now configure and start up a gateway to front this server:
# Spring Cloud Gateway Config
spring:
  cloud:
    gateway:
      routes:
        - id: websocket
          uri: http://localhost:5678
          predicates:
            - Host=**
server:
  port: 6600
logging:
  level:
    org:
      springframework:
        cloud:
          gateway: TRACE
  1. The client.html page needs to be updated to go through the gateway. This is done by changing the port number from 5678 to 6600.
<script>
    //port = "5678" // Go direct to server
    port = "6600" // Go through gateway
  1. Go back to the browser and refresh the client.html page. You should one again see hi from the server on the page. Now examine the network tab and notice our two added headers are absent from the server response.
Screen Shot 2021-11-09 at 10 38 39 AM

Setting a breakpoint in WebsocketRoutingFilter.java, I can see the missing response headers in the proxySession.handshakeinfo.headers map.

But how to get them into the response back to the browser... 🤔. I would appreciate any tips and/or hacks to get these headers through the gateway! Thanks.

andrewkcarter avatar Nov 12 '21 13:11 andrewkcarter

Related to https://github.com/spring-cloud/spring-cloud-gateway/issues/2225

andrewkcarter avatar Nov 12 '21 13:11 andrewkcarter

@andrewkcarter Hi, hope you are doing well. did you mange to find a solution for this issue?

mydani-dev avatar Jul 30 '24 08:07 mydani-dev

Not through SCG. We had to put that websocket application behind nginx instead.

andrewkcarter avatar Jul 30 '24 12:07 andrewkcarter

Still not fixed. Is there any workaround to this issue other than implementing WebSocketRoutingFilter ourselves?

julodnik2 avatar Aug 09 '24 07:08 julodnik2