private void HandleResponse(HttpListenerContext context) { // There are bunch of exceptions that can happen here. // 1. Client closes connection. This will cause our response's stream to close. // We may see errors such as "The specified network name is no longer available" // or "The I/O operation has been aborted because of either a thread exit or an application request". // NEITHER or these should cause the program to crash. Simply grab the next connection and move on. // 2. ObjectDisposeExceptions can happen if the above happens as well. We'll handle those when needed. try { HttpListenerRequest request = context.Request; HttpListenerResponse response = context.Response; HttpResponseInfo info = null; // ---- Determine Response and Action to take ---- try { NameValueCollection queryString; using (StreamReader reader = new StreamReader(request.InputStream)) { queryString = HttpUtility.ParseQueryString(reader.ReadToEnd()); } info = this.responseHandler.HandleResposne(request.RawUrl, request.HttpMethod, queryString); } catch (Exception e) { StringBuilder builder = new StringBuilder(); builder.AppendLine("Unexpected Exception while determining HTTP Response:"); builder.AppendLine(e.ToString()); this.OnError?.Invoke(builder.ToString()); info = new HttpResponseInfo { ContentType = ContentType.Xml, Error = ErrorMessage.Unknown, Message = e.Message, ResponseStatus = HttpResponseStatus.ServerError }; } finally { // ---- Write Response (error or okay) to the client. ---- try { response.StatusCode = Convert.ToInt32(info.HttpStatusCode); byte[] buffer = Encoding.UTF8.GetBytes(info.ToString()); response.ContentLength64 = buffer.LongLength; response.OutputStream.Write(buffer, 0, buffer.Length); } catch (Exception e) { StringBuilder builder = new StringBuilder(); builder.AppendLine("Error when writing HTTP Response"); builder.AppendLine(e.ToString()); this.OnError?.Invoke(builder.ToString()); } finally { response.Close(); // <- REMEMBER TO CLOSE THIS! } } } catch (Exception e) { StringBuilder builder = new StringBuilder(); builder.AppendLine("Caught Exception when handling HTTP Response: "); builder.AppendLine(e.Message); builder.AppendLine("This can happen for several expected reasons, we're probably okay!"); builder.AppendLine(e.StackTrace); this.OnError?.Invoke(builder.ToString()); } }
// ---------------- Functions ---------------- public HttpResponseInfo HandleResposne(string url, string method, NameValueCollection queryString) { ContentType contentType = ContentType.Xml; try { if (queryString.AllKeys.Contains("format")) { if (Enum.TryParse(queryString["format"], out ContentType parsedType) == false) { return(new HttpResponseInfo { ContentType = contentType, Error = ErrorMessage.InvalidFormat, Message = "Invalid format: " + queryString["format"], ResponseStatus = HttpResponseStatus.ClientError }); } else { contentType = parsedType; } } if (method.EqualsIgnoreCase("POST") == false) { return(new HttpResponseInfo { ContentType = contentType, Error = ErrorMessage.InvalidMethod, Message = "Request must be a POST request, nothing else is currently supported. Got: " + method, ResponseStatus = HttpResponseStatus.ClientError }); } if (this.IsIrcConnected == false) { // If after all of this, we are not connected, tell the client // that the bot is not connected. return(new HttpResponseInfo { ContentType = contentType, Error = ErrorMessage.NotConnectedToIrc, Message = "IRC Bot not connected, can not send command.", ResponseStatus = HttpResponseStatus.ServerError }); } HttpResponseInfo info = DoRequestAction(url, queryString, contentType); return(info); } catch (Exception err) { return(new HttpResponseInfo { ContentType = contentType, Error = ErrorMessage.Unknown, Message = err.Message, ResponseStatus = HttpResponseStatus.ServerError }); } }