tjanczuk / iisnode

Hosting node.js applications in IIS on Windows
Other
1.85k stars 588 forks source link

Serving meteorjs app through IISNODE #235

Closed anu-rock closed 11 years ago

anu-rock commented 11 years ago

At my work, we are tied to the Microsoft platform for development and hosting. I needed to deploy our meteorjs app on a Windows 2008 R2 server and almost deployed it using "Vagrant VM" solution when I found out about this good IIS module called iisnode. So I installed nodejs, mongodb and iisnode on our Windows Server, bundled my meteorjs app (in Linux), replaced the Linux binaries of node-fiber inside that bundle with corresponding Windows ones, setup System-wide environment variables MONGO_URL and ROOT_URL, and finally deployed my meteorjs app to be served using iisnode. Now with everything setup correctly, it should have worked out fine, but it didn't. Instead, I got an "Internal Server Error" as reported by iisnode.

A little googling showed that someone else had also faced the same error, but their problem was unresolved. I suspected that the issue could be because iisnode sets the PORT environment variable to a named pipe, to facilitate port sharing between IIS and node, and I found this. I thought the pipe thingy made the code in server.js blow up in my meteorjs app where it says:

app.listen(process.env.PORT, function() {
  if (argv.keepalive)
    console.log("LISTENING"); // must match run.js
});

I even tried with the 1 argument overload of the listen function, but still got the same error.

app.listen(process.env.PORT);

On debugging using ETW, I got a log very similar to this.

Does anyone know if there is a way around this problem? In other words -- is it possible to host a meteorjs app using iisnode?

Thanks.

randyho-kk commented 11 years ago

i have no experience with meteorjs but your issue probably isnt due to the named pipe issue.

To confirm this, run the sample express example for iisnode.

http://tomasz.janczuk.org/2011/08/hosting-express-nodejs-applications-in.html

tjanczuk commented 11 years ago

Did you try running your meteor app self-hosted on Windows (i.e. directly using node.exe, without iisnode)? This is probably the best way to narrow down the issue at this point.

anu-rock commented 11 years ago

@kingkaeru Thanks for the link, but I don't think express is similar to meteor in a sense of giving me a clue.

