为什么 CookieContainer 不替换同名 cookie?

Why CookieContainer does not replace cookies with the same name?

问题

我正试图从一个著名的网站上抓取数据,并发现了一个问题。

此站点使用 cookie 进行身份验证,它设置 cookie,然后在身份验证流程中替换它。

我的问题是 CookieContainer 不会替换具有相同名称、相同域的 cookie,但在第二种情况下,域以点开头。

但是任何浏览器或 Postman 都会这样做。

因此结果是 double-sent cookie,并且该站点无法通过身份验证流程。

示例

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using NUnit.Framework;

...

[Test]
public void Cookies()
{
    CookieContainer container = new CookieContainer();
    Uri uri = new Uri("https://example.com/item");
    string cookie1 = "myCookie=value1; Domain=example.com;  Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly";
    string cookie2 = "myCookie=value2; Domain=.example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly";

    container.SetCookies(uri, cookie1);
    container.SetCookies(uri, cookie2);

    List<Cookie> cookies = container.GetCookies(uri).ToList();
    foreach (Cookie cookie in cookies)
    {
        Console.WriteLine(cookie);
    }

    Assert.AreEqual(1, cookies.Count);
}

输出为:

myCookie=value1
myCookie=value2

但预期输出是:

myCookie=value2

为什么 SetCookies

SetCookies 方法在 System.Net.HttpCookieHelper which is used inside of Http2Stream 的默认实现中。

HttpClientHandlerUseCookies 属性设置为true时使用:


HttpClientHandler handler = new HttpClientHandler()
{
    CookieContainer = new CookieContainer(),
    UseCookies = true,
};
var client = new HttpClient(handler);

//Make requests

我的运行时版本是 NET 5,因此 HttpClientHandler 隐藏 SocketsHttpHandler 实现(如果有必要)。

我的问题:

提前致谢!


UPD 1:样本 mockserver

第 1 步:在主机文件中将 example.com 设置为本地主机。

127.0.0.1 example.com

第 2 步:创建模拟服务器

initializerJson.json

[
  {
    "httpRequest": {
      "method": "GET",
      "path": "/1",
      "secure": true
    },
    "httpResponse": {
      "statusCode": 200,
      "headers": {
        "Set-Cookie": [
          "myCookie=value1; Domain=example.com;  Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly"
        ]
      },
      "body": "1"
    }
  },
  {
    "httpRequest": {
      "method": "GET",
      "path": "/2",
      "secure": true
    },
    "httpResponse": {
      "statusCode": 200,
      "headers": {
        "Set-Cookie": [
          "myCookie=value2; Domain=.example.com; Expires=Sat, 11-Feb-2090 02:41:39 GMT; Path=/; HttpOnly"
        ]
      },
      "body": "2"
    }
  },
  {
    "httpRequest": {
      "method": "GET",
      "path": "/3",
      "secure": true
    },
    "httpResponseTemplate": {
      "template": "cookie = request.headers.Cookie; return { statusCode: 200, body: cookie }; ",
      "templateType": "JAVASCRIPT"
    }
  }
]

docker-compose.yml

version: "2.4"
services:
  mockServer:
      image: mockserver/mockserver:latest
      ports:
        - 5000:1080
      environment:
        MOCKSERVER_WATCH_INITIALIZATION_JSON: "true"
        MOCKSERVER_PROPERTY_FILE: /config/mockserver.properties
        MOCKSERVER_INITIALIZATION_JSON_PATH: /config/initializerJson.json
      volumes:
        - type: bind
          source: .
          target: /config

运行 终端中的模拟服务器:

docker-compose up

之后,您将在端口 5000 上拥有一个具有 3 个端点的 http 服务器:

第 3 步:测试它

测试流程为:

  1. 请求https://example.com:5000/1端点
  2. 请求https://example.com:5000/2端点
  3. 请求https://example.com:5000/3端点并检查响应

示例 C# 代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using NUnit.Framework;

...

[Test]
public async Task CheckCookies()
{
    CookieContainer container = new CookieContainer();
    HttpClientHandler handler = new HttpClientHandler()
    {
        CookieContainer = container,
        UseCookies = true,

        //Bypass self-signed https cert
        ServerCertificateCustomValidationCallback = (message, certificate2, arg3, arg4) => true,
    };
    HttpClient client = new HttpClient(handler);
    
    await client.GetAsync("https://example.com:5000/1");
    await client.GetAsync("https://example.com:5000/2");
    
    var response = await client.GetAsync("https://example.com:5000/3");
    string responseText = await response.Content.ReadAsStringAsync();
    Console.WriteLine(responseText);
    
    Assert.AreEqual("[ \"myCookie=value2\" ]", responseText);
}

结果

经过测试得到如下结果:

有什么建议吗?

这是 dotnet 运行时中的错误:https://github.com/dotnet/runtime/issues/60628

待处理的 PR:https://github.com/dotnet/runtime/pull/64038