为什么 Chrome 79 不接受我用 SslStream (C#) 发送的 HTTPS 响应? err_response_headers_truncated

Why won't Chrome 79 accept my HTTPS response sent with SslStream (C#)? err_response_headers_truncated

我正在尝试使用 C# 在本地主机上构建一个简单的 TLS Web 服务器 运行。在这种情况下,我希望服务器做的就是发出重定向。为此,我使用了 Microsoft 提供的 SslStream class。从表面上看,它似乎很容易使用。我 运行 遇到的问题是 chrome 特别是似乎不喜欢我的 HTTPS。 (代码在底部)

响应非常简单:"HTTP/1.1 301 Moved Permanently\r\nCache-Control: no-store\r\nLocation: https://google.com/"

使用 HTTP 实现时,页面按预期重定向,客户端 (Chrome) 或服务器上没有出现错误或异常。

但是,在使用我的 HTTPS 实现时,我从 chrome:

得到了类似的东西

This site can't be reached

The webpage at.....

ERR_RESPONSE_HEADERS_TRUNCATED

我不确定它还想从我这里得到什么。我会说响应 header 几乎没有被截断,但我没有 chrome。我可以在调试时读取 chrome 的 GET 请求,但 chrome 显然不能或不会读取我的响应。

我已经成功地使用了完全相同的代码来重定向边缘(http 和 https),所以这似乎是 chrome 特定的

在服务器发送响应之前,快速的 wireshark 调查显示没有任何异常。举例说明:标准 tcp syn/ack 握手,tls 握手,然后是客户端应用程序。 data (the GET,) server app data (the 301.) 服务器发送 301 后,有 3 个 FIN/ACKs,一个 keep-alive,一个密码更改,然后是一堆 RST。鉴于此信息,chrome 似乎没有给我们任何东西,但同时也不希望我们抛弃它们并开始拼命请求重新连接。奇怪的是,如果我调试,它会重新连接。但是随后它抛出异常,因为服务器读取超时;我们给了 Chrome 第二次机会,但 Chrome 不愿意为这段已经破裂的关系重新投入任何东西,这留下了伤疤。

差不多,我在问我在做什么 Chrome 显然不希望我如何更正它以便它在 Chrome 上工作?我是否做了其他浏览器正在补偿的明显错误,或者这是 SslStream 的问题?

我的 TLS 代码基本上取自 Microsoft doc for SslStream and the http design follows the same pattern: accept, read, write, flush, stream close, tcp close. If you wanted to reproduce, the certificate created was used with X509Certificate.CreateFromCertFile(string). The TcpListener used IPAddress.Loopback. To generate my certificate I followed this tutorial 并用 openssl pkcs12

导出了我的 localhost.crtlocalhost.key
private string response = "HTTP/1.1 301 Moved Permanently\r\nCache-Control: no-store\r\nLocation: https://google.com/ \r\n";
private void listen() {
    while (true) {
        TcpClient client = listener.AcceptTcpClient();
        NetworkStream stream = client.GetStream();
        stream.Read(new Byte[1024], 0, 1024);
        stream.Write(Encoding.UTF8.GetBytes(response), 0, response.Length);
        stream.Flush();
        stream.Close();
        client.Close();
    }
}
private void listenSSL() {
    TcpClient client;
    while (true) {
        client = listener.AcceptTcpClient();
        SslStream sslStream = new SslStream(client.GetStream(), false);
        sslStream.AuthenticateAsServer(certificate, clientCertificateRequired: false, SslProtocols.Tls12, checkCertificateRevocation: true);
        sslStream.ReadTimeout = timeout;
        sslStream.WriteTimeout = timeout;

        string message = readMessage(sslStream);
        sslStream.Write(Encoding.UTF8.GetBytes(response), 0, response.Length);
        sslStream.Flush();
        sslStream.Close();
        client.Close();
    }
}

好的,问题原来是行尾问题。 Chrome 想要在响应末尾添加两个 \r\n。我在 support.google.com 上找到了关于它的 this 文章。 所以字符串应该看起来像

"HTTP/1.1 301 Moved Permanently\r\nCache-Control: no-store\r\nLocation: https://google.com/ \r\n\r\n";

而不是

"HTTP/1.1 301 Moved Permanently\r\nCache-Control: no-store\r\nLocation: https://google.com/ \r\n"