titanium-web-proxy icon indicating copy to clipboard operation
titanium-web-proxy copied to clipboard

Error ocurred whilst handling session request: Server connection was closed.

Open DiegoCarrilho opened this issue 4 years ago • 2 comments

App used: BasicClient from this Github.

I am having problems with persistent connections that remain inactive for more than a minute. When the web client makes a new request for this persistent connection and the proxy detects that the connection is not open to the server and trigger Retry to create another one and then send the request, it crashes.

I also tried to activate the connection pool but it returns the same error:

SessionEventArgs.TimeLine: Session Created: 04/06/2020 15:52:44 Request Received: 04/06/2020 15:52:44 Connection Ready: 04/06/2020 15:52:44 Request Sent: 04/06/2020 15:52:44 Error ocurred whilst handling session request: Server connection was closed.

This code is Original from this Git, and following the TimeLine, all steps are working perfectly.

private async Task<RetryResult> handleHttpSessionRequest(SessionEventArgs args,
          TcpServerConnection? serverConnection, SslApplicationProtocol sslApplicationProtocol,
          CancellationToken cancellationToken, CancellationTokenSource cancellationTokenSource)
        {
            args.HttpClient.Request.Locked = true;

            // do not cache server connections for WebSockets
            bool noCache = args.HttpClient.Request.UpgradeToWebSocket;

            if (noCache)
            {
                serverConnection = null;
            }

            // a connection generator task with captured parameters via closure.
            Func<Task<TcpServerConnection>> generator = () =>
                tcpConnectionFactory.GetServerConnection(this,
                    args,
                    false,
                    sslApplicationProtocol,
                    noCache,
                    cancellationToken);

            // for connection pool, retry fails until cache is exhausted.   
            return await retryPolicy<ServerConnectionException>().ExecuteAsync(async connection =>
            {
                // set the connection and send request headers
                args.HttpClient.SetConnection(connection);

                args.TimeLine["Connection Ready"] = DateTime.UtcNow;

                if (args.HttpClient.Request.UpgradeToWebSocket)
                {
                    // connectRequest can be null for SOCKS connection
                    if (args.HttpClient.ConnectRequest != null)
                    {
                        args.HttpClient.ConnectRequest!.TunnelType = TunnelType.Websocket;
                    }

                    // if upgrading to websocket then relay the request without reading the contents
                    await handleWebSocketUpgrade(args, args.ClientStream, connection, cancellationTokenSource, cancellationToken);
                    return false;
                }

                // construct the web request that we are going to issue on behalf of the client.
                await handleHttpSessionRequest(args);
                return true;

            }, generator, serverConnection);
        }

        private async Task handleHttpSessionRequest(SessionEventArgs args)
        {
            var cancellationToken = args.CancellationTokenSource.Token;
            var request = args.HttpClient.Request;

            var body = request.CompressBodyAndUpdateContentLength();

            await args.HttpClient.SendRequest(Enable100ContinueBehaviour, args.IsTransparent,
                cancellationToken);

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

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

                await args.ClearResponse(cancellationToken);
            }

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

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

            // parse and send response
            await handleHttpSessionResponse(args);
        }

Edit: The handler is not detecting the connection is closed, i did a test for check purposes only, and the error is "fixed":

private async Task handleHttpSessionRequest

                            if (args.HttpClient.Request.RequestUri.Host.Contains("pdc"))
                            {
                                connection = null;
                                Console.WriteLine("Connection set to null");
                            }
                            var result = await handleHttpSessionRequest(args, connection,
                                clientStream.Connection.NegotiatedApplicationProtocol,
                                  cancellationToken, cancellationTokenSource);

Now we have to understand why the handler can't figure out the connection is closed

DiegoCarrilho avatar Jun 04 '20 16:06 DiegoCarrilho

I have added another flag to Connection problem, and now i can detect when the connection is closed in all my cases, BUT...

Seems the Request is sent with the same connection what i have Released with: await tcpConnectionFactory.Release(connection, true); Like not waiting for the release.

On next request a New Tunel is fired and all works again while connection dont sleep for a minute.

                            if (connection != null)
                            {
                                var socket = connection.TcpSocket;
                                bool part1 = socket.Poll(1000, SelectMode.SelectRead);
                                bool part2 = socket.Available == 0;
                                bool part3 = socket.Connected;

                                if (part1 && part2 && part3)
                                {
                                    //connection is closed
                                    await tcpConnectionFactory.Release(connection, true);
                                    connection = null;
                                }
                            }

DiegoCarrilho avatar Jun 04 '20 19:06 DiegoCarrilho

OK, restored all code to original and only changed this:

                            if (args.HttpClient.Request.RequestUri.Host.Contains("pdc"))
                            {
                                connection = null;
                                Console.WriteLine("Connection set to null");
                            }
                            var result = await handleHttpSessionRequest(args, connection,
                                clientStream.Connection.NegotiatedApplicationProtocol,
                                  cancellationToken, cancellationTokenSource);

Is fully working now, but i could not figure out what's happen here...

DiegoCarrilho avatar Jun 04 '20 20:06 DiegoCarrilho