uHttpSharp icon indicating copy to clipboard operation
uHttpSharp copied to clipboard

sockets end in close_wait state after some time

Open GopinathSadasivam opened this issue 5 years ago • 2 comments

Looks like we are missing a time-out on stream reader in uhttpsharp\HttpClient.cs

When remote http client gets disconnected due to some network issues (or because of proxies in between not handling the keep-alive header properly), uHttpSharp library code fails to close the socket, this results in open sockets stuck in close_wait state. Am new to C# not sure how to add this time out.

  {
    try
    {
        await InitializeStream();

        while (_client.Connected)
        {
            // TODO : Configuration.
            var limitedStream = new NotFlushingStream(new LimitedStream(_stream));

            var request = await _requestProvider.Provide(new MyStreamReader(limitedStream)).ConfigureAwait(false);

            if (request != null)
            {
                UpdateLastOperationTime();

                var context = new HttpContext(request, _client.RemoteEndPoint);

                Logger.InfoFormat("{1} : Got request {0}", request.Uri, _client.RemoteEndPoint);


                await _requestHandler(context).ConfigureAwait(false);

                if (context.Response != null)
                {
                    var streamWriter = new StreamWriter(limitedStream) { AutoFlush = false };
                    streamWriter.NewLine = "\r\n";
                    await WriteResponse(context, streamWriter).ConfigureAwait(false);

                     //v----- connections seem to be stuck here, resulting in piling up of  sockets in close_wait state
                    await limitedStream.ExplicitFlushAsync().ConfigureAwait(false); 

                    if (!request.Headers.KeepAliveConnection() || context.Response.CloseConnection)
                    {
                        _client.Close();
                    }
                }

                UpdateLastOperationTime();
            }
            else
            {
                _client.Close();
            }
        }
    }
    catch (Exception e)
    {
        // Hate people who make bad calls.
        Logger.WarnException(string.Format("Error while serving : {0}", _remoteEndPoint), e);
        _client.Close();
    }

    Logger.InfoFormat("Lost Client {0}", _remoteEndPoint);
}

GopinathSadasivam avatar Jun 07 '19 07:06 GopinathSadasivam

private async void Process() { try { await InitializeStream();

            while (_client.Connected)
            {
                // TODO : Configuration.
                var limitedStream = new NotFlushingStream(new LimitedStream(_stream));

                //TimeOut Code start

                TimeSpan timeout = TimeSpan.FromSeconds(10);

                Logger.InfoFormat("{1} : Timeout set to {0} seconds", timeout.Seconds.ToString(), DateTime.Now.ToString());
                IHttpRequest request = null;

//v----- connections seem to be stuck here
//var request = await _requestProvider.Provide(new MyStreamReader(limitedStream)).ConfigureAwait(false);

                using (var timeoutCancellationTokenSource = new CancellationTokenSource())
                {
                    
                    var task = _requestProvider.Provide(new MyStreamReader(limitedStream));

                    Logger.InfoFormat("{1} : Fetching 'task' with the task.Id.ToString() = {0}", task.Id.ToString(), DateTime.Now.ToString());

                    var completedTask = await Task.WhenAny(task, Task.Delay(timeout, timeoutCancellationTokenSource.Token)); 

                    Logger.InfoFormat("{1} : Fetched 'completedTask' with the completedTask.Id.ToString() = {0}", completedTask.Id.ToString(), DateTime.Now.ToString());

                    if (completedTask == task)
                    {
                        timeoutCancellationTokenSource.Cancel();
                        request = task.GetAwaiter().GetResult();
                        
                    }
                    else
                    {
                        Logger.InfoFormat("{0} : TimeOut Exception occured.", DateTime.Now.ToString());
                        
                        throw new TimeoutException();
                    }
                }
               

                if (request != null)
                {
                    UpdateLastOperationTime();

                    var context = new HttpContext(request, _client.RemoteEndPoint);

                    Logger.InfoFormat("{1} : Got request {0}", request.Uri, _client.RemoteEndPoint);


                    await _requestHandler(context).ConfigureAwait(false);

                    if (context.Response != null)
                    {
                        var streamWriter = new StreamWriter(limitedStream) { AutoFlush = false };
                        streamWriter.NewLine = "\r\n";
                        await WriteResponse(context, streamWriter).ConfigureAwait(false);
                        await limitedStream.ExplicitFlushAsync().ConfigureAwait(false);

                        if (!request.Headers.KeepAliveConnection() || context.Response.CloseConnection)
                        {
                            
                            _client.Close();
                        }
                    }

                    UpdateLastOperationTime();
                }
                else
                {
                    
                    _client.Close();
                }
            }
        }
        catch (Exception e)
        {
            // Hate people who make bad calls.
            Logger.WarnException(string.Format("Error while serving : {0}", _remoteEndPoint), e);
            _client.Close();
        }

        Logger.InfoFormat("Lost Client {0}", _remoteEndPoint);
    }

We were also facing the same issue where the logic got stuck at the 'request' line indicated above. So we introduced a timeout value of 10 seconds to get out but it has caused another exception as given below:

[2] [Error] [Default]: An unobserved task exception has occurred in the application: Exception: System.IO.IOException: Unable to read data from the transport connection: A blocking operation was interrupted by a call to WSACancelBlockingCall. ---> System.Net.Sockets.SocketException: A blocking operation was interrupted by a call to WSACancelBlockingCall

Does anyone has a solution for this please?

Kind Regards

TahirAwan121 avatar Jun 18 '19 07:06 TahirAwan121

Not a solution but I think this library is poorly designed, trying close http/https ports is a struggle/impossible. Dispose etc. is simply missing.

smaudet avatar Feb 25 '24 21:02 smaudet