How to serve Node Express with React frontend from IIS using IISNode - node.js

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.

Related

host nodejs chat application with iis node

I have developed a chat application with nodejs. Currently, I am using the chat server hosted from a command prompt. This chat server is not bound to any domain name. I want it to bind to a domain with IIS. I have also tried to install IIS Node but could not get through. I have all my chat code in chatroom.js file, there is no HTML file to be rendered. I am not clear how to host it with IIS Node. I am using IIS 8 on Windows 8. The error I am get when hit the URL : http://localhost:3004/chatRoom.js
HTTP Error 500.21 - Internal Server Error
Handler "iisnode-socketio" has a bad module "iisnode" in its module list
Below is my web.config :
<configuration>
<system.web>
<compilation debug="true" targetFramework="4.0"/>
</system.web>
<system.webServer>
<!-- indicates that the server-faye.js and server-socketio.js files are node.js applications
to be handled by the iisnode module -->
<handlers>
<add name="iisnode-socketio" path="chatRoom.js" verb="*" modules="iisnode" />
</handlers>
<!-- indicate that all strafic the URL paths beginning with 'socket.io' should be
redirected to the server-socketio.js node.js application to avoid IIS attempting to
serve that content using other handlers (e.g. static file handlers)
-->
<globalModules>
<add name="iisnode" image="C:\Program Files (x86)\iisnode-express\iisnode.dll" />
</globalModules>
<rewrite>
<rules>
<clear />
<rule name="cdw">
<match url="/*" />
<action type="Rewrite" url="chatRoom.js" />
</rule>
</rules>
</rewrite>
<!-- disable the IIS websocket module to allow node.js to provide its own
WebSocket implementation -->
<webSocket enabled="false" />
</system.webServer>
</configuration>
node server side code looks as follows :
var express = require('express'),
http = require('http'),
app = express(),
server = http.createServer(app),
io = require('socket.io').listen(server),
Room = require('./room.js'),
defaultValues = require('./default.js'),
_ = require('underscore')._;
var request = require('request');
//require('request').debug = true; // uncomment for debugging
app.use('/', express.static(__dirname + '/public'));
server.listen(process.env.PORT);
app.get('/', function (req, res) {
res.sendFile(__dirname + '/public/indexroom.html')
});
Please let me know if I am missing anything.

HTTPS issues in azure

I imported the git socket.io chat room project! The code works normally with http = require ('http') but when exchanging for https = require ('https') my server responds with error 500 http
var express = require('express')
, app = express()
// , http = require('http')
, https = require('https')
, fs = require('fs')
, privateKey = fs.readFileSync('HTTPS_Permissions/key.key', 'utf8')
, certificate = fs.readFileSync('HTTPS_Permissions/cert.cert', 'utf8')
, credentials = {key: privateKey, cert: certificate}
, httpsServer = https.createServer(credentials, app)
// , httpServer = http.createServer(app)
, io = require('socket.io').listen(httpsServer)
//, port = process.env.PORT || 8080
, port = process.env.PORT
httpsServer.listen(port, function () {
console.log('Server listening on port %d', port);
});
//httpServer.listen(port);
// routing
app.get('/', function (req, res) {
res.sendfile(__dirname + '/index.html');
});
I followed the project you shared in the comment,it works on my side.
web.config
<?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 site 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 site entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="app.js"/>
</rule>
</rules>
</rewrite>
<!-- 'bin' directory has no special meaning in node.js and apps can be placed in it -->
<security>
<requestFiltering>
<hiddenSegments>
<remove segment="bin"/>
</hiddenSegments>
</requestFiltering>
</security>
<!-- Make sure error responses are left untouched -->
<httpErrors existingResponse="PassThrough" />
<!--
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>
If you throw the index.html into public folder which is created by yourself under wwwroot/ directly, you need to add the below code into your code based on this article.
app.use(express.static('public'))
I tested for that.
Update Answer:
I also turn on the Web Sockets Option.

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.

node web app in azure is blank

