/// <summary> /// /// </summary> /// <param name="requestObj"></param> public void ReceiveClientRequestHeaders(RequestObj requestObj) { // Read the client request headers this.ParseClientRequestHeaders(requestObj); // If Host header does not exist throw exception if (!requestObj.ClientRequestObj.ClientRequestHeaders.ContainsKey("Host")) { ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.NotFound); throw exception; } // if Host header contains illegal characters throw exception if (Regex.Match(requestObj.ClientRequestObj.ClientRequestHeaders["Host"][0], @"[^\w\d\-_\.]+").Success == true) { ClientNotificationException exception = new ClientNotificationException("Invalid characters in host name"); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.BadRequest); throw exception; } requestObj.ClientRequestObj.Host = requestObj.ClientRequestObj.ClientRequestHeaders["Host"][0]; // Parse Client request content type requestObj.ClientRequestObj.ContentTypeEncoding = this.DetermineClientRequestContentTypeEncoding(requestObj); // Parse Client request content length this.DetermineClientRequestContentLength(requestObj); requestObj.ProxyDataTransmissionModeC2S = this.DetermineDataTransmissionModeC2S(requestObj); Logging.Instance.LogMessage(requestObj.Id, requestObj.ProxyProtocol, Loglevel.Debug, "ReceiveClientRequestHeaders(): ProxyDataTransmissionModeC2S:{0}", requestObj.ProxyDataTransmissionModeC2S.ToString()); }
/// <summary> /// Send custom HttpReverseProxyServer error message to the client system. /// </summary> /// <param name="requestObj"></param> /// <param name="cnex"></param> public void SendErrorMessage2Client(RequestObj requestObj, ClientNotificationException cnex) { var tmpStatusCode = this.statusDescription[HttpStatusCode.InternalServerError].Code; var tmpStatusTitle = this.statusDescription[HttpStatusCode.InternalServerError].Title; var httpServerResponseStatus = $"HTTP/1.1 {tmpStatusCode} {tmpStatusTitle}"; var message = this.statusDescription[HttpStatusCode.InternalServerError].Description; if (cnex.Data.Contains(StatusCodeLabel.StatusCode)) { HttpStatusCode code = (HttpStatusCode)cnex.Data[StatusCodeLabel.StatusCode]; var tmpStatusCode2 = this.statusDescription[code].Code; var tmpStatusTitle2 = this.statusDescription[code].Title; httpServerResponseStatus = $"HTTP/1.1 {tmpStatusCode2} {tmpStatusTitle2}"; message = this.statusDescription[code].Description; } var serverHeaders = new string[] { httpServerResponseStatus, "Content-Type: text/html", "Connection: close", $"Content-Length: {message.Length}" }; // Send headers to client foreach (var tmpHeader in serverHeaders) { this.SendStringToClient(requestObj.ClientRequestObj.ClientBinaryWriter, tmpHeader, true); } // Send ... this.SendStringToClient(requestObj.ClientRequestObj.ClientBinaryWriter, "\n", false); // Send message to client this.SendStringToClient(requestObj.ClientRequestObj.ClientBinaryWriter, message, false); }
/// <summary> /// /// </summary> public void ProcessClientRequest() { PluginInstruction pluginInstr; this.requestObj.ClientRequestObj.ClientWebRequestHandler = new IncomingClientRequest(); while (true) { // (Re) Initialize request object values like client request and server response headers pluginInstr = null; //this.requestObj.InitRequestValues(); Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Debug, "HttpReverseProxy.ProcessClientRequest(): New {0} request to {1}{2}", this.requestObj.ProxyProtocol.ToString(), this.requestObj.ClientRequestObj.Host, this.requestObj.ClientRequestObj.RequestLine.Path); try { // Receive client data this.ReadClientRequestHeaders(); // Call post tcp-client request methodString of each loaded plugin bool mustBreakLoop = this.PostClientHeadersRequest(); if (mustBreakLoop == true) { break; } // Re(re)quest server pluginInstr = this.SendClientRequestToServer(); // Send server response to client this.SendServerResponseToClient(pluginInstr); } catch (ClientNotificationException cnex) { this.clientErrorHandler.SendErrorMessage2Client(this.requestObj, cnex); var innerException = cnex.InnerException?.Message ?? "No inner exception found"; Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Warning, "HttpReverseProxy.ProcessClientRequest(ClientNotificationException): Inner exception:{0}\r\nRegular exception: {1}\r\n{2}", innerException, cnex.Message, cnex.StackTrace); break; } catch (ProxyErrorException peex) { ClientNotificationException cnex = new ClientNotificationException(); cnex.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.BadRequest); this.clientErrorHandler.SendErrorMessage2Client(this.requestObj, cnex); var innerException = peex.InnerException?.Message ?? "No inner exception found"; Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Error, "HttpReverseProxy.ProcessClientRequest(ProxyErrorException): Inner exception:{0}\r\nRegular exception: {1}\r\n{2}", innerException, peex.Message, peex.StackTrace); break; } catch (WebException wex) { var innerException = wex.InnerException?.Message ?? "No inner exception found"; Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Warning, "HttpReverseProxy.ProcessClientRequest(WebException): Inner exception:{0}\r\nRegular exception: {1}\r\n{2}", innerException, wex.Message, wex.StackTrace); this.clientErrorHandler.ProcessWebException(this.requestObj, wex); } catch (System.IO.IOException ioex) { var innerException = ioex.InnerException?.Message ?? "No inner exception found"; Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Debug, "HttpReverseProxy.ProcessClientRequest(IOException): Client system closed the connection"); break; } catch (ObjectDisposedException odex) { var innerException = odex.InnerException?.Message ?? "No inner exception found"; Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Debug, "HttpReverseProxy.ProcessClientRequest(ObjectDisposedException): Inner exception:{0}\r\nRegular exception: {1}\r\n{2}", innerException, odex.Message, odex.StackTrace); break; } catch (SocketException sex) when(sex.SocketErrorCode == SocketError.HostNotFound) { ; ClientNotificationException cnex = new ClientNotificationException(); cnex.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.BadRequest); this.clientErrorHandler.SendErrorMessage2Client(this.requestObj, cnex); var innerException = sex.InnerException?.Message ?? "No inner exception found"; Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Warning, "HttpReverseProxy.ProcessClientRequest(SocketException): Inner exception:{0}\r\nRegular exception: {1}\r\nHost \"{2}\" not found", innerException, sex.Message, requestObj.ClientRequestObj.Host); break; } catch (SocketException sex) { ClientNotificationException cnex = new ClientNotificationException(); cnex.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.BadRequest); this.clientErrorHandler.SendErrorMessage2Client(this.requestObj, cnex); var innerException = sex.InnerException?.Message ?? "No inner exception found"; Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Warning, "HttpReverseProxy.ProcessClientRequest(SocketException): Inner exception:{0}\r\nRegular exception: {1}\r\n{2}", innerException, sex.Message, sex.StackTrace); break; } catch (EmptyRequestException erex) { Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Warning, "HttpReverseProxy.ProcessClientRequest(EmptyRequestException): Regular exception: {1}", erex.Message); this.requestObj.CurrentException = erex; } catch (Exception ex) { var innerException = ex.InnerException?.Message ?? "No inner exception found"; var src = ex.Source; var targetSiteName = ex.TargetSite.Name; var targetSiteModule = ex.TargetSite.Module; Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Error, "HttpReverseProxy.ProcessClientRequest(Exception): Inner exception:{0}\r\nRegular exception: {1}\r\n{2}", innerException, ex.Message, ex.StackTrace); Logging.Instance.LogMessage(this.requestObj.Id, this.requestObj.ProxyProtocol, Loglevel.Error, $"HONK: {src}, SiteName:{targetSiteName}, SiteModule:{targetSiteModule}"); break; } // If remote socket was closed or the client sent a "Conection: close" headerByteArray // break out of the loop if (this.CloseClientServerConnection()) { break; // Set keep-alive value } else { this.requestObj.ClientRequestObj.ClientBinaryReader.BaseStream.ReadTimeout = Config.ClientReadTimetout; } // Reinitialize request object. this.requestObj.InitRequestValues(); } }
/* * Http2http2XX -> Relay response SendServerResponseData2Client() * Http2Http3XX -> Relay response SendServerResponseData2Client() * Http2Https3XXSameUrl -> Remember redirect, strip SSL, request new Url SSLCacheAndRedirectClient2RedirectLocation() * Http2Https3XXDifferentUrl -> Remember redirect, strip SSL, relay response * * Process HTTP request. * 1. Detect HTTP Redirect requests * 2. Cache new HTTP Redirect locations * 3. Recognize incoming lClient requests that need to be SSL-Stripped * 4. Process reqular HTTP requests * */ #endregion #region PRIVATE METHODS /// <summary> /// /// </summary> /// <param name="requestString"></param> private void ParseRequestString(RequestObj requestObj) { if (requestObj == null) { throw new Exception("Request object is invalid"); } if (string.IsNullOrEmpty(requestObj?.ClientRequestObj?.RequestLine?.RequestLine)) { ClientNotificationException exception = new ClientNotificationException("The RequestLine is undefined"); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.BadRequest); throw exception; } if (!requestObj.ClientRequestObj.RequestLine.RequestLine.Contains(' ')) { ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.BadRequest); throw exception; } string[] requestSplitBuffer = requestObj.ClientRequestObj.RequestLine.RequestLine.Split(new char[] { ' ' }, 3); if (requestSplitBuffer.Count() != 3) { ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.BadRequest); throw exception; } if (!Regex.Match(requestSplitBuffer[0].ToLower(), @"^\s*(get|put|post|head|trace|delete|options|connect)\s*$").Success) { ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.MethodNotAllowed); throw exception; } if (!requestSplitBuffer[1].StartsWith("/")) { ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.BadRequest); throw exception; } if (!requestSplitBuffer[2].StartsWith("HTTP/1.")) { ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.HttpVersionNotSupported); throw exception; } // Evaluate request method requestObj.ClientRequestObj.RequestLine.MethodString = requestSplitBuffer[0]; requestObj.ClientRequestObj.RequestLine.Path = requestSplitBuffer[1]; requestObj.ClientRequestObj.RequestLine.HttpVersion = requestSplitBuffer[2]; if (requestObj.ClientRequestObj.RequestLine.MethodString == "GET") { requestObj.ClientRequestObj.RequestLine.RequestMethod = RequestMethod.GET; } else if (requestObj.ClientRequestObj.RequestLine.MethodString == "POST") { requestObj.ClientRequestObj.RequestLine.RequestMethod = RequestMethod.POST; } else if (requestObj.ClientRequestObj.RequestLine.MethodString == "HEAD") { requestObj.ClientRequestObj.RequestLine.RequestMethod = RequestMethod.HEAD; } else if (requestObj.ClientRequestObj.RequestLine.MethodString == "PUT") { requestObj.ClientRequestObj.RequestLine.RequestMethod = RequestMethod.PUT; ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.MethodNotAllowed); throw exception; } else if (requestObj.ClientRequestObj.RequestLine.MethodString == "DELETE") { requestObj.ClientRequestObj.RequestLine.RequestMethod = RequestMethod.DELETE; ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.MethodNotAllowed); throw exception; } else if (requestObj.ClientRequestObj.RequestLine.MethodString == "OPTIONS") { requestObj.ClientRequestObj.RequestLine.RequestMethod = RequestMethod.OPTIONS; ClientNotificationException exception = new ClientNotificationException(); exception.Data.Add(StatusCodeLabel.StatusCode, HttpStatusCode.MethodNotAllowed); throw exception; } else { requestObj.ClientRequestObj.RequestLine.RequestMethod = RequestMethod.Undefined; } if (!requestObj.ClientRequestObj.RequestLine.Path.StartsWith("/")) { requestObj.ClientRequestObj.RequestLine.Path = $"/{requestObj.ClientRequestObj.RequestLine.Path}"; } requestObj.HttpLogData = requestObj.ClientRequestObj.RequestLine.RequestLine.Trim(); }