0

I'm facing an issue only in the remote server side, it means that locally works fine even through a self-signed cert through https. But when I move the code into the server, it also works locally but it does't work remotely.

I have created a node app which is being hosted in the server under https port 3000. This app has the Socket IO lib which I have attached it to the same https server. I'm not using nodeiis because I'm using rewrite rules to pass it through the Windows IIS.

I have installed the websocket module in the IIS already. In fact I was already using websockets instead Socket.IO and it was working fine with the same configuration that I have now, I just replaced it with Socket.IO because is better for what I need.

Now my code

Html Page

<script src="/socket.io/socket.io.js"></script>
<script src="/js/Client.js"></script>

Client.js

$(document).ready(function() {

    var address = window.location.protocol + '//' + window.location.host;  
    var details = {  
       resource: (window.location.pathname.split('/').slice(0, -1).join('/') + '/socket.io').substring(1)  
    };  
    const socket = io.connect(address, details);

    socket.on('connect', function () {
        console.log('Client Connected');
        socket.emit('ping', 'hi server ping sent!');
    });

    socket.on('error', function (reason){
        console.log('Connection failed', reason); //Here is where it launch the error
    });
});

App.js

....
const https = require('https');

var socketlib = require('./socketLib');
const fs = require('fs');
const app = express();
var cookieParser = require('cookie-parser');
app.use(sessionParser);
var expiryDate = new Date( Date.now() + 60 * 60 * 1000 );

const sessionParser = session({
  secret: 'secret', resave: true, cookieName: 'sessionName',
  name: 'sessionId', saveUninitialized: true,
  ephemeral: true,
  cookie: { secure: true, expires: expiryDate, SameSite : 'None',
    maxAge: 24000 * 60 * 60, // One hour
  }
});

//// HTTPS Server ////
const options = {
  key: fs.readFileSync(config.certKey),
  cert: fs.readFileSync(config.certCert)
};

var httpsServer = https.createServer(options, app, function (req, res) {
    console.log('request starting...https');
});

httpsServer.listen(3000, function(req, res) {
  console.log('Server is running at', config.nodeApiUrl + ':', port)
});

socketlib(httpsServer, sessionParser);

app.all('/*', function (req, res, next) {
res.header('Access-Control-Allow-Origin', 'https://localhost:3000') 
res.header('Access-Control-Allow-Methods', 'GET,PUT,POST,DELETE')
res.header("Access-Control-Allow-Credentials", "true");
res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, X-Access-Token,X-Key");
    return next();
})

socketLib.js

module.exports = async function(httpsServer, sessionParser) {
    var io = require("socket.io")(httpsServer);

    io.use(function(socket, next) {
        sessionParser(socket.request, socket.request.res, next);
    });

    io.use((socket, next) => {
        if (socket.request.headers.cookie)
            return next();
        next(new Error('Authentication error'));
    });

    io.sockets.on('connection', function(socket) {
        console.log(`New connection from: ${socket.handshake.address}`)
    });
}

iis webconfig

<rule name="Websocket" stopProcessing="true" enabled="true">
      <match url="socket.io" ignoreCase="true" />
      <action type="Rewrite" url="https://localhost:3000/{R:0}/socket.io.js" appendQueryString="false" redirectType="Found" />
        <conditions>
           <add input="{HTTP_HOST}" pattern="domainxxx.com" />
        </conditions>
    </rule> 

And this is the error I can see in the Browser console

socketio.js:57 Error Error: server error
    at r.onPacket (socket.io.js:7)
    at o.<anonymous> (socket.io.js:7)
    at o.r.emit (socket.io.js:6)
    at o.r.onPacket (socket.io.js:7)
    at n (socket.io.js:7)
    at Object.e.decodePayload (socket.io.js:7)
    at o.r.onData (socket.io.js:7)
    at i.<anonymous> (socket.io.js:7)
    at i.r.emit (socket.io.js:6)
    at i.onData (socket.io.js:7)

if I click in any if those lines, this is where it takes me to:

switch(a('socket receive: type "%s", data "%s"',t.type,t.data),this.emit("packet",t),this.emit("heartbeat"),t.type){case"open":this.onHandshake(JSON.parse(t.data));break;case"pong":this.setPing(),this.emit("pong");break;case"error":var e=new Error("server error");e.code=t.data,this.onError(e);break;

You will see the:

var e=new Error("server error")

Finally, basically when I try to get accessed through domainxxx.com is like something happen where the socket cannot get connected just remotely, locally works fine, so I guess the only issue is after the rewrite rule, for what I can see, the rewrite rule is 100% correct but is obviously I'm missing something.

I hope you can help me! I did a lot of research and I couldn't find the cure for this!

Thank you in advance!

  • If it doesn't work remotely and this is a hosted server, then it's likely that something in the hosting configuration (proxy, load balancer, etc...) needs to be fixed to properly allow persistent webSocket connections. – jfriend00 Mar 27 '20 at 22:01
  • Here's a a related question [Websockets reverse proxy in IIS 8](https://stackoverflow.com/questions/34316825/websockets-reverse-proxy-in-iis-8) and a [specific article about configuring webSocket in IIS](https://www.microfocus.com/documentation/extend-acucobol/1011/GUID-81B323FA-6AE6-438A-A1F5-016D7F45A097.html) – jfriend00 Mar 27 '20 at 22:13
  • Thanks @jfriend00 for those posts. With some luck I could fix it. The problem was related to the rewrite rule! Thank you again! – Javier Destefanis Mar 27 '20 at 22:58
  • If you want, you can write an answer to your own question that shows what you were missing. I don't know IIS well enough to write an answer. – jfriend00 Mar 27 '20 at 22:59
  • I just did. Thanks! – Javier Destefanis Mar 27 '20 at 23:01

1 Answers1

1

Thanks to a 4 years old post I could fix the problem. The issues was on the rewrite rule. I just replaced it with the following and it worked like a charm.

        <rule name="WebSocketTestRule" stopProcessing="true">
            <match url=".*" />
            <conditions>
                <add input="{CACHE_URL}" pattern="domainxxx.com" />
            </conditions>
            <action type="Rewrite" url="https://localhost:3000/{R:0}" />
        </rule>