node.js and express : how to wait for udp response - javascript

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);

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.

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.

Related

Node.js server hangs occasionally

I have this node.js server. The problem is sometimes I notice that it gets stuck. That is the client can make requests, but the server doesn't respond, rather it doesn't end the response, it just gets stuck in the server side. If I look in client side dev tools on the http request, it has a gray circle icon meaning waiting for server response. Even if I wait 10 minutes, nothing happens.
The server side also writes things to console on the requests, which doesn't happen when it gets stuck.
On the node.js console, if I then press ctrl+c when it got stuck, I then suddenly see all the console.log messages just appear on the console, and at the same time, the dev tools, recieves all the responses from the server side, i.e. the gray circle changes to green.
Does anyone know what this problem is?
Thanks
var express = require('express');
var https = require("https");
var fse = require("fs-extra");
var bodyParser = require('body-parser');
// INFO
var root = __dirname + '/public';
setUpServer();
// SET UP SERVER
function setUpServer() {
var app = express();
app.use(express.static(root));
app.use(bodyParser.urlencoded({ extended: false }))
app.get('/', function (req, res) {
var dest = 'index.html';
res.sendFile(dest, { root: root + "/pong" });
});
app.post('/get_brain', function (req, res) {
res.end("1");
console.log('Sent master brain to a client!');
});
app.post('/train_and_get_brain', function (req, res) {
res.end("1");
console.log('Sent master brain to a client!');
});
var privateKey = fse.readFileSync('sslcert/key.pem', 'utf8');
var certificate = fse.readFileSync('sslcert/cert.pem', 'utf8');
var credentials = {key: privateKey, cert: certificate};
var httpsServer = https.createServer(credentials, app);
httpsServer.listen(process.env.PORT || 3000, function () {
var host = httpsServer.address().address;
var port = httpsServer.address().port;
console.log('AI started at https://%s:%s', host, port);
});
}

HTTP Proxy: Reject half-processed request

I have HTTP Proxy based on Node.js which transforms response body on fly in stream fashion. For instance, my proxy works like nginx, which compresses (using libz for gzipping) response body.
But I have open question: What if error occures on upstream connection during the data exchange. How to notify client about en error, when response have already been sent and transmitting body is in progress. Complexity of error determination on client side based on fact that Content-Length is absent in response due to source and transformed data mismatch.
To clarify some details I added a simple piece of code:
var express = require("express");
var http = require("http");
var url = require('url');
var webApp = express();
var httpServer = webApp.listen(8080, function () {
console.log("server started on ", 8080, " port");
});
webApp.get("*", function(req, res) {
var targetRequest = http.get(req.originalUrl, function (upstreamResponse) {
if (upstreamResponse.statusCode != 200) {
res.status(500).send("Internal Server Error");
return;
}
upstreamResponse.on("data", function (chunk) {
/// transform chunk and send it to the client
res.write(chunk);
});
upstreamResponse.on("end", function () {
res.end();
});
/// upstream connection error occured
upstreamResponse.on("error", function (error) {
/// How to properly notify client
/// about error ????
/// ????
});
});
});
Actually, the only one way to notify clients about some issues is just to drop downstream connections.

Prime the website using Express JS

I really hope to find some answers here as i tried everything by now.
Background:
Overtime we deploy code to web server, we need to do a cache warm up, i.e. access the site and make sure it loads. First load is always the slowest since IIS require to do some manipulations with a new code and cache it.
Task:
Create a page which will a checkbox and a button. Once button is pressed, array of links sent to server. Server visits each link and provides a feedback on time it took to load each page to the user.
Solution:
I am using node JS & express JS on server side. So far i manage to POST array to the server with links, but since i have limited experience with node JS, i can not figure out server side code to work.
Here is a code i got so far (it is bits and pieces, but it gives an idea of my progress). Any help will be greatly appreciated!!!
var express = require("express");
var app = express();
var bodyParser = require('body-parser');
var parseUrlencoded = bodyParser.urlencoded({ extended: false});
var http = require("http");
function siteToPrime(url){
http.get(url, function (http_res) {
// initialize the container for our data
var data = "";
// this event fires many times, each time collecting another piece of the response
http_res.on("data", function (chunk) {
// append this chunk to our growing `data` var
data += chunk;
});
// this event fires *one* time, after all the `data` events/chunks have been gathered
http_res.on("end", function () {
// you can use res.send instead of console.log to output via express
console.log(data);
});
});
};
//Tells express where to look for static content
app.use(express.static('public'));
app.post('/', parseUrlencoded, function(request, response){
var newBlock = request.body;
console.log(Object.keys(newBlock).length);
var key = Object.keys(newBlock)[0];
console.log(newBlock[key]);
siteToPrime("www.google.com");
response.status(201);
});
app.listen(3000, function(){
console.log("Server listening on port 3000...");
});
Assuming that you have access to the array in the post route:
var express = require("express"),
request = require("request"),
app = express();
var start = new Date();
app.use(express.static('public'));
app.use(bodyParser.urlencoded({extended: false}));
function siteToPrime(req, res, urls) {
urls.forEach(function(url)) {
request(url, function(error, res, body) {
if (!error && res.statusCode == 200) {
console.log(url +' : ' + body);
console.log('Request took: ', new Date() - start, 'ms');
}
});
}
res.redirect('/');
};
app.post('/', function(req, res){
var urls = req.body.urls // Array os urls.
siteToPrime(req, res, urls);
});
app.listen(3000, function(){
console.log("Server listening on port 3000...");
});

Node.js respond with asynchronous data

