Example #1
0
 internal void InvokeBeforeRequest(Context ctx) => BeforeRequest?.Invoke(ctx);
Example #2
0
 private void OnBeforeRequest(IHttpRequest request)
 {
     Logger.BeforeRequest(request);
     BeforeRequest.Raise(this, new BeforeRequestEventArgs(request));
 }
Example #3
0
        /// <summary>
        /// This is the core request handler method for a particular connection from client
        /// </summary>
        /// <param name="client"></param>
        /// <param name="httpCmd"></param>
        /// <param name="clientStream"></param>
        /// <param name="clientStreamReader"></param>
        /// <param name="clientStreamWriter"></param>
        /// <param name="httpsHostName"></param>
        /// <returns></returns>
        private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
                                                    CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName, ProxyEndPoint endPoint, List <HttpHeader> connectHeaders, ExternalProxy customUpStreamHttpProxy = null, ExternalProxy customUpStreamHttpsProxy = null)
        {
            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))
                {
                    Dispose(clientStream, clientStreamReader, clientStreamWriter, null);
                    break;
                }

                var args = new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse);
                args.ProxyClient.TcpClient     = client;
                args.WebSession.ConnectHeaders = connectHeaders;

                args.WebSession.ProcessId = new Lazy <int>(() =>
                {
                    var remoteEndPoint = (IPEndPoint)args.ProxyClient.TcpClient.Client.RemoteEndPoint;

                    //If client is localhost get the process id
                    if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
                    {
                        return(NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled));
                    }

                    //can't access process Id of remote request from remote machine
                    return(-1);
                });
                try
                {
                    //break up the line into three components (method, remote URL & Http Version)
                    var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);

                    var httpMethod = httpCmdSplit[0];

                    //find the request HTTP version
                    Version httpVersion = new Version(1, 1);
                    if (httpCmdSplit.Length == 3)
                    {
                        var httpVersionString = httpCmdSplit[2].ToLower().Trim();

                        if (httpVersionString == "http/1.0")
                        {
                            httpVersion = new Version(1, 0);
                        }
                    }


                    //Read the request headers in to unique and non-unique header collections
                    string tmpLine;
                    while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
                    {
                        var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);

                        var newHeader = new HttpHeader(header[0], header[1]);

                        //if header exist in non-unique header collection add it there
                        if (args.WebSession.Request.NonUniqueRequestHeaders.ContainsKey(newHeader.Name))
                        {
                            args.WebSession.Request.NonUniqueRequestHeaders[newHeader.Name].Add(newHeader);
                        }
                        //if header is alread in unique header collection then move both to non-unique collection
                        else if (args.WebSession.Request.RequestHeaders.ContainsKey(newHeader.Name))
                        {
                            var existing = args.WebSession.Request.RequestHeaders[newHeader.Name];

                            var nonUniqueHeaders = new List <HttpHeader>();

                            nonUniqueHeaders.Add(existing);
                            nonUniqueHeaders.Add(newHeader);

                            args.WebSession.Request.NonUniqueRequestHeaders.Add(newHeader.Name, nonUniqueHeaders);
                            args.WebSession.Request.RequestHeaders.Remove(newHeader.Name);
                        }
                        //add to unique header collection
                        else
                        {
                            args.WebSession.Request.RequestHeaders.Add(newHeader.Name, newHeader);
                        }
                    }

                    var httpRemoteUri = new Uri(httpsHostName == null ? httpCmdSplit[1]
                        : (string.Concat("https://", args.WebSession.Request.Host == null ?
                                         httpsHostName : args.WebSession.Request.Host, httpCmdSplit[1])));

                    args.WebSession.Request.RequestUri = httpRemoteUri;

                    args.WebSession.Request.Method      = httpMethod.Trim().ToUpper();
                    args.WebSession.Request.HttpVersion = httpVersion;
                    args.ProxyClient.ClientStream       = clientStream;
                    args.ProxyClient.ClientStreamReader = clientStreamReader;
                    args.ProxyClient.ClientStreamWriter = clientStreamWriter;

                    if (httpsHostName == null && (await CheckAuthorization(clientStreamWriter, args.WebSession.Request.RequestHeaders.Values) == false))
                    {
                        Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
                        break;
                    }

                    PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession);
                    args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority;

                    //If user requested interception do it
                    if (BeforeRequest != null)
                    {
                        Delegate[] invocationList = BeforeRequest.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);
                    }

                    //if upgrading to websocket then relay the requet without reading the contents
                    if (args.WebSession.Request.UpgradeToWebSocket)
                    {
                        await TcpHelper.SendRaw(BUFFER_SIZE, ConnectionTimeOutSeconds, httpRemoteUri.Host, httpRemoteUri.Port,
                                                httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps,
                                                SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate),
                                                new LocalCertificateSelectionCallback(SelectClientCertificate),
                                                clientStream, tcpConnectionFactory, UpStreamEndPoint);

                        Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
                        break;
                    }

                    //construct the web request that we are going to issue on behalf of the client.
                    await HandleHttpSessionRequestInternal(connection, args, customUpStreamHttpProxy, customUpStreamHttpsProxy, false).ConfigureAwait(false);


                    if (args.WebSession.Request.CancelRequest)
                    {
                        break;
                    }

                    //if connection is closing exit
                    if (args.WebSession.Response.ResponseKeepAlive == false)
                    {
                        break;
                    }

                    // read the next request
                    httpCmd = await clientStreamReader.ReadLineAsync();
                }
                catch (Exception e)
                {
                    ExceptionFunc(new ProxyHttpException("Error occured whilst handling session request", e, args));
                    Dispose(clientStream, clientStreamReader, clientStreamWriter, args);
                    break;
                }
            }

            if (connection != null)
            {
                //dispose
                connection.Dispose();
            }
        }
        /// <summary>
        /// This is the core request handler method for a particular connection from client
        /// </summary>
        /// <param name="client"></param>
        /// <param name="httpCmd"></param>
        /// <param name="clientStream"></param>
        /// <param name="clientStreamReader"></param>
        /// <param name="clientStreamWriter"></param>
        /// <param name="isHttps"></param>
        /// <returns></returns>
        private async Task HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
                                                    CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, bool isHttps)
        {
            TcpConnectionCache 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))
                {
                    Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
                    break;
                }

                var args = new SessionEventArgs(BUFFER_SIZE, HandleHttpSessionResponse);
                args.ProxyClient.TcpClient = client;

                try
                {
                    //break up the line into three components (method, remote URL & Http Version)
                    var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);

                    var httpMethod = httpCmdSplit[0];

                    //find the request HTTP version
                    Version version = new Version(1, 1);
                    if (httpCmdSplit.Length == 3)
                    {
                        var httpVersion = httpCmdSplit[2].ToLower().Trim();

                        if (httpVersion == "http/1.0")
                        {
                            version = new Version(1, 0);
                        }
                    }

                    args.WebSession.Request.RequestHeaders = new List <HttpHeader>();

                    //Read the request headers
                    string tmpLine;
                    while (!string.IsNullOrEmpty(tmpLine = await clientStreamReader.ReadLineAsync()))
                    {
                        var header = tmpLine.Split(ProxyConstants.ColonSplit, 2);
                        args.WebSession.Request.RequestHeaders.Add(new HttpHeader(header[0], header[1]));
                    }

                    var httpRemoteUri = new Uri(!isHttps ? httpCmdSplit[1] : (string.Concat("https://", args.WebSession.Request.Host, httpCmdSplit[1])));
