예제 #1
0
        private async Task handleHttpSessionRequest(TcpServerConnection connection, SessionEventArgs args)
        {
            var cancellationToken = args.CancellationTokenSource.Token;
            var request           = args.HttpClient.Request;

            request.Locked = true;

            var body = request.CompressBodyAndUpdateContentLength();

            // set the connection and send request headers
            args.HttpClient.SetConnection(connection);
            await args.HttpClient.SendRequest(Enable100ContinueBehaviour, args.IsTransparent,
                                              cancellationToken);

            // If a successful 100 continue request was made, inform that to the client and reset response
            if (request.ExpectationSucceeded)
            {
                var clientStreamWriter = args.ProxyClient.ClientStreamWriter;
                var response           = args.HttpClient.Response;

                var headerBuilder = new HeaderBuilder();
                headerBuilder.WriteResponseLine(response.HttpVersion, response.StatusCode, response.StatusDescription);
                headerBuilder.WriteHeaders(response.Headers);
                await clientStreamWriter.WriteHeadersAsync(headerBuilder, cancellationToken);

                await args.ClearResponse(cancellationToken);
            }

            // send body to server if available
            if (request.HasBody)
            {
                if (request.IsBodyRead)
                {
                    var writer = args.HttpClient.Connection.StreamWriter;
                    await writer.WriteBodyAsync(body !, request.IsChunked, cancellationToken);
                }
                else if (!request.ExpectationFailed)
                {
                    // get the request body unless an unsuccessful 100 continue request was made
                    HttpWriter writer = args.HttpClient.Connection.StreamWriter !;
                    await args.CopyRequestBodyAsync(writer, TransformationMode.None, cancellationToken);
                }
            }

            args.TimeLine["Request Sent"] = DateTime.Now;

            // parse and send response
            await handleHttpSessionResponse(args);
        }
        /// <summary>
        ///     Called asynchronously when a request was successful and we received the response.
        /// </summary>
        /// <param name="args">The session event arguments.</param>
        /// <returns> The task.</returns>
        protected async Task handleHttpSessionResponse(SessionEventArgs args)
        {
            var cancellationToken = args.CancellationTokenSource.Token;

            // read response & headers from server
            await args.HttpClient.ReceiveResponse(cancellationToken);

            // Server may send expect-continue even if not asked for it in request.
            // According to spec "the client can simply discard this interim response."
            if (args.HttpClient.Response.StatusCode == (int)HttpStatusCode.Continue)
            {
                await args.ClearResponse(cancellationToken);

                await args.HttpClient.ReceiveResponse(cancellationToken);
            }

            args.TimeLine["Response Received"] = DateTime.Now;

            var response = args.HttpClient.Response;

            args.ReRequest = false;

            // check for windows authentication
            if (args.EnableWinAuth)
            {
                if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
                {
                    await handle401UnAuthorized(args);
                }
                else
                {
                    WinAuthEndPoint.AuthenticatedResponse(args.HttpClient.Data);
                }
            }

            // save original values so that if user changes them
            // we can still use original values when syphoning out data from attached tcp connection.
            response.SetOriginalHeaders();

            // if user requested call back then do it
            if (!response.Locked)
            {
                await onBeforeResponse(args);
            }

            // it may changed in the user event
            response = args.HttpClient.Response;

            var clientStream = args.ClientStream;

            // user set custom response by ignoring original response from server.
            if (response.Locked)
            {
                // write custom user response with body and return.
                await clientStream.WriteResponseAsync(response, cancellationToken);

                if (args.HttpClient.HasConnection && !args.HttpClient.CloseServerConnection)
                {
                    // syphon out the original response body from server connection
                    // so that connection will be good to be reused.
                    await args.SyphonOutBodyAsync(false, cancellationToken);
                }

                return;
            }

            // if user requested to send request again
            // likely after making modifications from User Response Handler
            if (args.ReRequest)
            {
                if (args.HttpClient.HasConnection)
                {
                    await tcpConnectionFactory.Release(args.HttpClient.Connection);
                }

                // clear current response
                await args.ClearResponse(cancellationToken);
                await handleHttpSessionRequest(args, null, args.ClientConnection.NegotiatedApplicationProtocol,
                                               cancellationToken, args.CancellationTokenSource);

                return;
            }

            response.Locked = true;

            if (!args.IsTransparent)
            {
                response.Headers.FixProxyHeaders();
            }

            await clientStream.WriteResponseAsync(response, cancellationToken);

            if (response.OriginalHasBody)
            {
                if (response.IsBodySent)
                {
                    // syphon out body
                    await args.SyphonOutBodyAsync(false, cancellationToken);
                }
                else
                {
                    // Copy body if exists
                    var serverStream = args.HttpClient.Connection.Stream;
                    await serverStream.CopyBodyAsync(response, false, clientStream, TransformationMode.None,
                                                     args.OnDataReceived, cancellationToken);
                }
            }


            args.TimeLine["Response Sent"] = DateTime.Now;
        }
        /// <summary>
        ///     Called asynchronously when a request was successfull and we received the response.
        /// </summary>
        /// <param name="args">The session event arguments.</param>
        /// <returns> The task.</returns>
        private async Task handleHttpSessionResponse(SessionEventArgs args)
        {
            var cancellationToken = args.CancellationTokenSource.Token;

            // read response & headers from server
            await args.WebSession.ReceiveResponse(cancellationToken);

            var response = args.WebSession.Response;

            args.ReRequest = false;

            // check for windows authentication
            if (isWindowsAuthenticationEnabledAndSupported)
            {
                if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
                {
                    await handle401UnAuthorized(args);
                }
                else
                {
                    WinAuthEndPoint.AuthenticatedResponse(args.WebSession.Data);
                }
            }

            //save original values so that if user changes them
            //we can still use original values when syphoning out data from attached tcp connection.
            response.SetOriginalHeaders();

            // if user requested call back then do it
            if (!response.Locked)
            {
                await invokeBeforeResponse(args);
            }

            // it may changed in the user event
            response = args.WebSession.Response;

            var clientStreamWriter = args.ProxyClient.ClientStreamWriter;

            //user set custom response by ignoring original response from server.
            if (response.Locked)
            {
                //write custom user response with body and return.
                await clientStreamWriter.WriteResponseAsync(response, cancellationToken : cancellationToken);

                if (args.WebSession.ServerConnection != null &&
                    !args.WebSession.CloseServerConnection)
                {
                    // syphon out the original response body from server connection
                    // so that connection will be good to be reused.
                    await args.SyphonOutBodyAsync(false, cancellationToken);
                }

                return;
            }

            // if user requested to send request again
            // likely after making modifications from User Response Handler
            if (args.ReRequest)
            {
                // clear current response
                await args.ClearResponse(cancellationToken);
                await handleHttpSessionRequestInternal(args.WebSession.ServerConnection, args);

                return;
            }

            response.Locked = true;

            // 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", cancellationToken);

                await clientStreamWriter.WriteLineAsync(cancellationToken);
            }
            else if (response.ExpectationFailed)
            {
                await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
                                                                  (int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken);

                await clientStreamWriter.WriteLineAsync(cancellationToken);
            }

            if (!args.IsTransparent)
            {
                response.Headers.FixProxyHeaders();
            }

            if (response.IsBodyRead)
            {
                await clientStreamWriter.WriteResponseAsync(response, cancellationToken : cancellationToken);
            }
            else
            {
                // Write back response status to client
                await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode,
                                                                  response.StatusDescription, cancellationToken);

                await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken : cancellationToken);

                // Write body if exists
                if (response.HasBody)
                {
                    await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None,
                                                     cancellationToken);
                }
            }
        }
        /// <summary>
        /// Handle windows NTLM authentication
        /// Can expand this for Kerberos in future
        /// Note: NTLM/Kerberos cannot do a man in middle operation
        /// we do for HTTPS requests.
        /// As such we will be sending local credentials of current
        /// User to server to authenticate requests.
        /// To disable this set ProxyServer.EnableWinAuth to false
        /// </summary>
        internal async Task <bool> Handle401UnAuthorized(SessionEventArgs args)
        {
            string     headerName = null;
            HttpHeader authHeader = null;

            //check in non-unique headers first
            var header =
                args.WebSession.Response.ResponseHeaders.NonUniqueHeaders.FirstOrDefault(
                    x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)));

            if (!header.Equals(new KeyValuePair <string, List <HttpHeader> >()))
            {
                headerName = header.Key;
            }

            if (headerName != null)
            {
                authHeader = args.WebSession.Response.ResponseHeaders.NonUniqueHeaders[headerName]
                             .FirstOrDefault(x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase)));
            }

            //check in unique headers
            if (authHeader == null)
            {
                //check in non-unique headers first
                var uHeader =
                    args.WebSession.Response.ResponseHeaders.Headers.FirstOrDefault(x => authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)));

                if (!uHeader.Equals(new KeyValuePair <string, HttpHeader>()))
                {
                    headerName = uHeader.Key;
                }

                if (headerName != null)
                {
                    authHeader = authSchemes.Any(x => args.WebSession.Response.ResponseHeaders.Headers[headerName].Value
                                                 .StartsWith(x, StringComparison.OrdinalIgnoreCase))
                        ? args.WebSession.Response.ResponseHeaders.Headers[headerName]
                        : null;
                }
            }

            if (authHeader != null)
            {
                string scheme = authSchemes.FirstOrDefault(x => authHeader.Value.Equals(x, StringComparison.OrdinalIgnoreCase));

                //clear any existing headers to avoid confusing bad servers
                if (args.WebSession.Request.RequestHeaders.NonUniqueHeaders.ContainsKey("Authorization"))
                {
                    args.WebSession.Request.RequestHeaders.NonUniqueHeaders.Remove("Authorization");
                }

                //initial value will match exactly any of the schemes
                if (scheme != null)
                {
                    string clientToken = WinAuthHandler.GetInitialAuthToken(args.WebSession.Request.Host, scheme, args.Id);

                    var auth = new HttpHeader("Authorization", string.Concat(scheme, clientToken));

                    //replace existing authorization header if any
                    if (args.WebSession.Request.RequestHeaders.Headers.ContainsKey("Authorization"))
                    {
                        args.WebSession.Request.RequestHeaders.Headers["Authorization"] = auth;
                    }
                    else
                    {
                        args.WebSession.Request.RequestHeaders.Headers.Add("Authorization", auth);
                    }

                    //don't need to send body for Authorization request
                    if (args.WebSession.Request.HasBody)
                    {
                        args.WebSession.Request.ContentLength = 0;
                    }
                }
                //challenge value will start with any of the scheme selected
                else
                {
                    scheme = authSchemes.FirstOrDefault(x => authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) &&
                                                        authHeader.Value.Length > x.Length + 1);

                    string serverToken = authHeader.Value.Substring(scheme.Length + 1);
                    string clientToken = WinAuthHandler.GetFinalAuthToken(args.WebSession.Request.Host, serverToken, args.Id);

                    //there will be an existing header from initial client request
                    args.WebSession.Request.RequestHeaders.Headers["Authorization"] = new HttpHeader("Authorization", string.Concat(scheme, clientToken));

                    //send body for final auth request
                    if (args.WebSession.Request.HasBody)
                    {
                        args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length;
                    }
                }

                //Need to revisit this.
                //Should we cache all Set-Cokiee headers from server during auth process
                //and send it to client after auth?

                //clear current server response
                await args.ClearResponse();

                //request again with updated authorization header
                //and server cookies
                bool disposed = await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args, false);

                return(disposed);
            }

            return(false);
        }
