Creating a second HTTP request after a res.send

122 views
Skip to first unread message

Eric

unread,
Sep 20, 2013, 4:20:49 PM9/20/13
to nod...@googlegroups.com
Hi guys,

I'm trying to write a service that performs the following actions:

1) receive an HTTP request from client with some data payload in body.
2) immediately reply with HTTP response 200 - ok
3) perform some calculations based on the payload and than performs an HTTP request to an external HTTP server.

To do that once my http callback is fired I call res.send() and than I perform the calculations, then I create a new http object (var http = require('http')) and call it's request method. At this point the callback for this last call is never triggered.

How should I do to reply to my http client as soon as possible and than do another http request?

Thanks in advance,

Eric

Pedro Teixeira

unread,
Sep 20, 2013, 4:41:30 PM9/20/13
to nod...@googlegroups.com
Some code would perhaps be helpful.

--
Pedro

--
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en
 
---
You received this message because you are subscribed to the Google Groups "nodejs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nodejs+un...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.

Faysal Banna

unread,
Sep 21, 2013, 4:40:47 AM9/21/13
to nod...@googlegroups.com
Perhaps you would like to have a look at this code i wrote to minimize
some youtube loading


Have a look
#!/usr/bin/env node
/*
Constructing YouTubeStrip.js using wait.for package
for making things simple and elegant.
*/

var http = require ("http");
var url = require('url');
var fs = require("fs");
var cluster = require("cluster");
var numCPUs = require('os').cpus().length;
var Step = require('/usr/lib/node_modules/step');
var ch = require('/usr/lib/node_modules/cheerio');
// thinking of using pump package (future version)

// adding gzip module for making stream allot more compact
var gzip = require('zlib');
var SCRIPT = fs.readFileSync('./YP2.js','ascii');
SCRIPT='<script>'+SCRIPT+'</script>';

// Main Clustering Method and server start up
// Start1
if (cluster.isMaster) {
for (var i = 0; i < numCPUs; i++) {
cluster.fork();
}
cluster.on('exit', function(worker, code, signal) {
console.log('worker ' + worker.process.pid + ' died');
});
}else {
var server = http.createServer(function (request, response) {
processRequest(request,response);
});
server.listen(8888);
}
// End1
// processRequest function that gets and delivers data
var processRequest=function(request,response){
var TempReq=url.parse(request.url,true);
//console.log(TempReq);
var ClientRequest = {
'method' : request['method'] || 'GET',
'headers' : request['headers'],
'hostname' : TempReq['hostname'],
'port' : request['port'] || 80,
'path' : TempReq['path'] || '',
'agent' : false,
};
ClientRequest['headers']['accept-encoding']='';
var ResData='';
http.request(ClientRequest,function(res){
res.on('data',function(data){
ResData+=data;
});
res.on('end',function(){
console.log('done reading data ');
// now we start Step Sequencer
Step(
function CheckHeaders(){
//checking headers for content-encoding
if
(res['headers'].hasOwnProperty('content-encoding') ){
switch(res['headers']['content-encoding']) {
case 'gzip':
return gzip.gunzip(ResData,this);
break;
case 'deflate' :
return gzip.inflate(ResData,this);
break;
default :
return ResData;
break;
}
}else {
return ResData;
}
},
function SplitEncoding(err,enc){
$ = ch.load(enc);
$('body').append(SCRIPT);
var ResponseHeaders=res['headers'];
ResponseHeaders['X-Encoded-By']='Faysal Banna';
ResponseHeaders['X-Encoded-Contact-Info']='degr...@gmail.com,
+961-3-258043';
delete ResponseHeaders['content-length'];
response.writeHead(res.statusCode,ResponseHeaders);
switch(res['headers']['content-encoding']) {
case 'gzip':
// here we need to transfirm gzip encoding
console.log('gzip Encoding ');
zlib.gzip(enc,function(err,data){
response.end(data);
});
break;
case 'deflate' :
// here we need to transfer deflate encoding
zlib.deflate(enc,function(err,data){
response.end(data);
})
console.log('deflate Encoding');
break;
default :
// here no encoding done just plain text
console.log('default no-encoding');
response.end($.html(),'utf8');
enc=null;
$=null;

break;
}
}
);

});

}).end();
}




