Client is not receiving event from SocketIO via IIS Node - node.js

I have a web application which is making a socket connection with a NodeJS server hosted via IIS Node. The connection seems to be made properly as I resolved a 404 polling error on the client that I was initially having. However now, it looks like the client is not receiving socket events from IIS Node.
My theory is that because IIS Node is acting like a proxy, the event sent back to the client is stopping somewhere at the IIS Node level.
exports.register = function(socket) {
User.schema.post('save', function (doc) {
console.log("Save socket");
onSave(socket, doc);
});
}
function onSave(socket, doc, cb) {
socket.emit('User List Update', doc);
}
In Node, the above event fires, but it is not received on the front end.
The client looks like this:
var socket = io.connect("http://myinternallyhostedserver:7777/reception");
socket.on('User List Update', function () {
console.log('Received socket event!');
if (vm.enableAlert) {
vm.alertSound.play();
}
vm.getUsers();
});
My web.config file looks like this:
<configuration>
<appSettings>
<add key="virtualDirPath" value="/reception" />
</appSettings>
<system.webServer>
<handlers>
<add name="iisnode" path="server/app.js" verb="*" modules="iisnode" />
</handlers>
<iisnode enableXFF="true" />
<webSocket enabled="false"/>
<rewrite>
<rules>
<rule name="reception">
<match url="/*" />
<action type="Rewrite" url="server/app.js" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Running all of this on my local machine works properly. Socket events are sent and received. Adding IIS Node into the workflow seems to be where it stopped working. Any help would be greatly appreciated.

