Spring cloud gateway: Websockets on the same URL as HTTP(S)
Asked Answered
F

1

7

I'm using Spring Cloud Gateway with a routing configuration like this:

spring:
  cloud:
    gateway:
      routes:
      - id: http
        uri: http://kubernetes-ingres-controller

In other words, I'm sending all paths onwards to somewhere else, which happens to be a K8s ingress controller. Spring adds some headers and handles the translation of session cookies into bearer tokens.

I'm starting to add some endpoints which handle websockets. However, since we are using websockets to do GraphQL subscriptions, we tend to have a single /graphql path which handles both ordinary GraphQL queries and websockets for subscriptions.

Is there a way to configure Spring Cloud Gateway to support both http and ws (or https and wss) on a single path?

I can do this:

spring:
  cloud:
    gateway:
      routes:
      - id: http
        uri: http://kubernetes-ingres-controller
      - id: ws
        uri: ws://kubernetes-ingres-controller
        predicates:
          - Path=/graphql

However it's not clear to me that ordinary HTTP will work on the graphql path - and in practice there are many such paths; I'd prefer not to have to enumerate them all.

Some sort of setting to allow http with optional upgrade to wss on a single route would be ideal.

Fromenty answered 30/10, 2023 at 17:15 Comment(2)
I'm not sure if I can understand the question. It seems to me that the problem is about having two routes with overlapping incoming paths (/ and /graphql). If that's the case, you can use order to control traffic to both routes.Enos
Have you tried using headers as predicate? Websocket handshakes always include dedicated headers - 'Sec-WebSocket-Key', 'Sec-WebSocket-Protocol', 'Sec-WebSocket-Version' and 'Upgrade: websocket', 'Connection: upgrade'Machute
L
0

My answer is conceptual one and this would help in your work.

  1. Write a new class and extend AbstractRoutePredicateFactory like WebSocketPredicateFactory

  2. Add below code with your own logic to identify that the request need to upgrade to web socket or not

  3. Need to update the yaml file to configure your routes to use this custom predicate factory

     @override   
    
    public Predicate apply(Config config) {
               return exchange -> {
                     // Check if the incoming request is for WebSocket upgrade
                    if (isWebSocketUpgradeRequest(exchange)) {
                         exchange.getRequest().mutate().headers(httpHeaders -> 
                          httpHeaders.setUpgrade("websocket"));
    
                   exchange.getResponse().getHeaders().setUpgrade("websocket");
                    return true;
                }
                return false;
            };
        }
    
        private boolean isWebSocketUpgradeRequest(ServerWebExchange exchange) {
            // Implement your own logic to determine WebSocket upgrade based on path, headers, etc.
            // For example:
            return exchange.getRequest().getURI().getPath().equals("/graphql") &&
                    exchange.getRequest().getHeaders().getUpgrade() != null &&
                    exchange.getRequest().getHeaders().getUpgrade().contains("websocket");
        }
    
    

Update the route also like below

spring: cloud: gateway: routes: - id: http-and-ws uri: http://kubernetes-ingres-controller predicates: - name: WebSocketPredicateFactory args: # any orgs
Logarithm answered 20/11, 2023 at 11:54 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.