Can you use node.js with IIS? - windows

This may be an extremely simple quesiton, but can I use node.js in a windows server 2008 environment with IIS? Is there a "Microsoft" library or some other solution that works better?

Sure you can, check out the IISNode Project.

You can install Node.js on Windows, but it is its own server, so unless you're using IIS as a proxy to it, there's no need for IIS at all. Note, though, the following as quoted from Node.js's installation instructions:
Neither [Windows] builds are satisfactorily stable but it is possible to get something running.

You essentially have two routes for running a Node.js application via IIS.
IISNode
Reverse Proxy using Application Request Routing
If you are dedicating an entire application to Node.js and simply need the public facing endpoint to work through your existing IIS Application, I would suggest using ARR to route the entire site through. I'm doing this for a couple of projects, and it works fairly well.
To be honest, I haven't liked IISNode, as it seems like you are making alien endpoints in your node code vs. IIS. It works, and if you are targeting Azure in particular it may be your best option. It also may be the best option if you have to shoe horn it into an existing .Net application.

I have been using Node on Windows with Cygwin and had few problems. You can use IIS to serve on default port 80 and run your Node apps on different ports.
If you want to proxy then most are using Nginx.

You can build node.js on Windows, but it's not recommended to use it due to possible stability issues. If IIS is using thread based pools then you shouldn't even use it as a reverse proxy (on linux based systems nginx is usually used to do this) for node.js because pool may quickly become fully loaded. If you want something similar to node.js on windows then you should try to look at manos.

I wanted to make it as easy as possible.
Issues with iisnode
I installed iisnode and ran the samples with no problem but...
I tried to deploy it on IIS with iisnode, but I had to bundle my meteor app and then deploy it as a node app. The problem I ran into discouraged me. I could not get fibers to install at all. compilation process kept trowing errors, so I gave up.
Reverse Proxy IIS
What I did to solve this for me is use a reverse proxy on IIS.
see my post on meteor forum
My final web.config entry was:
I did the same, however, the way I had the reverse proxy on IIS to use
a sub folder on the domain threw me of.
I was not aware that by using ROOT_URL we could specify the a sub
path.
example, if i run the following command inside my meteor app folder:
set ROOT_URL=http://localhost:3100/n/todos && meteor
I will be able to access my app at http://localhost:3100/n/todos,
notice I omitted the trailing /. And if we try to surf to the
address http://localhost:3100/n or http://localhost:3100/ will
give us an error Unknown path.
So, when I first setup the reverse proxy, I was getting the Unknown
Path error every time.
Turns out that on my IIS config, I have to specify the
http://localhost:3100/n/todos as the url value on the action, please
notice the "n/todos" at the end.
So my rewrite rule ended up like this: [file #
c:/inetpub/wwroot/web.config]
```
<configuration>
<system.webServer>
<rewrite>
<rules>
<rule name="TODOs meteor app. Route the requests" stopProcessing="true" enabled="true">
<match url="^n/todos/(.*)" />
<conditions>
<add input="{CACHE_URL}" pattern="^(https?)://" />
</conditions>
<action type="Rewrite" url="{C:1}://localhost:3100/n/todos/{R:1}" /> <!-- I was missing the /n/todos here -->
<serverVariables>
<set name="HTTP_ACCEPT_ENCODING" value="" />
</serverVariables>
</rule>
</rules>
<outboundRules>
<rule name="TODOs ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1" enabled="false">
<match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script" pattern="^http(s)?://localhost:3100/(.*)" />
<action type="Rewrite" value="/n/todos/{R:2}" />
</rule>
<rule name="TODOs RewriteRelativePaths" preCondition="ResponseIsHtml1" enabled="false">
<match filterByTags="A, Area, Base, Form, Frame, Head, IFrame, Img, Input, Link, Script" pattern="^/(.*)" negate="false" />
<action type="Rewrite" value="/n/todos/{R:1}" />
</rule>
<rule name="ReverseProxyOutboundRule1" preCondition="ResponseIsHtml1">
<match filterByTags="A, Form, Img" pattern="^http(s)?://localhost:3100/(.*)" />
<action type="Rewrite" value="http{R:1}://localhost/{R:2}" />
</rule>
<preConditions>
<preCondition name="ResponseIsHtml1">
<add input="{RESPONSE_CONTENT_TYPE}" pattern="^text/html" />
</preCondition>
</preConditions>
</outboundRules>
</rewrite>
</system.webServer>
</configuration>
```
Thanks

