var http = require("http");
var sys = require('sys')
var filename = process.ARGV[2];
var exec = require('child_process').exec;
var com = exec('uptime');
http.createServer(function(req,res){
res.writeHead(200,{"Content-Type": "text/plain"});
com.on("output", function (data) {
res.write(data, encoding='utf8');
});
}).listen(8000);
sys.puts('Node server running')
How do I get the data streamed to the browser ?
If you're just generally asking whats going wrong, there are two main things:
You're using child_process.exec() incorrectly
You never called res.end()
What you're looking for is something more like this:
var http = require("http");
var exec = require('child_process').exec;
http.createServer(function(req, res) {
exec('uptime', function(err, stdout, stderr) {
if (err) {
res.writeHead(500, {"Content-Type": "text/plain"});
res.end(stderr);
}
else {
res.writeHead(200,{"Content-Type": "text/plain"});
res.end(stdout);
}
});
}).listen(8000);
console.log('Node server running');
Note that this doesn't actually require 'streaming' in the sense that the word is generally used. If you had a long running process, such that you didn't want to buffer stdout in memory until it completed (or if you were sending a file to the browser, etc), then you would want to 'stream' the output. You would use child_process.spawn to start the process, immediately write the HTTP headers, then whenever a 'data' event fired on stdout you would immediately write the data to HTTP stream. On an 'exit' event you would call end on the stream to terminate it.
Related
Currently, I have a lot of linux based clients downloading firmware updates from my webserver.
After the client has successfully downloaded the firmware file, my server needs to execute a few scripts, which logs in to the client and performs some tasks.
Is there a way for a node server to keep track of the clients download progress, so I can execute the needed scripts once the file has been downloaded?
Ok so I will try.
If you serve your Firmware as static files through Apache/Nginx and direct url call. You don't get the progress inside your NodeJS script.
If you serve your files via stream inside a express controller you can listen to the progress. Look at this answer here https://stackoverflow.com/a/42273080/3168392
You will have to use a socket connection to make sure the node server gets update from the client of the progress of the file being downloaded.
Something like this
CLIENT_CODE
var socket = io('http://localhost');
socket.on('connect', function(){});
socket.on('data_reciving', parts.pop(),function(percentage){
if(parse_data(percentage) === 100){
client.socket.emit('downloadCompleted', {fileName:'test'});
}else{
//do nothing
}
});
SERVER_CODE:
sockets.on('connection', function (socket) {
//listen to the event from client
socket.on('downloadCompleted', function (data) {
connect_to_client();
do_some_operation();
socket.emit('ALLDONE',{some_data});
});
});
I guess this helps ,you can use this post for reference.
If you just want to run some code when a download has finished, you can use on-finished:
const onFinished = require('on-finished');
app.use((req, res, next) => {
onFinished(res, (err, res) => {
...log some data, perform some housekeeping, etc...
});
next();
});
As is, it will attach a "finished" listener to all responses, which is probably not what you want. Since this is plain Express middleware, you can attach it to specific routes instead (but how depends on how exactly the files are being served).
I found some code that seems to fit my needs.
With the code below, I can detect both the progress of a user's download from the server-side, and fire an event once the file transfer completes.
var http = require("http");
var fs = require("fs");
var filename = "./large-file";
var serv = http.createServer(function (req, res) {
var sent = 0;
var lastChunkSize = 0;
var stat = fs.statSync(filename);
res.setHeader('Content-disposition', 'attachment; filename=large-file.iso');
res.setHeader('Accept-Ranges', 'bytes');
res.setHeader('Keep-Alive', 'timeout=5, max=100');
res.writeHeader(200, {"Content-Length": stat.size});
var fReadStream = fs.createReadStream(filename, { highWaterMark: 128 * 1024 });
fReadStream.on('data', function (chunk) {
if(!res.write(chunk)){
fReadStream.pause();
lastChunkSize = chunk.length;
console.log('Sent', sent, 'of', stat.size);
}
});
fReadStream.on('end', function () {
console.log('Transfer complete.');
res.end();
});
res.on("drain", function () {
sent += lastChunkSize;
fReadStream.resume();
});
});
serv.listen(3001);
To show my students a simple HTTP request and response that they could capture using Wireshark, I whipped up a simple Node.js HTTP server:
var fs = require('fs');
var http = require('http');
var port = 80;
var file = process.argv[2]; //This file contains a 42 byte HTML page
http.createServer(function (req, res) {
res.writeHead(200, { 'content-type' : 'text/html' }); // Sends first packet
fs.createReadStream(file).pipe(res); // Sends second packet
}).listen(port);
Unfortunately, the two lines transmitting the HTTP header and the HTML are sent as two separate TCP packets (even though they are both quite small). It would be simpler for my students if the HTTP header and HTML were just one packet. How could I change my code to do this?
var http = require('http');
var fs = require('fs');
var file = process.argv[2];
http.createServer(function(request, response) {
response.writeHeader(200, {"Content-Type": "text/html;"});
fs.readFile(file, function (err, html) {
if (err) {
throw err;
}
response.write(html);
response.end();
});
}).listen(8000);
the reason it won't work is that Node.js runs everything asynchronously. When you are loading your html file, the server creation starts the same time. By the time you are about to write your html to your tcp socket, the file most likely won't be ready.
I see what you were trying to do before... I misread your code because of the indentation. Let me know if this snippet works.
try using something like-
var file = process.argv[2];
fs.readFile(file, function (err, html) {
if (err) {
throw err;
}
http.createServer(function(request, response) {
response.writeHeader(200, {"Content-Type": "text/html"});
response.write(html);
response.end();
}).listen(8000);
});
I want to run some predefined shell commands and return them as plain text in a http server.
The content written at (1) is being served to my browser, but the content at (2) which eventually has to be the stdout is not being served. Can anybody help me how to achieve this?
var http = require('http'),
url = require('url'),
exec = require('child_process').exec,
child,
poort = 8088;
http.createServer(function(req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
var pathname = url.parse(req.url).pathname;
if (pathname == '/who'){
res.write('Who:'); // 1
child = exec('who',
function(error, stdout, stderr){
res.write('sdfsdfs'); //2
})
} else {
res.write('operation not allowed');
}
res.end();
}).listen(poort);
It's because of where you place res.end().
Since exec is asynchronous, res.end() actually happens before the res.write you label as (2). No more writes can be issued after an .end, so the browser doesn't get any further data.
You should call res.end() inside the exec callback, after res.write. The exec callback will be issued when the child process terminates and will get the complete output.
On nodejs.org socket.setTimeout, it says
When an idle timeout is triggered the socket will receive a 'timeout' event but the connection will not be severed.
But when I test code like this:
var http = require('http');
server = http.createServer(function (request, response) {
request.socket.setTimeout(500);
request.socket.on('timeout', function () {
response.writeHead(200, {'content-type': 'text/html'});
response.end('hello world');
console.log('timeout');
});
});
server.listen(8080);
The socket is closed immediately after timeout, and no data is replied to the browser. Which is quite different from the document. Is this a bug or is there any tricks dealing socket under http module?
The documentation is indeed correct, however it looks like the http module adds a 'timeout' listener which calls socket.destroy(). So what you need to do is get rid of that listener by calling request.socket.removeAllListeners('timeout').
So your code should look like:
var http = require('http');
server = http.createServer(function (request, response) {
request.socket.setTimeout(500);
request.socket.removeAllListeners('timeout');
request.socket.on('timeout', function () {
response.writeHead(200, {'content-type': 'text/html'});
response.end('hello world');
console.log('timeout');
});
});
server.listen(8080);
What I am trying to do is to make a thumbnail of a video using ffmpeg. The video data is received in a HTTP request and then piped to ffmpeg. The problem is that once the ffmpeg child process exits I simply can't send the response back.
Here is the code:
var http = require('http'),
sys = require('sys'),
child = require('child_process')
http.createServer(function (req, res) {
im = child.spawn('ffmpeg',['-i','-','-vcodec','mjpeg','-ss','00:00:03','-vframes','1','-s','100x80','./thumb/thumbnail.jpg']);
im.on('exit', function (code, signal) {
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('{"success":true}\n');
});
req.connection.pipe(im.stdin);
}).listen(5678, "127.0.0.1");
The problem is that calling:
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('{"success":true}\n');
does nothing, the client never receives the response.
After two days of debugging and googling It seems like I have found the problem.
There are two related open bugs in node.js responsible:
https://github.com/joyent/node/issues/777
https://github.com/joyent/node/issues/782
I will try to describe what I think the problem is with the 'pipe' method:
The request stream fails to invoke end on ffmpeg.stdin (probably bug #777), this causes a broken pipe error, but node.js doesn't handle the error because of bug #782, meanwhile the request stream remains paused - this blocks any response from being sent.
The hack/workaround is to resume the request stream once ffmpeg exits.
Here is the fixed code sample:
var http = require('http'),
sys = require('sys'),
child = require('child_process')
http.createServer(function (req, res) {
im = child.spawn('ffmpeg',['-i','-','-vcodec','mjpeg','-ss','00:00:03','-vframes','1','-s','100x80','./thumb/thumbnail.jpg']);
im.on('exit', function (code, signal) {
req.resume();
res.writeHead(200, {'Content-Type': 'text/plain'});
res.end('{"success":true}\n');
});
req.connection.pipe(im.stdin);
}).listen(5678, "127.0.0.1");
Please keep in mind that this is a hack/workaround and may lead to problems with future node.js releases once they do something about those bugs
I would try something like this.
var http = require('http'):
var sys = require('sys'):
var child = require('child_process'):
http.createServer(function (req, res) {
res.writeHead(200, {'Content-Type': 'text/plain'});
im = child.spawn('ffmpeg',['-i','-','-vcodec','mjpeg','-ss','00:00:03','-vframes','1','-s','100x80','./thumb/thumbnail.jpg']);
im.on('exit', function (code, signal) {
res.end('{"success":true}\n');
});
req.connection.pipe(im.stdin);
}).listen(5678, "127.0.0.1");
You are trying to pipe out data to the socket before sending the header.