예제 #5
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);
        }
예제 #6
0
        /// <summary>
        /// Handle windows NTLM authentication
        /// Can expand this for Kerberos in future
        /// Note: NTLM/Kerberos cannot do a man in middle operation
        /// we do for HTTPS requests.
        /// As such we will be sending local credentials of current
        /// User to server to authenticate requests.
        /// To disable this set ProxyServer.EnableWinAuth to false
        /// </summary>
        internal async Task <bool> Handle401UnAuthorized(SessionEventArgs args)
        {
            string     headerName = null;
            HttpHeader authHeader = null;

            //check in non-unique headers first
            var header = args.WebSession.Response
                         .NonUniqueResponseHeaders
                         .FirstOrDefault(x =>
                                         authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)));

            if (!header.Equals(new KeyValuePair <string, List <HttpHeader> >()))
            {
                headerName = header.Key;
            }

            if (headerName != null)
            {
                authHeader = args.WebSession.Response
                             .NonUniqueResponseHeaders[headerName]
                             .Where(x => authSchemes.Any(y => x.Value.StartsWith(y, StringComparison.OrdinalIgnoreCase)))
                             .FirstOrDefault();
            }

            //check in unique headers
            if (authHeader == null)
            {
                //check in non-unique headers first
                var uHeader = args.WebSession.Response
                              .ResponseHeaders
                              .FirstOrDefault(x =>
                                              authHeaderNames.Any(y => x.Key.Equals(y, StringComparison.OrdinalIgnoreCase)));

                if (!uHeader.Equals(new KeyValuePair <string, HttpHeader>()))
                {
                    headerName = uHeader.Key;
                }

                if (headerName != null)
                {
                    authHeader = authSchemes.Any(x => args.WebSession.Response
                                                 .ResponseHeaders[headerName].Value.StartsWith(x, StringComparison.OrdinalIgnoreCase)) ?
                                 args.WebSession.Response.ResponseHeaders[headerName] : null;
                }
            }

            if (authHeader != null)
            {
                var scheme = authSchemes.FirstOrDefault(x => authHeader.Value.Equals(x, StringComparison.OrdinalIgnoreCase));

                //initial value will match exactly any of the schemes
                if (scheme != null)
                {
                    var clientToken = WinAuthHandler.GetInitialAuthToken(args.WebSession.Request.Host, scheme, args.Id);
                    args.WebSession.Request.RequestHeaders.Add("Authorization", new HttpHeader("Authorization", string.Concat(scheme, clientToken)));
                }
                //challenge value will start with any of the scheme selected
                else
                {
                    scheme = authSchemes.FirstOrDefault(x => authHeader.Value.StartsWith(x, StringComparison.OrdinalIgnoreCase) &&
                                                        authHeader.Value.Length > x.Length + 1);

                    var serverToken = authHeader.Value.Substring(scheme.Length + 1);
                    var clientToken = WinAuthHandler.GetFinalAuthToken(args.WebSession.Request.Host, serverToken, args.Id);


                    args.WebSession.Request.RequestHeaders["Authorization"]
                        = new HttpHeader("Authorization", string.Concat(scheme, clientToken));
                }

                //clear current response
                await args.ClearResponse();

                var disposed = await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args, false);

                return(disposed);
            }

            return(false);
        }