Related

Azure CDN Premium Verizon Rules Engine V4 URL Rewrite for Storage Static Website sub directories

I'm trying to host an Angular project with Azure Storage Static Website through Azure CDN Verizon Premium
Let's say i have containers for each Angular builds named like v0.3.13.3 and in that container there are also multi language builds of angular project.
What i'm trying to achieve is to serve the right index.html with correct routing for each version and language.
You may find example routes below:
my.domain.com/v0.3.13.3 should serve /v0.3.13.3/index.html
my.domain.com/v0.3.13.3/dashboard should serve /v0.3.13.3/index.html
my.domain.com/v0.3.13.3/fr should serve /v0.3.13.3/fr/index.html
my.domain.com/v0.3.13.3/fr/dashboard should serve /v0.3.13.3/fr/index.html
I've managed to do it with Azure Standard Rules Engine like the screen shot below.
But as far as i've seen from the docs, in standard rules engine Contains field can't be filled with regex. I've digged all over google but can't seem to find any solution that suits me.
So i've switched my CDN service to Verizon Premium and tried to apply (v(?:(\d+)\.)?(?:(\d+)\.)?(?:(\d+)\.\d+)) to path regex.
The main issue i'm having with Verizon Premium is that i can only apply URL Rewrite feature if i state my IF field to Always
As you can see URL Rewrite is not listed.
Also i've made couple of deployments using Always statement but they did not any good.
The single example that everyone shares in their blogs
So above example does not cover File Extensions and sub routes.
Are there any ways to achieve my goal?
Thanks in advance.
I would share my current config that I using in Verizon Premium in Rule V4:
The steps are:
New rule: General Always
Feature: URL -> URL Rewrite
Source: /80XXXXXX/XXXXXX/((?:[^\?]*/)?)($|\?.*)
Destination: /80XXXXXX/XXXXXX/index.html$2
And add a second rule. Source: /80XXXXXX/XXXXXX/((?:[^\?]*\/)?[^\?\/.]+)($|\?.*)
Destination: /80XXXXXX/XXXXXX/index.html$2
Also you can obtainer the origin as described in here https://stackoverflow.com/a/58700538/3591687
This is the XML Rule was generated
<policy>
<rules>
<rule>
<match.always>
<feature.url.url-rewrite source="/80XXXXXX/XXXXXX/((?:[^\?]*/)?)($|\?.*)" destination="/80XXXXXX/XXXXXX/index.html$2"/>
<feature.url.url-rewrite source="/80XXXXXX/XXXXXX/((?:[^\?]*\/)?[^\?\/.]+)($|\?.*)" destination="/80XXXXXX/XXXXXX/index.html$2"/>
</match.always>
</rule>
</rules>
</policy>
I hope that helps to somebody
Please see below in Rules Engine 4.0 as to how you select the URL Rewrite in Azure CDN - the documentation is currently lacking and still attempting to get this to work. I will update this post once it is set up correctly
I had a bear of a time on this issue in March 2022 using V4 of the rules engine. I finally got the redirect to work by duplicating the redirects to account for both hashes for the origin server, starting with 00 and 08 in my case. It feels like the CDN is using a blue/green deployment methodology, and each time you deploy, it switches to the other server.
So if you deploy using the hash starting with 00, it will move your deployment to the 08 server.
Then you attempt to deploy using the 08 hash, and the system complains you haven't changed anything. :)
So I just duplicated code and cried into my pillow at night.
For the record, if you attempt to deploy in the "feature.url.url-redirect source" section using a regex containing something like /[0-9]+ when you change the draft into a policy, the system will complain that the source does not match any known origin.
This is what worked for me. If someone has a more elegant solution, I welcome it!
<policy>
<rules>
<rule>
<description>Redirect HTTP to HTTPS 00 Version</description>
<match.request.request-scheme.literal value="http">
<feature.url.url-redirect source="/00XXXXXX/www-example-com/(.*)" destination="https://%{host}/$1" code="302"/>
</match.request.request-scheme.literal>
</rule>
<rule>
<description>Redirect HTTP to HTTPS 80 Version</description>
<match.request.request-scheme.literal value="http">
<feature.url.url-redirect source="/80XXXXXX/www-example-com/(.*)" destination="https://%{host}/$1" code="302"/>
</match.request.request-scheme.literal>
</rule>
<rule>
<description>Site redirects to 00 Version</description>
<match.request.request-scheme.literal value="https">
<select.first-match>
<match.url.url-path.regex result="match" value="[\/#]*\/dir1\/subdir1.*" ignore-case="true" encoded="false">
<feature.url.url-redirect source="/00XXXXXX/www-example-com/.*" destination="https://www.example.com/#/dir1/subdir1" code="302"/>
</match.url.url-path.regex>
<match.url.url-path.regex result="match" value="[\/#]*\/folder2\/subfolder2.*" ignore-case="true" encoded="false">
<feature.url.url-redirect source="/00XXXXXX/www-example-com/.*" destination="https://www.example.com/#/folder2/subfolder2" code="302"/>
</match.url.url-path.regex>
<match.url.url-path.regex result="match" value=".*" ignore-case="true" encoded="false">
<feature.url.url-redirect source="/00XXXXXX/www-example-com/.*" destination="https://www.example.com/" code="302"/>
</match.url.url-path.regex>
</select.first-match>
</match.request.request-scheme.literal>
</rule>
<rule>
<description>Site redirects to 80 Version</description>
<match.request.request-scheme.literal value="https">
<select.first-match>
<match.url.url-path.regex result="match" value="[\/#]*\/dir1\/subdir1.*" ignore-case="true" encoded="false">
<feature.url.url-redirect source="/80XXXXXX/www-example-com/.*" destination="https://www.example.com/#/dir1/subdir1" code="302"/>
</match.url.url-path.regex>
<match.url.url-path.regex result="match" value="[\/#]*\/folder2\/subfolder2.*" ignore-case="true" encoded="false">
<feature.url.url-redirect source="/80XXXXXX/www-example-com/.*" destination="https://www.example.com/#/folder2/subfolder2" code="302"/>
</match.url.url-path.regex>
<match.url.url-path.regex result="match" value=".*" ignore-case="true" encoded="false">
<feature.url.url-redirect source="/80XXXXXX/www-example-com/.*" destination="https://www.example.com/" code="302"/>
</match.url.url-path.regex>
</select.first-match>
</match.request.request-scheme.literal>
</rule>
</rules>

