<p>In a Hosted Rancher Kubernetes cluster, I have a service that exposes a websocket service (a Spring SockJS server). 
This service is exposed to the outside thanks an ingress rule:</p>

<pre><code>apiVersion: extensions/v1beta1
kind: Ingress
metadata:
  name: myIngress
  annotations:
    kubernetes.io/ingress.class: "nginx"
    nginx.ingress.kubernetes.io/rewrite-target: /
    nginx.ingress.kubernetes.io/proxy-read-timeout: "3600s"
    nginx.ingress.kubernetes.io/proxy-send-timeout: "3600s"
    nginx.ingress.kubernetes.io/enable-access-log: "true"
spec:
  rules:
    - http:
        paths:
        - path: /app1/mySvc/
          backend:
            serviceName: mySvc
            servicePort: 80

</code></pre>

<p>A <strong>web application</strong> connects to the web socket service throught an ingress nginx and it works fine. The loaded js scripts is:</p>

<pre class="lang-js prettyprint-override"><code>    var socket = new SockJS('ws');
    stompClient = Stomp.over(socket);

    stompClient.connect({}, onConnected, onError);
</code></pre>

<p>On the contrary, the <strong>standalone clients</strong> (js or python) do not work as they returns a 400 http error. </p>

<p>For example, here is the request sent by curl and the response from nginx:</p>

<pre class="lang-sh prettyprint-override"><code>curl  --noproxy '*' --include \
     --no-buffer \
     -Lk \
--header "Sec-WebSocket-Key: l3ApADGCNFGSyFbo63yI1A==" \
--header "Sec-WebSocket-Version: 13" \
--header "Host: ingressHost" \
--header "Origin: ingressHost" \
--header "Connection: keep-alive, Upgrade" \
--header "Upgrade: websocket" \
--header "Sec-WebSocket-Extensions: permessage-deflate" \
--header "Sec-WebSocket-Protocol: v10.stomp, v11.stomp, v12.stomp" \
--header "Access-Control-Allow-Credentials: true" \
https://ingressHost/app1/mySvc/ws/websocket


HTTP/2 400
date: Wed, 20 Nov 2019 14:37:36 GMT
content-length: 34
vary: Origin
vary: Access-Control-Request-Method
vary: Access-Control-Request-Headers
access-control-allow-origin: ingressHost
access-control-allow-credentials: true
set-cookie: JSESSIONID=D0BC1540775544E34FFABA17D14C8898; Path=/; HttpOnly
strict-transport-security: max-age=15724800; includeSubDomains

Can "Upgrade" only to "WebSocket".

</code></pre>

<p>Why does it work with the browser and not standalone clients ?</p>

<p>Thanks</p>

crepu
<p>The problem doesn't seem to be in the nginx Ingress. The presence of the JSESSIONID cookie most likely indicates that the Spring applications gets the request and sends a response.</p>

<p>A quick search through the Spring's code shows that <code>Can "Upgrade" only to "WebSocket".</code> is returned by <a href="https://github.com/spring-projects/spring-framework/blob/master/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java#L294-L300" rel="nofollow noreferrer">AbstractHandshakeHandler.java</a> when the <a href="https://github.com/spring-projects/spring-framework/blob/master/spring-websocket/src/main/java/org/springframework/web/socket/server/support/AbstractHandshakeHandler.java#L251-L254" rel="nofollow noreferrer"><code>Upgrade</code> header isn't equal to <code>WebSocket</code></a> (case-insensitive match).</p>

<p>I'd suggest double-checking that the <code>"Upgrade: websocket"</code> header is present when making the call with <code>curl</code>. </p>

<p>Also, this appears to be a <a href="https://stackoverflow.com/questions/38376316/handle-the-same-url-with-spring-mvc-requestmappinghandlermapping-and-spring-webs">similar problem</a> and may apply here as well if the application has several controllers.</p>

<p>And for what it's worth, after replacing <code>ingressHost</code> appropriately I tried the same <code>curl</code> command as in the question against <code>https://echo.websocket.org</code> and a local STOMP server I've implemented some time ago. It worked for both. </p>

<p>You may have already done this but have you tried capturing in the browser the Network traffic to see the request/response exchange, especially the one that returns HTTP <code>101 Switching Protocols</code>? Then try to exactly replicate the call that the browser makes and that is successful. For example, the STOMP client generates a session id and uses a queue/topic, which are put in the URL path in requests to the server (e.g. <code>/ws/329/dt1hvk2v/websocket</code>). The test request with <code>curl</code> doesn't seem to have them. </p>
