diff options
| author | RblSb <msrblsb@gmail.com> | 2021-02-10 05:02:36 +0300 |
|---|---|---|
| committer | RblSb <msrblsb@gmail.com> | 2021-02-10 05:02:36 +0300 |
| commit | 71d2415a7992c7142200656857ecee3512b238a4 (patch) | |
| tree | 86ad20e7ecfe2e260da3df29e217de9ea5b06b00 | |
| parent | 2bb127e1cd582d9151ef1b70c3496bc79776a95a (diff) | |
Support partial content streams
| -rw-r--r-- | build/server.js | 47 | ||||
| -rw-r--r-- | src/server/HttpServer.hx | 49 |
2 files changed, 65 insertions, 31 deletions
diff --git a/build/server.js b/build/server.js index 63ed3f4..5555428 100644 --- a/build/server.js +++ b/build/server.js @@ -3482,9 +3482,15 @@ server_HttpServer.serveFiles = function(req,res) { url = "/index.html"; } var filePath = server_HttpServer.dir + url; + var ext = haxe_io_Path.extension(filePath).toLowerCase(); + res.setHeader("Accept-Ranges","bytes"); + res.setHeader("Content-Type",server_HttpServer.getMimeType(ext)); if(server_HttpServer.allowLocalRequests && req.connection.remoteAddress == req.connection.localAddress || server_HttpServer.allowedLocalFiles.h[url]) { - if(server_HttpServer.serveLocalFile(res,url)) { - return; + if(server_HttpServer.isMediaExtension(ext)) { + server_HttpServer.allowedLocalFiles.h[url] = true; + if(server_HttpServer.serveMedia(req,res,url)) { + return; + } } } if(!server_HttpServer.isChildOf(server_HttpServer.dir,filePath)) { @@ -3508,13 +3514,16 @@ server_HttpServer.serveFiles = function(req,res) { } } } + if(server_HttpServer.isMediaExtension(ext)) { + if(server_HttpServer.serveMedia(req,res,filePath)) { + return; + } + } js_node_Fs.readFile(filePath,function(err,data) { if(err != null) { server_HttpServer.readFileError(err,res,filePath); return; } - var ext = haxe_io_Path.extension(filePath).toLowerCase(); - res.setHeader("Content-Type",server_HttpServer.getMimeType(ext)); if(ext == "html") { data = server_HttpServer.localizeHtml(data.toString(),req.headers["accept-language"]); } @@ -3530,25 +3539,31 @@ server_HttpServer.readFileError = function(err,res,filePath) { res.end("Error getting the file: " + Std.string(err) + "."); } }; -server_HttpServer.serveLocalFile = function(res,filePath) { - var ext = haxe_io_Path.extension(filePath).toLowerCase(); - if(ext != "mp4" && ext != "mp3" && ext != "wav") { +server_HttpServer.serveMedia = function(req,res,filePath) { + var range = req.headers["range"]; + if(range == null) { return false; } if(!js_node_Fs.existsSync(filePath)) { return false; } - server_HttpServer.allowedLocalFiles.h[filePath] = true; - js_node_Fs.readFile(filePath,function(err,data) { - if(err != null) { - server_HttpServer.readFileError(err,res,filePath); - return; - } - res.setHeader("Content-Type",server_HttpServer.getMimeType(ext)); - res.end(data); - }); + var videoSize = js_node_Fs.statSync(filePath).size; + var _this_r = new RegExp("[^0-9]","g".split("u").join("")); + var start = Std.parseInt(range.replace(_this_r,"")); + var end = Math.min(start + 5242880,videoSize - 1) | 0; + res.setHeader("Content-Range","bytes " + start + "-" + end + "/" + videoSize); + res.setHeader("Content-Length","" + (end - start + 1)); + res.statusCode = 206; + js_node_Fs.createReadStream(filePath,{ start : start, end : end}).pipe(res); return true; }; +server_HttpServer.isMediaExtension = function(ext) { + if(!(ext == "mp4" || ext == "mp3")) { + return ext == "wav"; + } else { + return true; + } +}; server_HttpServer.localizeHtml = function(data,lang) { if(lang != null && server_HttpServer.matchLang.match(lang)) { lang = server_HttpServer.matchLang.matched(0); diff --git a/src/server/HttpServer.hx b/src/server/HttpServer.hx index 0388c28..46540a3 100644 --- a/src/server/HttpServer.hx +++ b/src/server/HttpServer.hx @@ -53,10 +53,17 @@ class HttpServer { var url = req.url; if (url == "/") url = "/index.html"; var filePath = dir + url; + final ext = Path.extension(filePath).toLowerCase(); + + res.setHeader("Accept-Ranges", "bytes"); + res.setHeader("Content-Type", getMimeType(ext)); if (allowLocalRequests && req.connection.remoteAddress == req.connection.localAddress || allowedLocalFiles[url]) { - if (serveLocalFile(res, url)) return; + if (isMediaExtension(ext)) { + allowedLocalFiles[url] = true; + if (serveMedia(req, res, url)) return; + } } if (!isChildOf(dir, filePath)) { @@ -81,13 +88,15 @@ class HttpServer { } } + if (isMediaExtension(ext)) { + if (serveMedia(req, res, filePath)) return; + } + Fs.readFile(filePath, (err:Dynamic, data:Buffer) -> { if (err != null) { readFileError(err, res, filePath); return; } - final ext = Path.extension(filePath).toLowerCase(); - res.setHeader("Content-Type", getMimeType(ext)); if (ext == "html") { // replace ${textId} to localized strings data = cast localizeHtml(data.toString(), req.headers["accept-language"]); @@ -107,22 +116,32 @@ class HttpServer { } } - static function serveLocalFile(res:ServerResponse, filePath:String):Bool { - final ext = Path.extension(filePath).toLowerCase(); - if (ext != "mp4" && ext != "mp3" && ext != "wav") return false; + static function serveMedia(req:IncomingMessage, res:ServerResponse, filePath:String):Bool { + final range:String = req.headers["range"]; + if (range == null) return false; if (!Fs.existsSync(filePath)) return false; - allowedLocalFiles[filePath] = true; - Fs.readFile(filePath, (err:Dynamic, data:Buffer) -> { - if (err != null) { - readFileError(err, res, filePath); - return; - } - res.setHeader("Content-Type", getMimeType(ext)); - res.end(data); - }); + final videoSize = Fs.statSync(filePath).size; + // range example: "bytes=24182784-" + final CHUNK_SIZE = 1024 * 1024 * 5; // 5 MB + final start = Std.parseInt(~/[^0-9]/g.replace(range, "")); + final end = Std.int(Math.min(start + CHUNK_SIZE, videoSize - 1)); + final contentLength = end - start + 1; + + res.setHeader("Content-Range", 'bytes ${start}-${end}/${videoSize}'); + res.setHeader("Content-Length", '$contentLength'); + // HTTP Status 206 for Partial Content + res.statusCode = 206; + // create video read stream for this particular chunk + final videoStream = Fs.createReadStream(filePath, {start: start, end: end}); + // stream the video chunk to the client + videoStream.pipe(res); return true; } + static function isMediaExtension(ext:String):Bool { + return ext == "mp4" || ext == "mp3" || ext == "wav"; + } + static final matchLang = ~/^[A-z]+/; static final matchVarString = ~/\${([A-z_]+)}/g; |