How to redirect to HTTPS in Azure CDN

I want to serve a PWA using Static websites in Azure Storage and Azure CDN.
I need to redirect every HTTP request to HTTPS.
I followed the official documentation and waited more than 4 hours but it's not working. Do I need a custom domain for the rule to work?
When SCHEMA = HTTP
URL Redirect (301)
Source: (.*)
Destination: https://%{host}/$1
So I had the same issue. Tried and failed at a few approaches based on a few other stack answers and articles I had stumbled on. This is the XML for the rewrite/redirect rules that ended up working for me:
<rules schema-version="2" rulesetversion="10" rulesetid="960471" xmlns="http://www.whitecdn.com/schemas/rules/2.0/rulesSchema.xsd">
<rule id="1871708" platform="http-large" status="active" version="5" custid="AA123">
<description>Redirect HTTP to HTTPs</description>
<!--If-->
<match.request-scheme value="http">
<feature.url-redirect code="301" pattern="/80AA3CF/myblog/(.*)" value="https://blog.mydomain.com/" />
</match.request-scheme>
</rule>
<rule id="1871579" platform="http-large" status="active" version="0" custid="AA123">
<description>Rewrite to index.html</description>
<!--If-->
<match.always>
<feature.url-user-rewrite pattern="/80AA123/myblog/((?:[^\?]*/)?)($|\?.*)" value="/80AA123/myblog/$1index.html$2" />
<feature.url-user-rewrite pattern="/80AA123/myblog/((?:[^\?]*/)?[^\?/.]+)($|\?.*)" value="/80AA123/myblog/$1/index.html$2" />
</match.always>
</rule>
</rules>
Note that the Rewrite had to appear after the Redirect, and that the Redirect is to a static path - in this case my root domain that the cdn is caching.
In the end. I had to talk with Microsoft and they unlock the config.

