2009-06-26 13 views
16

Sono confuso su come CookieContainer gestisce il dominio, quindi creo questo test. Questo test mostra che cookieContainer non restituisce alcun cookie per "esempio.com" ma secondo RFC dovrebbe restituire almeno 2 cookie.Bug CookieContainer?

Non è un bug?

Come farlo funzionare?

Ecco una discussione su questo bug:

http://social.msdn.microsoft.com/Forums/en-US/ncl/thread/c4edc965-2dc2-4724-8f08-68815cf1dce6

<%@ Page Language="C#" %> 

<%@ Import Namespace="System.Net" %> 
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> 

<script runat="server"> 
    CookieContainer getContainer() 
    { 
     CookieContainer result = new CookieContainer(); 

     Uri uri = new Uri("http://sub.example.com"); 
     string cookieH = @"Test1=val; domain=sub.example.com; path=/"; 
     result.SetCookies(uri, cookieH); 

     cookieH = @"Test2=val; domain=.example.com; path=/"; 
     result.SetCookies(uri, cookieH); 

     cookieH = @"Test3=val; domain=example.com; path=/"; 
     result.SetCookies(uri, cookieH); 

     return result; 
    } 

    void Test() 
    { 
     CookieContainer cookie = getContainer(); 
     lblResult.Text += "<br>Total cookies count: " + cookie.Count + " &nbsp;&nbsp; expected: 3"; 

     Uri uri = new Uri("http://sub.example.com"); 
     CookieCollection coll = cookie.GetCookies(uri); 
     lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " &nbsp;&nbsp; expected: 2"; 

     uri = new Uri("http://other.example.com"); 
     coll = cookie.GetCookies(uri); 
     lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " &nbsp;&nbsp; expected: 2"; 

     uri = new Uri("http://example.com"); 
     coll = cookie.GetCookies(uri); 
     lblResult.Text += "<br>For " + uri + " Cookie count: " + coll.Count + " &nbsp;&nbsp; expected: 2"; 

    } 

    protected void Page_Load(object sender, EventArgs e) 
    { 
     Test(); 
    } 
</script> 

<html xmlns="http://www.w3.org/1999/xhtml"> 
<head runat="server"> 
    <title>CookieContainer Test Page</title> 
</head> 
<body> 
    <form id="frmTest" runat="server"> 
    <asp:Label ID="lblResult" EnableViewState="false" runat="server"></asp:Label> 
    </form> 
</body> 
</html> 
+0

Ho provato anche questo molte volte prima. Ho finito per leggere io stesso l'intestazione del cookie e memorizzarlo da qualche altra parte. – Nippysaurus

+0

Devo usare CookieContainer perché è l'unico modo per inviare cookie a HttpWebRequest. – Salar

+0

Non posso credere che finalmente ho avuto uno scenario in cui cambiare il framework da 4.0 a 3.5 (non stavo usando 4.0 roba) ha rotto il mio programma.Mi ci è voluto un po 'di tempo per capire perché mancavano improvvisamente i cookie di sessione per l'autenticazione. Hanno risolto questo problema in 4.0, quindi cambiare il framework ha introdotto un bug nel mio programma :-) – VVS

risposta

24

Ho appena trovato la correzione di questo bug e discusso qui: http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain-handling-bug-fix.html

ecco la soluzione:

  1. Non usare .Add (Cookie), utilizzare solo il metodo .Add (Uri, Cookie).
  2. chiamata BugFix_CookieDomain ogni volta che si aggiunge un cookie al contenitore o prima di utilizzare .GetCookie o prima che il sistema utilizza il contenitore.

    private void BugFix_CookieDomain(CookieContainer cookieContainer) 
    { 
        System.Type _ContainerType = typeof(CookieContainer); 
        Hashtable table = (Hashtable)_ContainerType.InvokeMember("m_domainTable", 
               System.Reflection.BindingFlags.NonPublic | 
               System.Reflection.BindingFlags.GetField | 
               System.Reflection.BindingFlags.Instance, 
               null, 
               cookieContainer, 
               new object[] { }); 
        ArrayList keys = new ArrayList(table.Keys); 
        foreach (string keyObj in keys) 
        { 
         string key = (keyObj as string); 
         if (key[0] == '.') 
         { 
          string newKey = key.Remove(0, 1); 
          table[newKey] = table[keyObj]; 
         } 
        } 
    } 
    
+0

Grazie mille! Ho passato 2 giorni a eseguire il debug di questo, pensando che un principiante come me dovrebbe imparare a calcolare le librerie da solo. Ma era un insetto! –

+0

Argh ... da dove viene _ContainerType? Il mio compilatore non lo troverà! –

+0

Ah! L'ho trovato ... È necessario sostituire _ContainerType di cookieContainer.GetType() –

