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 /src/server/HttpServer.hx | |
| parent | 2bb127e1cd582d9151ef1b70c3496bc79776a95a (diff) | |
Support partial content streams
Diffstat (limited to 'src/server/HttpServer.hx')
| -rw-r--r-- | src/server/HttpServer.hx | 49 |
1 files changed, 34 insertions, 15 deletions
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; |
