/// <summary> /// Starts listening. /// </summary> /// <param name="port">Listening port.</param> /// <param name="guid">Listening Guid.</param> private void StartListener(int port, string guid) { this.listener = new HttpListener(); string address = String.Format(LISTEN_URL_PATTERN, port, guid); string validPath = String.Format(VALID_URL_PATTERN, guid); this.listener.Prefixes.Add(address); this.listener.Start(); int threads = App.Settings.HttpListenerThreads; this.rateLimitManager.Reset(); var counter = new HttpListenerCounter(threads); for (int i = 0; i < threads; i++) { Task.Run( async() => { await Listen(this.listener, validPath, counter); } ); } }
/// <summary> /// Listens. /// </summary> /// <param name="listener">HTTP listener.</param> /// <param name="validPath">Valid path.</param> /// <param name="counter">Counter for HttpListener.</param> /// <returns>Task.</returns> private async Task Listen(HttpListener listener, string validPath, HttpListenerCounter counter) { Log.Information("Start HTTP Listen: ThreadId = {ThreadId}", Thread.CurrentThread.ManagedThreadId); try { counter.ReportListenerStarted(); while (listener.IsListening) { var context = await listener.GetContextAsync(); try { var request = context.Request; var response = context.Response; var status = HttpStatusCode.Forbidden; try { int retryAfter = this.rateLimitManager.CheckGlobalRetryAfterSecounds(); if (retryAfter <= 0 && request.RawUrl == validPath && request.HttpMethod == HttpMethod.Post.Method && request.ContentType.StartsWith("application/json") && request.ContentLength64 <= AppSettings.HTTP_LISTENER_LIMIT_REQUESTED_BYTES) { using var input = request.InputStream; using var reader = new StreamReader(input, UTF8); string body = reader.ReadToEnd(); if (Log.IsVerboseEnabled) { Log.Verbose("HTTP Listener received: body = {body}", body); } var audioRenerings = JsonSerializer.Deserialize <AudioRendering>(body, AppSettings.JsonSerializerOptionsForHttpRead); if (audioRenerings.SoundId == this.SoundId && audioRenerings.Secret == this.secretString && !String.IsNullOrWhiteSpace(audioRenerings.Id) && !String.IsNullOrWhiteSpace(audioRenerings.UserHash)) { retryAfter = this.rateLimitManager.CheckUserRetryAfterSecounds(audioRenerings.UserHash); if (retryAfter <= 0) { AudioItem audioItem; if (this.audioManager.TryGetAudioItem(audioRenerings.Id, out audioItem)) { this.audioPlayer?.Play(audioItem.Data, PlaybackMode.Once, audioRenerings.Volume); this.notification.Notify( String.Format(LocalizedInfo.MessagePatternPlaying, audioItem.Name), Notification.NotificationLevel.Info); status = HttpStatusCode.NoContent; } else { status = HttpStatusCode.NotFound; } } } } if (retryAfter > 0) { status = HttpStatusCode.TooManyRequests; response.Headers.Add("Retry-After", retryAfter.ToString()); } } catch (Exception ex) { Log.Error(ex, "Error on processing requested content."); status = HttpStatusCode.InternalServerError; } finally { response.StatusCode = (int)status; response.Close(); } } catch (Exception ex) { Log.Error(ex, "Unexpected Error on receiving request."); } } } catch (ObjectDisposedException ode) { Log.Error(ode, "Error on closing listener."); } catch (HttpListenerException hlex) { if (hlex.ErrorCode != 995) { Log.Error(hlex, "Unexpected Error on listening context."); } } finally { if (listener.IsListening) { counter.ReportListenerTerminatedUnexpectedly(); Log.Warning("HttpListener was stopped while listening."); } else { counter.ReportListenerStopped(); } if (!this.isStopProcessing && !counter.IsAlive) { Log.Error("Service will be stopped automatically."); this.notification.ShowNotification(LocalizedInfo.MessageStopServiceWithUnexpectedError, NotificationLevel.Error); this.autoCloseAction(); } Log.Information("Exit HTTP Listen: ThreadId = {ThreadId}", Thread.CurrentThread.ManagedThreadId); } }