#if DEBUG
                    //Just ignore local requests while Debugging
                    //Its annoying
                    if (httpRemoteUri.Host.Contains("localhost"))
                    {
                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, null);
                        break;
                    }
#endif
                    args.WebSession.Request.RequestUri = httpRemoteUri;

                    args.WebSession.Request.Method      = httpMethod;
                    args.WebSession.Request.HttpVersion = version;
                    args.ProxyClient.ClientStream       = clientStream;
                    args.ProxyClient.ClientStreamReader = clientStreamReader;
                    args.ProxyClient.ClientStreamWriter = clientStreamWriter;

                    PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession);
                    args.WebSession.Request.Host = args.WebSession.Request.RequestUri.Authority;

                    //If user requested interception do it
                    if (BeforeRequest != null)
                    {
                        Delegate[] invocationList = BeforeRequest.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);
                    }

                    //if upgrading to websocket then relay the requet without reading the contents
                    if (args.WebSession.Request.UpgradeToWebSocket)
                    {
                        await TcpHelper.SendRaw(clientStream, httpCmd, args.WebSession.Request.RequestHeaders,
                                                httpRemoteUri.Host, httpRemoteUri.Port, args.IsHttps, SupportedSslProtocols);

                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                        return;
                    }

                    //construct the web request that we are going to issue on behalf of the client.
                    connection = await tcpConnectionCacheManager.GetClient(args.WebSession.Request.RequestUri.Host, args.WebSession.Request.RequestUri.Port, args.IsHttps, version,
                                                                           UpStreamHttpProxy, UpStreamHttpsProxy, BUFFER_SIZE, SupportedSslProtocols, new RemoteCertificateValidationCallback(ValidateServerCertificate),
                                                                           new LocalCertificateSelectionCallback(SelectClientCertificate));

                    args.WebSession.Request.RequestLocked = true;

                    //If request was cancelled by user then dispose the client
                    if (args.WebSession.Request.CancelRequest)
                    {
                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                        break;
                    }

                    //if expect continue is enabled then send the headers first
                    //and see if server would return 100 conitinue
                    if (args.WebSession.Request.ExpectContinue)
                    {
                        args.WebSession.SetConnection(connection);
                        await args.WebSession.SendRequest(Enable100ContinueBehaviour);
                    }

                    //If 100 continue was the response inform that to the client
                    if (Enable100ContinueBehaviour)
                    {
                        if (args.WebSession.Request.Is100Continue)
                        {
                            await WriteResponseStatus(args.WebSession.Response.HttpVersion, "100",
                                                      "Continue", args.ProxyClient.ClientStreamWriter);

                            await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
                        }
                        else if (args.WebSession.Request.ExpectationFailed)
                        {
                            await WriteResponseStatus(args.WebSession.Response.HttpVersion, "417",
                                                      "Expectation Failed", args.ProxyClient.ClientStreamWriter);

                            await args.ProxyClient.ClientStreamWriter.WriteLineAsync();
                        }
                    }

                    //If expect continue is not enabled then set the connectio and send request headers
                    if (!args.WebSession.Request.ExpectContinue)
                    {
                        args.WebSession.SetConnection(connection);
                        await args.WebSession.SendRequest(Enable100ContinueBehaviour);
                    }

                    //If request was modified by user
                    if (args.WebSession.Request.RequestBodyRead)
                    {
                        if (args.WebSession.Request.ContentEncoding != null)
                        {
                            args.WebSession.Request.RequestBody = await GetCompressedResponseBody(args.WebSession.Request.ContentEncoding, args.WebSession.Request.RequestBody);
                        }
                        //chunked send is not supported as of now
                        args.WebSession.Request.ContentLength = args.WebSession.Request.RequestBody.Length;

                        var newStream = args.WebSession.ServerConnection.Stream;
                        await newStream.WriteAsync(args.WebSession.Request.RequestBody, 0, args.WebSession.Request.RequestBody.Length);
                    }
                    else
                    {
                        if (!args.WebSession.Request.ExpectationFailed)
                        {
                            //If its a post/put request, then read the client html body and send it to server
                            if (httpMethod.ToUpper() == "POST" || httpMethod.ToUpper() == "PUT")
                            {
                                await SendClientRequestBody(args);
                            }
                        }
                    }

                    //If not expectation failed response was returned by server then parse response
                    if (!args.WebSession.Request.ExpectationFailed)
                    {
                        await HandleHttpSessionResponse(args);
                    }

                    //if connection is closing exit
                    if (args.WebSession.Response.ResponseKeepAlive == false)
                    {
                        Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                        return;
                    }

                    //send the tcp connection to server back to connection cache for reuse
                    await tcpConnectionCacheManager.ReleaseClient(connection);

                    // read the next request
                    httpCmd = await clientStreamReader.ReadLineAsync();
                }
                catch
                {
                    Dispose(client, clientStream, clientStreamReader, clientStreamWriter, args);
                    break;
                }
            }
        }
