如何订购HttpClient或WebRequest发送的HTTP头?

时间:2017-09-30 23:27:54

标签: c#

我希望能够控制在使用HttpClient或WebRequest发送Web请求时如何排序标头。

使用HttpClient时,总是在末尾添加主机和连接,并对其他标头进行排序,以便将它们添加到Headers集合中。

WebRequest将主机添加到标题的末尾。

我知道标题的顺序与网络服务器无关,但是,id喜欢以特定的方式对它们进行排序,因为订单将在服务器端以编程方式进行checekd。

有办法做到这一点吗?

1 个答案:

答案 0 :(得分:0)

这是我的临时解决方法:

创建本地代理并通过它传递HttpClient请求。在代理内部,我按照我的要求重新排序标题。

我使用的本地代理是开源C#Titanium Web Proxy,它可以正常工作。它支持.NET Standard 2.0,因此它是跨平台的。

以下是测试代理设置和使用代理的客户端的代码示例。您必须自己实现事件处理程序。

<强>客户端

using System.Net;
using System.Net.Http;
using System.Threading.Tasks;

namespace TestClient
{
    class Program
    {
        static void Main(string[] args)
        {
            var handler = new HttpClientHandler
            {
                Proxy = new WebProxy($"http://localhost:8000", false),
                UseProxy = true,
            };

            var client = new System.Net.Http.HttpClient(handler);

            var t  = Task.Run(() => GetDataAsync(client, "http://google.com/"));
            t.Wait();
            var result = t.Result;
        }

        static async Task<string> GetDataAsync(System.Net.Http.HttpClient client, string path)
        {
            HttpResponseMessage response = await client.GetAsync(path);
            if (response.IsSuccessStatusCode)
            {
                return await response.Content.ReadAsStringAsync();
            }
            else
            {
                return null;
            }
        }
    }
}

代理服务器设置:

using System;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Titanium.Web.Proxy;
using Titanium.Web.Proxy.EventArguments;
using Titanium.Web.Proxy.Models;

namespace ClassLibrary6
{
    public class ProxySetup
    {
        public ProxySetup()
        {
            var proxyServer = new ProxyServer();

            //locally trust root certificate used by this proxy 
            proxyServer.TrustRootCertificate = true;

            //optionally set the Certificate Engine
            //Under Mono only BouncyCastle will be supported
            //proxyServer.CertificateEngine = Network.CertificateEngine.BouncyCastle;

            proxyServer.BeforeRequest += OnRequest;
            proxyServer.BeforeResponse += OnResponse;
            proxyServer.ServerCertificateValidationCallback += OnCertificateValidation;
            proxyServer.ClientCertificateSelectionCallback += OnCertificateSelection;


            var explicitEndPoint = new ExplicitProxyEndPoint(IPAddress.Any, 8000, true)
            {
                //Exclude HTTPS addresses you don't want to proxy
                //Useful for clients that use certificate pinning
                //for example dropbox.com
                // ExcludedHttpsHostNameRegex = new List<string>() { "google.com", "dropbox.com" }

                //Use self-issued generic certificate on all HTTPS requests
                //Optimizes performance by not creating a certificate for each HTTPS-enabled domain
                //Useful when certificate trust is not required by proxy clients
                // GenericCertificate = new X509Certificate2(Path.Combine(System.IO.Path.GetDirectoryName(System.Reflection.Assembly.GetExecutingAssembly().Location), "genericcert.pfx"), "password")
            };

            //An explicit endpoint is where the client knows about the existence of a proxy
            //So client sends request in a proxy friendly manner
            proxyServer.AddEndPoint(explicitEndPoint);
            proxyServer.Start();

            //Transparent endpoint is useful for reverse proxy (client is not aware of the existence of proxy)
            //A transparent endpoint usually requires a network router port forwarding HTTP(S) packets or DNS
            //to send data to this endPoint
            var transparentEndPoint = new TransparentProxyEndPoint(IPAddress.Any, 8001, true)
            {
                //Generic Certificate hostname to use
                //when SNI is disabled by client
                GenericCertificateName = "google.com"
            };

            proxyServer.AddEndPoint(transparentEndPoint);

            //proxyServer.UpStreamHttpProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };
            //proxyServer.UpStreamHttpsProxy = new ExternalProxy() { HostName = "localhost", Port = 8888 };

            foreach (var endPoint in proxyServer.ProxyEndPoints)
                Console.WriteLine("Listening on '{0}' endpoint at Ip {1} and port: {2} ",
                    endPoint.GetType().Name, endPoint.IpAddress, endPoint.Port);

            //Only explicit proxies can be set as system proxy!
            //proxyServer.SetAsSystemHttpProxy(explicitEndPoint);
            //proxyServer.SetAsSystemHttpsProxy(explicitEndPoint);

            //wait here (You can use something else as a wait function, I am using this as a demo)
            Console.Read();

            //Unsubscribe & Quit
            proxyServer.BeforeRequest -= OnRequest;
            proxyServer.BeforeResponse -= OnResponse;
            proxyServer.ServerCertificateValidationCallback -= OnCertificateValidation;
            proxyServer.ClientCertificateSelectionCallback -= OnCertificateSelection;

            proxyServer.Stop();
        }

        public async Task OnRequest(object sender, SessionEventArgs e)
        {
            e.WebSession.Request.RequestHeaders.AddHeader("Ab", "Moo");

            var headers = e.WebSession.Request.RequestHeaders.GetAllHeaders();

            var ordered = headers.OrderBy(x => x.Name);

            e.WebSession.Request.RequestHeaders.Clear();

            foreach (var httpHeader in ordered)
            {
                e.WebSession.Request.RequestHeaders.AddHeader(httpHeader);
            }

        }

        public async Task OnResponse(object sender, SessionEventArgs e)
        {
        }

        public Task OnCertificateValidation(object sender, CertificateValidationEventArgs e)
        {
            return null;
        }

        public Task OnCertificateSelection(object sender, CertificateSelectionEventArgs e)
        {
            return null;
        }
    }
}