2

I want to make a https request BUT I want to initiate the connection first without sending the headers, even the GET /something/ HTTP/1.1 part. In other words I want to keep the connection ready and send everything as soon as I'm done.

I would have done this using a simple TCP/IP but I am having some issues with it at the moment.

I have tried SendChunked property but it sends only the data to be posted part chunked.

EDIT: To be more clear, what I want is:

  • Connect to the IP
  • Create SSL stream and AuthenticateAsClient
  • Wait for signal
  • Now send the web request

I have practiced waiting part so there is no need for a detailed explanation on it.

Community
  • 1
  • 1
gunakkoc
  • 1,069
  • 11
  • 30

2 Answers2

4

Short Answer

HttpWebRequest exposes no interface to allow pre-warming the connection prior to making the request.

Motivations

The reason someone would want to do this is because opening a TCP connection requires the initiator to wait one RTT before sending any data AND because HTTPS can require another RTT wait for performing the key exchange.

If you had an RTT from the client to the data center of 50 ms, then pre-warming the connection like this could drop the time required to send a simple request from 125 ms (2.5 RTTs to send the request, but not get the response) to 25 ms (0.5 RTTs to send the request, but not get the response). That's a reduction of 80%.

Alternative Solution

It is possible to achieve some of this reduction without writing your own Http client or using something other than HttpWebRequest, but in some cases it will have to wait.

The general technique is to rely on HTTP 1.1 resuse of connections (otherwise known as keep-alive) to send requests over existing TCP sockets / SSL sessions.

Prerequisite: You need to know the keep-alive interval of the web server you're calling. 60 seconds is pretty typical, but don't assume it, observe it with Fiddler or check the settings on the server and load balancer if you have access to them.

Let's say the keep-alive interval is 60 seconds. Create an HttpWebRequest and send one request to a "ping" url (even to a non-existant file... the connection should still not close on a 404 response, unless the server is misconfigured). At some interval less than the keep-alive interval (e.g. every 55 seconds) re-send a request again. When you want to send a request to the server you need to wait for a lock around the single HttpWebRequest that you have. When the lock is available without waiting then your request will typically go through immediately over the existing TCP socket and SSL session. In the rare case that the HttpWebRequest is locked you'll have to wait for up to 1 RTT for it to be released (on average you'll wait 0.5 RTTs, when it's locked, which is rare, since chances are that the request has already been sent on the wire and the response might be on the wire already as well).

This solution can't guarantee that the socket will be kept alive, but it will work pretty well.

Not to be snide, but if performance is this critical you probably shouldn't be using HTTP and/or worried about writing or using a non-HttpWebRequest client.

huntharo
  • 2,616
  • 21
  • 23
2

I took a look at your other post that was about performance between WebClient and TcpClient. I wanted to make sure that My answer to the this post was inline with what you had tried in the other post. The answer I wanted to give was to use TcpClient, but in your other post you had found that to be slow. I coded up the same test but I have different results. In my tests the TcpClient was on average 10ms faster than WebClient.

Using WebClient

Stopwatch sw = new Stopwatch();
sw.Start();

using (WebClient client = new WebClient())
{
    string response = client.DownloadString("https://www.google.com");
}

sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);

Using TcpClient

Stopwatch sw = new Stopwatch();
sw.Start();

using (TcpClient client = new TcpClient("www.google.com", 443))
using (SslStream stream = new SslStream(client.GetStream(), true))
{

    stream.AuthenticateAsClient("www.google.com");

    using (StreamWriter writer = new StreamWriter(stream))
    using (StreamReader reader = new StreamReader(stream))
    {
        writer.AutoFlush = true;

        // Wait for your signal here


        writer.Write("GET /\r\n");

        string response = reader.ReadToEnd();
    }
}

sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);

You will need to use TcpClient class for what you want to do. HttpWebRequest does not provide the level access you need to the underlying stream and connection.

Using TcpClient you can wait for your signal before sending the "GET/".

Mike Hixson
  • 5,071
  • 1
  • 19
  • 24