/// <summary> /// Invoke before response if it is set. /// </summary> /// <param name="args"></param> /// <returns></returns> protected async Task onBeforeResponse(SessionEventArgs args) { if (BeforeResponse != null) { await BeforeResponse.InvokeAsync(this, args, ExceptionFunc); } }
/// <summary> /// Invoke before response if it is set. /// </summary> /// <param name="args"></param> /// <returns></returns> private async Task invokeBeforeResponse(SessionEventArgs args) { if (BeforeResponse != null) { await BeforeResponse.InvokeAsync(this, args, ExceptionFunc); } }
private void OnBeforeResponse(RawContext context, SessionEventArgs e) { if (context == null) { return; } BeforeResponse?.Invoke(context, e); }
/// <summary> /// Override OnReceiveResponse to read response data /// </summary> protected override void OnReceiveResponse() { #region Nekoxy Code AfterReadResponseHeaders?.Invoke(new HttpResponse(this.ResponseStatusLine, this.ResponseHeaders, null)); if (this.ResponseStatusLine.StatusCode != 200) { return; } var response = this.ResponseHeaders.IsUnknownLength() ? this.GetContentWhenUnknownLength() : this.GetContent(); this.State.NextStep = null; using (var ms = new MemoryStream()) { var stream = this.GetResponseMessageStream(response); stream.CopyTo(ms); var content = ms.ToArray(); this.currentSession.Response = new HttpResponse(this.ResponseStatusLine, this.ResponseHeaders, content); } #endregion if (BeforeResponse != null) { response = BeforeResponse?.Invoke(this.currentSession, this.currentSession.Response.Body); this.ResponseHeaders.ContentEncoding = null; // remove gzip and chunked... } #region Nekoxy Code this.ResponseHeaders.TransferEncoding = null; this.ResponseHeaders.ContentLength = (uint)response.Length; this.SendResponseStatusAndHeaders(); // Send HTTP status & head to client this.SocketBP.TunnelDataTo(this.TunnelBP, response); // Send response body to client if (!this.State.bPersistConnectionPS) { this.SocketPS?.CloseSocket(); this.SocketPS = null; } AfterSessionComplete?.Invoke(this.currentSession); #endregion }
/// <summary> /// Invocator for BeforeResponse event. /// </summary> /// <param name="sender"></param> /// <param name="e"></param> /// <returns></returns> protected virtual void OnBeforeResponse(object sender, SessionEventArgs e) { BeforeResponse?.Invoke(sender, e); }
//Called asynchronously when a request was successfully and we received the response public async Task HandleHttpSessionResponse(SessionEventArgs args) { //read response & headers from server await args.WebSession.ReceiveResponse(); try { if (!args.WebSession.Response.ResponseBodyRead) { args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream; } args.ReRequest = false; //If user requested call back then do it if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) { Delegate[] invocationList = BeforeResponse.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ((Func <object, SessionEventArgs, Task>)invocationList[i])(this, args); } await Task.WhenAll(handlerTasks); } if (args.ReRequest) { await HandleHttpSessionRequestInternal(null, args, null, null, true).ConfigureAwait(false); return; } args.WebSession.Response.ResponseLocked = true; //Write back to client 100-conitinue response if that's what server returned if (args.WebSession.Response.Is100Continue) { await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", "Continue", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); } else if (args.WebSession.Response.ExpectationFailed) { await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", "Expectation Failed", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); } //Write back response status to client await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession.Response.ResponseStatusCode, args.WebSession.Response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter); if (args.WebSession.Response.ResponseBodyRead) { var isChunked = args.WebSession.Response.IsChunked; var contentEncoding = args.WebSession.Response.ContentEncoding; if (contentEncoding != null) { args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody); if (isChunked == false) { args.WebSession.Response.ContentLength = args.WebSession.Response.ResponseBody.Length; } else { args.WebSession.Response.ContentLength = -1; } } await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response); await args.ProxyClient.ClientStream.WriteResponseBody(args.WebSession.Response.ResponseBody, isChunked); } else { await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response); //Write body only if response is chunked or content length >0 //Is none are true then check if connection:close header exist, if so write response until server or client terminates the connection if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || !args.WebSession.Response.ResponseKeepAlive) { await args.WebSession.ServerConnection.StreamReader .WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); } //write response if connection:keep-alive header exist and when version is http/1.0 //Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream) else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0) { await args.WebSession.ServerConnection.StreamReader .WriteResponseBody(BUFFER_SIZE, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); } } await args.ProxyClient.ClientStream.FlushAsync(); } catch { Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args); } finally { args.Dispose(); } }
/// <summary> /// This is the core request handler method for a particular connection from client /// Will create new session (request/response) sequence until /// client/server abruptly terminates connection or by normal HTTP termination /// </summary> /// <param name="client"></param> /// <param name="httpCmd"></param> /// <param name="clientStream"></param> /// <param name="clientStreamReader"></param> /// <param name="clientStreamWriter"></param> /// <param name="httpsConnectHostname"></param> /// <param name="endPoint"></param> /// <param name="connectRequest"></param> /// <param name="isTransparentEndPoint"></param> /// <returns></returns> private async Task <bool> HandleHttpSessionRequest(TcpClient client, string httpCmd, CustomBufferedStream clientStream, CustomBinaryReader clientStreamReader, HttpResponseWriter clientStreamWriter, string httpsConnectHostname, ProxyEndPoint endPoint, ConnectRequest connectRequest, bool isTransparentEndPoint = false) { bool disposed = false; TcpConnection connection = null; //Loop through each subsequest request on this particular client connection //(assuming HTTP connection is kept alive by client) while (true) { if (string.IsNullOrEmpty(httpCmd)) { break; } var args = new SessionEventArgs(BufferSize, endPoint, HandleHttpSessionResponse) { ProxyClient = { TcpClient = client }, WebSession = { ConnectRequest = connectRequest } }; try { string httpMethod; string httpUrl; Version version; Request.ParseRequestLine(httpCmd, out httpMethod, out httpUrl, out version); //Read the request headers in to unique and non-unique header collections await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.RequestHeaders); var httpRemoteUri = new Uri(httpsConnectHostname == null ? isTransparentEndPoint ? string.Concat("http://", args.WebSession.Request.Host, httpUrl) : httpUrl : string.Concat("https://", args.WebSession.Request.Host ?? httpsConnectHostname, httpUrl)); args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.OriginalRequestUrl = httpUrl; args.WebSession.Request.Method = httpMethod; args.WebSession.Request.HttpVersion = version; args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamWriter = clientStreamWriter; //proxy authorization check if (httpsConnectHostname == null && await CheckAuthorization(clientStreamWriter, args) == false) { args.Dispose(); break; } PrepareRequestHeaders(args.WebSession.Request.RequestHeaders); args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority; #if NET45 //if win auth is enabled //we need a cache of request body //so that we can send it after authentication in WinAuthHandler.cs if (EnableWinAuth && !RunTime.IsRunningOnMono && args.WebSession.Request.HasBody) { await args.GetRequestBody(); } #endif //If user requested interception do it if (BeforeRequest != null) { await BeforeRequest.InvokeParallelAsync(this, args, ExceptionFunc); } if (args.WebSession.Request.CancelRequest) { args.Dispose(); break; } //create a new connection if hostname changes if (connection != null && !connection.HostName.Equals(args.WebSession.Request.RequestUri.Host, StringComparison.OrdinalIgnoreCase)) { connection.Dispose(); UpdateServerConnectionCount(false); connection = null; } if (connection == null) { connection = await GetServerConnection(args, false); } //if upgrading to websocket then relay the requet without reading the contents if (args.WebSession.Request.UpgradeToWebSocket) { //prepare the prefix content var requestHeaders = args.WebSession.Request.RequestHeaders; byte[] requestBytes; using (var ms = new MemoryStream()) using (var writer = new HttpRequestWriter(ms)) { writer.WriteLine(httpCmd); writer.WriteHeaders(requestHeaders); requestBytes = ms.ToArray(); } await connection.Stream.WriteAsync(requestBytes, 0, requestBytes.Length); string httpStatus = await connection.StreamReader.ReadLineAsync(); Version responseVersion; int responseStatusCode; string responseStatusDescription; Response.ParseResponseLine(httpStatus, out responseVersion, out responseStatusCode, out responseStatusDescription); args.WebSession.Response.HttpVersion = responseVersion; args.WebSession.Response.ResponseStatusCode = responseStatusCode; args.WebSession.Response.ResponseStatusDescription = responseStatusDescription; await HeaderParser.ReadHeaders(connection.StreamReader, args.WebSession.Response.ResponseHeaders); await clientStreamWriter.WriteResponseAsync(args.WebSession.Response); //If user requested call back then do it if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) { await BeforeResponse.InvokeParallelAsync(this, args, ExceptionFunc); } await TcpHelper.SendRaw(clientStream, connection.Stream, (buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); }); args.Dispose(); break; } //construct the web request that we are going to issue on behalf of the client. disposed = await HandleHttpSessionRequestInternal(connection, args, false); if (disposed) { //already disposed inside above method args.Dispose(); break; } //if connection is closing exit if (args.WebSession.Response.ResponseKeepAlive == false) { args.Dispose(); break; } args.Dispose(); // read the next request httpCmd = await clientStreamReader.ReadLineAsync(); } catch (Exception e) { ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request", e, args)); break; } } if (!disposed) { Dispose(clientStream, clientStreamReader, clientStreamWriter, connection); } return(true); }
public void OnBeforeResponse(Session session) { BeforeResponse?.Invoke(session); }
private int onBeforeResponse(long handle) { BeforeResponse?.Invoke(new Session(handle, new Request(handle), new Response(handle))); return(0); }
/// <summary> /// Called asynchronously when a request was successfully and we received the response /// </summary> /// <param name="args"></param> /// <returns>true if client/server connection was terminated (and disposed) </returns> private async Task <bool> HandleHttpSessionResponse(SessionEventArgs args) { try { //read response & headers from server await args.WebSession.ReceiveResponse(); var response = args.WebSession.Response; #if NET45 //check for windows authentication if (EnableWinAuth && !RunTime.IsRunningOnMono && response.ResponseStatusCode == (int)HttpStatusCode.Unauthorized) { bool disposed = await Handle401UnAuthorized(args); if (disposed) { return(true); } } #endif args.ReRequest = false; //If user requested call back then do it if (BeforeResponse != null && !response.ResponseLocked) { await BeforeResponse.InvokeParallelAsync(this, args, ExceptionFunc); } //if user requested to send request again //likely after making modifications from User Response Handler if (args.ReRequest) { //clear current response await args.ClearResponse(); bool disposed = await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args, false); return(disposed); } response.ResponseLocked = true; var clientStreamWriter = args.ProxyClient.ClientStreamWriter; //Write back to client 100-conitinue response if that's what server returned if (response.Is100Continue) { await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, (int)HttpStatusCode.Continue, "Continue"); await clientStreamWriter.WriteLineAsync(); } else if (response.ExpectationFailed) { await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, (int)HttpStatusCode.ExpectationFailed, "Expectation Failed"); await clientStreamWriter.WriteLineAsync(); } //Write back response status to client await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.ResponseStatusCode, response.ResponseStatusDescription); response.ResponseHeaders.FixProxyHeaders(); if (response.ResponseBodyRead) { bool isChunked = response.IsChunked; string contentEncoding = response.ContentEncoding; if (contentEncoding != null) { response.ResponseBody = await GetCompressedResponseBody(contentEncoding, response.ResponseBody); if (isChunked == false) { response.ContentLength = response.ResponseBody.Length; } else { response.ContentLength = -1; } } await clientStreamWriter.WriteHeadersAsync(response.ResponseHeaders); await clientStreamWriter.WriteResponseBodyAsync(response.ResponseBody, isChunked); } else { await clientStreamWriter.WriteHeadersAsync(response.ResponseHeaders); //Write body if exists if (response.HasBody) { await clientStreamWriter.WriteResponseBodyAsync(BufferSize, args.WebSession.ServerConnection.StreamReader, response.IsChunked, response.ContentLength); } } await clientStreamWriter.FlushAsync(); } catch (Exception e) { ExceptionFunc(new ProxyHttpException("Error occured whilst handling session response", e, args)); Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); return(true); } return(false); }
private void onBeforeResponse(long handle) { BeforeResponse?.Invoke(new Session(handle, new Request(handle), new Response(handle))); }
/// <summary> /// This is the core request handler method for a particular connection from client /// Will create new session (request/response) sequence until /// client/server abruptly terminates connection or by normal HTTP termination /// </summary> /// <param name="client"></param> /// <param name="clientStream"></param> /// <param name="clientStreamReader"></param> /// <param name="clientStreamWriter"></param> /// <param name="httpsConnectHostname"></param> /// <param name="endPoint"></param> /// <param name="connectRequest"></param> /// <param name="isTransparentEndPoint"></param> /// <returns></returns> private async Task HandleHttpSessionRequest(TcpClient client, CustomBufferedStream clientStream, CustomBinaryReader clientStreamReader, HttpResponseWriter clientStreamWriter, string httpsConnectHostname, ProxyEndPoint endPoint, ConnectRequest connectRequest, bool isTransparentEndPoint = false) { TcpConnection connection = null; try { //Loop through each subsequest request on this particular client connection //(assuming HTTP connection is kept alive by client) while (true) { // read the request line string httpCmd = await clientStreamReader.ReadLineAsync(); if (string.IsNullOrEmpty(httpCmd)) { break; } var args = new SessionEventArgs(BufferSize, endPoint, ExceptionFunc) { ProxyClient = { TcpClient = client }, WebSession = { ConnectRequest = connectRequest } }; try { Request.ParseRequestLine(httpCmd, out string httpMethod, out string httpUrl, out var version); //Read the request headers in to unique and non-unique header collections await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.Headers); Uri httpRemoteUri; if (uriSchemeRegex.IsMatch(httpUrl)) { try { httpRemoteUri = new Uri(httpUrl); } catch (Exception ex) { throw new Exception($"Invalid URI: '{httpUrl}'", ex); } } else { string host = args.WebSession.Request.Host ?? httpsConnectHostname; string hostAndPath = host; if (httpUrl.StartsWith("/")) { hostAndPath += httpUrl; } string url = string.Concat(httpsConnectHostname == null ? "http://" : "https://", hostAndPath); try { httpRemoteUri = new Uri(url); } catch (Exception ex) { throw new Exception($"Invalid URI: '{url}'", ex); } } args.WebSession.Request.RequestUri = httpRemoteUri; args.WebSession.Request.OriginalUrl = httpUrl; args.WebSession.Request.Method = httpMethod; args.WebSession.Request.HttpVersion = version; args.ProxyClient.ClientStream = clientStream; args.ProxyClient.ClientStreamReader = clientStreamReader; args.ProxyClient.ClientStreamWriter = clientStreamWriter; //proxy authorization check if (!args.IsTransparent && httpsConnectHostname == null && await CheckAuthorization(clientStreamWriter, args) == false) { break; } if (!isTransparentEndPoint) { PrepareRequestHeaders(args.WebSession.Request.Headers); args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority; } //if win auth is enabled //we need a cache of request body //so that we can send it after authentication in WinAuthHandler.cs if (isWindowsAuthenticationEnabledAndSupported && args.WebSession.Request.HasBody) { await args.GetRequestBody(); } //If user requested interception do it if (BeforeRequest != null) { await BeforeRequest.InvokeAsync(this, args, ExceptionFunc); } var response = args.WebSession.Response; if (args.WebSession.Request.CancelRequest) { await HandleHttpSessionResponse(args); if (!response.KeepAlive) { break; } continue; } //create a new connection if hostname/upstream end point changes if (connection != null && (!connection.HostName.Equals(args.WebSession.Request.RequestUri.Host, StringComparison.OrdinalIgnoreCase) || (args.WebSession.UpStreamEndPoint != null && !args.WebSession.UpStreamEndPoint.Equals(connection.UpStreamEndPoint)))) { connection.Dispose(); connection = null; } if (connection == null) { connection = await GetServerConnection(args, false); } //if upgrading to websocket then relay the requet without reading the contents if (args.WebSession.Request.UpgradeToWebSocket) { //prepare the prefix content var requestHeaders = args.WebSession.Request.Headers; await connection.StreamWriter.WriteLineAsync(httpCmd); await connection.StreamWriter.WriteHeadersAsync(requestHeaders); string httpStatus = await connection.StreamReader.ReadLineAsync(); Response.ParseResponseLine(httpStatus, out var responseVersion, out int responseStatusCode, out string responseStatusDescription); response.HttpVersion = responseVersion; response.StatusCode = responseStatusCode; response.StatusDescription = responseStatusDescription; await HeaderParser.ReadHeaders(connection.StreamReader, response.Headers); if (!args.IsTransparent) { await clientStreamWriter.WriteResponseAsync(response); } //If user requested call back then do it if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) { await BeforeResponse.InvokeAsync(this, args, ExceptionFunc); } await TcpHelper.SendRaw(clientStream, connection.Stream, BufferSize, (buffer, offset, count) => { args.OnDataSent(buffer, offset, count); }, (buffer, offset, count) => { args.OnDataReceived(buffer, offset, count); }, ExceptionFunc); break; } //construct the web request that we are going to issue on behalf of the client. await HandleHttpSessionRequestInternal(connection, args); //if connection is closing exit if (!response.KeepAlive) { break; } } catch (Exception e) when(!(e is ProxyHttpException)) { throw new ProxyHttpException("Error occured whilst handling session request", e, args); } finally { args.Dispose(); } } } finally { connection?.Dispose(); } }
//Called asynchronously when a request was successfully and we received the response public static async Task HandleHttpSessionResponse(SessionEventArgs args) { await args.WebSession.ReceiveResponse().ConfigureAwait(false); try { if (!args.WebSession.Response.ResponseBodyRead) { args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream; } if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) { Delegate[] invocationList = BeforeResponse.GetInvocationList(); Task[] handlerTasks = new Task[invocationList.Length]; for (int i = 0; i < invocationList.Length; i++) { handlerTasks[i] = ((Func <object, SessionEventArgs, Task>)invocationList[i])(null, args); } await Task.WhenAll(handlerTasks).ConfigureAwait(false); } args.WebSession.Response.ResponseLocked = true; if (args.WebSession.Response.Is100Continue) { await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", "Continue", args.Client.ClientStreamWriter); await args.Client.ClientStreamWriter.WriteLineAsync(); } else if (args.WebSession.Response.ExpectationFailed) { await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", "Expectation Failed", args.Client.ClientStreamWriter); await args.Client.ClientStreamWriter.WriteLineAsync(); } await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession.Response.ResponseStatusCode, args.WebSession.Response.ResponseStatusDescription, args.Client.ClientStreamWriter); if (args.WebSession.Response.ResponseBodyRead) { var isChunked = args.WebSession.Response.IsChunked; var contentEncoding = args.WebSession.Response.ContentEncoding; if (contentEncoding != null) { args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody).ConfigureAwait(false); if (isChunked == false) { args.WebSession.Response.ContentLength = args.WebSession.Response.ResponseBody.Length; } else { args.WebSession.Response.ContentLength = -1; } } await WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders).ConfigureAwait(false); await WriteResponseBody(args.Client.ClientStream, args.WebSession.Response.ResponseBody, isChunked).ConfigureAwait(false); } else { await WriteResponseHeaders(args.Client.ClientStreamWriter, args.WebSession.Response.ResponseHeaders); if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || (args.WebSession.Response.HttpVersion.Major == 1 && args.WebSession.Response.HttpVersion.Minor == 0)) { await WriteResponseBody(args.WebSession.ServerConnection.StreamReader, args.Client.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength).ConfigureAwait(false); } } await args.Client.ClientStream.FlushAsync(); } catch { Dispose(args.Client.TcpClient, args.Client.ClientStream, args.Client.ClientStreamReader, args.Client.ClientStreamWriter, args); } finally { args.Dispose(); } }
/// <summary> /// Called asynchronously when a request was successfully and we received the response /// </summary> /// <param name="args"></param> /// <returns>true if client/server connection was terminated (and disposed) </returns> private async Task <bool> HandleHttpSessionResponse(SessionEventArgs args) { try { //read response & headers from server await args.WebSession.ReceiveResponse(); if (!args.WebSession.Response.ResponseBodyRead) { args.WebSession.Response.ResponseStream = args.WebSession.ServerConnection.Stream; } //check for windows authentication if (EnableWinAuth && !RunTime.IsRunningOnMono && args.WebSession.Response.ResponseStatusCode == "401") { var disposed = await Handle401UnAuthorized(args); if (disposed) { return(true); } } args.ReRequest = false; //If user requested call back then do it if (BeforeResponse != null && !args.WebSession.Response.ResponseLocked) { await BeforeResponse.InvokeParallelAsync(this, args); } //if user requested to send request again //likely after making modifications from User Response Handler if (args.ReRequest) { //clear current response await args.ClearResponse(); var disposed = await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args, false); return(disposed); } args.WebSession.Response.ResponseLocked = true; //Write back to client 100-conitinue response if that's what server returned if (args.WebSession.Response.Is100Continue) { await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100", "Continue", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); } else if (args.WebSession.Response.ExpectationFailed) { await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417", "Expectation Failed", args.ProxyClient.ClientStreamWriter); await args.ProxyClient.ClientStreamWriter.WriteLineAsync(); } //Write back response status to client await WriteResponseStatus(args.WebSession.Response.HttpVersion, args.WebSession.Response.ResponseStatusCode, args.WebSession.Response.ResponseStatusDescription, args.ProxyClient.ClientStreamWriter); if (args.WebSession.Response.ResponseBodyRead) { var isChunked = args.WebSession.Response.IsChunked; var contentEncoding = args.WebSession.Response.ContentEncoding; if (contentEncoding != null) { args.WebSession.Response.ResponseBody = await GetCompressedResponseBody(contentEncoding, args.WebSession.Response.ResponseBody); if (isChunked == false) { args.WebSession.Response.ContentLength = args.WebSession.Response.ResponseBody.Length; } else { args.WebSession.Response.ContentLength = -1; } } await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response); await args.ProxyClient.ClientStream.WriteResponseBody(args.WebSession.Response.ResponseBody, isChunked); } else { await WriteResponseHeaders(args.ProxyClient.ClientStreamWriter, args.WebSession.Response); //Write body only if response is chunked or content length >0 //Is none are true then check if connection:close header exist, if so write response until server or client terminates the connection if (args.WebSession.Response.IsChunked || args.WebSession.Response.ContentLength > 0 || !args.WebSession.Response.ResponseKeepAlive) { await args.WebSession.ServerConnection.StreamReader .WriteResponseBody(BufferSize, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); } //write response if connection:keep-alive header exist and when version is http/1.0 //Because in Http 1.0 server can return a response without content-length (expectation being client would read until end of stream) else if (args.WebSession.Response.ResponseKeepAlive && args.WebSession.Response.HttpVersion.Minor == 0) { await args.WebSession.ServerConnection.StreamReader .WriteResponseBody(BufferSize, args.ProxyClient.ClientStream, args.WebSession.Response.IsChunked, args.WebSession.Response.ContentLength); } } await args.ProxyClient.ClientStream.FlushAsync(); } catch (Exception e) { ExceptionFunc(new ProxyHttpException("Error occured whilst handling session response", e, args)); Dispose(args.ProxyClient.ClientStream, args.ProxyClient.ClientStreamReader, args.ProxyClient.ClientStreamWriter, args.WebSession.ServerConnection); return(true); } return(false); }