/// <summary> /// Establish secured connection to destination server. /// </summary> /// <param name="context">current request context</param> protected override void ConnectToServer(ProcessingContext context) { base.ConnectToServer(context); if (context.ServerStream == null) { throw new InvalidContextException("ServerStream"); } if (context.ServerEndPoint == null) { throw new InvalidContextException("ServerEndPoint"); } var sslServerStream = new SslStream(context.ServerStream, false, _certificateValidationCallback); sslServerStream.AuthenticateAsClient(context.ServerEndPoint.Host); context.ServerStream = sslServerStream; if (Logger.IsDebugEnabled) { Logger.DebugFormat("Server SSL connection established: {0}:{1}", context.ServerEndPoint.Host, context.ServerEndPoint.Port ); } }
public void Start(ProcessingContext context) { for (context.Stage = ProcessingStage.ReceiveRequest; context.Stage <= ProcessingStage.Completed; context.Stage++) { if (!_processingActions.ContainsKey(context.Stage)) { continue; } var action = _processingActions[context.Stage]; if (action == null) { continue; } try { action.Invoke(context); } catch (Exception ex) { context.Exception = ex; context.Stage = ProcessingStage.Completed; } } }
/// <summary> /// Accept client connection, create <see cref="ProcessingContext" /> and <see cref="ProcessingContext.ClientStream" /> /// and start processing request. /// </summary> /// <param name="clientSocket">Socket opened by the client</param> public void HandleClient(Socket clientSocket) { Contract.Requires <ArgumentNullException>(clientSocket != null, "clientSocket"); var context = new ProcessingContext { ClientSocket = clientSocket, ClientStream = new NetworkStream(clientSocket, true) { ReadTimeout = (Int32)ClientReadTimeout.TotalMilliseconds, WriteTimeout = (Int32)ClientWriteTimeout.TotalMilliseconds } }; _pipeline.Start(context); if (context.Exception != null) { var errorMessage = new StringBuilder("Request processing failed.").AppendLine(); if (context.RequestHeader != null) { errorMessage.AppendLine("Request:"); errorMessage.WriteHttpTrace(context.RequestHeader); } if (context.ResponseHeader != null) { errorMessage.AppendLine("Response:"); errorMessage.WriteHttpTrace(context.ResponseHeader); } errorMessage.AppendLine("Exception:"); errorMessage.AppendLine(context.Exception.ToString()); Logger.Error(errorMessage.ToString()); } }
/// <summary> /// Resolve <see cref="ProcessingContext.ServerEndPoint" /> based on <see cref="ProcessingContext.RequestHeader" />, /// establish connection to destination server and open <see cref="ProcessingContext.ServerStream" />. /// <see cref="ProcessingContext.RequestHeader" /> should be defined. /// </summary> /// <param name="context">current request context</param> protected virtual void ConnectToServer(ProcessingContext context) { ContractUtils.Requires <ArgumentNullException>(context != null, "context"); ContractUtils.Requires <InvalidContextException>(context.RequestHeader != null, "RequestHeader"); context.ServerEndPoint = DnsUtils.ResolveRequestEndpoint(context.RequestHeader, _defaultPort); context.ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { ReceiveTimeout = (int)ServerReadTimeout.TotalMilliseconds, SendTimeout = (int)ServerWriteTimeout.TotalMilliseconds }; context.ServerSocket.Connect(context.ServerEndPoint.Host, context.ServerEndPoint.Port); context.ServerStream = new NetworkStream(context.ServerSocket, true); OnLog(LogLevel.Debug, "Connection Established: {0}:{1}", context.ServerEndPoint.Host, context.ServerEndPoint.Port ); }
/// <summary> /// Send respose to <see cref="ProcessingContext.ClientStream" /> containing /// <see cref="ProcessingContext.ResponseHeader" /> /// and rest of<see cref="ProcessingContext.ServerStream" />. /// Expect <see cref="ProcessingContext.ServerStream" />, <see cref="ProcessingContext.ClientStream" /> and /// <see cref="ProcessingContext.ResponseHeader" /> to be defined. /// </summary> /// <param name="context">current request context</param> protected virtual void SendResponse(ProcessingContext context) { ContractUtils.Requires <ArgumentNullException>(context != null, "context"); ContractUtils.Requires <InvalidContextException>(context.ServerStream != null, "ServerStream"); ContractUtils.Requires <InvalidContextException>(context.ResponseHeader != null, "ResponseHeader"); ContractUtils.Requires <InvalidContextException>(context.ClientStream != null, "ClientStream"); ContractUtils.Requires <InvalidContextException>(context.ServerSocket != null, "ServerSocket"); var responseWriter = new HttpResponseWriter(context.ClientStream); try { responseWriter.Write(context.ResponseHeader, context.ServerStream, context.ServerSocket.Available); OnLog(LogLevel.Debug, "Response Sent. {0}", TraceUtils.GetHttpTrace(context.ResponseHeader)); } catch (IOException ex) { if (ex.IsSocketException(SocketError.TimedOut)) { OnLog(LogLevel.Warn, "Request to remote server has timed out. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); responseWriter.WriteGatewayTimeout(); } else if (ex.IsSocketException(SocketError.ConnectionReset, SocketError.ConnectionAborted)) { OnLog(LogLevel.Debug, "Request Aborted. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); } else { throw; } context.StopProcessing(); } }
/// <summary> /// Establish secured connection with client and receive HTTP request using it. /// </summary> /// <param name="context">current request context</param> protected override void ReceiveRequest(ProcessingContext context) { base.ReceiveRequest(context); if (context.RequestHeader == null) { throw new InvalidOperationException("Not SSL request"); } if (context.RequestHeader.MethodType != RequestMethodTypes.CONNECT) { throw new InvalidContextException("RequestHeader"); } if (context.ClientStream == null) { throw new InvalidContextException("ClientStream"); } var responseWriter = new HttpResponseWriter(context.ClientStream); var sslStream = new SslStream(context.ClientStream, false, _certificateValidationCallback); try { responseWriter.WriteConnectionEstablished(); sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls, false); context.ClientStream = sslStream; if (Logger.IsDebugEnabled) { Logger.Debug("Client SSL connection established"); } base.ReceiveRequest(context); } catch (IOException ex) { context.StopProcessing(); if (SocketUtils.IsSocketException(ex, SocketError.ConnectionReset, SocketError.ConnectionAborted)) { if (Logger.IsDebugEnabled) { Logger.Debug("Request aborted"); } } else { Logger.Error("Failed to read request", ex); Logger.Error(context.RequestHeader); throw; } } }
/// <summary> /// Establish secured connection with client and receive HTTP request using it. /// </summary> /// <param name="context">current request context</param> protected override void ReceiveRequest(ProcessingContext context) { base.ReceiveRequest(context); if (context.Processed) { return; } if (context.RequestHeader == null) { throw new InvalidContextException("RequestHeader"); } if (context.RequestHeader.MethodType != RequestMethodTypes.CONNECT) { throw new InvalidOperationException("Not SSL request"); } if (context.ClientStream == null) { throw new InvalidContextException("ClientStream"); } var responseWriter = new HttpResponseWriter(context.ClientStream); var sslStream = new SslStream(context.ClientStream, false, _certificateValidationCallback); try { responseWriter.WriteConnectionEstablished(); sslStream.AuthenticateAsServer(_certificate, false, SslProtocols.Tls, false); context.ClientStream = sslStream; base.ReceiveRequest(context); } catch (IOException ex) { if (ex.IsSocketException(SocketError.ConnectionReset, SocketError.ConnectionAborted)) { Logger.WarnFormat("Request Aborted. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); } else if(ex.IsSocketException(SocketError.TimedOut)) { Logger.WarnFormat("Client request time out. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); } else { throw; } context.StopProcessing(); } }
/// <summary> /// Send respose to <see cref="ProcessingContext.ClientStream" /> containing /// <see cref="ProcessingContext.ResponseHeader" /> /// and rest of<see cref="ProcessingContext.ServerStream" />. /// Expect <see cref="ProcessingContext.ServerStream" />, <see cref="ProcessingContext.ClientStream" /> and /// <see cref="ProcessingContext.ResponseHeader" /> to be defined. /// </summary> /// <param name="context">current request context</param> protected virtual void SendResponse(ProcessingContext context) { Contract.Requires<ArgumentNullException>(context != null, "context"); Contract.Requires<InvalidContextException>(context.ServerStream != null, "ServerStream"); Contract.Requires<InvalidContextException>(context.ResponseHeader != null, "ResponseHeader"); Contract.Requires<InvalidContextException>(context.ClientStream != null, "ClientStream"); Contract.Requires<InvalidContextException>(context.ServerSocket != null, "ServerSocket"); var responseWriter = new HttpResponseWriter(context.ClientStream); try { responseWriter.Write(context.ResponseHeader, context.ServerStream, context.ServerSocket.Available); if (Logger.IsDebugEnabled) { Logger.DebugFormat("Response Sent. {0}", TraceUtils.GetHttpTrace(context.ResponseHeader)); } } catch (IOException ex) { if (ex.IsSocketException(SocketError.TimedOut)) { Logger.WarnFormat("Request to remote server has timed out. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); responseWriter.WriteGatewayTimeout(); } else if (ex.IsSocketException(SocketError.ConnectionReset, SocketError.ConnectionAborted)) { if (Logger.IsDebugEnabled) { Logger.DebugFormat("Request Aborted. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); } } else { throw; } context.StopProcessing(); } }
/// <summary> /// Read <see cref="ProcessingContext.RequestHeader" /> from <see cref="ProcessingContext.ClientStream" />. /// <see cref="ProcessingContext.ClientStream" /> should be defined at this point. /// </summary> /// <param name="context">current request context</param> protected virtual void ReceiveRequest(ProcessingContext context) { Contract.Requires<ArgumentNullException>(context != null, "context"); Contract.Requires<InvalidContextException>(context.ClientStream != null, "ClientStream"); var headerReader = new HttpHeaderReader(new PlainStreamReader(context.ClientStream)); try { context.RequestHeader = new HttpRequestHeader(headerReader.ReadHttpMessageHeader()); if (Logger.IsDebugEnabled) { Logger.DebugFormat("Request Received. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); } } catch (IOException ex) { if (ex.IsSocketException(SocketError.OperationAborted, SocketError.ConnectionReset)) { Logger.WarnFormat("Request was terminated by client. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); } else if (ex is EndOfStreamException) { Logger.ErrorFormat("Failed to read request. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); } else if(ex.IsSocketException(SocketError.TimedOut)) { Logger.WarnFormat("Client request time out. {0}", TraceUtils.GetHttpTrace(context.RequestHeader)); } else { throw; } context.StopProcessing(); } }
/// <summary> /// Resolve <see cref="ProcessingContext.ServerEndPoint" /> based on <see cref="ProcessingContext.RequestHeader" />, /// establish connection to destination server and open <see cref="ProcessingContext.ServerStream" />. /// <see cref="ProcessingContext.RequestHeader" /> should be defined. /// </summary> /// <param name="context">current request context</param> protected virtual void ConnectToServer(ProcessingContext context) { Contract.Requires<ArgumentNullException>(context != null, "context"); Contract.Requires<InvalidContextException>(context.RequestHeader != null, "RequestHeader"); context.ServerEndPoint = DnsUtils.ResolveRequestEndpoint(context.RequestHeader, _defaultPort); context.ServerSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { ReceiveTimeout = (Int32) ServerReadTimeout.TotalMilliseconds, SendTimeout = (Int32) ServerWriteTimeout.TotalMilliseconds }; context.ServerSocket.Connect(context.ServerEndPoint.Host, context.ServerEndPoint.Port); context.ServerStream = new NetworkStream(context.ServerSocket, true); if (Logger.IsDebugEnabled) { Logger.DebugFormat("Connection Established: {0}:{1}", context.ServerEndPoint.Host, context.ServerEndPoint.Port ); } }
/// <summary> /// Close client and server connections. /// Expect <see cref="ProcessingContext.ClientStream" /> and <see cref="ProcessingContext.ServerStream" /> to be /// defined. /// </summary> /// <param name="context"></param> protected virtual void CompleteProcessing(ProcessingContext context) { Contract.Requires<ArgumentNullException>(context != null, "context"); if (context.ClientStream != null) { context.ClientStream.Close(); } if (context.ServerStream != null) { context.ServerStream.Close(); } if (Logger.IsDebugEnabled) { Logger.DebugFormat("[{0}] processed", context.RequestHeader.StartLine); } }
/// <summary> /// Accept client connection, create <see cref="ProcessingContext" /> and <see cref="ProcessingContext.ClientStream" /> /// and start processing request. /// </summary> /// <param name="clientSocket">Socket opened by the client</param> public void HandleClient(Socket clientSocket) { Contract.Requires<ArgumentNullException>(clientSocket != null, "clientSocket"); var context = new ProcessingContext { ClientSocket = clientSocket, ClientStream = new NetworkStream(clientSocket, true) { ReadTimeout = (Int32) ClientReadTimeout.TotalMilliseconds, WriteTimeout = (Int32) ClientWriteTimeout.TotalMilliseconds } }; _pipeline.Start(context); if (context.Exception != null) { var errorMessage = new StringBuilder("Request processing failed.").AppendLine(); if (context.RequestHeader != null) { errorMessage.AppendLine("Request:"); errorMessage.WriteHttpTrace(context.RequestHeader); } if (context.ResponseHeader != null) { errorMessage.AppendLine("Response:"); errorMessage.WriteHttpTrace(context.ResponseHeader); } errorMessage.AppendLine("Exception:"); errorMessage.AppendLine(context.Exception.ToString()); Logger.Error(errorMessage.ToString()); } }