IIS cached files never replaced

Perhaps I'm missing something by not wording my Google searches correctly, but I've run into an issue with IIS 8.5 and caching. I have a server set up that by all standards should be serving only static files. Obviously, when a file is changed, the new file should be served up. The issue is that even after a server restart, setting files to immediately expire, didsabling caching, disabling compression, and turning off any other caching feature, the old file with its old timestamp is still being served.
I have the following settings:
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<system.webServer>
<security>
<requestFiltering allowHighBitCharacters="false">
<verbs allowUnlisted="false">
<add verb="GET" allowed="true" />
</verbs>
</requestFiltering>
</security>
<caching enabled="false" enableKernelCache="false" />
<urlCompression doStaticCompression="false" />
</system.webServer>
<location path="" overrideMode="Deny">
<system.webServer>
</system.webServer>
</location>
<location path="" overrideMode="Allow">
<system.webServer>
</system.webServer>
</location>
</configuration>
The folder in which the files are located has read only permissions. The interesing fact is that if I go to mydomain.com, the old version shows up, but going to newmydomain.com loads the new file (even though they both point to the same IP address).
An HTTP client can use the old version of a file if the cache control header(s) sent with the response indicated that the content would not change for a given period of time. It does not matter if the content changed on the server or not.
For example, if the file is sent with the header:
Cache-Control: Max-age=86400
then for 24 hours the client can use the file without contacting the server. If the file changes on the server, the client won't know that the file changed because it won't even make a request to the server.
You can add the must-revalidate cache control attribute to force the client to always make a server request.
As noted in my reply to storsoc, our issue was that our load balancer, an F5 server, was trying offload as much as possible from our web servers by caching our site. See K13255: Displaying and deleting HTTP cache entries from the command line (11.x and later) for how to forcefully remove cached entries.

WebDAV and WebAPI together cause Error: Handler "ExtensionlessUrlHandler-Integrated-4.0" has a bad module "ManagedPipelineHandler" in its module list

