Secure WebSockets with Socket.io, Node.js, and iisnode - node.js

I have an application that works perfectly fine with websockets through iisnode to a Node.js/Express/Socket.io application.
Windows Server 2012
iisnode 2.21
IIS 8.5
Node 6.2.1
socket.io 1.4.8
express.js 4.14
When visiting via HTTPS, however, WebSockets are falling back to polling after a seemingly successful wss negotiation:
GET wss://x.com/socket/a145e1f7-c8e7-4b26-96d3-a4d9869b5f3a/?id=a145e1f7-c8e7-4b26-96d3-a4d9869b5f3a&EIO=3&transport=websocket&sid=2nKT2dHWKrmO5xaHAAAM HTTP/1.1
Host: x.com
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Origin: https://x.com
Sec-WebSocket-Version: 13
DNT: 1
User-Agent: Mozilla/5.0 (Windows NT 6.1; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/52.0.2743.116 Safari/537.36
Accept-Encoding: gzip, deflate, sdch, br
Accept-Language: en-US,en;q=0.8
Cookie: io=2AAAM; _ga=GA1.2.452745265.1462472455; connect.sid=s%3M8aVwM; rl-sticky-key=!1z8WBml+V4=
Sec-WebSocket-Key: qocdK/MRQ==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Server: Microsoft-IIS/8.5
server: Microsoft-IIS/8.5
Connection: Upgrade
sec-websocket-accept: VOFznVr/l4fsY=
sec-websocket-extensions: permessage-deflate
x-powered-by: ASP.NET
X-Powered-By: ASP.NET
Date: Mon, 19 Sep 2016 18:20:04 GMT
But there are no frames in this WebSocket, and Socket.io returns to HTTP polling. If TLS is handled by IIS, how could WebSockets be effected after wss protocol negotiation?

It turned out there were load balancer problems that caused this issue, nothing that the technology software stack was doing wrong.

Related

Azure APIM throws error for WebSocket API due to Connection: Keep-Alive,Upgrade

I have REST and WebSocket APIs on the Azure API Management services portal. WebSocket is redirecting to different Web Pubsub service based on input parameters from clients/frontend for different development environments.
When hitting WebSocket api from Google Chrome, I am able to successfully establish connection end-to-end.
When hitting same WebSocket api from Firefox, I am getting InvalidWebsocketUpgrade error from APIM service.
This happens because Chrome is sending Connection: Upgrade in the socket connection request header, while Firefox is sending Connection: Keep-Alive, Upgrade in the header.
Chrome Request:
GET wss://apim-test.azure-api.net/qa/socket?access_token=eyJhbGc HTTP/1.1
Host: apim-ecv.azure-api.net
Connection: Upgrade
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/94.0.4606.71 Safari/537.36
Upgrade: websocket
Origin: https://abc.xyz.com
Sec-WebSocket-Version: 13
Accept-Encoding: gzip, deflate, br
Accept-Language: en-GB,en-US;q=0.9,en;q=0.8
Sec-WebSocket-Key: GTWCGvTFJN82sAl8gVv+VA==
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Protocol: json.webpubsub.azure.v1
Firefox request:
GET wss://apim-test.azure-api.net/qa/socket?access_token=eyJhbGciOi HTTP/1.1
Host: apim-ecv.azure-api.net
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:96.0) Gecko/20100101 Firefox/96.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 13
Origin: https://az-qa2.ecarevault.com
Sec-WebSocket-Protocol: json.webpubsub.azure.v1
Sec-WebSocket-Extensions: permessage-deflate
Sec-WebSocket-Key: r764n2hSpKKr0Y63z1Ok3A==
Connection: keep-alive, Upgrade
Sec-Fetch-Dest: websocket
Sec-Fetch-Mode: websocket
Sec-Fetch-Site: cross-site
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket
Do I need to configure anything to support this on APIM or anywhere else??
Glad that you got the Solution in Microsoft Q&A. Posting the suggestions over here to help the other community members as this is a feature request and would be helpful for the members with related discussions.
According to this document in the connection header, you need to use only Upgrade because it is a WebSocket protocol and as of now APIM is enabled only with Upgrade as a value of connection header and MS is working on enabling keep-alive, Upgrade values this could be a feature request.

Is there any chances of having websocket client connecting to an aws ec2 server behind a load balancer?