Example #5
0
        async Task <string> IRCallbacks.ReadConsole(IReadOnlyList <IRContext> contexts, string prompt, int len, bool addToHistory, CancellationToken ct)
        {
            await TaskUtilities.SwitchToBackgroundThread();

            if (!_initializedTcs.Task.IsCompleted)
            {
                await AfterHostStarted(_startupInfo);
            }

            var callback = _callback;

            if (!addToHistory && callback != null)
            {
                using (_readUserInputReentrancyCounter.Increment()) {
                    return(await callback.ReadUserInput(prompt, len, ct));
                }
            }

            var currentRequest = Interlocked.Exchange(ref _currentRequestSource, null);

            _contexts = contexts;
            Prompt    = GetDefaultPrompt(prompt);
            MaxLength = len;

            IsProcessing = contexts.Count != 1;
            var requestEventArgs = new RBeforeRequestEventArgs(contexts, Prompt, len, addToHistory);

            BeforeRequest?.Invoke(this, requestEventArgs);

            OnMutated();

            currentRequest?.CompleteResponse();

            string consoleInput = null;

            do
            {
                ct.ThrowIfCancellationRequested();
                try {
                    consoleInput = await ReadNextRequest(Prompt, len, ct);
                } catch (OperationCanceledException ex) when(!(ex is CancelAllException))
                {
                    // If request was canceled through means other than our token, it indicates the refusal of
                    // that requestor to respond to that particular prompt, so move on to the next requestor.
                    // If it was canceled through the token, then host itself is shutting down, and cancellation
                    // will be propagated on the entry to next iteration of this loop.
                    //
                    // If request was canceled due to CancelAllAsync, then we should not continue to process this
                    // ReadConsole call at all. Under normal conditions, ct will also be marked as canceled; but
                    // there is a potential for a race condition where we get a cancellation exception here, but
                    // ct is not marked as canceled yet. Explicitly checking for CancelAllException handles this.
                }
            } while (consoleInput == null);


            // We only want to fire 'directory changed' events when it is initiated by the user
            _processingChangeDirectoryCommand = consoleInput.StartsWithOrdinal("setwd");

            consoleInput = consoleInput.EnsureLineBreak();
            AfterRequest?.Invoke(this, new RAfterRequestEventArgs(contexts, Prompt, consoleInput, addToHistory, currentRequest?.IsVisible ?? false));
            IsProcessing = true;

            return(consoleInput);
        }