While using socket.io with proxy pass , you need to pass some variable like upgrade etc. I didnt even try using iis to proxy pass socket.io but I came across some problem in apache proxy pass . Therefore I choose nginx to proxy server.
Maybe you need to write this kind of parameters in iis , alternatively you can use nginx as a proxy pass server.
location / {
proxy_pass http://socket_nodes_2/doSomething;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
proxy_redirect off;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
upstream socket_nodes_2 {
server 192.168.1.155:1338;
server 192.168.1.156:1338;
}

Related

How to serve Node Express with React frontend from IIS using IISNode

I am in the final stages of my project. I have an operating website running from NodeJS Express which will serve the built create-react-app frontend. Now I need to configure it to run from IIS using IISNode. I have followed a number of directions but without success. The reason for IIS is so that the site will be served without the user being logged in and available so long as the server (Windows server 2012 R2, IIS 8) is running.
I have read of numerous "hacks" which people used to get their own site running.
The node is listening on process.env.PORT || 8000. When I am running directly from node (npm run start) there is no difficulty, but when attempting to do the same from IIS it gives me errors finding the backend URLs. When attempting to run React as a separate site I get a 404 error searching for the initial API, while when running it with the frontend hosted in the "public" folder I am getting 403.14: Cannot list directory contents.
I currently don't even know which code to display in order to solve this issue, but I suspect the web.config might be part of it. I am including that along with my bin/www file from node.
// /bin/www
#!/usr/bin/env node
/**
* Module dependencies.
*/
var app = require('../app');
var debug = require('debug')('node-backend:server');
var http = require('http');
/**
* Get port from environment and store in Express.
*/
var port = normalizePort(process.env.PORT || '8000'); //
app.set('port', port);
/**
* Create HTTP server.
*/
var server = http.createServer(app);
/**
* Listen on provided port, on all network interfaces.
*/
server.listen(port);
server.on('error', onError);
server.on('listening', onListening);
/**
* Normalize a port into a number, string, or false.
*/
function normalizePort(val) {
var port = parseInt(val, 10);
if (isNaN(port)) {
// named pipe
return val;
}
if (port >= 0) {
// port number
return port;
}
return false;
}
/**
* Event listener for HTTP server "error" event.
*/
function onError(error) {
if (error.syscall !== 'listen') {
throw error;
}
var bind = typeof port === 'string'
? 'Pipe ' + port
: 'Port ' + port;
// handle specific listen errors with friendly messages
switch (error.code) {
case 'EACCES':
console.error(bind + ' requires elevated privileges');
process.exit(1);
break;
case 'EADDRINUSE':
console.error(bind + ' is already in use');
process.exit(1);
break;
default:
throw error;
}
}
/**
* Event listener for HTTP server "listening" event.
*/
function onListening() {
var addr = server.address();
var bind = typeof addr === 'string'
? 'pipe ' + addr
: 'port ' + addr.port;
debug('Listening on ' + bind);
console.log('Listening on ' + bind);
}
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode" />
</handlers>\
<!-- Removed in an effort to get a valid URL response -->
<!-- <rewrite>
<rules>
<rule name="api">
<match url="/*" />
<action type="Rewrite" url="/*:8000" />
</rule>
</rules>
</rewrite> -->
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
</security>
</system.webServer>
</configuration>
Please let me know what other, if any, data is needed to solve this problem.
*** EDIT ***
Some progress made by altering web.config enabling the section and pointing all requests to /app.js. Here is the error message I am now receiving:
iisnode encountered an error when processing the request.
HRESULT: 0x2
HTTP status: 500
HTTP subStatus: 1002
HTTP reason: Internal Server Error
You are receiving this HTTP 200 response because system.webServer/iisnode/#devErrorsEnabled configuration setting is 'true'.
In addition to the log of stdout and stderr of the node.exe process, consider using debugging and ETW traces to further diagnose the problem.
The node.exe process has not written any information to stderr or iisnode was unable to capture this information. Frequent reason is that the iisnode module is unable to create a log file to capture stdout and stderr output from node.exe. Please check that the identity of the IIS application pool running the node.js application has read and write access permissions to the directory on the server where the node.js application is located. Alternatively you can disable logging by setting system.webServer/iisnode/#loggingEnabled element of web.config to 'false'.
The 2 links it refers to were both written over 10 years ago and gave no helpful information that I could discern.
I have also tried wrapping my app.js code in a try/catch block and using fs to write the error log to a text file, with no success. I believe that it is not even accessing the app.js before throwing the error. All paths have been given a blanket "full control" to "EVERYONE" just to completely eliminate permissions as an issue (for now).
Updated web.config:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<handlers accessPolicy="Execute">
<add name="iisnode" path="app.js" verb="*" modules="iisnode" resourceType="Either" requireAccess="Execute" />
</handlers>
<rewrite>
<rules>
<rule name="api">
<match url="/*" />
<action type="Rewrite" url="/app.js/*" />
</rule>
</rules>
</rewrite>
<security>
<requestFiltering>
<hiddenSegments>
<add segment="node_modules" />
</hiddenSegments>
</requestFiltering>
</security>
<tracing>
<traceFailedRequests>
<add path="*">
<traceAreas>
<add provider="ISAPI Extension" verbosity="Verbose" />
<add provider="WWW Server" areas="Authentication,Security,Filter,StaticFile,CGI,Compression,Cache,RequestNotifications,Module,FastCGI,WebSocket,ANCM,Rewrite,RequestRouting,iisnode" verbosity="Verbose" />
</traceAreas>
<failureDefinitions statusCodes="403.14" verbosity="Warning" />
</add>
</traceFailedRequests>
</tracing>
<iisnode watchedFiles="*.js" loggingEnabled="true" logDirectory="iisnode" devErrorsEnabled="true" />
</system.webServer>
</configuration>
I have dramatically modified my approach and am now running the application as 2 sites on IIS. The backend is running on port 90 and the front end (REACT) is running on port 80 with the requests to port 90 hard coded in the code.
Progressing now to determine why cors() isn't functioning correctly.

How do i configure minio using iis as a reverse proxy

I am trying to configure minio using IIS as a reverse proxy.
I want the main service to run from minio.mysite.com and the console to run from console.minio.mysite.com, both of which is already configured with my webhost/dns.
docker-compose.yml
version: '3.7'
networks:
minio_network2:
name: minio_network2
services:
minio_web2:
image: quay.io/minio/minio:RELEASE.2021-10-23T03-28-24Z
container_name: minio_web2
restart: unless-stopped
command: server --console-address ":9001" /data
environment:
MINIO_ROOT_USER: multinerd#mysite.com
MINIO_ROOT_PASSWORD: super_secure_pass
# MINIO_SERVER_URL: https://minio.mysite.com
# MINIO_BROWSER_REDIRECT_URL: https://console.minio.mysite.com
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost:9000/minio/health/live"]
interval: 30s
timeout: 20s
retries: 3
networks:
- minio_network2
volumes:
- ./minio-data/minio:/data
minio_nginx2:
image: nginx:1.19.2-alpine
container_name: minio_nginx2
restart: unless-stopped
hostname: nginx
volumes:
- ./nginx.conf:/etc/nginx/nginx.conf:ro
ports:
- "3009:9000" ## Client
- "30091:9001" ## Console
depends_on:
- minio_web2
networks:
- minio_network2
nginx.conf
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 4096;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
keepalive_timeout 65;
# include /etc/nginx/conf.d/*.conf;
upstream minio {
server minio_web2:9000;
}
upstream console {
ip_hash;
server minio_web2:9001;
}
server {
listen 9000;
listen [::]:9000;
server_name localhost;
# To allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# To disable buffering
proxy_buffering off;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_connect_timeout 300;
# Default is HTTP/1, keepalive is only enabled in HTTP/1.1
proxy_http_version 1.1;
proxy_set_header Connection "";
chunked_transfer_encoding off;
proxy_pass http://minio;
}
}
server {
listen 9001;
listen [::]:9001;
server_name localhost;
# To allow special characters in headers
ignore_invalid_headers off;
# Allow any size file to be uploaded.
# Set to a value such as 1000m; to restrict file size to a specific value
client_max_body_size 0;
# To disable buffering
proxy_buffering off;
location / {
proxy_set_header Host $http_host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-NginX-Proxy true;
# This is necessary to pass the correct IP to be hashed
real_ip_header X-Real-IP;
proxy_connect_timeout 300;
# To support websocket
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection "upgrade";
chunked_transfer_encoding off;
proxy_pass http://console;
}
}
}
In IIS, I've set up 2 websites.
S_Minio
binds to minio.mysite.com
rewrite rules
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="RedirectBrowsers" enabled="true" patternSyntax="Wildcard" stopProcessing="true">
<match url="*" negate="false" />
<conditions logicalGrouping="MatchAny" trackAllCaptures="false">
<add input="{HTTP_USER_AGENT}" pattern="*firefox*" />
<add input="{HTTP_USER_AGENT}" pattern="*edge*" />
<add input="{HTTP_USER_AGENT}" pattern="*chrome*" />
</conditions>
<action type="Redirect" url="https://console.minio.mysite.com/login" logRewrittenUrl="true" redirectType="Found" />
</rule>
<rule name="Docker reverse proxy for minio" enabled="true" stopProcessing="false">
<match url="(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false">
<add input="{HTTP_HOST}" pattern="*console*" />
</conditions>
<action type="Rewrite" url="http://10.200.2.67:3009/{R:1}" logRewrittenUrl="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
S_Minio_Console
binds to console.minio.mysite.com
rewrite rules
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Docker reverse proxy for minio console" enabled="true" stopProcessing="true">
<match url="(.*)" />
<action type="Rewrite" url="http://10.200.2.67:30091/{R:1}" appendQueryString="true" logRewrittenUrl="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
Up until this point, everything works well.
If I visit https://minio.mysite.com, I get redirected to https://console.minio.mysite.com.
I can log in to the console and view my files.
If I try to share a file, the following url gets generated: http://172.18.0.2:9000/test/my_excel_file.xlsx?query_strings_ommitied which is no good.
According to the things to consider section, I updated my docker-compose.yml file with the following changes
services:
minio_web2:
...
environment:
...
+ MINIO_SERVER_URL: https://minio.mysite.com
+ MINIO_BROWSER_REDIRECT_URL: https://console.minio.mysite.com
Now when I log in, I get a error Post "https://minio.mysite.com/": dial tcp: i/o timeout.
nginx and my rewrite rules have not changed and im not sure what to change from this point.