I'm new to node and express but I'm having an issue when I host my node web app in windows azure which by the way works completely fine on localhost. I just get a blank white screen.
this is my:
server.js
var root = require('root');
var github = require('github-auth');
var express = require('express');
var path = require('path');
// var app = root();
var app = express();
var gh = github('clientid', 'clientsecret, {
organization: 'my-org',
team: 'my-team',
autologin: true // This automatically redirects you to github to login
});
app.get('/login', gh.login);
app.use(express.static(__dirname + '/'));
app.all('*', gh.authenticate);
app.all('*', function(req, res, next) {
if (!req.github) return res.sendFile(__dirname + '/login.html');
if (!req.github.authenticated) res.sendFile(path.join(__dirname+'/kickout.html'));
next();
});
app.get('/main',function(req,res){
res.sendFile(path.join(__dirname+'/main.html'));
});
app.get('/about',function(req,res){
res.sendFile('/kickout.html');
});
app.listen(3000);
console.log("Running at Port 3000");
Node.js application running on Azure Web Apps Service, is hosted on IIS handled mapping via IISNode, which gives a Named Pipe to receive the incoming requests, not a TCP port like you would use when running locally.
This Named Pipe has been defined as the port in Node.js runtime on Azure Web Apps. You can define the port in your app link: process.env.PORT || 3000, with which your app can run on Azure or locally.
And you may check whether there is a file web.config in your root directory, which is the configurations of IIS of your application. It should have the similar content:
<configuration>
<system.webServer>
<handlers>
<!-- indicates that the app.js file is a node.js application to be handled by the iisnode module -->
<add name="iisnode" path="server.js" verb="*" modules="iisnode"/>
</handlers>
<rewrite>
<rules>
<!-- Don't interfere with requests for node-inspector debugging -->
<rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
<match url="^server.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 application entry point -->
<rule name="DynamicContent">
<conditions>
<add input="{REQUEST_FILENAME}" matchType="IsFile" negate="True"/>
</conditions>
<action type="Rewrite" url="server.js"/>
</rule>
</rules>
</rewrite>
<!-- You can control how Node is hosted within IIS using the following options -->
<!--<iisnode
node_env="%node_env%"
nodeProcessCommandLine=""%programfiles%\nodejs\node.exe""
nodeProcessCountPerApplication="1"
maxConcurrentRequestsPerProcess="1024"
maxNamedPipeConnectionRetry="3"
namedPipeConnectionRetryDelay="2000"
maxNamedPipeConnectionPoolSize="512"
maxNamedPipePooledConnectionAge="30000"
asyncCompletionThreadCount="0"
initialRequestBufferSize="4096"
maxRequestBufferSize="65536"
watchedFiles="*.js"
uncFileChangesPollingInterval="5000"
gracefulShutdownTimeout="60000"
loggingEnabled="true"
logDirectoryNameSuffix="logs"
debuggingEnabled="true"
debuggerPortRange="5058-6058"
debuggerPathSegment="debug"
maxLogFileSizeInKB="128"
appendToExistingLog="false"
logFileFlushInterval="5000"
devErrorsEnabled="true"
flushResponse="false"
enableXFF="false"
promoteServerVars=""
/>-->
<iisnode watchedFiles="*.js;node_modules\*;routes\*.js;views\*.jade"/>
</system.webServer>
</configuration>
When hosting Node.js apps via Azure Web Apps, you need to modify your port initialization as shown in the following simplified sample:
var http = require('http'),
port = process.env.PORT || 1337;
http.createServer(function(req, res) {
res.writeHead(200, { 'Content-Type': 'text/plain' });
res.end('Hello World\n');
}).listen(port);
When running locally, the literal port will be used (e.g. 1337 above), but when running in Azure a port will be assigned to process.env.port by the hosting platform and that will be used. You can find a complete walkthrough on deploying a Node.js app here. When running in a WebApp, a technology called IIS Node is used to run your Node app. More details on that can be found here.

socket.io routing issue with a node.js express 3 app running in a virtual directory with iisnode

