diff options
| author | RblSb <msrblsb@gmail.com> | 2024-01-05 03:47:20 +0300 |
|---|---|---|
| committer | RblSb <msrblsb@gmail.com> | 2024-01-05 03:47:20 +0300 |
| commit | f6a89cb793ff5272f0d259b0e296b3862f0c4798 (patch) | |
| tree | 4c77d9516fe10a7d7bc22a6aaade09a3d14889e3 | |
| parent | c9b920cea600b5fbe84ad7ab0b279275243faacb (diff) | |
Fix file descriptor leaks when streaming files
| -rw-r--r-- | build/server.js | 53 | ||||
| -rw-r--r-- | src/server/HttpServer.hx | 44 |
2 files changed, 64 insertions, 33 deletions
diff --git a/build/server.js b/build/server.js index c4fd7c8..5fe867b 100644 --- a/build/server.js +++ b/build/server.js @@ -3756,32 +3756,50 @@ server_HttpServer.serveMedia = function(req,res,filePath) { return false; } var videoSize = js_node_Fs.statSync(filePath).size; - var range = req.headers["range"]; - if(range == null) { + var rangeHeader = req.headers["range"]; + if(rangeHeader == null) { res.statusCode = 200; res.setHeader("Content-Length","" + videoSize); var videoStream = js_node_Fs.createReadStream(filePath); videoStream.pipe(res); + res.on("error",function() { + return videoStream.destroy(); + }); + res.on("close",function() { + return videoStream.destroy(); + }); return true; } - var ranges = new EReg("[-=]","g").split(range); + var range = server_HttpServer.parseRangeHeader(rangeHeader,videoSize); + var start = range.start; + var end = range.end; + res.setHeader("Content-Range","bytes " + start + "-" + end + "/" + videoSize); + res.setHeader("Content-Length","" + (end - start + 1)); + res.statusCode = 206; + var videoStream1 = js_node_Fs.createReadStream(filePath,{ start : start, end : end}); + videoStream1.pipe(res); + res.on("error",function() { + return videoStream1.destroy(); + }); + res.on("close",function() { + return videoStream1.destroy(); + }); + return true; +}; +server_HttpServer.parseRangeHeader = function(rangeHeader,videoSize) { + var ranges = new EReg("[-=]","g").split(rangeHeader); var start = parseFloat(ranges[1]); if(server_Utils.isOutOfRange(start,0,videoSize - 1)) { start = 0; } var end = parseFloat(ranges[2]); if(isNaN(end)) { - end = start + 5242880; + end = start + server_HttpServer.CHUNK_SIZE; } if(server_Utils.isOutOfRange(end,start,videoSize - 1)) { end = videoSize - 1; } - res.setHeader("Content-Range","bytes " + start + "-" + end + "/" + videoSize); - res.setHeader("Content-Length","" + (end - start + 1)); - res.statusCode = 206; - var videoStream = js_node_Fs.createReadStream(filePath,{ start : start, end : end}); - videoStream.pipe(res); - return true; + return { start : start, end : end}; }; server_HttpServer.isMediaExtension = function(ext) { if(!(ext == "mp4" || ext == "webm" || ext == "mp3")) { @@ -3805,24 +3823,24 @@ server_HttpServer.localizeHtml = function(data,lang) { server_HttpServer.proxyUrl = function(req,res) { var url = StringTools.replace(req.url,"/proxy?url=",""); var proxy = server_HttpServer.proxyRequest(url,req,res,function(proxyReq) { - var url = proxyReq.headers["location"]; - if(url == null) { + var tmp = proxyReq.headers["location"]; + if(tmp == null) { return false; } - var proxy2 = server_HttpServer.proxyRequest(url,req,res,function(proxyReq) { + var proxy2 = server_HttpServer.proxyRequest(tmp,req,res,function(proxyReq) { return false; }); if(proxy2 == null) { - res.end("Proxy error: multiple redirects for url " + url); + res.end("Proxy error: multiple redirects for url " + tmp); return true; } - req.pipe(proxy2,{ end : true}); + req.pipe(proxy2); return true; }); if(proxy == null) { return false; } - req.pipe(proxy,{ end : true}); + req.pipe(proxy); return true; }; server_HttpServer.proxyRequest = function(url,req,res,fn) { @@ -3843,7 +3861,7 @@ server_HttpServer.proxyRequest = function(url,req,res,fn) { } proxyReq.headers["Content-Type"] = "application/octet-stream"; res.writeHead(proxyReq.statusCode,proxyReq.headers); - proxyReq.pipe(res,{ end : true}); + proxyReq.pipe(res); }); proxy.on("error",function(err) { res.end("Proxy error: " + url1.href); @@ -5317,6 +5335,7 @@ server_HttpServer.mimeTypes = (function($this) { server_HttpServer.hasCustomRes = false; server_HttpServer.allowedLocalFiles = new haxe_ds_StringMap(); server_HttpServer.allowLocalRequests = false; +server_HttpServer.CHUNK_SIZE = 5242880; server_HttpServer.matchLang = new EReg("^[A-z]+",""); server_HttpServer.matchVarString = new EReg("\\${([A-z_]+)}","g"); server_HttpServer.ctrlCharacters = new EReg("[\\u0000-\\u001F\\u007F-\\u009F\\u2000-\\u200D\\uFEFF]","g"); diff --git a/src/server/HttpServer.hx b/src/server/HttpServer.hx index acb8955..c953bb0 100644 --- a/src/server/HttpServer.hx +++ b/src/server/HttpServer.hx @@ -43,6 +43,7 @@ class HttpServer { static var hasCustomRes = false; static var allowedLocalFiles:Map<String, Bool> = []; static var allowLocalRequests = false; + static final CHUNK_SIZE = 1024 * 1024 * 5; // 5 MB public static function init(dir:String, ?customDir:String, allowLocalRequests:Bool):Void { HttpServer.dir = dir; @@ -125,25 +126,22 @@ class HttpServer { static function serveMedia(req:IncomingMessage, res:ServerResponse, filePath:String):Bool { if (!Fs.existsSync(filePath)) return false; final videoSize = Fs.statSync(filePath).size; - var range:String = req.headers["range"]; - if (range == null) { + final rangeHeader:String = req.headers["range"]; + if (rangeHeader == null) { res.statusCode = 200; res.setHeader("Content-Length", '$videoSize'); final videoStream = Fs.createReadStream(filePath); videoStream.pipe(res); + res.on("error", () -> videoStream.destroy()); + res.on("close", () -> videoStream.destroy()); return true; } - // if (range == null) range = "bytes=0-"; - final ranges = ~/[-=]/g.split(range); - var start = Std.parseFloat(ranges[1]); - if (Utils.isOutOfRange(start, 0, videoSize - 1)) start = 0; - final CHUNK_SIZE = 1024 * 1024 * 5; // 5 MB - var end = Std.parseFloat(ranges[2]); - if (Math.isNaN(end)) end = start + CHUNK_SIZE; - if (Utils.isOutOfRange(end, start, videoSize - 1)) end = videoSize - 1; + final range = parseRangeHeader(rangeHeader, videoSize); + final start = range.start; + final end = range.end; final contentLength = end - start + 1; - res.setHeader("Content-Range", 'bytes ${start}-${end}/${videoSize}'); + res.setHeader("Content-Range", 'bytes $start-$end/$videoSize'); res.setHeader("Content-Length", '$contentLength'); // HTTP Status 206 for Partial Content res.statusCode = 206; @@ -151,9 +149,24 @@ class HttpServer { final videoStream = Fs.createReadStream(filePath, {start: cast start, end: cast end}); // stream the video chunk to the client videoStream.pipe(res); + res.on("error", () -> videoStream.destroy()); + res.on("close", () -> videoStream.destroy()); return true; } + static function parseRangeHeader(rangeHeader:String, videoSize:Float):{start:Float, end:Float} { + final ranges = ~/[-=]/g.split(rangeHeader); + var start = Std.parseFloat(ranges[1]); + if (Utils.isOutOfRange(start, 0, videoSize - 1)) start = 0; + var end = Std.parseFloat(ranges[2]); + if (Math.isNaN(end)) end = start + CHUNK_SIZE; + if (Utils.isOutOfRange(end, start, videoSize - 1)) end = videoSize - 1; + return { + start: start, + end: end + }; + } + static function isMediaExtension(ext:String):Bool { return ext == "mp4" || ext == "webm" || ext == "mp3" || ext == "wav"; } @@ -175,18 +188,17 @@ class HttpServer { static function proxyUrl(req:IncomingMessage, res:ServerResponse):Bool { final url = req.url.replace("/proxy?url=", ""); final proxy = proxyRequest(url, req, res, proxyReq -> { - final url = proxyReq.headers["location"]; - if (url == null) return false; + final url = proxyReq.headers["location"] ?? return false; final proxy2 = proxyRequest(url, req, res, proxyReq -> false); if (proxy2 == null) { res.end('Proxy error: multiple redirects for url $url'); return true; } - req.pipe(proxy2, {end: true}); + req.pipe(proxy2); return true; }); if (proxy == null) return false; - req.pipe(proxy, {end: true}); + req.pipe(proxy); return true; } @@ -209,7 +221,7 @@ class HttpServer { if (fn(proxyReq)) return; proxyReq.headers["Content-Type"] = "application/octet-stream"; res.writeHead(proxyReq.statusCode, proxyReq.headers); - proxyReq.pipe(res, {end: true}); + proxyReq.pipe(res); }); proxy.on("error", err -> { res.end('Proxy error: ${url.href}'); |