@tjanczuk Yes, I tried running my meteor app directly using node.exe and it worked fine locally (listening on localhost:80). But I was not able to configure it in such a way that clients could connect to the running site from outside (http://nodeserver.mycompany.com). Also, all ports except for 80 are blocked on the server due to security reasons, and so running a node webserver on port 80 means having to stop IIS to free up that port. But in my case I want both node and IIS to run.

tjanczuk commented 11 years ago

Which iisnode and node version are you using? Are you hosing the app in the root of the IIS Web Site (e.g. http://foo.com), or in a virtual directory (e.g. http://foo.com/bar)? How does your web.config look like? Did you configure the URL rewriting rules to direct all traffic to the entry point of your application (server.js? app.js?) Is the meteor app using vanialla HTTP or something like WebSockets? What is the exact error you are getting? Did you try setting loggingEnabled and debuggingEnabled to true in iinode.yml to see if you ge a more descriptive error? (https://github.com/tjanczuk/iisnode/blob/master/src/samples/configuration/iisnode.yml)

anu-rock commented 11 years ago

@tjanczuk Sorry for the delay in response; I was on vacation for the past 4 days. I'll address your questions in order as follows.

  1. iisnode v0.2.0.0 and node v0.8.14
  2. The app is hosted in a virtual directory (http://foo.com/node/app)
  3. My web.config -- view it at pastebin
  4. I've set "main.js" as the default document of the app, but haven't configured any URL Rewriting rules for the same. Should I do that?
  5. I'm not sure if meteor is using WebSockets or not. But going by this, it seems to be using SockJS
  6. ETL debug trace -- view it at pastebin
tjanczuk commented 11 years ago

Looking at the ETW traces I see this:

iisnode was unable to establish named pipe connection to the node.exe process before the process terminated

which most likely indicates the application generated an uncaught exception during initialization that caused the process to terminate.

Ideally you should capture the uncaught exception and save it to a file somewhere on disk to see what is going on. Is you set loggingEnabled=true in iisnode.yml iisnode should log this exception to the logging directory for you. In some circumstances it may not be able to do that, in which case you can try saving it to a file using fs.writeFileSync yourself.

Another targeted experiment you can try is to attempt to run the application self-hosted but request it is hosted over named pipes. If the app is not equipped to handle that it should break during initialization, which will be easier to diagnose. To try running the app self-hosted over named pipes, do this:

set PORT=\\.\pipe\mypipe
node main.js

Everything else aside, more trouble down the road may be waiting due to the fact you are hosting your app in a virtual directory rather than at the root of an IIS WebSite (where it would be accessible via http://foo.com). Note that all requests to the app will have a URL path that starts with /node/app. Is your application set up to expect it? Are your application routes configured appropriately?

anu-rock commented 11 years ago

Thanks for the suggestions. I was finally able to get past "Error 500". So meteor apparently supports named pipes as ports. I was able to get IIS / iisnode to serve my app in both cases -- app setup in server's root and app setup in a virtual directory. But as you suggested, I deleted the latter setup and kept my app in the root of IIS such that the "Default Website" now points to my app.

meteor apps rely on an environment variable called ROOT_URL to allow setting any absolute URL. But as I mentioned, I've for now kept it as http://foo.com only. The page that main.js outputs contains virtual references to combined & minified CSS and JS files. I had to create rewrite rules for these files to load successfully.

Unfortunetely, my app doesn't work beyond this. The loaded combined js file starts making calls to /sockjs/info webmethod, which IIS thinks is a reference to a virtual directory called /sockjs/info and subsequently returns a 404 code, which meteor detects and then retries, then fails, then retries.... and an endless loop ensues.

Now my question is -- am I required to setup more rewrite rules to get past this problem? A successful run of my app (through meteor's bundled webserver) shows many such webmethod calls in Firebug. I hope there's a better way around this problem than rewriting.

Excerpt from sockjs.js (a node module used by meteor):

dispatcher = [['GET', p(''), ['welcome_screen']], ['GET', p('/iframe[0-9-.a-z_]*.html'), ['iframe', 'cache_for', 'expose']], ['OPTIONS', p('/info'), opts_filters('info_options')], ['GET', p('/info'), ['xhr_cors', 'h_no_cache', 'info', 'expose']], ['OPTIONS', p('/chunking_test'), opts_filters()], ['POST', p('/chunking_test'), ['xhr_cors', 'expect_xhr', 'chunking_test']], ['GET', p('/websocket'), ['raw_websocket']], ['GET', t('/jsonp'), ['h_sid', 'h_no_cache', 'jsonp']], ['POST', t('/jsonp_send'), ['h_sid', 'h_no_cache', 'expect_form', 'jsonp_send']], ['POST', t('/xhr'), ['h_sid', 'h_no_cache', 'xhr_cors', 'xhr_poll']], ['OPTIONS', t('/xhr'), opts_filters()], ['POST', t('/xhr_send'), ['h_sid', 'h_no_cache', 'xhr_cors', 'expect_xhr', 'xhr_send']], ['OPTIONS', t('/xhr_send'), opts_filters()], ['POST', t('/xhr_streaming'), ['h_sid', 'h_no_cache', 'xhr_cors', 'xhr_streaming']], ['OPTIONS', t('/xhr_streaming'), opts_filters()], ['GET', t('/eventsource'), ['h_sid', 'h_no_cache', 'eventsource']], ['GET', t('/htmlfile'), ['h_sid', 'h_no_cache', 'htmlfile']]];
tjanczuk commented 11 years ago

In your URL redirect rules you must make sure that all traffic that is supposed to be handled by the node.js application is redirected to the main entry point of your application (main.js in your case). Getting URL rewrite rules right is a bit tricky. For a configuration that may give you a good start check out http://tomasz.janczuk.org/2012/05/yaml-configuration-support-in-iisnode.html. The last URL rewrite rule in the web.config there is an "else" rule that redirects everything except known URLs to node.js.

anu-rock commented 11 years ago

That worked! Those were the only other rewrite rules needed to be included to make it work.

Thanks a lot for all the help. :)

anu-rock commented 11 years ago

A little update -- I recently wrote a blog post on how to successfully host a meteor.js app in IIS using iisnode. If someone is looking to do the same, head over to - http://www.anuragbhandari.com/2012/11/hosting-meteor-js-app-in-iis/

knu2xs commented 10 years ago

@anuragbhd, I just tried your link and the site apparently appears to be down right now. I really would like to see your solution as this is something I need to get done as well.

anu-rock commented 10 years ago

@knu2xs - Please try again. The site was temporarily down and is back up again.

lmvijay24 commented 6 years ago

@anuragbhd , Tried the link you provided, seems site is down right now, could you please make it up again.

anu-rock commented 6 years ago

Try this: http://www.anuragbhandari.com/blog/2012/11/hosting-meteor-js-app-in-iis/