I have both WebDAV installed and running on my site, as a virtual sub site i have a MVC WebAPI site, the API works great, until I try to send a PUT request to it, then i get the below error:
HTTP Error 500.21 - Internal Server Error
Handler "ExtensionlessUrlHandler-Integrated-4.0" has a bad module "ManagedPipelineHandler" in its module list
If I disable WebDAV, then everything works fine and I get no errors. This only happens when WebDAV is enabled.
I have all of the following code in my web.config:
<system.webServer>
<validation validateIntegratedModeConfiguration="false" />
<modules runAllManagedModulesForAllRequests="false">
<remove name="WebDAVModule" />
</modules>
<handlers>
<remove name="WebDAV" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" />
<remove name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" />
<remove name="ExtensionlessUrlHandler-Integrated-4.0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_32bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness32" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-ISAPI-4.0_64bit" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" modules="IsapiModule" scriptProcessor="%windir%\Microsoft.NET\Framework64\v4.0.30319\aspnet_isapi.dll" preCondition="classicMode,runtimeVersionv4.0,bitness64" responseBufferLimit="0" />
<add name="ExtensionlessUrlHandler-Integrated-4.0" path="*." verb="GET,HEAD,POST,DEBUG,PUT,DELETE,PATCH,OPTIONS" type="System.Web.Handlers.TransferRequestHandler" preCondition="integratedMode,runtimeVersionv4.0" />
</handlers>
</system.webServer>
I've tried messing with various app pools.
I've also tired all of the things mentioned in all of these questions:
How to get rid of this error Handler "PageHandlerFactory-Integrated" has a bad module "ManagedPipelineHandler" in its module list
Handler "ExtensionlessUrlHandler-Integrated-4.0" has a bad module "ManagedPipelineHandler" in its module list
How to fix: Handler "PageHandlerFactory-Integrated" has a bad module "ManagedPipelineHandler" in its module list
None of this has solved my issue, is there anything else out there that I have not tried?
We ended up going to Microsoft with this, they reviewed it for several weeks before coming back saying that it's not possible to run WebDAV and WebAPI in the same site.
They will try to address this issue in a future release of IIS.
#jblaske has a good response.
If you want to remove it temporarily then maybe this article is the best solution.
If you want to remove the handler all together then follow these steps.
Open IIS and go to the site in question.
Click on "Handler Mappings"
Find the handler named "WebDAV"
Select it and Remove it
This is my original post.
I started getting this error after I removed WebDAV module and handler manually from IIS to get PUT working. I had to remove "WebDAV Publishing" server role to get over this error. After removing this role and restarting the IIS, PUT is working fine.
You might have a typo issue in the declaration of handlers. Thomas Marquardt's Blog says
5.0 Troubleshooting
If you receive an error similar to the one below, your
section is probably invalid.
HTTP Error 500.21 - Internal Server Error Handler "" has
a bad module "ManagedPipelineHandler" in its module list You probably
have a handler mapping that does not have the correct precondition.
IIS is not forgiving in regard to typos, and preconditions are
case-sensitive. The text must be preCondition=”integratedMode” or
preCondition=”classicMode”.
Also, another suggestion from the comments on that article:
Andrew Johnson 25 Jan 2011 3:20 AM #:
I found that I can also get the "Handler has a bad
module ManagedPipelineHandler in its module list" if the handler has
requireAccess="None". For me, changing this to requireAccess="Read"
made the error go away.
That comment might apply to your case as I see that in applicationHost.config the entry for WebDav handlers is (note the requredAccess="None"):
<add name="WebDAV" path="*" verb="PROPFIND,PROPPATCH,MKCOL,PUT,COPY,DELETE,MOVE,LOCK,UNLOCK" modules="WebDAVModule" resourceType="Unspecified" requireAccess="None" />
Found an alternate for this issue.
When the patches of the server were pushed,The default framework were set back to 2.0.
Re register .Net framework by going to framework 4.0 folder C:\Windows\Microsoft.NET\Framework64\v4.0.30319
Executing the command aspnet_regiis -I
Check the web.config to match the .Net framework version
Go to IIS -> Application pool, set the .Net framework values to 4.0
Restart the IIS.

WCF Service response "HTTP/1.1 400 Bad Request" on shared hosting <aka Blank Page, XML Parsing Error, Invalid Address, Webpage cannot be found>