and YP2.js is just simple script taken and modified from userscripts.org
website

// ==UserScript==
// @name Youtube High Definition
// @namespace lenni
// @version 1.3.5
// @updateURL https://userscripts.org/scripts/source/127028.meta.js
// @grant none
// @include *youtube.com/watch*
// @include *youtube.com/user*
// ==/UserScript==

// Author: www.lennart-glauer.de
// Date: 02.08.13
// License: GNU General Public License v3 (GPL)
// Url: http://userscripts.org/scripts/show/127028

// contentEval (http://wiki.greasespot.net/Content_Script_Injection)
(function(source){
// Check for function input.
if ('function' == typeof source) {
// Execute this function with no arguments, by adding parentheses.
// One set around the function, required for valid syntax, and a
// second empty set calls the surrounded function.
source = '(' + source + ')();'
}

// Create a script node holding this source code.
var script = document.createElement('script');
script.setAttribute("type", "application/javascript");
script.textContent = source;

// Insert the script node into the page, so it will run, and
immediately
// remove it to clean up.
document.body.appendChild(script);
document.body.removeChild(script);
})

(function(){
// Dom window
var w = window,

// Dom document
d = w.document,

// No operation
nop = function(){},

// Player object
p = null,

// onYouTubePlayerReady
_onYouTubePlayerReady = w.onYouTubePlayerReady || nop,

// ytPlayerOnYouTubePlayerReady
_ytPlayerOnYouTubePlayerReady = w.ytPlayerOnYouTubePlayerReady || nop;

// Quality levels
var q = {
'tiny':0,
'small':1,
'medium':2,
'large':3,
'hd720':4,
'hd1080':5,
'highres':6
},

// Local maximum
maximum = 'highres';

// Hook YouTubePlayerReady callbacks
w.onYouTubePlayerReady = w.ytPlayerOnYouTubePlayerReady = function(){
// Get Feather player object
if(w.videoPlayer){
for(var i in w.videoPlayer){
if(w.videoPlayer[i] &&
w.videoPlayer[i].setPlaybackQuality){
p = w.videoPlayer[i];
break;
}
}
}

// Get Flash / HTML5 player object
else{
p = d.getElementById('movie_player') ||
d.getElementById('movie_player-flash') ||
d.getElementById('movie_player-html5') ||
d.getElementById('movie_player-html5-flash');
}

// Check for valid player object
if(p){
console.log('Youtube player type: ' + typeof p);

// Greasemonkey with unsafe window: Unwrap XPCNativeWrapper
//if(typeof XPCNativeWrapper === 'function'){
// p = XPCNativeWrapper.unwrap(p);
//}

// Add onStateChange listener
p.addEventListener('onStateChange','onPlayerStateChange');

// Pause video to prevent interrupts
p.pauseVideo();
}

// Call original functions
_ytPlayerOnYouTubePlayerReady();
_onYouTubePlayerReady();
};

// onStateChange callback
w.onPlayerStateChange = function(z){
console.log('Youtube player state: ' + z);

// Catch internal exceptions to prevent flash crashs
try{
// Get actual playback quality
var aq = p.getPlaybackQuality();

// Get available video quality
var testVQ=p.getAvailableQualityLevels();
//var vq = p.getAvailableQualityLevels()[0];
var vq=testVQ[testVQ.length -1];
console.log(vq, "is vq -> ", aq ," is aq");
// Set playback quality
/*if(q[aq] < q[maximum] && q[aq] < q[vq]){
p.setPlaybackQuality(q[maximum] < q[vq] ? maximum : vq);
console.log('Youtube player quality: ' + aq + ' -> ' + vq);
}*/
p.setPlaybackQuality(vq);

// Disable hook
if(z === 1)
w.onPlayerStateChange = nop;

// Play video now
else if(z === 2)
p.playVideo();

}catch(e){
console.log('Youtube player exception: ' + e);
}
};

// HTML5 fallback
//setTimeout(w.onYouTubePlayerReady, 1500);
});