I'm working on a webchat that runs nodejs on port 8080 to publish and 4555 to subscribe using websocket at client side(javascript). When I do this connection direclty to the ec2 instance it works like a charm but in this case I won't have scalability and also have to open port 443 to world. If I make the connection through Load Balancer, chrome/firefox console shows this output and it fails to connect:
WebSocket connection to 'wss://www.domain.com/socket.io/?EIO=3&transport=websocket' failed: Error during WebSocket handshake: Unexpected response code: 200
These are the headers:
Request URL: wss://www.domain.com/socket.io/?EIO=3&transport=websocket
Request Method: GET
Status Code: 200 OK
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Connection: keep-alive
Content-Type: text/html; charset=UTF-8
Date: Sat, 25 May 2019 13:52:55 GMT
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Pragma: no-cache
Server: nginx/1.12.2
Transfer-Encoding: chunked
X-Powered-By: PHP/5.6.40
Accept-Encoding: gzip, deflate, br
Accept-Language: pt-BR,pt;q=0.9,en-US;q=0.8,en;q=0.7
Cache-Control: no-cache
Connection: Upgrade
Cookie: PHPSESSID=nt35ln6ipvbkhflic3obfhd615; AWSALB=l1rePGzfT+/sXpFl0uWliWsb9/Cqdy7V/FjiHF8slLY2pRJ2IbN86SsQJ2UBQZBIfFk8iHKKuCBbe+lLrKcN6toxjiOVVJNxzL2Xaz7BC9aShLzOCTuxkRgmC9/I
Host: www.domain.com
Origin: https:/ /www.domain.com
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: fN1LLn8igDR5CadSE9UVIw==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/74.0.3729.157 Safari/537.36
EIO: 3
transport: websocket
The javascript client is very simple, for tests purposes:
var socket = io('https: //www.domain.com/chat',{'secure':true,transports: ['websocket', 'polling', 'flashsocket']});
In nginx config files I have a location /chat proxying the request to :4555 and there is nodejs running and listening.
I have spent 2 days on this and then decided to hire someone who could do that, I had two guys working on it and they couldn't be able to fix.
Can anyone here show me the way?

Apache + Socket.io result in error establishing websocket connection on Mac

