Monday 29 May 2017

Node.js, Express, Socket.io, and IIS

I am developing a web server in Node.js with Express, and I want to use Socket.io for WebSockets specifically. However, the server environment that my app is being hosted in is IIS, and that cannot be changed. The IIS version is high enough that WebSockets are enabled, and iisnode v0.2.21 is installed. Express works properly, serving my pages and content, but WebSockets do not. Instead, the websocket connection is repeatedly terminated with a HTTP 200 status instead of the HTTP 101 that it is expecting, preventing the WebSockets from working. I have set permissions for the IIS_IUSRS group everywhere I can think of.

Sometimes I get an error page, with this text:

HRESULT: 0x6d
HTTP status: 500
HTTP subStatus: 1013
HTTP reason: Internal Server Error

I am not attempting to use SSL or Secure WebSockets yet, so the other solutions that I've found on StackOverflow do not seem to apply.

I get errors like this on the client side:

polling-xhr.js:264 POST http://my-website/socket.io/?EIO=3&transport=polling&t=LnMfjZr&sid=LQjLpiGwsN3K-gNPAAAA 500 (Internal Server Error)
polling-xhr.js:264 POST http://my-website/socket.io/?EIO=3&transport=polling&t=LnMfjaC&sid=LQjLpiGwsN3K-gNPAAAA 500 (Internal Server Error)
websocket.js:112 WebSocket connection to 'ws://my-website/socket.io/?EIO=3&transport=websocket&sid=P8q_HoyfgJb0F75rAAAA' failed: Error during WebSocket handshake: Unexpected response code: 200
polling-xhr.js:264 POST http://my-website/socket.io/?EIO=3&transport=polling&t=LnMfkAb&sid=P8q_HoyfgJb0F75rAAAA 500 (Internal Server Error)

I followed all the instructions I could find about web.config:

<system.webServer>
    <!--so that IIS websockets don't interfere with Node websockets-->
    <webSocket enabled="false" />

    <!-- indicates that the hello.js file is a node.js application
      to be handled by the iisnode module -->
    <httpErrors existingReponse="PassThrough"></httpErrors>

    <handlers>
      <add name="iisnode" path="app.js" verb="*" modules="iisnode" />
    </handlers>

    <modules runAllManagedModulesForAllRequests="false"/>

    <rewrite>
      <rules>
        <!-- Don't interfere with requests for node-inspector debugging -->
        <rule name="NodeInspector" patternSyntax="ECMAScript" stopProcessing="true">
          <match url=".+\.js\/debug[\/]?"/>
        </rule>

        <!-- All other URLs are mapped to the Node.js application entry point -->
        <rule name="DynamicContent">
          <match url="/*"/>
          <action type="Rewrite" url="app.js"/>
        </rule>
      </rules>
    </rewrite>
    <directoryBrowse enabled="false"/>

    <!-- exclude node_modules directory and subdirectories from serving
           by IIS since these are implementation details of node.js applications -->

    <security>
      <requestFiltering>
        <hiddenSegments>
          <!--<add segment="bin" />-->
        </hiddenSegments>
      </requestFiltering>
    </security>
  </system.webServer>
</configuration>

And here is app.js:

'use strict';
var debug = require('debug');
var express = require('express');
var path = require('path');
var favicon = require('serve-favicon');
var logger = require('morgan');
var cookieParser = require('cookie-parser');
var bodyParser = require('body-parser');

var app = express();
var server = require('http').Server(app);

var routes = require('./routes/index');
var users = require('./routes/users');
var api = require('./routes/api')(server);

// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));

app.use('/', routes);
app.use('/users', users);
app.use('/api', api);

// catch 404 and forward to error handler
app.use(function (req, res, next) {
    var err = new Error('Not Found');
    err.status = 404;
    next(err);
});

// error handlers

// development error handler
// will print stacktrace
if (app.get('env') === 'development') {
    app.use(function (err, req, res, next) {
        res.status(err.status || 500);
        res.render('error', {
            message: err.message,
            error: err
        });
    });
}

// production error handler
// no stacktraces leaked to user
app.use(function (err, req, res, next) {
    res.status(err.status || 500);
    res.render('error', {
        message: err.message,
        error: {}
    });
});

app.set('port', process.env.PORT || 3000);

server.listen(app.get('port'), function () {
    debug('Express server listening on port ' + server.address().port);
});

And api.js, which actually sets up the Socket.IO server:

module.exports = function (server)
{
    var socketio = sio(server);
    var router = express.Router();

    var game = new model.Game();

    socketio.on("connection", (socket) =>
    {
        console.log(`houston we have socket ${socket.id}`);

        try 
        {
            socket.on('message', function (data)
            {
                console.log(`message: ${JSON.stringify(data)}`);

                // my code here
            });

            socket.on("disconnect", (socket) =>
            {
                socketio.send("player.disconnected", { id: player.id });

                // my code here
            });
        }
        catch (err)
        {
            // I put a breakpoint here, we never end up here
        }

        // debugger always makes it to the end of this function
    });
}

When I step through this code with the debugger, it appears that execution makes it to the end of the connection handler, and then the server immediately restarts. What am I doing wrong, why is the socket being sent a 200 response?

I know that the error pages are encoded as 200 responses, but since they are being sent into a WebSocket connection, Chrome does not allow me to inspect them, instead just showing me "(Opcode -1)".



via 333

No comments:

Post a Comment