Example #6
0
 public Task <IRSessionInteraction> BeginInteractionAsync(bool isVisible = true, CancellationToken cancellationToken = default(CancellationToken))
 {
     _inter = new RSessionInteractionMock();
     BeforeRequest?.Invoke(this, new RRequestEventArgs(_inter.Contexts, Prompt, 4096, true));
     return(Task.FromResult(_inter));
 }
        /// <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);
        }
Example #8
0
 private int onBeforeRequest(long handle)
 {
     return((int)BeforeRequest?.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="httpCmd"></param>
        /// <param name="clientStream"></param>
        /// <param name="clientStreamReader"></param>
        /// <param name="clientStreamWriter"></param>
        /// <param name="httpsHostName"></param>
        /// <param name="endPoint"></param>
        /// <param name="connectHeaders"></param>
        /// <returns></returns>
        private async Task <bool> HandleHttpSessionRequest(TcpClient client, string httpCmd, Stream clientStream,
                                                           CustomBinaryReader clientStreamReader, StreamWriter clientStreamWriter, string httpsHostName,
                                                           ProxyEndPoint endPoint, List <HttpHeader> connectHeaders)
        {
            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, HandleHttpSessionResponse)
                {
                    ProxyClient = { TcpClient = client },
                    WebSession  = { ConnectHeaders = connectHeaders }
                };

                args.WebSession.ProcessId = new Lazy <int>(() =>
                {
                    var remoteEndPoint = (IPEndPoint)args.ProxyClient.TcpClient.Client.RemoteEndPoint;

                    //If client is localhost get the process id
                    if (NetworkHelper.IsLocalIpAddress(remoteEndPoint.Address))
                    {
                        return(NetworkHelper.GetProcessIdFromPort(remoteEndPoint.Port, endPoint.IpV6Enabled));
                    }

                    //can't access process Id of remote request from remote machine
                    return(-1);
                });

                try
                {
                    //break up the line into three components (method, remote URL & Http Version)
                    var httpCmdSplit = httpCmd.Split(ProxyConstants.SpaceSplit, 3);

                    var httpMethod = httpCmdSplit[0];

                    //find the request HTTP version
                    var httpVersion = HttpHeader.Version11;
                    if (httpCmdSplit.Length == 3)
                    {
                        var httpVersionString = httpCmdSplit[2].Trim();

                        if (string.Equals(httpVersionString, "HTTP/1.0", StringComparison.OrdinalIgnoreCase))
                        {
                            httpVersion = HttpHeader.Version10;
                        }
                    }

                    //Read the request headers in to unique and non-unique header collections
                    await HeaderParser.ReadHeaders(clientStreamReader, args.WebSession.Request.NonUniqueRequestHeaders, args.WebSession.Request.RequestHeaders);

                    var httpRemoteUri = new Uri(httpsHostName == null
                        ? httpCmdSplit[1]
                        : string.Concat("https://", args.WebSession.Request.Host ?? httpsHostName, httpCmdSplit[1]));

                    args.WebSession.Request.RequestUri = httpRemoteUri;

                    args.WebSession.Request.Method      = httpMethod.Trim().ToUpper();
                    args.WebSession.Request.HttpVersion = httpVersion;
                    args.ProxyClient.ClientStream       = clientStream;
                    args.ProxyClient.ClientStreamReader = clientStreamReader;
                    args.ProxyClient.ClientStreamWriter = clientStreamWriter;

                    if (httpsHostName == null &&
                        await CheckAuthorization(clientStreamWriter,
                                                 args.WebSession.Request.RequestHeaders.Values) == false)
                    {
                        args.Dispose();
                        break;
                    }

                    PrepareRequestHeaders(args.WebSession.Request.RequestHeaders, args.WebSession);
                    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 (EnableWinAuth &&
                        !RunTime.IsRunningOnMono &&
                        args.WebSession.Request.HasBody)
                    {
                        await args.GetRequestBody();
                    }

                    //If user requested interception do it
                    if (BeforeRequest != null)
                    {
                        await BeforeRequest.InvokeParallelAsync(this, args);
                    }

                    //if upgrading to websocket then relay the requet without reading the contents
                    if (args.WebSession.Request.UpgradeToWebSocket)
                    {
                        await TcpHelper.SendRaw(this,
                                                httpRemoteUri.Host, httpRemoteUri.Port,
                                                httpCmd, httpVersion, args.WebSession.Request.RequestHeaders, args.IsHttps,
                                                clientStream, tcpConnectionFactory, connection);

                        args.Dispose();
                        break;
                    }

                    if (connection == null)
                    {
                        connection = await GetServerConnection(args);
                    }

                    //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 (args.WebSession.Request.CancelRequest)
                    {
                        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);
        }
Example #10
0
 private void onBeforeRequest(long handle)
 {
     BeforeRequest?.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();
            }
        }
Example #12
0
 public virtual void OnBeforeRequest(WebEventArgs e)
 {
     BeforeRequest?.Invoke(e.Context, e);
 }
Example #13
0
 protected virtual void OnBeforeRequest(ClientRequestEventArgs e)
 {
     BeforeRequest?.Invoke(this, e);
 }
Example #14
0
 /// <summary>
 /// Invocator for BeforeRequest event.
 /// </summary>
 /// <param name="sender"></param>
 /// <param name="e"></param>
 protected virtual void OnBeforeRequest(object sender, SessionEventArgs e)
 {
     BeforeRequest?.Invoke(sender, e);
 }