I have node.js with socket.io module in it and the tutorial chat example runs perfectly on localhost:3000.
I also have Apache server where I put all my php files for the project. Server works fine on localhost:8080.
Part of the project is communicating a message upon pushing send button on a form in a page that is created by compose.php script on Apache server. When pushing send a websocket connection has to be established (if not yet) and a test emit is sent. Just like the tutorial chat example. But that never happens.
When examining the problem on Chrome Console/Network errors are:
Console:
WebSocket connection to 'ws://localhost:8080/socket.io/?EIO=3&transport=websocket&sid=dhRg2iHG7dJqL3JEAAF4' failed: Error during WebSocket handshake: Unexpected response code: 302
Network:
First:
Request URL: ws://localhost:8080/socket.io/?EIO=3&transport=websocket&sid=BFoFZNDQamsdNurlAAIr
Request Method: GET
Status Code: 302 Found
Content-Length: 273
Content-Type: text/html; charset=iso-8859-1
Date: Sat, 29 Sep 2018 11:43:07 GMT
Location: ws://localhost:3000/socket.io/?EIO=3&transport=websocket&sid=BFoFZNDQamsdNurlAAIr
Server: Apache/2.4.33 (Unix) OpenSSL/1.0.2o PHP/7.2.6 mod_perl/2.0.8-dev Perl/v5.16.3
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,ru;q=0.8,ja;q=0.7,uk;q=0.6
Cache-Control: no-cache
Connection: Upgrade
Cookie: PHPSESSID=aaa8a06d8e58534d5ebbe62919085074; io=BFoFZNDQamsdNurlAAIr
Host: localhost:8080
Origin: http://localhost:8080
Pragma: no-cache
Sec-WebSocket-Extensions: permessage-deflate; client_max_window_bits
Sec-WebSocket-Key: WqB/VJfA2Fx/+0BrcHPnBA==
Sec-WebSocket-Version: 13
Upgrade: websocket
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
EIO: 3
transport: websocket
sid: BFoFZNDQamsdNurlAAIr
Then:
Request URL: http://localhost:3000/socket.io/?EIO=3&transport=polling&t=MOb913S&sid=BFoFZNDQamsdNurlAAIr
Request Method: GET
Status Code: 500 Internal Server Error
Remote Address: [::1]:3000
Referrer Policy: no-referrer-when-downgrade
Connection: keep-alive
Date: Sat, 29 Sep 2018 11:43:32 GMT
Transfer-Encoding: chunked
Accept: */*
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.9,ru;q=0.8,ja;q=0.7,uk;q=0.6
Connection: keep-alive
Content-type: text/plain;charset=UTF-8
Cookie: PHPSESSID=aaa8a06d8e58534d5ebbe62919085074; io=BFoFZNDQamsdNurlAAIr
Host: localhost:3000
Origin: http://localhost:8080
Referer: http://localhost:8080/compose.php?crate_id=53
User-Agent: Mozilla/5.0 (Macintosh; Intel Mac OS X 10_13_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/69.0.3497.100 Safari/537.36
EIO: 3
transport: polling
t: MOb913S
sid: BFoFZNDQamsdNurlAAIr
My rewrite rule setup in .htaccess is this:
RewriteCond %{HTTP:Upgrade} websocket [NC]
RewriteCond %{QUERY_STRING} transport=websocket [NC]
RewriteRule ^/?socket.io ws://localhost:3000/socket.io/
RewriteRule ^/?socket.io http://localhost:3000/socket.io/
I almost certain the problem is in the way I redirect socket.io calls from localhost:8080 to localhost:3000. I am just that bad at networks to completely not understand that. I have tried different configurations, including proxy and reverse proxy settings in httpd.conf such as:
<IfModule proxy_module>
ProxyRequests Off
ProxyPass /socket.io http://localhost:3000/socket.io/
ProxyPassReverse /socket.io http://localhost:3000/socket.io/
</IfModule>
I've been trying to fix this for 2 weeks now, it's driving me crazy. How can I get the socket.io on localhost:3000 to emit info upon request from a page originated from localhost:8080/compose.php ?
As most things I fail to do in life, solution was quite simple.
When running socket = io(); in a .js file, that is being served by Apache server, the socket object is set uri which is uri of the Apache server, in my case localhost:8080.
By adding this line
socket.io.uri = "http://localhost:3000/";
the problem is solved. The connection fails to be established right away, but will be established at reconnect attempt.

gzip compression not working with IIS 8.5

I have a Server 2012 R2 box running IIS. I've tried enabling compression for several sites running on that box, but I can't figure out why it won't work. My request headers all show accept-encoding, but the response headers are always Transfer-Encoding:chunked and Vary:Accept-Encoding. The following steps have been performed to try to get gzip compression working:
Dynamic and Static compression have been enabled on each site and at the machine level
Both compression methods are installed from Server Manager
Httpcompression and urlcompression nodes have been manually added to web.configs
Mime types are defined for compression
frequentHitThreshold has been set to 1, so all content should be compressed after the first attempt to access it
A trace has been done to see why compression isn't occurring. The only information I have is the code DYNAMIC_COMPRESSION_NOT_SUCCESS with a reason of 1.
Here are the headers:
GET http://redactedservername:8082/ HTTP/1.1
Host: redactedservername:8082
Connection: keep-alive
Cache-Control: max-age=0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,*/*;q=0.8
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/50.0.2661.102 Safari/537.36
DNT: 1
Accept-Encoding: gzip, deflate, sdch
Accept-Language: en-US,en;q=0.8
Cookie: ASP.NET_SessionId=gnqovt55ggt22lycufudc0ns
`
HTTP/1.1 200 OK
Cache-Control: private
Content-Type: text/html; charset=utf-8
Vary: Accept-Encoding
Date: Wed, 22 Jun 2016 14:00:57 GMT
Transfer-Encoding: chunked
What other steps can be performed to get compression to work?
Compression was working, but ESET Antivirus was doing its job of monitoring web traffic. This modified the response and I didn't get gzip content encoding as expected. Disabling ESET and testing again showed that compression was functioning.

Client caching recommendations for custom web fonts

What is the best way to get IIS to set the headers for woff files so that they can be served from the client browser cache.
I'm working on an MVC .NET site that is hosted in IIS7.5, served through cloudflare with static caching turned on. The site uses a custom woff web font. When requests are made for pages that use these fonts IIS is serving them with the headers shown below. Subsequent requests all look the same. To me it looks like these are not getting cached by the client browser. I'd expect the server to be responding with 304 (Not Modified) and then the browser should serve the woff from its cache.
Request Headers
GET /blah/Content/fonts/AzoSans-Thin-webfont.woff HTTP/1.1
Host: blah.co.uk
Connection: keep-alive
Pragma: no-cache
Cache-Control: no-cache
User-Agent: Mozilla/5.0 (Windows NT 6.3; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/38.0.2125.111 Safari/537.36
Accept: */*
DNT: 1
Referer: http://blah.co.uk/bundles/Content/stylesheets/main?v=f9NXr53WMUdV9DfYJMkEU_5QZZi0g8eB1lB5lqxgdXc1
Accept-Encoding: gzip,deflate,sdch
Accept-Language: en-US,en;q=0.8
Cookie: __cfduid=d96b367152ae58725c15e5946cf1d67f41415385741070; ASP.NET_SessionId=3tcc3e1nd0z005tlknrbph5h; redesign#lang=en
Response Headers
HTTP/1.1 200 OK
Date: Fri, 07 Nov 2014 19:27:57 GMT
Content-Type: application/font-woff
Content-Length: 27728
Connection: keep-alive
Last-Modified: Thu, 06 Mar 2014 10:40:46 GMT
ETag: "0cbfc872839cf1:0"
X-Powered-By: ASP.NET
CF-Cache-Status: HIT
Vary: Accept-Encoding
Expires: Fri, 07 Nov 2014 23:27:57 GMT
Cache-Control: public, max-age=14400
Accept-Ranges: bytes
Server: cloudflare-nginx
CF-RAY: 185bee60092d0a90-LHR
The problem here seems to be that cloudflare is changing the headers. If I bypass cloudflare I get
Cache-Control:max-age=0
and the server responds with a 304 and the browser uses content from its cache.

Resources