Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Networking breaking changes #43351

Merged
merged 8 commits into from
Nov 6, 2024
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/core/compatibility/9.0.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,12 @@ If you're migrating an app to .NET 9, the breaking changes listed here might aff

| Title | Type of change | Introduced version |
|-----------------------------------------------------------------------------------|---------------------|--------------------|
| [HttpClient metrics report `server.port` unconditionally](networking/9.0/server-port-attribute.md) | Behavioral change | Preview 7 |
| [HttpClientFactory logging redacts header values by default](networking/9.0/redact-headers.md) | Behavioral change | RC 1 |
| [HttpClientFactory uses SocketsHttpHandler as primary handler](networking/9.0/default-handler.md) | Behavioral change | Preview 6 |
| [HttpListenerRequest.UserAgent is nullable](networking/9.0/useragent-nullable.md) | Source incompatible | Preview 1 |
| [URI query redaction in HttpClient EventSource events](networking/9.0/query-redaction-events.md) | Behavioral change | Preview 7 |
| [URI query redaction in IHttpClientFactory logs](networking/9.0/query-redaction-logs.md) | Behavioral change | Preview 7 |

## SDK and MSBuild

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The following table lists the custom diagnostic IDs and their corresponding warn

| Diagnostic ID | Description | Severity |
| - | - |
| [SYSLIB0013](../../../../fundamentals/syslib-diagnostics/syslib0013.md) | <xref:System.Uri.EscapeUriString(System.String)?displayProperty=nameWithType> can corrupt the Uri string in some cases. Consider using <xref:System.Uri.EscapeDataString(System.String)?displayProperty=nameWithType> for query string components instead. | Warning |
| [SYSLIB0013](../../../../fundamentals/syslib-diagnostics/syslib0013.md) | <xref:System.Uri.EscapeUriString(System.String)?displayProperty=nameWithType> can corrupt the URI string in some cases. Consider using <xref:System.Uri.EscapeDataString(System.String)?displayProperty=nameWithType> for query string components instead. | Warning |
| [SYSLIB0014](../../../../fundamentals/syslib-diagnostics/syslib0014.md) | <xref:System.Net.WebRequest>, <xref:System.Net.HttpWebRequest>, <xref:System.Net.ServicePoint>, and <xref:System.Net.WebClient> are obsolete. Use <xref:System.Net.Http.HttpClient> instead. | Warning |
| [SYSLIB0015](../../../../fundamentals/syslib-diagnostics/syslib0015.md) | <xref:System.Runtime.CompilerServices.DisablePrivateReflectionAttribute> has no effect in .NET 6+. | Warning |
| [SYSLIB0016](../../../../fundamentals/syslib-diagnostics/syslib0016.md) | Use the <xref:System.Drawing.Graphics.GetContextInfo%2A?displayProperty=nameWithType> overloads that accept arguments for better performance and fewer allocations. | Warning |
Expand Down
95 changes: 95 additions & 0 deletions docs/core/compatibility/networking/9.0/default-handler.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
---
title: "HttpClientFactory uses SocketsHttpHandler as primary handler"
description: Learn about the breaking change in networking in .NET 9 where HttpClientFactory now uses SocketsHttpHandler as the default primary handler.
ms.date: 11/5/2024
---

# HttpClientFactory uses SocketsHttpHandler as primary handler

`HttpClientFactory` allows you to configure an <xref:System.Net.Http.HttpMessageHandler> pipeline for named and typed <xref:System.Net.Http.HttpClient> objects. The inner-most handler, or the one that actually sends the request on the wire, is called a *primary handler*. If not configured, this handler was previously always an <xref:System.Net.Http.HttpClientHandler>. While the default primary handler is an implementation detail, there were users who depended on it. For example, some users cast the primary handler to `HttpClientHandler` to set properties like <xref:System.Net.Http.HttpClientHandler.ClientCertificates>, <xref:System.Net.Http.HttpClientHandler.UseCookies>, and <xref:System.Net.Http.HttpClientHandler.UseProxy>.

With this change, the default primary handler is a <xref:System.Net.Http.SocketsHttpHandler> on platforms that support it. On other platforms, for example, .NET Framework, <xref:System.Net.Http.HttpClientHandler> continues to be used.

`SocketsHttpHandler` now also has the <xref:System.Net.Http.SocketsHttpHandler.PooledConnectionLifetime> property preset to match the <xref:Microsoft.Extensions.Http.HttpClientFactoryOptions.HandlerLifetime> value. (It reflects the latest value, if `HandlerLifetime` was configured by the user).

## Version introduced

.NET 9 Preview 6

## Previous behavior

The default primary handler was `HttpClientHandler`. Casting it to `HttpClientHandler` to update the properties happened to work.

