dotnet / runtime

.NET is a cross-platform runtime for cloud, mobile, desktop, and IoT apps.
https://docs.microsoft.com/dotnet/core/
MIT License
14.23k stars 4.45k forks source link

Strange behavior when adding a user agent #101658

Closed Mr0N closed 2 weeks ago

Mr0N commented 2 weeks ago

Description

When adding a user agent, for some reason, many user agents are added instead of just one. Ideally, after adding one user agent, adding another should result in an error.

Reproduction Steps

image


var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

var app = builder.Build();

app.MapGet("/test", (HttpContext a) =>
{
    var response = a.Request.Headers.UserAgent;
    Console.WriteLine(response);
    return new { check = true };
});
Task.Run(async () =>
{
    await Execute();
});
app.Run("http://localhost:1111");

async Task Execute()
{
    //const string userAgent = "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/124.0.0.0 Safari/537.36";
    const string userAgent = "12345";
    using var client = new HttpClient();
    for (int i = 0; i < 5; i++)
    {
        client.DefaultRequestHeaders.UserAgent.ParseAdd(userAgent);
        string text = await client.GetStringAsync("http://localhost:1111/test");
        Console.WriteLine(text);
    }

}

https://github.com/Mr0N/TestHost/blob/master/TestHost/Program.cs

Expected behavior

-

Actual behavior

-

Regression?

-

Known Workarounds

-

Configuration

-

Other information

No response

MihaZupan commented 2 weeks ago

DefaultRequestHeaders are shared for all requests on the HttpClient. You're adding multiple values to that shared collection.

If you want to customize headers for each request, create an HttpRequestMessage yourself and call SendAsync instead.

Mr0N commented 2 weeks ago

DefaultRequestHeaders are shared for all requests on the HttpClient. You're adding multiple values to that shared collection.

If you want to customize headers for each request, create an HttpRequestMessage yourself and call SendAsync instead.

It contradicts the HTTP protocol, about one user agent.

MihaZupan commented 2 weeks ago

Http allows for multiple user agent values to be specified

Mr0N commented 2 weeks ago

DefaultRequestHeaders are shared for all requests on the HttpClient. You're adding multiple values to that shared collection.

Well, let it add headers to all requests, but that's not the problem here. The problem is that the client sends many user agents to the server instead of just one. So, the client behaves incorrectly.

Mr0N commented 2 weeks ago

Http allows for multiple user agent values to be specified

In one header?

MihaZupan commented 2 weeks ago

Yes https://www.rfc-editor.org/rfc/rfc9110.html#section-10.1.5

Mr0N commented 2 weeks ago

Reference Yes https://www.rfc-editor.org/rfc/rfc9110.html#section-10.1.5

Well, it doesn't say anywhere that there can be 20 user agents in one request. It says that the user agent is one line, which essentially identifies the device.

MihaZupan commented 2 weeks ago

It does

User-Agent = product *( RWS ( product / comment ) )
Mr0N commented 2 weeks ago

image

Mr0N commented 2 weeks ago

It's like a user agent structure, not two

Mr0N commented 2 weeks ago

Another possibility is that there could be a couple of identical headers in the request with different keys, so that the headers are within one key, separated by spaces; I haven't heard of this before.

Mr0N commented 2 weeks ago

image Well, here the products are separated. It's not user agents; there's a UserAgent property that parses the user agent, not the products. It's like if int.Parse parsed only one digit at a time in a number.

Mr0N commented 2 weeks ago

If we even suppose that products are added to the user agent and then the user agent is created from them, why do regular user agents pass validation? They don't match the product characteristics specified.