Recently I started learning a little bit about Node.js and it's capabilities and tried to use it for some web services.
I wanted to create a web service which will serve as a proxy for web requests.
I wanted my service to work that way:
User will access my service -> http://myproxyservice.com/api/getuserinfo/tom
My service will perform request to -> http://targetsite.com/user?name=tom
Responded data would get reflected to the user.
To implement it I used the following code:
app.js:
var express = require('express');
var bodyParser = require('body-parser');
var app = express();
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
var proxy = require('./proxy_query.js')
function makeProxyApiRequest(name) {
return proxy.getUserData(name, parseProxyApiRequest);
}
function parseProxyApiRequest(data) {
returned_data = JSON.parse(data);
if (returned_data.error) {
console.log('An eror has occoured. details: ' + JSON.stringify(returned_data));
returned_data = '';
}
return JSON.stringify(returned_data);
}
app.post('/api/getuserinfo/tom', function(request, response) {
makeProxyApiRequest('tom', response);
//response.end(result);
});
var port = 7331;
proxy_query.js:
var https = require('https');
var callback = undefined;
var options = {
host: 'targetsite.com',
port: 443,
method: 'GET',
};
function resultHandlerCallback(result) {
var buffer = '';
result.setEncoding('utf8');
result.on('data', function(chunk){
buffer += chunk;
});
result.on('end', function(){
if (callback) {
callback(buffer);
}
});
}
exports.getUserData = function(name, user_callback) {
callback = user_callback
options['path'] = user + '?name=' + name;
var request = https.get(options, resultHandlerCallback);
request.on('error', function(e){
console.log('error from proxy_query:getUserData: ' + e.message)
});
request.end();
}
app.listen(port);
I wish I didn't screwed this code because I replaced some stuff to fit my example.
Anyway, the problem is that I want to post the response to the user when the HTTP request is done and I cant find how to do so because I use express and express uses asynchronous calls and so do the http request.
I know that if I want to do so, I should pass the makeProxyApiRequest the response object so he would be able to pass it to the callback but it is not possible because of asyn problems.
any suggestions?
help will be appreciated.
As you're using your functions to process requests inside your route handling, it's better to write them as express middleware functions, taking the specific request/response pair, and making use of express's next cascade model:
function makeProxyApiRequest(req, res, next) {
var name = parseProxyApiRequest(req.name);
res.locals.userdata = proxy.getUserData(name);
next();
}
function parseProxyApiRequest(req, res, next) {
try {
// remember that JSON.parse will throw if it fails!
data = JSON.parse(res.locals.userdata);
if (data .error) {
next('An eror has occoured. details: ' + JSON.stringify(data));
}
res.locals.proxyData = data;
next();
}
catch (e) { next("could not parse user data JSON."); }
}
app.post('/api/getuserinfo/tom',
makeProxyApiRequest,
parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
Even better would be to move those middleware functions into their own file now, so you can simply do:
var middleware = require("./lib/proxy_middleware");
app.post('/api/getuserinfo/tom',
middleware.makeProxyApiRequest,
middleware.parseProxyApiRequest,
function(req, res) {
// res.write or res.json or res.render or
// something, with this specific request's
// data that we stored in res.locals.proxyData
}
);
And keep your app.js as small as possible. Note that the client's browser will simply wait for a response by express, which happens once res.write, res.json or res.render etc is used. Until then the connection is simply kept open between the browser and the server, so if your middleware calls take a long time, that's fine - the browser will happily wait a long time for a response to get sent back, and will be doing other things in the mean time.
Now, in order to get the name, we can use express's parameter construct:
app.param("name", function(req, res, next, value) {
req.params.name = value;
// do something if we need to here, like verify it's a legal name, etc.
// for instance:
var isvalidname = validator.checkValidName(name);
if(!isvalidname) { return next("Username not valid"); }
next();
});
...
app.post("/api/getuserinfo/:name", ..., ..., ...);
Using this system, the :name part of any route will be treated based on the name parameter we defined using app.param. Note that we don't need to define this more than once: we can do the following and it'll all just work:
app.post("/api/getuserinfo/:name", ..., ..., ...);
app.post("/register/:name", ..., ..., ... );
app.get("/api/account/:name", ..., ..., ... );
and for every route with :name, the code for the "name" parameter handler will kick in.
As for the proxy_query.js file, rewriting this to a proper module is probably safer than using individual exports:
// let's not do more work than we need: http://npmjs.org/package/request
// is way easier than rolling our own URL fetcher. In Node.js the idea is
// to write as little as possible, relying on npmjs.org to find you all
// the components that you need to glue together. If you're writing more
// than just the glue, you're *probably* doing more than you need to.
var request = require("request");
module.exports = {
getURL: function(name, url, callback) {
request.get(url, function(err, result) {
if(err) return callback(err);
// do whatever processing you need to do to result:
var processedResult = ....
callback(false, processedResult);
});
}
};
and then we can use that as proxy = require("./lib/proxy_query"); in the middleware we need to actually do the URL data fetching.

node.js : Express, socket.io and PostgreSQL

I am using Express, node.js and socket.io. What I would like to implement is a system in which everytime a user connects to the page (upon 'connection' event), an SQL request is performed so the results are emitted to the user by trigerring the event 'get_recipes'. However, after refreshing the page several times, the event 'get recipes' is not triggered anymore... Can someone tell me what is wrong with my code (do I need to log out from the database ? If so, how ?) ? Thanks a lot !
app.js :
var express = require('express');
var app = express();
var http = require('http');
var server = http.createServer(app);
var io = require('socket.io').listen(server);
var pg = require('pg').native;
var db_URL = "tcp://user1:default#localhost/dbtest";
...
io.sockets.on('connection', function (socket) {
pg.connect(db_URL, function(err, client) {
client.query("SELECT * FROM Recipes", function(err, results) {
socket.emit('get_recipes', results);
});
});
});

Categories