```csharp
services.AddHttpClient("test")
.ConfigurePrimaryHttpMessageHandler((h, _) =>
{
((HttpClientHandler)h).UseCookies = false;
});

// This worked.
var client = httpClientFactory.CreateClient("test");
```

## New behavior

On platforms where `SocketsHttpHandler` is supported, the default primary handler is now `SocketsHttpHandler` with `PooledConnectionLifetime` set to the `HandlerLifetime` value. Casting it to `HttpClientHandler` to update the properties throws an <xref:System.InvalidCastException>.

For example, the same code from the [Previous behavior](#previous-behavior) section now throws an <xref:System.InvalidCastException>:

> System.InvalidCastException: Unable to cast object of type 'System.Net.Http.SocketsHttpHandler' to type 'System.Net.Http.HttpClientHandler'.

## Type of breaking change

This change is a [behavioral change](../../categories.md#behavioral-change).

## Reason for change

One of the most common problems `HttpClientFactory` users run into is when a `Named` or `Typed` client erroneously gets captured in a singleton service, or, in general, stored somewhere for a period of time that's longer than the specified <xref:Microsoft.Extensions.Http.HttpClientFactoryOptions.HandlerLifetime>. Because `HttpClientFactory` can't rotate such handlers, they might end up not respecting DNS changes.

This problem can be mitigated by using <xref:System.Net.Http.SocketsHttpHandler>, which has an option to control <xref:System.Net.Http.SocketsHttpHandler.PooledConnectionLifetime>. Similarly to <xref:Microsoft.Extensions.Http.HttpClientFactoryOptions.HandlerLifetime>, the pooled connection lifetime allows regularly recreating connections to pick up DNS changes, but on a lower level. A client with `PooledConnectionLifetime` set up can be safely used as a singleton.

It is, unfortunately, easy and seemingly "intuitive" to inject a `Typed` client into a singleton. But it's hard to have any kind of check or analyzer to make sure `HttpClient` isn't captured when it wasn't supposed to be captured. It's also hard to troubleshoot the resulting issues. So as a preventative measure&mdash;to minimize the potential impact of erroneous usage patterns&mdash;the `SocketsHttpHandler` mitigation is now applied by default.

This change only affects cases when the client wasn't configured by the end user to use a custom <xref:Microsoft.Extensions.Http.HttpMessageHandlerBuilder.PrimaryHandler> (for example, via <xref:Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.ConfigurePrimaryHttpMessageHandler``1(Microsoft.Extensions.DependencyInjection.IHttpClientBuilder)>).

## Recommended action

There are three options to work around the breaking change:

- Explicitly specify and configure a primary handler for each of your clients:

```csharp
services.AddHttpClient("test")
.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false });
```

- Overwrite the default primary handler for all clients using <xref:Microsoft.Extensions.DependencyInjection.HttpClientFactoryServiceCollectionExtensions.ConfigureHttpClientDefaults(Microsoft.Extensions.DependencyInjection.IServiceCollection,System.Action{Microsoft.Extensions.DependencyInjection.IHttpClientBuilder})>:

```csharp
services.ConfigureHttpClientDefaults(b =>
b.ConfigurePrimaryHttpMessageHandler(() => new HttpClientHandler() { UseCookies = false }));
```

- In the configuration action, check for both `HttpClientHandler` and `SocketsHttpHandler`:

```csharp
services.AddHttpClient("test")
.ConfigurePrimaryHttpMessageHandler((h, _) =>
{
if (h is HttpClientHandler hch)
{
hch.UseCookies = false;
}

if (h is SocketsHttpHandler shh)
{
shh.UseCookies = false;
}
});
```

## Affected APIs

- <xref:Microsoft.Extensions.Http.HttpMessageHandlerBuilder.PrimaryHandler?displayProperty=fullName>
- <xref:Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.ConfigurePrimaryHttpMessageHandler(Microsoft.Extensions.DependencyInjection.IHttpClientBuilder,System.Action{System.Net.Http.HttpMessageHandler,System.IServiceProvider})?displayProperty=fullName>
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
title: "URI query redaction in HttpClient EventSource events"
description: "Learn about the breaking change in networking in .NET 9 where HttpClient EventSource events scrub query strings by default to enhance privacy."
ms.date: 11/5/2024
ai-usage: ai-assisted
---

# URI query redaction in HttpClient EventSource events

In .NET 9, the default behavior of <xref:System.Diagnostics.Tracing.EventSource> events emitted by <xref:System.Net.Http.HttpClient> and <xref:System.Net.Http.SocketsHttpHandler> (`EventSource` name: `System.Net.Http`) has been modified to scrub query strings. This change enhances privacy by preventing the logging of potentially sensitive information contained in query strings. If necessary, you can override this behavior.

## Version introduced

.NET 9 Preview 7

## Previous behavior

Previously, events emitted by `HttpClient` and `SocketsHttpHandler` included query string information, which could inadvertently expose sensitive information.

## New behavior

With the change in [dotnet/runtime#104741](https://github.com/dotnet/runtime/pull/104741), query strings are replaced by a `*` character in `HttpClient` and `SocketsHttpHandler` events, by default. This change affects specific events and parameters such as `pathAndQuery` in `RequestStart` and `redirectUri` in `Redirect`.

## Type of breaking change

This change is a [behavioral change](../../categories.md#behavioral-change).

## Reason for change

The primary reason for this change was to enhance privacy by reducing the risk of sensitive information being logged inadvertently. Query strings often contain sensitive data, and redacting them from logs by default helps protect this information.

## Recommended action

If you need query string information when consuming `HttpClient` or `SocketsHttpHandler` events and you're confident that it's safe to do so, you can enable query string logging globally by setting an AppContext switch in one of three ways:

- In the project file.

```xml
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Net.Http.DisableUriRedaction" Value="true" />
</ItemGroup>
```

- In the *runtimeconfig.json* file.

```json
{
"runtimeOptions": {
"configProperties": {
"System.Net.Http.DisableUriRedaction": true
}
}
}
```

- Through an environment variable.

Set `DOTNET_SYSTEM_NET_HTTP_DISABLEURIREDACTION` to `true` or 1.

Otherwise, no action is required, and the default behavior will help enhance the privacy aspects of your application.

> [!NOTE]
> This switch also disables query string redaction in the default `IHttpClientFactory` logs. For more information, see [URI query redaction in IHttpClientFactory logs](query-redaction-logs.md).

## Affected APIs

- [System.Net.Http.SocketsHttpHandler.Send](xref:System.Net.Http.HttpMessageHandler.Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken))
- [System.Net.Http.SocketsHttpHandler.SendAsync](xref:System.Net.Http.HttpMessageHandler.SendAsync(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken))
67 changes: 67 additions & 0 deletions docs/core/compatibility/networking/9.0/query-redaction-logs.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
---
title: "URI query redaction in IHttpClientFactory logs"
description: Learn about the breaking change in networking in .NET 9 where the IHttpClientFactory implementation scrubs query strings in logs to enhance privacy.
ms.date: 11/5/2024
ai-usage: ai-assisted
---

# URI query redaction in IHttpClientFactory logs

In .NET 9, the default implementation of <xref:System.Net.Http.IHttpClientFactory> has been modified to scrub query strings when logging URI information. This change enhances privacy by preventing the logging of potentially sensitive information contained in query strings. For scenarios where logging query strings is necessary and deemed safe, you can override this behavior.

## Version introduced

.NET 9 Preview 7

## Previous behavior

Previously, the default implementation of `IHttpClientFactory` logging included query strings in the messages passed to <xref:Microsoft.Extensions.Logging.ILogger>, which could inadvertently expose sensitive information.

## New behavior

The messages passed to <xref:Microsoft.Extensions.Logging.ILogger> now have query strings replaced by a `*` character.

## Type of breaking change

This change is a [behavioral change](../../categories.md#behavioral-change).

## Reason for change

The primary reason for this change is to enhance privacy by reducing the risk of sensitive information being logged inadvertently. Query strings often contain sensitive data and excluding them from logs by default helps protect this information.

## Recommended action

If your application relies on logging query strings and you're confident that it's safe to do so, you can enable query string logging globally by setting an AppContext switch in one of three ways:

- In the project file.

```xml
<ItemGroup>
<RuntimeHostConfigurationOption Include="System.Net.Http.DisableUriRedaction" Value="true" />
</ItemGroup>
```

- In the *runtimeconfig.json* file.

```json
{
"runtimeOptions": {
"configProperties": {
"System.Net.Http.DisableUriRedaction": true
}
}
}
```

- Through an environment variable.

Set `DOTNET_SYSTEM_NET_HTTP_DISABLEURIREDACTION` to `true` or 1.

Otherwise, no action is required, and the default behavior will help enhance the privacy aspects of your application.

> [!NOTE]
> This switch also disables query string redaction in `HttpClient` EventSource events. For more information, see [URI query redaction in HttpClient EventSource events](query-redaction-events.md).

## Affected APIs

- <xref:Microsoft.Extensions.DependencyInjection.HttpClientFactoryServiceCollectionExtensions.AddHttpClient*?displayProperty=fullName>
44 changes: 44 additions & 0 deletions docs/core/compatibility/networking/9.0/server-port-attribute.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
title: "HttpClient metrics report `server.port` unconditionally"
description: Learn about the breaking change in networking in .NET 9 where HttpClient metrics now report the `server.port` attribute unconditionally to maintain compliance with Open Telemetry standards.
ms.date: 11/5/2024
---

# HttpClient metrics report `server.port` unconditionally

When [HttpClient metrics](../../../../fundamentals/networking/telemetry/metrics.md) were added in .NET 8, `server.port` was introduced as a [`Conditionally Required`](https://opentelemetry.io/docs/specs/semconv/general/attribute-requirement-level/#conditionally-required) attribute in accordance with the state of the standard at that time. Being conditionally required meant that the port was only reported if it did not match the default port of the corresponding protocol (80 for HTTP, 443 for HTTPS). However, the standard [requirement level of the attribute](https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-client) has since been changed to `Required`.

To maintain compliance with the Open Telemetry standard while keeping the instrument's behaviors consistent with each other, the instruments `http.client.request.duration`, `http.client.connection.duration`, and `http.client.open_connections` have been changed to unconditionally report the `server.port` attribute.

This change can break existing queries in monitoring software like Prometheus.

## Version introduced

.NET 9 Preview 7

## Previous behavior

`http.client.request.duration`, `http.client.connection.duration`, and `http.client.open_connections` reported the `server.port` attribute only if it did not match the corresponding protocol's default port (80 for HTTP, 443 for HTTPS).

## New behavior

The `server.port` attribute is now unconditionally reported by the instruments `http.client.request.duration`, `http.client.connection.duration`, and `http.client.open_connections`.

## Type of breaking change

This change is a [behavioral change](../../categories.md#behavioral-change).

## Reason for change

The change maintains compliance with the [Open Telemetry specification](https://opentelemetry.io/docs/specs/semconv/http/http-spans/#http-client) while keeping `HttpClient` instruments consistent with each other.

## Recommended action

No action is needed if you don't rely on [HttpClient metrics](../../../../fundamentals/networking/telemetry/metrics.md). If you use the `http.client.request.duration`, `http.client.connection.duration`, or `http.client.open_connections` instruments, this change might break existing queries in monitoring software like Prometheus.

## Affected APIs

- `System.Net.Http.SocketsHttpHandler.Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)`
- `System.Net.Http.SocketsHttpHandler.SendAsync(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)`
- <xref:System.Net.Http.HttpClientHandler.Send(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)?displayProperty=fullName>
- <xref:System.Net.Http.HttpClientHandler.SendAsync(System.Net.Http.HttpRequestMessage,System.Threading.CancellationToken)?displayProperty=fullName>
18 changes: 17 additions & 1 deletion docs/core/compatibility/toc.yml
Original file line number Diff line number Diff line change
Expand Up @@ -74,10 +74,18 @@ items:
href: jit/9.0/sve-apis.md
- name: Networking
items:
- name: HttpClient metrics report `server.port` unconditionally
href: networking/9.0/server-port-attribute.md
- name: HttpClientFactory logging redacts header values by default
href: networking/9.0/redact-headers.md
- name: HttpClientFactory uses SocketsHttpHandler as primary handler
href: networking/9.0/default-handler.md
- name: HttpListenerRequest.UserAgent is nullable
href: networking/9.0/useragent-nullable.md
- name: Uri query redaction in HttpClient EventSource events
href: networking/9.0/query-redaction-events.md
- name: Uri query redaction in IHttpClientFactory logs
href: networking/9.0/query-redaction-logs.md
- name: SDK and MSBuild
items:
- name: "'dotnet workload' commands output change"
Expand Down Expand Up @@ -515,7 +523,7 @@ items:
- name: WPF
items:
- name: Revert behavior with text drag-and-drop operations
href: wpf/7.0/drag-and-drop.md
href: wpf/7.0/drag-and-drop.md
- name: .NET 6
items:
- name: Overview
Expand Down Expand Up @@ -1694,10 +1702,18 @@ items:
items:
- name: .NET 9
items:
- name: HttpClient metrics report `server.port` unconditionally
href: networking/9.0/server-port-attribute.md
- name: HttpClientFactory logging redacts header values by default
href: networking/9.0/redact-headers.md
- name: HttpClientFactory uses SocketsHttpHandler as primary handler
href: networking/9.0/default-handler.md
- name: HttpListenerRequest.UserAgent is nullable
href: networking/9.0/useragent-nullable.md
- name: Uri query redaction in HttpClient EventSource events
href: networking/9.0/query-redaction-events.md
- name: Uri query redaction in IHttpClientFactory logs
href: networking/9.0/query-redaction-logs.md
- name: .NET 8
items:
- name: SendFile throws NotSupportedException for connectionless sockets
Expand Down
Loading