예제 #7
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 HandleHttpSessionResponse(SessionEventArgs args)
        {
            try
            {
                //read response & headers from server
                await args.WebSession.ReceiveResponse();

                var response = args.WebSession.Response;

                //check for windows authentication
                if (isWindowsAuthenticationEnabledAndSupported && response.StatusCode == (int)HttpStatusCode.Unauthorized)
                {
                    await Handle401UnAuthorized(args);
                }

                args.ReRequest = false;

                //if user requested call back then do it
                if (!response.ResponseLocked)
                {
                    await InvokeBeforeResponse(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();
                    await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args);

                    return;
                }

                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.StatusCode, response.StatusDescription);

                if (!args.IsTransparent)
                {
                    response.Headers.FixProxyHeaders();
                }

                if (response.IsBodyRead)
                {
                    bool   isChunked       = response.IsChunked;
                    string contentEncoding = response.ContentEncoding;

                    if (contentEncoding != null)
                    {
                        response.Body = await GetCompressedResponseBody(contentEncoding, response.Body);

                        if (isChunked == false)
                        {
                            response.ContentLength = response.Body.Length;
                        }
                        else
                        {
                            response.ContentLength = -1;
                        }
                    }

                    await clientStreamWriter.WriteHeadersAsync(response.Headers);

                    await clientStreamWriter.WriteBodyAsync(response.Body, isChunked);
                }
                else
                {
                    await clientStreamWriter.WriteHeadersAsync(response.Headers);

                    //Write body if exists
                    if (response.HasBody)
                    {
                        await args.CopyResponseBodyAsync(clientStreamWriter, false);
                    }
                }
            }
            catch (Exception e) when(!(e is ProxyHttpException))
            {
                throw new ProxyHttpException("Error occured whilst handling session response", e, args);
            }
        }
