Skip to content

Instantly share code, notes, and snippets.

@antonfirsov
Last active September 15, 2020 17:57
Show Gist options
  • Save antonfirsov/8be6714110dc525ac48b7e580c4c9b13 to your computer and use it in GitHub Desktop.
Save antonfirsov/8be6714110dc525ac48b7e580c4c9b13 to your computer and use it in GitHub Desktop.

Proposed API

I propose to expose a public safe handle type with methods to set and query custom options. These methods would be a direct wrappers around WinHttpSetOption and WinHttpQueryOption, similarly to the concept of Socket.SetRawSocketOption.

public class SafeWinHttpHandle : Microsoft.Win32.SafeHandles.SafeHandleZeroOrMinusOneIsInvalid
{
    public uint GetWinHttpOption(uint option); 
    public void SetWinHttpOption(uint option, uint optionData);
   
    public void GetWinHttpOption(uint option, System.Span<byte> optionData);
    public void SetWinHttpOption(uint option, System.ReadOnlySpan<byte> optionData);
}

Since there is no control on lifecycle of the Session and Request handles, let's expose callbacks for which get invoked by WinHttpHandler internally.

public class WinHttpHandler : System.Net.Http.HttpMessageHandler
{
    public Action<SafeWinHttpHandle> ConfigureWinHttpSession { get; set; }
    public Action<SafeWinHttpHandle> ConfigureWinHttpRequest { get; set; }
}

Usage Example

struct tcp_keepalive
{
    public uint onoff;
    public uint keepalivetime;
    public uint keepaliveinterval;

    public override string ToString() => $"onoff:{onoff}, keepalivetime:{keepalivetime}, keepaliveinterval:{keepaliveinterval}";
};


const uint WINHTTP_OPTION_TCP_KEEPALIVE = 152;

public async Task SendRequestAsync()
{
    var handler = new WinHttpHandler
    {
        ConfigureWinHttpSession = h =>
        {
            tcp_keepalive[] readBuffer = new tcp_keepalive[1];
            h.GetWinHttpOption(WINHTTP_OPTION_TCP_KEEPALIVE, MemoryMarshal.AsBytes<tcp_keepalive>(readBuffer));
            Console.WriteLine($"WINHTTP_OPTION_TCP_KEEPALIVE before: {readBuffer[0]}");

            tcp_keepalive[] writeBuffer = new tcp_keepalive[]
            {
                new tcp_keepalive
                {
                    onoff = 1,
                    keepalivetime = 54321,
                    keepaliveinterval = 12345
                }
            };

            h.SetWinHttpOption(WINHTTP_OPTION_TCP_KEEPALIVE, MemoryMarshal.AsBytes<tcp_keepalive>(writeBuffer));

            h.GetWinHttpOption(WINHTTP_OPTION_TCP_KEEPALIVE, MemoryMarshal.AsBytes<tcp_keepalive>(readBuffer));
            Console.WriteLine($"WINHTTP_OPTION_TCP_KEEPALIVE before: {readBuffer[0]}");
        }
    };

    using var client = new HttpClient(handler);

    HttpResponseMessage response = await client.GetAsync(System.Net.Test.Common.Configuration.Http.RemoteEchoServer);
    string responseContent = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseContent);
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment