/// <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);
     }
 }
Ejemplo n.º 3
0
 private void OnBeforeResponse(RawContext context, SessionEventArgs e)
 {
     if (context == null)
     {
         return;
     }
     BeforeResponse?.Invoke(context, e);
 }
Ejemplo n.º 4
0
        /// <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
        }
Ejemplo n.º 5
0
 /// <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);
 }
Ejemplo n.º 6
0
        //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();
            }
        }
Ejemplo n.º 7
0
        /// <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);
        }
Ejemplo n.º 8
0
 public void OnBeforeResponse(Session session)
 {
     BeforeResponse?.Invoke(session);
 }
Ejemplo n.º 9
0
        private int onBeforeResponse(long handle)
        {
            BeforeResponse?.Invoke(new Session(handle, new Request(handle), new Response(handle)));

            return(0);
        }
Ejemplo n.º 10
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);
        }
Ejemplo n.º 11
0
 private void onBeforeResponse(long handle)
 {
     BeforeResponse?.Invoke(new Session(handle, new Request(handle), new Response(handle)));
 }
Ejemplo n.º 12
0
        /// <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();
            }
        }
Ejemplo n.º 14
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);
        }