Errors:
Trying to access http://localhost/appDirectory/socket.io gives:
Cannot GET /appDirectory/socket.io
Or to put it another way when trying to load the client file on the page I get this error:
GET http://localhost/appDirectory/socket.io/socket.io.js 404 (Not Found)
If I load the client file as static content the connect line produces this error:
GET http://localhost/appDirectory/socket.io/1/?t=1365535131937 404 (Not Found)
Server code:
var express = require('express'),
namespace = require('express-namespace'),
routes = require('./routes'),
http = require('http'),
app = express(),
server = app.listen(process.env.PORT),
io = require('socket.io').listen(server);
var appDir = '/appDirectory';
app.configure(function(){
app.set('env', process.env.NODE_ENV || 'development');
app.set('/views', __dirname + '/views');
app.set('view engine', 'jade');
app.use(app.router);
app.use(appDir, require('stylus').middleware(__dirname + '/public'));
app.use(appDir, express.static(path.join(__dirname, '/public')));
});
app.get(appDir + '/', routes.index);
io.sockets.on('connection', function (socket) {
socket.emit('message', 'lol');
});
Client code:
<script src="/appDirectory/socket.io/socket.io.js"></script>
<script type="text/javascript">
var socket = io.connect(appDir, { resource: appDir.substring(1) + '/socket.io' });
socket.on('connect', function () {
console.log('connected');
});
</script>
The web config file that's getting this to work is...
Web.config:
<configuration>
<system.webServer>
<handlers>
<add name="iisnode" path="app.js" verb="*" modules="iisnode" />
</handlers>
<iisnode loggingEnabled="true" debuggingEnabled="true" debuggerPathSegment="debug" />
<rewrite>
<rules>
<clear />
<rule name="Debug" patternSyntax="Wildcard" stopProcessing="true">
<match url="app.js/debug*" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="None" />
</rule>
<rule name="app" patternSyntax="Wildcard">
<match url="*" negate="false" />
<conditions logicalGrouping="MatchAll" trackAllCaptures="false" />
<action type="Rewrite" url="app.js" />
</rule>
</rules>
</rewrite>
</system.webServer>
</configuration>
My gut feeling is that the express routing is interfering with the socket.io routing or that the socket.io routing is not compatible with the virtual directory. I did see some mention of namespacing with socket.io and I did try the following...
io.of(appDir).on('connection', function (socket) {
socket.emit('message', 'lol');
});
But that didn't seem to fix the problem.
I have tried a number of different things on the client code side but I don't think that is the root problem or the http://localhost/appDirectory/socket.io/socket.io.js would be working.
The application is running as a virtual directory and is built on:
- node.js 0.10.3 for windows
- iis 7
- iisnode 0.2.4
- express.js 3.0.0rc1
- jade
- socket.io
The module "express namespace" seems to make things work.
I am aware that iisnode was "designed" to be run as a separate site and I'm also aware that express doesn't like being in a virtual directory, but I am pretty sure this is possible.
Interesting update!
Changing the line:
io = require('socket.io').listen(server);
To:
io = require('socket.io').listen(server, { resource: appDir + '/socket.io' });
Causes the iis worker process w3wp.exe to crash...
As seen here http://i.imgur.com/65RGia3.png?1
However the url http://localhost/appDirectory/socket.io then seems to work.
Failed attempts at fixing this FYI:
adding the web.config setting for iisnode nodeProcessCountPerApplication="1"
Possible solution:
If you are stuggling with this same issue, I found this which might help you...
http://tomasz.janczuk.org/2013/01/hosting-socketio-websocket-apps-in-iis.html
It looks like you're on the right track. Using the following configuration, it works for me (not using IIS, though):
// server
io = require('socket.io').listen(server, { resource : '/appDirectory/socket.io' });
// client
<script src="/appDirectory/socket.io/socket.io.js"></script>
...
var socket = io.connect('', { resource: 'appDirectory/socket.io' });
I wonder why your IIS process is crashing, could you post the image elsewhere (Dropbox, free image hoster) perhaps?
A little bit of a stab in the dark, but if you're using Express 3.x, your problem may be that you're using the older Express 2.x syntax for hooking up Socket.io with Express.
See: https://github.com/visionmedia/express/wiki/Migrating-from-2.x-to-3.x#socketio-compatibility for the new integration method.

Resources