From 19345ebf961c965c9ab96e8b99c40c5850ea11e7 Mon Sep 17 00:00:00 2001 From: JustArchi Date: Thu, 30 Nov 2017 23:09:07 +0100 Subject: [PATCH] Closes #700 --- ArchiSteamFarm/IPC.cs | 94 ++++++++++++++++++++++++--------------- ArchiSteamFarm/Program.cs | 11 +++-- 2 files changed, 66 insertions(+), 39 deletions(-) diff --git a/ArchiSteamFarm/IPC.cs b/ArchiSteamFarm/IPC.cs index ace764389..ebd36e199 100644 --- a/ArchiSteamFarm/IPC.cs +++ b/ArchiSteamFarm/IPC.cs @@ -30,20 +30,28 @@ using ArchiSteamFarm.Localization; namespace ArchiSteamFarm { internal static class IPC { - internal static bool IsRunning => HttpListener?.IsListening == true; + internal static bool IsRunning => IsHandlingRequests || IsListening; + + private static bool IsListening { + get { + try { + return HttpListener?.IsListening == true; + } catch (ObjectDisposedException) { + // HttpListener can dispose itself on error + return false; + } + } + } private static HttpListener HttpListener; + private static bool IsHandlingRequests; - internal static void Init(string host, ushort port) { + internal static void Start(string host, ushort port) { if (string.IsNullOrEmpty(host) || (port == 0)) { ASF.ArchiLogger.LogNullError(nameof(host) + " || " + nameof(port)); return; } - if (HttpListener != null) { - return; - } - if (!HttpListener.IsSupported) { ASF.ArchiLogger.LogGenericError(string.Format(Strings.WarningFailedWithError, "!HttpListener.IsSupported")); return; @@ -58,42 +66,34 @@ namespace ArchiSteamFarm { } string url = "http://" + host + ":" + port + "/"; + HttpListener = new HttpListener { IgnoreWriteExceptions = true }; try { - HttpListener = new HttpListener { IgnoreWriteExceptions = true }; + ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCStarting, url)); HttpListener.Prefixes.Add(url); - } catch (Exception e) { - ASF.ArchiLogger.LogGenericException(e); - HttpListener = null; - } - } - - internal static void Start() { - if ((HttpListener?.IsListening != false) || (HttpListener.Prefixes.Count == 0)) { - return; - } - - ASF.ArchiLogger.LogGenericInfo(string.Format(Strings.IPCStarting, HttpListener.Prefixes.First())); - - try { HttpListener.Start(); - } catch (HttpListenerException e) { + } catch (Exception e) { + // HttpListener can dispose itself on error, so don't keep it around + HttpListener = null; ASF.ArchiLogger.LogGenericException(e); return; } - Utilities.StartBackgroundFunction(Run); - + Utilities.StartBackgroundFunction(HandleRequests); ASF.ArchiLogger.LogGenericInfo(Strings.IPCReady); } internal static void Stop() { - if (HttpListener?.IsListening != true) { + if (!IsListening) { return; } - HttpListener.Stop(); + // We must set HttpListener to null before stopping it, so HandleRequests() knows that exception is expected + HttpListener httpListener = HttpListener; + HttpListener = null; + + httpListener.Stop(); } private static async Task BaseResponse(this HttpListenerContext context, byte[] response, HttpStatusCode statusCode = HttpStatusCode.OK) { @@ -219,18 +219,40 @@ namespace ArchiSteamFarm { } } - private static async Task Run() { - while (HttpListener.IsListening) { - HttpListenerContext context; + private static async Task HandleRequests() { + if (IsHandlingRequests) { + return; + } - try { - context = await HttpListener.GetContextAsync().ConfigureAwait(false); - } catch (Exception e) { - ASF.ArchiLogger.LogGenericException(e); - return; + IsHandlingRequests = true; + + try { + while (IsListening) { + Task task = HttpListener?.GetContextAsync(); + if (task == null) { + return; + } + + HttpListenerContext context; + + try { + context = await task.ConfigureAwait(false); + } catch (HttpListenerException e) { + // If HttpListener is null then we're stopping HttpListener, so this exception is expected, ignore it + if (HttpListener == null) { + return; + } + + // Otherwise this is an error, and HttpListener can dispose itself in this situation, so don't keep it around + HttpListener = null; + ASF.ArchiLogger.LogGenericException(e); + return; + } + + Utilities.StartBackgroundFunction(() => HandleRequest(context), false); } - - Utilities.StartBackgroundFunction(() => HandleRequest(context), false); + } finally { + IsHandlingRequests = false; } } diff --git a/ArchiSteamFarm/Program.cs b/ArchiSteamFarm/Program.cs index 240ee77af..cbf4cff0d 100644 --- a/ArchiSteamFarm/Program.cs +++ b/ArchiSteamFarm/Program.cs @@ -132,7 +132,13 @@ namespace ArchiSteamFarm { // New process might want to start IPC before we in fact close // Ensure that IPC is stopped before Process.Start() - IPC.Stop(); + if (IPC.IsRunning) { + IPC.Stop(); + + for (byte i = 0; (i < WebBrowser.MaxTries) && IPC.IsRunning; i++) { + await Task.Delay(1000).ConfigureAwait(false); + } + } string executableName = Path.GetFileNameWithoutExtension(ProcessFileName); IEnumerable arguments = Environment.GetCommandLineArgs().Skip(executableName.Equals(SharedInfo.AssemblyName) ? 1 : 0); @@ -434,8 +440,7 @@ namespace ArchiSteamFarm { goto default; } - IPC.Init(GlobalConfig.IPCHost, GlobalConfig.IPCPort); - IPC.Start(); + IPC.Start(GlobalConfig.IPCHost, GlobalConfig.IPCPort); break; case "--service": if (cryptKeyNext) {