Socket.io + IIS 10 + reverse proxy setup

The goal is to host a socket.io server on the same port and domain as my website, hosted on IIS 10 using a reverse proxy. And Ideally configure SSL in IIS rather than the socket.io app.
From what I read, this can be achieved using a reverse proxy URL rewrite rule.
However my javascript client application is showing this error:
No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'serve://dist' is therefore not allowed access. The response had HTTP status code 502.
I have setup a reverse proxy URL rewrite rule to match (regex) the URL socket\.io\/(.*) and rewrite it to localhost:8001/{R:0}
My web.config contains this:
<rewrite>
<rules>
<clear />
<rule name="ReverseProxyInboundRule1" enabled="true" patternSyntax="ECMAScript" stopProcessing="true">
<match url="socket\.io\/(.*)" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="localhost:8001/{R:0}" />
</rule>
</rules>
</rewrite>
I don't see anywhere where you can differentiate between http:// and ws://.
I honestly don't have a clue what I'm doing. A lot of the stuff I read online is outdated and really not very helpful.
Any help would be appreciated?

Failed to load resource: the server responded with a status of 404 (Not Found) socket.io/?EIO=3&transport=polling&t=1503062449710-0

I was finally able to deploy my app to Azure, however I always get this error with socket.io
/socket.io/?EIO=3&transport=polling&t=1503062449710-0 Failed to load resource: the server responded with a status of 404 (Not Found)
even though it worked on my localhost.
Server Side:
var app = require('express')();
var server = require('http').Server(app);
var io = require('socket.io')(server);
server.listen(80);
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
io.on('connection', function (socket) {
socket.emit('news', { hello: 'world' });
socket.on('my other event', function (data) {
console.log(data);
});
});
Client Side (index.html):
<html>
<head>
<script src="/socket.io/socket.io.js"></script>
<script>
var socket = io.connect('http://chattranslatortwo.azurewebsites.net/');
socket.on('news', function(data) {
console.log(data);
socket.emit('my other event', {
my: 'data'
});
});
</script>
</head>
<body>
</body>
</html>
Now I've done many things to attempt to fix this.
1) Since many posts were saying to change socket.io to socket.io-client, or to "https://cdn.socket.io/socket.io-1.3.7.js" this didn't change anything for me, same result.
2) I tried reinstalling my node.js and socket.io, but wasn't sure if that changed anything either.
3) Enabled my Azure Web Sockets in Application Settings
4) Made sure that my connection was the correct site.
I always come back to this same error message calling out the socket.io:
/socket.io/?EIO=3&transport=polling&t=1503062449710-0 Failed to load resource: the server responded with a status of 404 (Not Found)
Either I'm completely missing something or it seems that sockets work completely different on WebServices than from the localhost.
I've been working on this problem for quite some time now.
On Azure App Service, you'd need to change the following line
server.listen(80);
to
server.listen(process.env.PORT);
Also, you should create a web.config in the root of your Node.js application if not exists. For reference, the below is a default web.config for an application that uses app.js as the entry point.
<?xml version="1.0" encoding="UTF-8"?>
<!--
This configuration file is required if iisnode is used to run node processes behind
IIS or IIS Express. For more information, visit:
https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config
-->
<configuration>
<system.webServer>
<!-- Visit http://blogs.msdn.com/b/windowsazure/archive/2013/11/14/introduction-to-websockets-on-windows-azure-web-sites.aspx for more information on WebSocket support -->
<webSocket enabled="false" />
<handlers>
<!-- Indicates that the server.js file is a node.js web app to be handled by the iisnode module -->
<add name="iisnode" path="app.js" verb="*" modules="iisnode" />
</handlers>
<rewrite>
<rules>
<!-- Do not interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^app.js\/debug[\/]?" />
</rule>
<!-- First we consider whether the incoming URL matches a physical file in the /public folder -->
<rule name="StaticContent">
<action type="Rewrite" url="public{REQUEST_URI}" />
</rule>
<!-- All other URLs are mapped to the node.js web app entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True" />
</conditions>
<action type="Rewrite" url="app.js" />
</rule>
</rules>
</rewrite>
<!--
You can control how Node is hosted within IIS using the following options:
* watchedFiles: semi-colon separated list of files that will be watched for changes to restart the server
* node_env: will be propagated to node as NODE_ENV environment variable
* debuggingEnabled - controls whether the built-in debugger is enabled
See https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/web.config for a full list of options
-->
<!--<iisnode watchedFiles="web.config;*.js"/>-->
</system.webServer>
</configuration>
For more information, please refer to Create a Node.js chat application with Socket.IO in Azure App Service.

