【问题标题】:How to send lower-case Keep-Alive header through HttpWebRequest如何通过 HttpWebRequest 发送小写 Keep-Alive 标头
【发布时间】:2022-01-12 04:32:51
【问题描述】:

我正在编写一个机器人,它应该尽可能地模拟 firefox。 通过检查它发送的标头,我发现了一个小区别,我不知道如何摆脱:

Firefox 使用以下 keep-alive 标头:

Connection: keep-alive

虽然 c# 总是发送出去:

Connection: Keep-Alive

我知道这可能无关紧要,但我仍然很想知道是否有任何方法/hack 可以将该标头修改为全部小写。

任何想法如何做到这一点?

【问题讨论】:

    标签: c# http keep-alive


    【解决方案1】:

    在 .net 4.0 中有效:

    request.Headers.GetType().InvokeMember(
        "ChangeInternal",
        BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod,
        Type.DefaultBinder, 
        request.Headers, 
        new object[] { "Connection", "keep-alive" }
    );
    

    请确保您实际上并未在请求中设置 KeepAlive 属性

    【讨论】:

    • 谢谢你拯救了我的一天 :)
    • 这会导致header被设置为keep-alive,Keep-Alive,但它可能仍然工作得更好。
    • 即使您将 KeepAlive 属性设置为 false?
    【解决方案2】:

    我还需要使用 C# 来执行此操作,并且一直在尝试使用 HttpWebRequest。我要调用的非 .NET SOAP Web 服务也需要小写的 Connection: keep-alive

    我宁愿不为此做套接字编程,所以如果你对如何解决这个问题有任何建议,如果你这样做了,那将非常有帮助。

    到目前为止我的调查:

    使用 http 协议 1.1 版时,即使您指定了属性,也不会发送标头。

    例如

    var request = (HttpWebRequest)WebRequest.Create(Endpoint); 
    request.KeepAlive = true;
    

    解决方案是使用 System.Reflection 来修改 httpBehaviour,这意味着发送 Keep-Alive。这将在每个请求上发送一个初始的大写 K 和 A 'Keep-Alive'。

    var sp = request.ServicePoint; 
    var prop = sp.GetType().GetProperty("HttpBehaviour", BindingFlags.Instance | BindingFlags.NonPublic);
    prop.SetValue(sp, (byte)0, null);
    

    我也尝试使用 System.Reflection 来修改标题。下面的代码将以小写正确添加标志:

    request.Headers.GetType().InvokeMember("ChangeInternal", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, Type.DefaultBinder, request.Headers, new object[] { "Connection", "keep-alive" });
    

    但是,在我调用 GetResponse 时

    var response = (HttpWebResponse)request.GetResponse()

    标题被清除。查看 HttpWebRequest.cs 的源代码,这发生在将标头发送到线路之前。

        //
        // The method is called right before sending headers to the wire*
        // The result is updated internal _WriteBuffer
        //
        // See ClearRequestForResubmit() for the matching cleanup code path.
        // 
        internal void SerializeHeaders() {
    
        ....
        ....
    
            string connectionString = HttpKnownHeaderNames.Connection; 
            if (UsesProxySemantics || IsTunnelRequest ) { 
                _HttpRequestHeaders.RemoveInternal(HttpKnownHeaderNames.Connection);
                connectionString = HttpKnownHeaderNames.ProxyConnection; 
                if (!ValidationHelper.IsBlankString(Connection)) {
                    _HttpRequestHeaders.AddInternal(HttpKnownHeaderNames.ProxyConnection, _HttpRequestHeaders[HttpKnownHeaderNames.Connection]);
                }
            } 
            else {
                _HttpRequestHeaders.RemoveInternal(HttpKnownHeaderNames.ProxyConnection); 
            } 
    

    RemoveInternal 还将删除我们使用反射入侵的标头。

    所以这仍然让我陷入困境。

    除了在套接字级别上,还有其他方法吗? 是否有其他类或 3rd 方库允许我根据需要修改标头?

    对不起,这不是一个答案,但我还不能评论你的问题。

    【讨论】:

    • 我最终放弃了。这在 c# 中根本不可行。我与我试图连接的服务的开发人员进行了交谈,猜猜是什么 - 他们修复了他们这边的 BUG
    【解决方案3】:

    Connection: keep-alive 是 Chrome 和 Firefox 浏览器的默认标题。

    Connection: Keep-Alive 是 Internet Explorer 的默认标头。绝对的

    Connection: Keep-Alive 是 HttpWebRequest 的默认标头。如果使用 HttpWebRequest,我认为你应该编写一个像 IE 这样的机器人是最好的选择。

    【讨论】:

      【解决方案4】:

      使用反射,您可以将WebHeaderCollection 的内部NameValueCollection 替换为自定义实现,如下所示:

      // When injected into a WebHeaderCollection, ensures that
      // there's always exactly one "Connection: keep-alive" header.
      public class CustomNameValueCollection : NameValueCollection {
      
          private const BindingFlags allInstance =
              BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic;
      
          private static readonly PropertyInfo innerCollProperty =
              typeof(WebHeaderCollection).GetProperty("InnerCollection", allInstance);
      
          private static readonly FieldInfo innerCollField =
              typeof(WebHeaderCollection).GetField("m_InnerCollection", allInstance);
      
          public static void InjectInto(WebHeaderCollection coll) {
              // WebHeaderCollection uses a custom IEqualityComparer for its internal
              // NameValueCollection. Here we get the InnerCollection property so that
              // we can reuse its IEqualityComparer (via our constructor).
              var innerColl = (NameValueCollection) innerCollProperty.GetValue(coll);
              innerCollField.SetValue(coll, new CustomNameValueCollection(innerColl));
          }
      
          private CustomNameValueCollection(NameValueCollection coll) : base(coll) {
              Remove("Connection");
              base.Add("Connection", "keep-alive");
          }
      
          public override void Add(string name, string value) {
              if (name == "Connection") return;
              base.Add(name, value);
          }
      }
      

      像这样使用它:

      var request = (HttpWebRequest) WebRequest.Create("https://www.google.com/");
      CustomNameValueCollection.InjectInto(request.Headers);
      using (var response = request.GetResponse()) {
          ...
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-11-19
        • 1970-01-01
        • 2012-09-20
        • 1970-01-01
        • 1970-01-01
        • 2012-09-15
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多