Faysal Banna
Meteorological Services
Rafic Harriri International Airport
Beirut - Lebanon
Mob: +961-3-258043

greelgorke

unread,
Sep 23, 2013, 4:31:57 AM9/23/13
to nod...@googlegroups.com
don't use res.send for that. use res.writeHead, perform yur calcuation and request to the external server, receive the response from it, do your stuf with it and res.write/res.end to your client then

Eric Chaves

unread,
Sep 23, 2013, 8:35:09 AM9/23/13
to nod...@googlegroups.com
Hi guys,

Thanks all for the answers. I still digging into this problem but I believe I found the problem. The method in question is used to perform statistics and manage log regarding the client request. The client answer is independent from this task, so we try to reply it's HTTP request as soon as possible for better UI/UX experience on client side, and than manage the rest of the task.

The code is fairly complex due to several calls to database and log managers, creating a hell of callbacks. We use a middleware framework (a kind of express) and my guess is that when I call "res next" in the middleware it ends the current thread even with some callbacks pending.

We are trying to move this post-reply tasks into an emitter object and handle this whit events + process.nexttick. What do you think?

@pedro due to the code complexity is not viable for me to post it here.

@faysal thanks for the code. We plan to use promises on our code but haven't found the time yet to do it. I'm sure it will solve several needs we have today.

@greelgorke as a troubleshooting exercise we did something similar to your suggestion and the problem went away. However we can't keep the client awaiting, this could lead us into other business problems.

Cheers,

Eric

--
Eric Chaves
(11) 98139-9880


2013/9/23 greelgorke <greel...@gmail.com>

--
--
Job Board: http://jobs.nodejs.org/
Posting guidelines: https://github.com/joyent/node/wiki/Mailing-List-Posting-Guidelines
You received this message because you are subscribed to the Google
Groups "nodejs" group.
To post to this group, send email to nod...@googlegroups.com
To unsubscribe from this group, send email to
nodejs+un...@googlegroups.com
For more options, visit this group at
http://groups.google.com/group/nodejs?hl=en?hl=en
 
---
You received this message because you are subscribed to a topic in the Google Groups "nodejs" group.
To unsubscribe from this topic, visit https://groups.google.com/d/topic/nodejs/gv0Wiq1ULp0/unsubscribe.
To unsubscribe from this group and all its topics, send an email to nodejs+un...@googlegroups.com.

greelgorke

unread,
Sep 23, 2013, 11:14:01 AM9/23/13
to nod...@googlegroups.com
 with my sugestion, the client doesn't wait. in fact you could do something like that:

function(req, res){
    res.writeHead(200, {/*some other headers, you also can use trailing headers*/})
    requestSomething(req.body, function(err, result){
       res.write(makeResponseFromResult(result))
       res.write(resultContainerClosing())
       res.end()
    })

   res.write(headerOfTheResponseSite())
   res.write(sideBarOfTheSite())
   res.write(footerContainer())
   res.write(resultContainerOpening())
}


but ajax is ok too i guess.

Faysal Banna

unread,
Sep 23, 2013, 11:42:29 AM9/23/13
to nod...@googlegroups.com
Sir..
promises is a good choice . i used step as it was the easiest to work with .....

much regards
        Faysal Banna
 Meteorological Services
Rafic Harriri International Airport
      Beirut - Lebanon
    Mob: +961-3-258043
You received this message because you are subscribed to the Google Groups "nodejs" group.
To unsubscribe from this group and stop receiving emails from it, send an email to nodejs+un...@googlegroups.com.
Reply all
Reply to author
Forward
0 new messages