Reproducing IIS Reverse Proxy Config with nginx

I am trying to reproduce an IIS reverse proxy configuration with nginx. The application is for a mobile iphone app to communicate to a configuration server on our LAN. Both the mobile app and web server are third party and lack documentation.
web.config
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="Authenticate" stopProcessing="true">
<match url="^authenticate(.*)" />
<action type="Rewrite" url="http://INTERNALIP:80{R:1}" logRewrittenUrl="true" />
</rule>
<rule name="SAC" stopProcessing="true">
<match url="^sac(.*)" />
<action type="Rewrite" url="http://INTERNALIP:5447{R:1}" logRewrittenUrl="true" />
</rule>
<rule name="Config" stopProcessing="true">
<match url="^config(.*)" />
<action type="Rewrite" url="http://INTERNALIP:5449{R:1}" logRewrittenUrl="true" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
After reading through the documentation and looking through other posts. I came up with this for my server directive.
nginx.conf
server {
listen 5600;
server_name mobile.example.com;
access_log /var/log/nginx/log/rproxy.log;
error_log /var/log/nginx/log/error.log;
location ^~ /authenticate(.*){
rewrite ^/authenticate(.*)/ /$1 break;
proxy_pass http://INTERNALIP;
}
location ^~ /sac(.*){
rewrite ^/sac(.*)/ /$1 break;
proxy_pass http://INTERNALIP:5447;
}
location ^~ /config(.*){
rewrite ^/config(.*)/ /$1 break;
proxy_pass http://INTERNALIP:5449;
}
}
I am seeing some data in the log, but it wasn't really what I was expecting and I'm having difficulties troubleshooting from here. I really wish I knew what the requests looked like and had some sort of request flow, but I do not.
access.log
xx.xx.xx.xx - - [24/Jan/2014:18:33:45 +0000] "\x16\x03\x01\x00\xA1\x01\x00\x00\x9D\x03\x01R\xE2\xB2\x09\xBC\x9F\xE4h\x04_\x8C\x0C[\x94\x1E\xE66H\x1DLY^H\x16\xF5U\xF4\xF8" 400 172 "-" "-"
xx.xx.xx.xx - - [24/Jan/2014:18:33:45 +0000] "\x16\x03\x01\x00\xA1\x01\x00\x00\x9D\x03\x01R\xE2\xB2\x09\xD8>e\x15\x89\xF1\xC1,\xC6_Qj\x96\x88\xC8\x11\x06P=\xB2OE\xB6\xA4,\xE7;/\x00\x00J\x00\xFF\xC0$\xC0#\xC0" 400 172 "-" "-"
xx.xx.xx.xx - - [24/Jan/2014:18:33:45 +0000] "\x16\x03\x00\x00E\x01\x00\x00A\x03\x00R\xE2\xB2\x09\x09\xEC(\xCE\xD3\xB7$\xA7T\x0C\xEA\xEF^0\xF9In*Y#\xFE\x9F\x09\xD3W\xA8)f\x00\x00\x1A\x00\xFF\x00=\x00<\x00/\x00\x05\x00\x04\x005\x00" 400 172 "-" "-"
Any help would be greatly appreciated.
Thanks
I found this question searching for "\x16\x03\x01\x00", which I was also seeing throughout my access.log and not understanding what it was I was looking at. It's raw ssl coming through. For me, I had http redirecting to https but my server definition looked like
server {
listen 443;
server_name my.server.com;
...
}
I fixed the problem by adding in the missing "default ssl" to the listen line.
server {
listen 443 default ssl;
server_name my.server.com;
...
}

Resources