HttpClient 忽略 AllowAutoRedirect 指令

Posted

技术标签:

【中文标题】HttpClient 忽略 AllowAutoRedirect 指令【英文标题】:HttpClient ignores AllowAutoRedirect directive 【发布时间】:2014-05-12 22:26:31 【问题描述】:

我正在尝试使用 System.Net.Http.HttpClient 执行一些 HEAD 检查。它适用于除 301 重定向之外的所有内容。在这种情况下,HttpClient 说状态 200,即使服务器说 301 并提供了一个新位置。

这里有一个快速测试来验证我所看到的:

[Test]
public async void Test301Capture()

    const string url = "http://www.youtube.com/"; // http://www.youtube.com/ does a 301 to https://www.youtube.com/

    var httpClientHandler = new HttpClientHandler()  AllowAutoRedirect = false ;

    using (var client = new HttpClient(httpClientHandler))
    using (var headRequest = new HttpRequestMessage(HttpMethod.Head, url))
    using (var headResponse = await client.SendAsync(headRequest))
    
        Log.InfoFormat("Result of HEAD check on \"0\" is: 1", url, headResponse.StatusCode); // *should* be a 301
        Assert.AreNotEqual(200, (int)headResponse.StatusCode); // *fails* because StatusCode *is* 200
    

上面的输出是:

Result of HEAD check on "http://www.youtube.com/" is: OK

我知道服务器说的是 301,因为使用高级 REST 客户端进行快速 HEAD 检查以测试 http://www.youtube.com/ 说:

Redirect
To:https://www.youtube.com/ with status: 301 Show explanation HTTP/1.1 301 Moved Permanently
Redirection information has not been cached.
Date: Tue, 01 Apr 2014 20:08:09 GMT 
Server: gwiseguy/2.0 
Content-Length: 0 
Cache-Control: no-cache 
X-Content-Type-Options: nosniff
Content-Type: text/html; charset=utf-8 
Expires: Tue, 27 Apr 1971 19:44:06 EST 
X-XSS-Protection: 1; mode=block; report=https://www.google.com/appserve/security-bugs/log/youtube
Location: https://www.youtube.com/ 
Alternate-Protocol: 80:quic

我确实找到了这个答案,Using HttpClient, how would I prevent automatic redirects and get original status code and forwading Url in the case of 301,但是我的结果不同。

【问题讨论】:

Upshot:@kyleb 使用 pinvoke 的经验,以及我自己使用 .NET 套接字的使用测试(以及 Python 的野兔刺伤)让我相信 301 的自动重定向已融入低级别Windows 上的网络。 【参考方案1】:

我也尝试了这个的 win32 pinvoke 版本,但我仍然得到 200 OK 作为状态码。但我确实找到了这个答案,它应该可以工作。 http://mail-archives.apache.org/mod_mbox/hc-dev/200501.mbox/%3CA68B76BAB1C27346BE22FED8D74ACE2C0283BB@owm-exc-1.telecomsys.com%3E

如果你想看,下面是我用于研究的 pinvoke 代码:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;
using System.Threading.Tasks;

namespace HttpTest

    class Program
    
        const int INTERNET_OPEN_TYPE_DIRECT = 1;     // direct to net
        const short INTERNET_DEFAULT_HTTP_PORT = 80;
        const int INTERNET_SERVICE_HTTP = 3;

        const uint INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP = 0x00008000;

        const uint INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS = 0x00004000;
        const uint INTERNET_FLAG_KEEP_CONNECTION = 0x00400000;
        const uint INTERNET_FLAG_NO_AUTH = 0x00040000;
        const uint INTERNET_FLAG_NO_AUTO_REDIRECT = 0x00200000;
        const uint INTERNET_FLAG_NO_COOKIES = 0x00080000;
        const uint INTERNET_FLAG_NO_UI = 0x00000200;
        const uint INTERNET_FLAG_RELOAD = 0x80000000;

        const int HTTP_QUERY_STATUS_CODE = 19;

        [DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr InternetOpen(string lpszAgent, int dwAccessType, string lpszProxyName, string lpszProxyBypass, int dwFlags);

        [DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern IntPtr InternetConnect(IntPtr hInternet, string lpszServerName, short nServerPort, string lpszUsername, string lpszPassword, int dwService, int dwFlags, IntPtr dwContext);

        [DllImport("wininet.dll", SetLastError = true)]
        static extern IntPtr HttpOpenRequest(
            IntPtr hConnect,
            string lpszVerb,
            string lpszObjectName,
            string lpszVersion,
            string lpszReferer,
            string[] lplpszAcceptTypes,
            uint dwFlags,
            IntPtr dwContext);

        [DllImport("wininet.dll", SetLastError = true, CharSet = CharSet.Auto)]
        static extern bool HttpSendRequest(IntPtr hRequest, IntPtr lpszHeaders, uint dwHeadersLength, IntPtr lpOptional, uint dwOptionalLength);

        [DllImport("wininet.dll", SetLastError = true)]
        static extern bool HttpQueryInfo(IntPtr hInternet, int dwInfoLevel, IntPtr lpBuffer, ref long lpdwBufferLength, ref long lpdwIndex);

        static void Main(string[] args)
        
            IntPtr openPtr = InternetOpen("Kyle", INTERNET_OPEN_TYPE_DIRECT, null, null, 0);
            IntPtr context = IntPtr.Zero;
            IntPtr connectPtr = InternetConnect(openPtr, "www.youtube.com", INTERNET_DEFAULT_HTTP_PORT, null, null, INTERNET_SERVICE_HTTP, 0, context);
            IntPtr openRequestPtr = HttpOpenRequest(connectPtr, "GET", "/", null, null, null,
                INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTP |
                INTERNET_FLAG_IGNORE_REDIRECT_TO_HTTPS |
                INTERNET_FLAG_KEEP_CONNECTION |
                INTERNET_FLAG_NO_AUTH |
                INTERNET_FLAG_NO_AUTO_REDIRECT |
                INTERNET_FLAG_NO_COOKIES |
                INTERNET_FLAG_NO_UI |
                INTERNET_FLAG_RELOAD, IntPtr.Zero);

            if (HttpSendRequest(openRequestPtr, IntPtr.Zero, 0, IntPtr.Zero, 0))
            
                IntPtr buffer = Marshal.AllocHGlobal(4); long lBufferLen = 4; long lHeaderIndex = 0;
                HttpQueryInfo(openRequestPtr, HTTP_QUERY_STATUS_CODE, buffer, ref lBufferLen, ref lHeaderIndex);
                string str2 = Marshal.PtrToStringAnsi(buffer);
            
        
    

【讨论】:

以上是关于HttpClient 忽略 AllowAutoRedirect 指令的主要内容,如果未能解决你的问题,请参考以下文章

C# HttpClient 是不是忽略 SSL 证书错误?

HttpClient 无法忽略或绕过自签名证书错误

如何使 HttpClient 忽略 Content-Length 标头

忽略 Apache HttpClient 4.3 中的 SSL 证书

如何忽略 Apache HttpComponents HttpClient 5.1 中的 SSL 证书错误

在 ktor httpClient(js) JS 引擎中忽略自签名证书的配置