This repository has been archived by the owner on Jun 7, 2023. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 4
/
helpers.js
125 lines (112 loc) · 4.12 KB
/
helpers.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
var fs = require("fs");
var mime = require("mime");
var timeEpoch = new Date(0); // Thu, 01 Jan 1970 00:00:00 GMT
// Accepts an Array, returns a new Array that contains only
// the unique items of the input Array.
// http://www.shamasis.net/2009/09/fast-algorithm-to-find-unique-items-in-javascript-array/
function arrayUniq(array) {
var o = {}, i, l = array.length, r = [];
for(i=0; i<l;i++) o[array[i]] = array[i];
for(i in o) r.push(o[i]);
return r;
}
// Accepts an Array, and returns an Array with only the deepest Arrays (that
// is, Arrays containing Strings, not other Arrays) uniquified.
function arrayUniqDeep(array) {
if (Array.isArray(array[0])) {
array.forEach(function(item, i) {
array[i] = arrayUniqDeep(item);
});
return array;
} else {
return arrayUniq(array);
}
}
// Serve a standard static file to an HTTP client.
// Respectfully borrowed and modified from:
// http://github.com/biggie/biggie-router/blob/f31711ae7d8c3a10b0ff658d1ec5b3d02225b575/lib/biggie-router/modules/static.js
function serveStaticFile(request, response, filename) {
fs.stat(filename, function(error, stat) {
if (error || !stat.isFile()) {
console.error(error.stack);
return;
}
// Prepare for streaming file to client
var responseCode = 200;
var responseHeaders = {
'Content-Type': mime.lookup(filename.indexOf(".") != -1 ? filename.substring(filename.lastIndexOf(".")+1) : filename),
'Content-Length': stat.size,
'Last-Modified': stat.mtime.toUTCString(),
'Expires': timeEpoch.toUTCString(),
'Cache-Control': 'no-cache'
};
var readOpts = {};
// If the client requested a "Range", then prepare the headers
// and set the read stream options to read the specified range.
// Range is implemented for HTML5 <audio> and <video>. See note here:
// http://stackoverflow.com/questions/1995589/html5-audio-safari-live-broadcast-vs-not
if (request.headers['range']) {
var range = request.headers['range'].substring(6).split('-');
//console.log(range);
readOpts.start = Number(range[0]);
readOpts.end = Number(range[1]);
if (range[1].length === 0) {
readOpts.end = stat.size - 1;
} else if (range[0].length === 0) {
readOpts.end = stat.size - 1;
readOpts.start = readOpts.end - range[1] + 1;
}
var contentLength = readOpts.end - readOpts.start + 1;
responseCode = 206;
responseHeaders['Accept-Ranges'] = "bytes";
responseHeaders['Content-Length'] = contentLength;
responseHeaders['Content-Range'] = "bytes " + readOpts.start + "-" + readOpts.end + "/" + stat.size;
}
// Stream the file
response.writeHead(responseCode, responseHeaders);
var file_stream = fs.createReadStream(filename, readOpts);
// Detect if the connection is prematurely closed, and manually close the
// file descriptor when it is. We have it be a named function because we
// have to unbind it in the "pump" callback.
function onClose() {
file_stream.destroy();
}
request.connection.on("close", onClose);
// Listen for "error" events on the read stream, in case anything goes
// wrong (though this is not likely).
file_stream.addListener('error', function(error) {
console.error(error, error.stack);
response.end();
});
pump(file_stream, response, function() {
request.connection.removeListener("close", onClose);
response.end();
});
});
}
// A stripped down "pump" function that only pumps the readable
// into the writable, but doesn't call .end() on the writable at
// the end. This allows to pump more streams, or whatever before closing.
function pump(readable, writable, cb) {
readable.on('data', function(data) {
if (!writable.write(data)) {
readable.pause();
}
});
writable.on('drain', function() {
readable.resume();
});
if (cb) {
readable.on('end', function() {
cb();
});
}
};
// Export everything as a messy module...
module.exports = {
arrayUniq: arrayUniq,
arrayUniqDeep: arrayUniqDeep,
mime: mime.lookup,
pump: pump,
serveStaticFile: serveStaticFile
}