Skip to content

Commit

Permalink
Merge pull request #36971 from rzikm/34263-Document-how-to-manually-c…
Browse files Browse the repository at this point in the history
…ontrol-SNI

Document how to manipulate SNI with HttpClient
  • Loading branch information
rzikm authored Sep 7, 2023
2 parents e5d033b + 67275d4 commit 77c8db3
Show file tree
Hide file tree
Showing 2 changed files with 76 additions and 0 deletions.
73 changes: 73 additions & 0 deletions docs/core/extensions/httpclient-sni.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
---
title: Customize SNI in HTTP requests
description: Learn How to control Server Name Indication TLS extension in HTTP requests.
author: rzikm
ms.author: radekzikmund
ms.date: 9/5/2023
---

# Customize SNI in HTTP requests

When negotiating an HTTPS connection, a TLS connection needs to be established first. As part of the TLS handshake, the client sends the domain name of the server it's connecting to in one of the TLS extensions. When hosting multiple (virtual) servers on the same machine, this feature of the TLS protocol allows clients to distinguish which of these servers they're connecting to and to configure TLS settings, such as the server certificate, accordingly.

When making an HTTP request using `HttpClient`, the implementation automatically selects a value for the server name indication (SNI) extension based on the URL the client is connecting to. For scenarios that require more manual control of the extension, you can use one of the following approaches.

## Host header

Host HTTP header performs a similar function as the SNI extension in TLS. It lets the target server distinguish among requests for multiple host names on a single IP address. `HttpClient` automatically fills in the Host header using the request URI. However, you can also set its value manually, and `HttpClient` will also use the new value in the SNI extension. You can use either `HttpRequestMessage.Headers.Host` or `HttpClient.DefaultRequestHeaders.Host` to achieve this effect.

```csharp
using HttpClient client = new();

client.DefaultRequestHeaders.Host = "www.microsoft.com";

using var response = await client.GetAsync("https://127.0.0.1:5001/");

System.Console.WriteLine(response);
```

> [!NOTE]
> This method doesn't allow you to avoid sending SNI altogether when connecting to a URL with a hostname. If the header is set to empty string, `HttpClient` uses the hostname from the URL instead.
> [!NOTE]
> Customizing the Host header affects server certificate validation. By default, client will expect the server certificate to match the hostname in the Host header.
## Manual SslStream authentication via ConnectCallback

A more complicated, but also more powerful, option is to use the `SocketsHttpHandler.ConnectCallback`. Since .NET 7, it is possible to return an authenticated `SslStream` and thus customize how the TLS connection is established. Inside the callback, arbitrary `SslClientAuthenticationOptions` options can be used to perform client-side authentication.

```csharp
var handler = new SocketsHttpHandler
{
ConnectCallback = async (context, cancellationToken) =>
{
var socket = new Socket(SocketType.Stream, ProtocolType.Tcp) { NoDelay = true };
try
{
await socket.ConnectAsync(context.DnsEndPoint, cancellationToken);

var sslStream = new SslStream(new NetworkStream(socket, ownsSocket: true));

// When using HTTP/2, you must also keep in mind to set options like ApplicationProtocols
await sslStream.AuthenticateAsClientAsync(new SslClientAuthenticationOptions
{
TargetHost = context.DnsEndPoint.Host,

}, cancellationToken);

return sslStream;
}
catch
{
socket.Dispose();
throw;
}
}
};

using HttpClient client = new(handler);

using var response = await client.GetAsync("https://www.microsoft.com");

System.Console.WriteLine(response);
```
3 changes: 3 additions & 0 deletions docs/fundamentals/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -833,6 +833,9 @@ items:
- name: Rate limit an HTTP handler
href: ../core/extensions/http-ratelimiter.md
displayName: networking,http,rate limit,rate limiting,rate limit http,rate limiting http,rate limit http handler,rate limiting http handler
- name: Customize SNI in HTTP requests
href: ../core/extensions/httpclient-sni.md
displayName: networking,http,httpclient,sni,server name indication,server name indication http,server name indication httpclient,server name indication http client
- name: Sockets
items:
- name: Sockets support
Expand Down

0 comments on commit 77c8db3

Please sign in to comment.