예제 #8
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();

                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);
        }
예제 #9
0
        /// <summary>
        ///     Called asynchronously when a request was successfully and we received the response.
        /// </summary>
        /// <param name="args">The session event arguments.</param>
        /// <returns> The task.</returns>
        private async Task HandleHttpSessionResponse(SessionEventArgs args)
        {
            try
            {
                var cancellationToken = args.CancellationTokenSource.Token;

                // read response & headers from server
                await args.WebSession.ReceiveResponse(cancellationToken);

                var response = args.WebSession.Response;
                args.ReRequest = false;

                // check for windows authentication
                if (isWindowsAuthenticationEnabledAndSupported)
                {
                    if (response.StatusCode == (int)HttpStatusCode.Unauthorized)
                    {
                        await Handle401UnAuthorized(args);
                    }
                    else
                    {
                        WinAuthEndPoint.AuthenticatedResponse(args.WebSession.Data);
                    }
                }

                response.OriginalHasBody = response.HasBody;

                // if user requested call back then do it
                if (!response.Locked)
                {
                    await InvokeBeforeResponse(args);
                }

                // it may changed in the user event
                response = args.WebSession.Response;

                var clientStreamWriter = args.ProxyClient.ClientStreamWriter;

                if (response.TerminateResponse || response.Locked)
                {
                    await clientStreamWriter.WriteResponseAsync(response, cancellationToken : cancellationToken);

                    if (!response.TerminateResponse)
                    {
                        // syphon out the response body from server before setting the new body
                        await args.SyphonOutBodyAsync(false, cancellationToken);
                    }
                    else
                    {
                        args.WebSession.ServerConnection.Dispose();
                        args.WebSession.ServerConnection = null;
                    }

                    return;
                }

                // if user requested to send request again
                // likely after making modifications from User Response Handler
                if (args.ReRequest)
                {
                    // clear current response
                    await args.ClearResponse(cancellationToken);
                    await HandleHttpSessionRequestInternal(args.WebSession.ServerConnection, args);

                    return;
                }

                response.Locked = true;

                // 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", cancellationToken);

                    await clientStreamWriter.WriteLineAsync(cancellationToken);
                }
                else if (response.ExpectationFailed)
                {
                    await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion,
                                                                      (int)HttpStatusCode.ExpectationFailed, "Expectation Failed", cancellationToken);

                    await clientStreamWriter.WriteLineAsync(cancellationToken);
                }

                if (!args.IsTransparent)
                {
                    response.Headers.FixProxyHeaders();
                }

                if (response.IsBodyRead)
                {
                    await clientStreamWriter.WriteResponseAsync(response, cancellationToken : cancellationToken);
                }
                else
                {
                    // Write back response status to client
                    await clientStreamWriter.WriteResponseStatusAsync(response.HttpVersion, response.StatusCode,
                                                                      response.StatusDescription, cancellationToken);

                    await clientStreamWriter.WriteHeadersAsync(response.Headers, cancellationToken : cancellationToken);

                    // Write body if exists
                    if (response.HasBody)
                    {
                        await args.CopyResponseBodyAsync(clientStreamWriter, TransformationMode.None,
                                                         cancellationToken);
                    }
                }
            }
            catch (Exception e) when(!(e is ProxyHttpException))
            {
                throw new ProxyHttpException("Error occured whilst handling session response", e, args);
            }
        }