0
//bug fix, exists only in 3.5 FW, please wrap it with defines 
//http://dot-net-expertise.blogspot.com/2009/10/cookiecontainer-domain-handling-bug-fix.html 
if(!value.Contains("://www.")) //we are going to hit the bug 
{ 
    string urlWWW = value.Replace("://", "://www."); 
    Uri uriWWW = new Uri(urlWWW); 
    foreach (Cookie c in _cookieContainer.GetCookies(uriWWW)) 
     if (c.Domain.StartsWith(".")) 
      request.Headers["Cookies"] += c.Name + "=" + c.Value + ";"; //manually add the cookies 
} 
//~bug fix 
0

perso la mia giornata con questo problema. La risposta di CallMeLaNN non mi ha aiutato (sto usando .Net 4.5). Nel mio caso il problema era nell'ordine di impostazione del corpo dei cookie di richiesta e impostazioni.

In questo caso i cookie non verrà inviato al server:

  var response = (HttpWebRequest)WebRequest.Create("http://localhost:4433/"); 

      using (var requestStream = response.GetRequestStream()) 
      { 
       using (var streamWriter = new StreamWriter(requestStream)) 
       { 
        requestStream.Write(RequestContent); 
       } 
      } 

      response.CookieContainer.Add(new Cookie("Name", "Value")); 
      await response.GetResponseAsync(); 

Per farlo funzionare cambiare l'ordine richiesto:

  var response = (HttpWebRequest)WebRequest.Create("http://localhost:4433/"); 

      response.CookieContainer.Add(new Cookie("Name", "Value")); 
      await response.GetResponseAsync(); 

      using (var requestStream = response.GetRequestStream()) 
      { 
       using (var streamWriter = new StreamWriter(requestStream)) 
       { 
        requestStream.Write(RequestContent); 
       } 
      } 
0

Ho creato una correzione per questo problema che funziona su app Windows 10/UWP/.NET Core. Il problema è che gli interni di CookieContainer sono diversi, ma altrettanto schifosi, come nel .NET Framework. Quindi la soluzione accettata non funziona più.

Ma invece di "fissare" il CookieContainer, ho appena scritto una versione di GetCookies() che ottiene tutti i cookie per un determinato dominio con una stringa, indipendentemente dal loro stato "sicuro" o se sono preceduti da un punto. Sentiti libero di modificarlo come ritieni adatto alle tue esigenze, e vedrò di averne implementato una versione in una futura versione di .NET Core.

using System.Collections.Generic; 
using System.Reflection; 

namespace System.Net 
{ 

    /// <summary> 
    /// Contains extensions for the <see cref="CookieContaner"/> class. 
    /// </summary> 
    public static class CookieContainerExtensions 
    { 

     /// <summary> 
     /// Uses Reflection to get ALL of the <see cref="Cookie">Cookies</see> where <see cref="Cookie.Domain"/> 
     /// contains part of the specified string. Will return cookies for any subdomain, as well as dotted-prefix cookies. 
     /// </summary> 
     /// <param name="cookieContainer">The <see cref="CookieContainer"/> to extract the <see cref="Cookie">Cookies</see> from.</param> 
     /// <param name="domain">The string that contains part of the domain you want to extract cookies for.</param> 
     /// <returns></returns> 
     public static IEnumerable<Cookie> GetCookies(this CookieContainer cookieContainer, string domain) 
     { 
      var domainTable = GetFieldValue<dynamic>(cookieContainer, "_domainTable"); 
      foreach (var entry in domainTable) 
      { 
       string key = GetPropertyValue<string>(entry, "Key"); 

       if (key.Contains(domain)) 
       { 
        var value = GetPropertyValue<dynamic>(entry, "Value"); 

        var internalList = GetFieldValue<SortedList<string, CookieCollection>>(value, "_list"); 
        foreach (var li in internalList) 
        { 
         foreach (Cookie cookie in li.Value) 
         { 
          yield return cookie; 
         } 
        } 
       } 
      } 
     } 

     /// <summary> 
     /// Gets the value of a Field for a given object instance. 
     /// </summary> 
     /// <typeparam name="T">The <see cref="Type"/> you want the value to be converted to when returned.</typeparam> 
     /// <param name="instance">The Type instance to extract the Field's data from.</param> 
     /// <param name="fieldName">The name of the Field to extract the data from.</param> 
     /// <returns></returns> 
     internal static T GetFieldValue<T>(object instance, string fieldName) 
     { 
      BindingFlags bindFlags = BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static; 
      FieldInfo fi = instance.GetType().GetField(fieldName, bindFlags); 
      return (T)fi.GetValue(instance); 
     } 

     /// <summary> 
     /// Gets the value of a Property for a given object instance. 
     /// </summary> 
     /// <typeparam name="T">The <see cref="Type"/> you want the value to be converted to when returned.</typeparam> 
     /// <param name="instance">The Type instance to extract the Property's data from.</param> 
     /// <param name="propertyName">The name of the Property to extract the data from.</param> 
     /// <returns></returns> 
     internal static T GetPropertyValue<T>(object instance, string propertyName) 
     { 
      var pi = instance.GetType().GetProperty(propertyName); 
      return (T)pi.GetValue(instance, null); 
     } 

    } 

}