2

So, I have a route function like the following:

var http = require('http').createServer(start);

function start(req, res){
//routing stuff
}

and below that,I have a socket.io event listener:

io.sockets.on('connection', function(socket){

    socket.on('event', function(data){
        //perform an http response
    }

}

When the socket event 'event' is called, I would like to perform an http response like the following:

res.writeHead(200, {'Content-disposition': 'attachment; filename=file.zip'})
res.writeHead(200, { 'Content-Type': 'application/zip' });
var filestream = fs.createReadStream('file.zip');

filestream.on('data', function(chunk) {
    res.write(chunk);
});

filestream.on('end', function() {
    res.end();
});

This last part, when performed within the routing function works just fine, but unfortunately when it is called from the socket event, it of course does not work, because it has no reference to the 'req' or 'res' objects. How would I go about doing this? Thanks.

Ari
  • 3,489
  • 5
  • 26
  • 47

1 Answers1

2

Hmmm... interesting problem:

  • It's not impossible to do something like what you're trying to do, the flow would be something like this:

    1. Receive http request, don't respond, keep res object saved somewhere.
    2. Receive websocket request, do your auth/"link" it to the res object saved earlier.
    3. Respond with file via res.
  • BUT it's not very pretty for a few reasons:

    1. You need to keep res objects saved, if your server restarts a whole bunch of response objects are lost.
    2. You need to figure out how to link websocket clients to http request clients. You could do something with cookies/localstorage to do this, I think.
    3. Scaling to another server will become a lot harder / will you proxy clients to always be served by the same server somehow? Otherwise the linking will get harder.

I would propose a different solution for you: You want to do some client/server steps using websockets before you let someone download a file?

Check here whether this solution is cross-browser enough for you: http://caniuse.com/#feat=datauri

Another option would be to generate a unique URL for the download, and only append it to the browser's window (either as a hidden iframe download, or as a simple download button) once you've done your logic via websocket. This option would be more available cross-browser and easier to code.

Community
  • 1
  • 1
rdrey
  • 9,379
  • 4
  • 40
  • 52
  • Well, I am definitely more inclined toward using the second solution (due to both simplicity and compatibility), but I am still having an issue with it. How I would like it to work is that I create this unique url and redirect the client, then route this request in the router function where I would download their file. The problem: to route this 'on-the-fly' url, I will have to add it to my url handler array, which is a public array; therefore, this so-called "unique" url is no longer unique. How would I save it as a client specific url? – Ari Nov 11 '12 at 00:18
  • A lot of sites do this simply through obscure URLs: When you buy a [humble indie bundle](http://www.humblebundle.com/) you get a unique URL, but nothing stops you from sending that URL to someone else. To add a layer of security, you'd have to link it to a user account and do authentication (with cookies). – rdrey Nov 11 '12 at 12:56
  • Alright, I guess that'll have to do for now then. I got it working correctly, but I'll probably add a cookie check as you said when I get around to it. Thanks for the help! – Ari Nov 11 '12 at 19:46