HTTPClient is not reusing the SSL/TLS1.2 session and does handshake on every request

39 Views Asked by At

Like said on the web everywhere, created a private static read only HTTPClient which is being reused throughout the application, but SSL/TLS1.2 hand shake is happening on every request,due to which it takes almost 6secs to get response for each request I make. here is my code where I am trying to post a byte array to server in chunks. I am using dotnet6.

    private static readonly HttpClient client = new HttpClient(new HttpClientHandler
    {
        Credentials = new NetworkCredential("admin", "private"),
        ServerCertificateCustomValidationCallback = (message, cert, chain, errors) => 
        true,
        CheckCertificateRevocationList = true,
        PreAuthenticate = true,
        //UseCookies = true,
        //CookieContainer = cookieContainer,

    });

    private async void button1_Click(object sender, EventArgs e)
    {
        //System.Net.ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12| SecurityProtocolType.Tls11 | SecurityProtocolType.Tls| SecurityProtocolType.Tls13;

        try
        {
            string http = "https://";
            string fwupdate = "/fwup_install";
            string fwreset = "/fwup_reset";
            string ip = txt_ip.Text;
            byte[] Data = null;
            byte[] array1 = null;
            string filePath = "D:\\fw\\iol_mix_mp_https.fwu";
            Data = File.ReadAllBytes(TxT_SelectedFwuFile.Text);
            var url = http + ip + fwupdate;   

            HttpContent content = new StringContent("size=" + Data.Length);

            var response = await client.PostAsync(url, content);

            richTextBox1.Text = richTextBox1.Text + ("Step1 : " + response.StatusCode);

            int sendbytes = 0;
            int Var = 1024 * 32;
            int Len = Convert.ToInt32(Data.Length / Var);
            int Counter = 0;

            while (Data.Length > sendbytes)
            {

                //Thread.Sleep(1000);
                if (Counter < Len)
                {
                    array1 = new byte[Var];

                    for (int i = 0; i < array1.Length; i++)
                    {
                        if (sendbytes < Data.Length)
                        {
                            array1[i] = Data[sendbytes];
                            sendbytes++;
                        }
                        else
                        {
                            break;
                        }
                    }
                    Counter++;
                    // textBox1.Text = Counter.ToString();
                }
                else
                {

                    array1 = new byte[Data.Length - (Var * Len)];
                    //textBox1.Text += "Remaining Array Length = " + array1.Length.ToString();
                    for (int i = 0; i < array1.Length; i++)
                    {
                        array1[i] = Data[sendbytes];
                        sendbytes++;
                    }

                }

                ByteArrayContent Bytcontent = new ByteArrayContent(array1);
                Bytcontent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream");

                var response1 = await client.PostAsync(url, Bytcontent);
             
                richTextBox1.Text = richTextBox1.Text + (response1.StatusCode);

            }
        }
        catch (Exception ex)
        {
            richTextBox1.Text = richTextBox1.Text + ex.Message;
            richTextBox1.Text = richTextBox1.Text + ex.InnerException;

        }
    }

    private void button2_Click(object sender, EventArgs e)
    {
        OpenFileDialog ofd = new OpenFileDialog();

        ofd.Filter = "FWU file|*.fwu*";
        ofd.Title = "Load FWU file";
        if (ofd.ShowDialog() == DialogResult.OK)
        {
            TxT_SelectedFwuFile.Text = ofd.FileName;
        }
    }

    private void Form1_Load(object sender, EventArgs e)
    {
        TxT_SelectedFwuFile.ReadOnly = true;
    }
}
1

There are 1 best solutions below

3
Charlieface On

It's presumably using HTTP 1.1, which does not support multiple streams. This is not necessarily an issue by itself.

This is then compounded by the fact that you are not disposing the requests, so the HTTP handler thinks you still want to read from it, and therefore cannot use that connection. It therefore opens another one.

var response = await client.PostAsync(url, content);
// etc
var response1 = await client.PostAsync(url, Bytcontent);

Should have been

using (var response = await client.PostAsync(url, content))
{
    // do stuff with response
}

using (var response1 = await client.PostAsync(url, Bytcontent))
{
    // do stuff with response
}

Other points:

  • TLS should not take 6 seconds. It may be a poor connection to a CRL server, you should probably try to get the webserver to use OCSP Stapling so the CRL server is not needed.
  • Consider using Array.Copy instead of writing copy-loops.
  • Consider just passing a FileStream directly to a StreamContent, instead of copying into an array in the first place. Again, don't forget to dispose it.
  • Your ServerCertificateCustomValidationCallback is completely insecure, and you may as well not use TLS encryption at all for all the good it's doing. Why would you not want to verify the server certificate?
  • Do not use ServicePointManager to set the TLS version, allow the system to define the best protocol to use.