From c32677a0b21d66c0b9b191cdffeb8281324373c3 Mon Sep 17 00:00:00 2001 From: RblSb Date: Fri, 18 Dec 2020 15:07:16 +0300 Subject: Don't crash with busy port --- build/server.js | 119 +++++++++++++++++++++++++++++++++++----------------- src/server/Main.hx | 21 +++++++++- src/server/Utils.hx | 24 +++++++++++ 3 files changed, 123 insertions(+), 41 deletions(-) diff --git a/build/server.js b/build/server.js index 2050377..764c7f6 100644 --- a/build/server.js +++ b/build/server.js @@ -3716,41 +3716,59 @@ var server_Main = function() { if(envPort != null) { this.port = envPort; } - haxe_Log.trace("Local: http://" + this.localIp + ":" + this.port,{ fileName : "src/server/Main.hx", lineNumber : 88, className : "server.Main", methodName : "new"}); - server_Utils.getGlobalIp(function(ip) { - _gthis.globalIp = ip; - haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + _gthis.port,{ fileName : "src/server/Main.hx", lineNumber : 91, className : "server.Main", methodName : "new"}); - }); - var dir = "" + this.rootDir + "/res"; - server_HttpServer.init(dir,"" + this.rootDir + "/user/res",this.config.localAdmins); - Lang.init("" + dir + "/langs"); - var server = js_node_Http.createServer(function(req,res) { - server_HttpServer.serveFiles(req,res); - }); - this.wss = new js_npm_ws_Server({ server : server}); - this.wss.on("connection",$bind(this,this.onConnect)); - server.listen(this.port); - new haxe_Timer(25000).run = function() { - var _g = 0; - var _g1 = _gthis.clients; - while(_g < _g1.length) { - var client = _g1[_g]; - ++_g; - if(client.isAlive) { - client.isAlive = false; - client.ws.ping(); - continue; + var attempts = 5; + var preparePort = null; + preparePort = function() { + server_Utils.isPortFree(_gthis.port,function(free) { + if(!free && attempts > 0) { + haxe_Log.trace("Warning: port " + _gthis.port + " is already in use. Changed to " + (_gthis.port + 1),{ fileName : "src/server/Main.hx", lineNumber : 92, className : "server.Main", methodName : "new"}); + attempts -= 1; + _gthis.port++; + preparePort(); + return; } - client.ws.terminate(); - } + _gthis.runServer(); + }); }; + preparePort(); }; server_Main.__name__ = true; server_Main.main = function() { new server_Main(); }; server_Main.prototype = { - exit: function() { + runServer: function() { + var _gthis = this; + haxe_Log.trace("Local: http://" + this.localIp + ":" + this.port,{ fileName : "src/server/Main.hx", lineNumber : 105, className : "server.Main", methodName : "runServer"}); + server_Utils.getGlobalIp(function(ip) { + _gthis.globalIp = ip; + haxe_Log.trace("Global: http://" + _gthis.globalIp + ":" + _gthis.port,{ fileName : "src/server/Main.hx", lineNumber : 108, className : "server.Main", methodName : "runServer"}); + }); + var dir = "" + this.rootDir + "/res"; + server_HttpServer.init(dir,"" + this.rootDir + "/user/res",this.config.localAdmins); + Lang.init("" + dir + "/langs"); + var server = js_node_Http.createServer(function(req,res) { + server_HttpServer.serveFiles(req,res); + }); + this.wss = new js_npm_ws_Server({ server : server}); + this.wss.on("connection",$bind(this,this.onConnect)); + server.listen(this.port); + new haxe_Timer(25000).run = function() { + var _g = 0; + var _g1 = _gthis.clients; + while(_g < _g1.length) { + var client = _g1[_g]; + ++_g; + if(client.isAlive) { + client.isAlive = false; + client.ws.ping(); + continue; + } + client.ws.terminate(); + } + }; + } + ,exit: function() { this.saveState(); this.logger.saveLog(); process.exit(); @@ -3801,7 +3819,7 @@ server_Main.prototype = { var field = _g1[_g]; ++_g; if(Reflect.field(config,field) == null) { - haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 153, className : "server.Main", methodName : "getUserConfig"}); + haxe_Log.trace("Warning: config field \"" + field + "\" is unknown",{ fileName : "src/server/Main.hx", lineNumber : 170, className : "server.Main", methodName : "getUserConfig"}); } config[field] = Reflect.field(customConfig,field); } @@ -3812,14 +3830,14 @@ server_Main.prototype = { var emote = _g1[_g]; ++_g; if(emoteCopies_h[emote.name]) { - haxe_Log.trace("Warning: emote name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 158, className : "server.Main", methodName : "getUserConfig"}); + haxe_Log.trace("Warning: emote name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 175, className : "server.Main", methodName : "getUserConfig"}); } emoteCopies_h[emote.name] = true; if(!this.verbose) { continue; } if(emoteCopies_h[emote.image]) { - haxe_Log.trace("Warning: emote url of name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 161, className : "server.Main", methodName : "getUserConfig"}); + haxe_Log.trace("Warning: emote url of name \"" + emote.name + "\" has copy",{ fileName : "src/server/Main.hx", lineNumber : 178, className : "server.Main", methodName : "getUserConfig"}); } emoteCopies_h[emote.image] = true; } @@ -3838,7 +3856,7 @@ server_Main.prototype = { js_node_Fs.writeFileSync("" + folder + "/users.json",JSON.stringify(users,null,"\t")); } ,saveState: function() { - haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 183, className : "server.Main", methodName : "saveState"}); + haxe_Log.trace("Saving state...",{ fileName : "src/server/Main.hx", lineNumber : 200, className : "server.Main", methodName : "saveState"}); var json = JSON.stringify({ videoList : this.videoList, isPlaylistOpen : this.isPlaylistOpen, itemPos : this.itemPos, messages : this.messages, timer : { time : this.videoTimer.getTime(), paused : this.videoTimer.isPaused()}},null,"\t"); js_node_Fs.writeFileSync(this.statePath,json); } @@ -3846,7 +3864,7 @@ server_Main.prototype = { if(!sys_FileSystem.exists(this.statePath)) { return; } - haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 200, className : "server.Main", methodName : "loadState"}); + haxe_Log.trace("Loading state...",{ fileName : "src/server/Main.hx", lineNumber : 217, className : "server.Main", methodName : "loadState"}); var data = JSON.parse(js_node_Fs.readFileSync(this.statePath,{ encoding : "utf8"})); this.videoList.length = 0; this.messages.length = 0; @@ -3863,7 +3881,7 @@ server_Main.prototype = { this.videoTimer.pause(); } ,logError: function(type,data) { - haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 214, className : "server.Main", methodName : "logError", customParams : [data]}); + haxe_Log.trace(type,{ fileName : "src/server/Main.hx", lineNumber : 231, className : "server.Main", methodName : "logError", customParams : [data]}); var crashesFolder = "" + this.rootDir + "/user/crashes"; server_Utils.ensureDir(crashesFolder); js_node_Fs.writeFileSync("" + crashesFolder + "/" + (DateTools.format(new Date(),"%Y-%m-%d_%H_%M_%S") + "-" + type) + ".json",JSON.stringify(data,null,"\t")); @@ -3879,7 +3897,7 @@ server_Main.prototype = { if(_gthis.clients.length == 0) { return; } - haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 229, className : "server.Main", methodName : "initIntergationHandlers"}); + haxe_Log.trace("Ping " + url,{ fileName : "src/server/Main.hx", lineNumber : 246, className : "server.Main", methodName : "initIntergationHandlers"}); js_node_Http.get(url,null,function(r) { }); }; @@ -3893,7 +3911,7 @@ server_Main.prototype = { } this.userList.admins.push({ name : name, hash : hash}); this.writeUsers(this.userList); - haxe_Log.trace("Admin " + name + " added.",{ fileName : "src/server/Main.hx", lineNumber : 244, className : "server.Main", methodName : "addAdmin"}); + haxe_Log.trace("Admin " + name + " added.",{ fileName : "src/server/Main.hx", lineNumber : 261, className : "server.Main", methodName : "addAdmin"}); } ,replayLog: function(events) { var _gthis = this; @@ -3937,7 +3955,7 @@ server_Main.prototype = { var ip = req.connection.remoteAddress; var id = this.freeIds.length > 0 ? this.freeIds.shift() : this.clients.length; var name = "Guest " + (id + 1); - haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 282, className : "server.Main", methodName : "onConnect"}); + haxe_Log.trace("" + name + " connected (" + ip + ")",{ fileName : "src/server/Main.hx", lineNumber : 299, className : "server.Main", methodName : "onConnect"}); var client = new Client(ws,req,id,name,0); client.setGroupFlag(ClientGroup.Admin,this.config.localAdmins && req.connection.localAddress == ip); this.clients.push(client); @@ -3949,7 +3967,7 @@ server_Main.prototype = { var obj = _gthis.wsEventParser.fromJson(data); if(_gthis.wsEventParser.errors.length > 0 || _gthis.noTypeObj(obj)) { var errors = "" + ("Wrong request for type \"" + obj.type + "\":") + "\n" + json2object_ErrorUtils.convertErrorArray(_gthis.wsEventParser.errors); - haxe_Log.trace(errors,{ fileName : "src/server/Main.hx", lineNumber : 298, className : "server.Main", methodName : "onConnect"}); + haxe_Log.trace(errors,{ fileName : "src/server/Main.hx", lineNumber : 315, className : "server.Main", methodName : "onConnect"}); _gthis.serverMessage(client,errors); return; } @@ -4046,7 +4064,7 @@ server_Main.prototype = { if(!internal) { return; } - haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 353, className : "server.Main", methodName : "onMessage"}); + haxe_Log.trace("Client " + client.name + " disconnected",{ fileName : "src/server/Main.hx", lineNumber : 370, className : "server.Main", methodName : "onMessage"}); server_Utils.sortedPush(this.freeIds,client.id); HxOverrides.remove(this.clients,client); this.sendClientList(); @@ -4449,9 +4467,32 @@ server_Utils.ensureDir = function(path) { sys_FileSystem.createDirectory(path); } }; +server_Utils.isPortFree = function(port,callback) { + var server = js_node_Http.createServer(); + var timeout = 1000; + var status = false; + server.setTimeout(timeout); + server.once("error",function(err) { + status = false; + server.close(); + }); + server.once("timeout",function() { + status = false; + haxe_Log.trace("Timeout (" + timeout + "ms) occurred waiting for port " + port + " to be available",{ fileName : "src/server/Utils.hx", lineNumber : 27, className : "server.Utils", methodName : "isPortFree"}); + server.close(); + }); + server.once("listening",function() { + status = true; + server.close(); + }); + server.once("close",function() { + callback(status); + }); + server.listen(port); +}; server_Utils.getGlobalIp = function(callback) { var onError = function(e) { - haxe_Log.trace("Warning: connection error, server is local.",{ fileName : "src/server/Utils.hx", lineNumber : 16, className : "server.Utils", methodName : "getGlobalIp"}); + haxe_Log.trace("Warning: connection error, server is local.",{ fileName : "src/server/Utils.hx", lineNumber : 40, className : "server.Utils", methodName : "getGlobalIp"}); callback("127.0.0.1"); }; var url = new js_node_url_URL("https://myexternalip.com/raw"); diff --git a/src/server/Main.hx b/src/server/Main.hx index 8a5f381..07f782a 100644 --- a/src/server/Main.hx +++ b/src/server/Main.hx @@ -31,10 +31,10 @@ class Main { public final logsDir:String; final verbose:Bool; final statePath:String; - final wss:WSServer; + var wss:WSServer; final localIp:String; var globalIp:String; - final port:Int; + var port:Int; public final config:Config; final userList:UserList; final clients:Array = []; @@ -85,6 +85,23 @@ class Main { final envPort = (process.env : Dynamic).PORT; if (envPort != null) port = envPort; + var attempts = 5; + function preparePort():Void { + Utils.isPortFree(port, free -> { + if (!free && attempts > 0) { + trace('Warning: port $port is already in use. Changed to ${port + 1}'); + attempts--; + port++; + preparePort(); + return; + } + runServer(); + }); + } + preparePort(); + } + + function runServer():Void { trace('Local: http://$localIp:$port'); Utils.getGlobalIp(ip -> { globalIp = ip; diff --git a/src/server/Utils.hx b/src/server/Utils.hx index 4e2fc9d..bab6300 100644 --- a/src/server/Utils.hx +++ b/src/server/Utils.hx @@ -2,6 +2,7 @@ package server; import js.node.url.URL; import js.node.Https; +import js.node.Http; import js.node.Os; import sys.FileSystem; @@ -11,6 +12,29 @@ class Utils { if (!FileSystem.exists(path)) FileSystem.createDirectory(path); } + public static function isPortFree(port:Int, callback:(free:Bool)->Void):Void { + final server = Http.createServer(); + final timeout = 1000; + var status = false; + + server.setTimeout(timeout); + server.once("error", function(err) { + status = false; + server.close(); + }); + server.once("timeout", function () { + status = false; + trace('Timeout (${timeout}ms) occurred waiting for port $port to be available'); + server.close(); + }); + server.once("listening", function() { + status = true; + server.close(); + }); + server.once("close", () -> callback(status)); + server.listen(port); + } + public static function getGlobalIp(callback:(ip:String)->Void):Void { function onError(e):Void { trace("Warning: connection error, server is local."); -- cgit v1.2.3