This is both information to those experiencing the issue and a question.
edit: The question is why does dropping "www." from the URL cause this error when a website running at the same address can be referenced without "www.".
I recently reproduced this problem using a trivial WCF service (the one from endpoint.tv) after resolving the usual config issues one faces moving a service from local IIS to shared hosting.
The problem was the following response (from fiddler) upon checking the url in browser. In searching the web for posts on the topic I found a number of unresolved issues pointing to the same problem in addition to the posts where the usual shared hosting config issues fix them up.
HTTP/1.1 400 Bad Request
Server: Microsoft-IIS/7.0
X-Powered-By: ASP.NET
Date: Tue, 17 Aug 2010 00:27:52 GMT
Content-Length: 0
In Safari/Chrome this manifests as a blank page.
In IE you get "The webpage cannot be found".
In FF you get "XML Parsing Error: no element found Location: http://................ Line Number 1, Column 1:" (which I saw in numerous unresolved posts on the web - feel free to backlink a possible solution)
In Opera you get "Invalid Address"
I was scratching my head regarding this for a while, then I thought to try putting in the "www." which I was previously omitting from my url for no particular reason.
Problem solved.
I can now see the normal output in the browser and interact with the service via WCF Test Client.
So the question is:
Why does this make a difference to the hosted WCF service when I know it does not make a difference for browsing to the website hosted at the same address? With or without the "www." I can browse to the website at the same domain, hosted on the same account.
So far I've tested this repro on a GoDaddy service. I may try some others later.
Also, if you happen to know - I'd be interested to know what features are likely to make my WCF services need full trust rather than medium trust. And any thoughts you have on whether it is a good idea to utilise such features (in context of least priv ideology).
For reference this is the web.config, including an additional endpoint suggested by Mike to try and resolve this.
<?xml version="1.0"?>
<configuration>
<system.web>
<customErrors mode="Off"/>
<compilation><!--debug="true"-->
<buildProviders>
<remove extension=".svc"/>
<add extension=".svc" type="System.ServiceModel.Activation.ServiceBuildProvider,System.ServiceModel, Version=3.0.0.0, Culture=neutral,PublicKeyToken=b77a5c561934e089"/>
</buildProviders>
</compilation>
</system.web>
<!-- When deploying the service library project, the content of the config file must be added to the host's
app.config file. System.Configuration does not support config files for libraries. -->
<system.serviceModel>
<services>
<service behaviorConfiguration="blah"
name="WCFServ.EvalService">
<endpoint address="http://www.abcdomain.com/WCFServ/WCFServ.EvalService.svc"
binding="basicHttpBinding"
contract="WCFServ.IEvalService" />
<endpoint address="http://abcdomain.com/WCFServ/WCFServ.EvalService.svc"
binding="basicHttpBinding"
contract="WCFServ.IEvalService" />
<!--<endpoint address=""
binding="mexHttpBinding"
contract="IMetadataExchange" />-->
<!--<host>
<baseAddresses>
<add baseAddress="http://abcdomain.com/WCFServ/" />
</baseAddresses>
</host>-->
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="blah">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
</behaviors>
<serviceHostingEnvironment>
<baseAddressPrefixFilters>
<add prefix="http://www.abcdomain.com/WCFServ/"/>
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
</system.serviceModel>
<!--http://localhost/WCFServ/WCFServ.EvalService.svc-->
<startup><supportedRuntime version="v2.0.50727"/></startup></configuration>
Because you're using absolute URLs as your endpoint addresses, WCF needs to see a specific host header in HTTP requests in order to bind to those addresses.
Web servers are no different; if they're configured for a specific host, the request headers must have the host name or they won't serve up content. However, multiple host names can be bound to web sites, however, so sometimes a site may be tied to both www.example.com and example.com. Also, some web browsers, if you go to example.com and get a 404 or if the DNS lookup fails, will automatically retry the request at www.example.com.
I think the easiest thing for you to do to resolve your issue is to modify your endpoint(s) so they are host neutral. For example:
<services>
<service behaviorConfiguration="blah" name="WCFServ.EvalService">
<endpoint address="/WCFServ/WCFServ.EvalService.svc"
binding="basicHttpBinding"
contract="WCFServ.IEvalService"/>
</service>
</services>
<!-- Just leave this out
<serviceHostingEnvironment>
<baseAddressPrefixFilters>
<add prefix="http://www.abcdomain.com/WCFServ/"/>
</baseAddressPrefixFilters>
</serviceHostingEnvironment>
-->
Make sure that you have endpoints defined without the www in your web config.
This page has some good explanations about WCF addressing:
WCF Adressing In Depth.
Is your problem solved by adding the following attribute on your serviceclass?
[ServiceBehavior(AddressFilterMode=AddressFilterMode.Any)]

Resources