3
votes

I am studying a bit node.js programming here and faced a problem.

When express gets POST request it should make a radius authentication over UDP with dgram module. The response for the Radius authentication comes later on, but the POST request callback has already exit and the req and res variables are lost and the response using same TCP connection can not be done.

If I should wait for the radius response (which is quite fast) how should I do the waiting in node.js & express ?

The code below does the basic POST handing and radius authentication.

Thank you very much for any tips.


    var http = require('http');
    var express = require('express');
    var bodyParser = require('body-parser');
    var radius = require('radius');
    var dgram = require('dgram');

    var radius_secret = 'really_secret';
    var radius_server = '127.0.0.1';
    var radius_port = '1812';

    function handleAuthentication(req, res) {
        console.log("BODY:",req.body);
        var client = dgram.createSocket("udp4");
        var account = req.body.account; 
        var password = req.body.password;
        var packet = {
            code: 'Access-Request',
            secret: radius_secret,
            identifier: 0,
            attributes: [
                ['NAS-IP-Address', radius_server],
                ['User-Name', account + "@exampledomain.something"],
                ['User-Password', password]
            ]
        };

        client.on('error', function(e) {
                throw e;
            });

        client.on('message', function(msg, rinfo) {
            var radius_response = radius.decode({packet: msg, secret: radius_secret});
            console.log(radius_response);
        });

        var encoded = radius.encode(packet);
        client.send(encoded, 0, encoded.length, radius_port, radius_server);
    }

    var app = express();
    app.use(bodyParser.json());
    app.use(bodyParser.urlencoded({ extended: false}));

    app.post('/authenticate', function(req, res) {
        // Authenticate against radius server
        handleAuthentication(req, res);

        // The radius response is not received yet, and we will exit the post request
        // here and loose the req and res. 
    });

    var server = http.createServer(app);
    server.listen(80);

2

2 Answers

5
votes

You need to respond in the client callbacks. So, for example, to respond after the message:

client.on('message', function(msg, rinfo) {
     var radius_response = radius.decode({packet: msg, secret: radius_secret});
     console.log(radius_response);
     res.send('somethingCalculatedWithUDPResponse');
 });

And you should handle it similarly in the error callback.

res and req don't "die" when the function ends, they can still be referenced by the closures passed as callbacks.

0
votes

If you wouldn't be able to use res within the handleAuthentication function, or otherwise, don't want to respond to the client there for some code design purposes, you could use the new JS async/await function.

app.post('/authenticate', async function(req, res) {
    // Authenticate against radius server
    try{
        let udpResponse = await handleAuthentication(req, res);
    } catch (error){
        // handle error. use 'res' to notify about error
    }

    // if ok
    res.send("your response")

    //or better
    res.send(udpResponse)
});

And within the function:

client.on('message', function(msg, rinfo) {
        var radius_response = radius.decode({packet: msg, secret: radius_secret});
        return radius_response;
    });

Async/await would wrap the function into a promise, and return statements will be converted in the background as a resolve/reject accordingly, then you can do whatever you want outside the function.