The Labs \ Source Viewer \ SSCLI \ System.Net \ HeaderVariantInfo

  1. //------------------------------------------------------------------------------
  2. // <copyright file="cookiecontainer.cs" company="Microsoft">
  3. //
  4. // Copyright (c) 2006 Microsoft Corporation. All rights reserved.
  5. //
  6. // The use and distribution terms for this software are contained in the file
  7. // named license.txt, which can be found in the root of this distribution.
  8. // By using this software in any fashion, you are agreeing to be bound by the
  9. // terms of this license.
  10. //
  11. // You must not remove this notice, or any other, from this software.
  12. //
  13. // </copyright>
  14. //------------------------------------------------------------------------------
  15. namespace System.Net
  16. {
  17.    
  18.     using System.Collections;
  19.     using System.Threading;
  20.     using System.Globalization;
  21.     using System.Net.NetworkInformation;
  22.    
  23.     internal struct HeaderVariantInfo
  24.     {
  25.        
  26.         string m_name;
  27.         CookieVariant m_variant;
  28.        
  29.         internal HeaderVariantInfo(string name, CookieVariant variant)
  30.         {
  31.             m_name = name;
  32.             m_variant = variant;
  33.         }
  34.        
  35.         internal string Name {
  36.             get { return m_name; }
  37.         }
  38.        
  39.         internal CookieVariant Variant {
  40.             get { return m_variant; }
  41.         }
  42.     }
  43.    
  44.     //
  45.     // CookieContainer
  46.     //
  47.     // Manage cookies for a user (implicit). Based on RFC 2965
  48.     //
  49.    
  50.     /// <devdoc>
  51.     /// <para>[To be supplied.]</para>
  52.     /// </devdoc>
  53.     [Serializable()]
  54.     public class CookieContainer
  55.     {
  56.        
  57.         public const int DefaultCookieLimit = 300;
  58.         public const int DefaultPerDomainCookieLimit = 20;
  59.         public const int DefaultCookieLengthLimit = 4096;
  60.        
  61.         static readonly HeaderVariantInfo[] HeaderInfo = {new HeaderVariantInfo(HttpKnownHeaderNames.SetCookie, CookieVariant.Rfc2109), new HeaderVariantInfo(HttpKnownHeaderNames.SetCookie2, CookieVariant.Rfc2965)};
  62.        
  63.         // fields
  64.        
  65.         Hashtable m_domainTable = new Hashtable();
  66.         int m_maxCookieSize = DefaultCookieLengthLimit;
  67.         int m_maxCookies = DefaultCookieLimit;
  68.         int m_maxCookiesPerDomain = DefaultPerDomainCookieLimit;
  69.         int m_count = 0;
  70.         string m_fqdnMyDomain = String.Empty;
  71.        
  72.         // constructors
  73.        
  74.         /// <devdoc>
  75.         /// <para>[To be supplied.]</para>
  76.         /// </devdoc>
  77.         public CookieContainer()
  78.         {
  79.             string domain = IPGlobalProperties.InternalGetIPGlobalProperties().DomainName;
  80.             if (domain != null && domain.Length > 1) {
  81.                 m_fqdnMyDomain = '.' + domain;
  82.             }
  83.             //Otherwise it will remain string.Empty
  84.         }
  85.        
  86.         /// <devdoc>
  87.         /// <para>[To be supplied.]</para>
  88.         /// </devdoc>
  89.         public CookieContainer(int capacity) : this()
  90.         {
  91.             if (capacity <= 0) {
  92.                 throw new ArgumentException(SR.GetString(SR.net_toosmall), "Capacity");
  93.             }
  94.             m_maxCookies = capacity;
  95.         }
  96.        
  97.         /// <devdoc>
  98.         /// <para>[To be supplied.]</para>
  99.         /// </devdoc>
  100.         public CookieContainer(int capacity, int perDomainCapacity, int maxCookieSize) : this(capacity)
  101.         {
  102.             if (perDomainCapacity != Int32.MaxValue && (perDomainCapacity <= 0 || perDomainCapacity > capacity)) {
  103.                 throw new ArgumentOutOfRangeException("perDomainCapacity", SR.GetString(SR.net_cookie_capacity_range, "PerDomainCapacity", 0, capacity));
  104.             }
  105.             m_maxCookiesPerDomain = perDomainCapacity;
  106.             if (maxCookieSize <= 0) {
  107.                 throw new ArgumentException(SR.GetString(SR.net_toosmall), "MaxCookieSize");
  108.             }
  109.             m_maxCookieSize = maxCookieSize;
  110.         }
  111.        
  112.         // properties
  113.        
  114.         /// <devdoc>
  115.         /// <para>Note that after shrinking the capacity Count can become greater than Capacity.</para>
  116.         /// </devdoc>
  117.         public int Capacity {
  118.             get { return m_maxCookies; }
  119.             set {
  120.                 if (value <= 0 || (value < m_maxCookiesPerDomain && m_maxCookiesPerDomain != Int32.MaxValue)) {
  121.                     throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_cookie_capacity_range, "Capacity", 0, m_maxCookiesPerDomain));
  122.                 }
  123.                 if (value < m_maxCookies) {
  124.                     m_maxCookies = value;
  125.                     AgeCookies(null);
  126.                 }
  127.                 m_maxCookies = value;
  128.             }
  129.         }
  130.        
  131.         /// <devdoc>
  132.         /// <para>returns the total number of cookies in the container.</para>
  133.         /// </devdoc>
  134.         public int Count {
  135.             get { return m_count; }
  136.         }
  137.        
  138.         /// <devdoc>
  139.         /// <para>[To be supplied.]</para>
  140.         /// </devdoc>
  141.         public int MaxCookieSize {
  142.             get { return m_maxCookieSize; }
  143.             set {
  144.                 if (value <= 0) {
  145.                     throw new ArgumentOutOfRangeException("value");
  146.                 }
  147.                 m_maxCookieSize = value;
  148.             }
  149.         }
  150.        
  151.         /// <devdoc>
  152.         /// <para>After shrinking domain capacity each domain will less hold than new domain capacity</para>
  153.         /// </devdoc>
  154.         public int PerDomainCapacity {
  155.             get { return m_maxCookiesPerDomain; }
  156.             set {
  157.                 if (value <= 0 || (value > m_maxCookies && value != Int32.MaxValue)) {
  158.                     throw new ArgumentOutOfRangeException("value");
  159.                 }
  160.                 if (value < m_maxCookiesPerDomain) {
  161.                     m_maxCookiesPerDomain = value;
  162.                     AgeCookies(null);
  163.                 }
  164.                 m_maxCookiesPerDomain = value;
  165.             }
  166.         }
  167.        
  168.         // methods
  169.        
  170.         /// <devdoc>
  171.         /// <para>[To be supplied.]</para>
  172.         /// </devdoc>
  173.        
  174.         //This method will construct faked URI, Domain property is required for param.
  175.         public void Add(Cookie cookie)
  176.         {
  177.             if (cookie == null) {
  178.                 throw new ArgumentNullException("cookie");
  179.             }
  180.            
  181.             if (cookie.Domain.Length == 0) {
  182.                 throw new ArgumentException(SR.GetString(SR.net_emptystringcall), "cookie.Domain");
  183.             }
  184.            
  185.             // We don't know cookie verification status -> re-create cookie and verify it
  186.             Cookie new_cookie = new Cookie(cookie.Name, cookie.Value);
  187.             Uri uri;
  188.            
  189.             new_cookie.Version = cookie.Version;
  190.            
  191.             // We cannot add an invalid cookie into the container.
  192.             // Trying to prepare Uri for the cookie verification
  193.             string uriStr = (cookie.Secure ? Uri.UriSchemeHttps : Uri.UriSchemeHttp) + Uri.SchemeDelimiter;
  194.            
  195.             if (cookie.Domain[0] == '.') {
  196.                 uriStr += "0";
  197.                 // Uri cctor should eat this, faked host.
  198.                 new_cookie.Domain = cookie.Domain;
  199.                 // Otherwise keep Domain as implicitly set
  200.             }
  201.             uriStr += cookie.Domain;
  202.            
  203.            
  204.             // Either keep Port as implici or set it according to original cookie
  205.             if (cookie.PortList != null) {
  206.                 new_cookie.Port = cookie.Port;
  207.                 uriStr += ":" + cookie.PortList[0];
  208.             }
  209.            
  210.             // Path must be present, set to root by default
  211.             new_cookie.Path = cookie.Path.Length == 0 ? "/" : cookie.Path;
  212.             uriStr += cookie.Path;
  213.            
  214.             if (!Uri.TryCreate(uriStr, UriKind.Absolute, out uri))
  215.                 throw new CookieException(SR.GetString(SR.net_cookie_attribute, "Domain", cookie.Domain));
  216.            
  217.             new_cookie.VerifySetDefaults(CookieVariant.Unknown, uri, IsLocal(uri.Host), m_fqdnMyDomain, true, true);
  218.            
  219.             Add(new_cookie, true);
  220.         }
  221.        
  222.         private void AddRemoveDomain(string key, PathList value)
  223.         {
  224.             // Hashtable support multiple readers ans one writer
  225.             // Synchronize writers (make them to be just one)
  226.             lock (this) {
  227.                 if (value == null) {
  228.                     m_domainTable.Remove(key);
  229.                 }
  230.                 else {
  231.                     m_domainTable[key] = value;
  232.                 }
  233.             }
  234.         }
  235.        
  236.         // This method is called *only* when cookie verification is done,
  237.         // so unlike with public Add(Cookie cookie) the cookie is in sane condition
  238.         internal void Add(Cookie cookie, bool throwOnError)
  239.         {
  240.            
  241.             PathList pathList;
  242.            
  243.             if (cookie.Value.Length > m_maxCookieSize) {
  244.                 if (throwOnError) {
  245.                     throw new CookieException(SR.GetString(SR.net_cookie_size, cookie.ToString(), m_maxCookieSize));
  246.                 }
  247.                 return;
  248.             }
  249.            
  250.             try {
  251.                
  252.                 pathList = (PathList)m_domainTable[cookie.DomainKey];
  253.                 if (pathList == null) {
  254.                     pathList = new PathList();
  255.                     AddRemoveDomain(cookie.DomainKey, pathList);
  256.                 }
  257.                 int domain_count = pathList.GetCookiesCount();
  258.                
  259.                 CookieCollection cookies = (CookieCollection)pathList[cookie.Path];
  260.                
  261.                 if (cookies == null) {
  262.                     cookies = new CookieCollection();
  263.                     pathList[cookie.Path] = cookies;
  264.                 }
  265.                
  266.                 if (cookie.Expired) {
  267.                     //Explicit removal command (Max-Age == 0)
  268.                     lock (cookies) {
  269.                         int idx = cookies.IndexOf(cookie);
  270.                         if (idx != -1) {
  271.                             cookies.RemoveAt(idx);
  272.                             --m_count;
  273.                         }
  274.                     }
  275.                 }
  276.                 else {
  277.                     //This is about real cookie adding, check Capacity first
  278.                     if (domain_count >= m_maxCookiesPerDomain && !AgeCookies(cookie.DomainKey)) {
  279.                         return;
  280.                         //cannot age -> reject new cookie
  281.                     }
  282.                     else if (this.m_count >= m_maxCookies && !AgeCookies(null)) {
  283.                         return;
  284.                         //cannot age -> reject new cookie
  285.                     }
  286.                    
  287.                     //about to change the collection
  288.                     lock (cookies) {
  289.                         m_count += cookies.InternalAdd(cookie, true);
  290.                     }
  291.                 }
  292.             }
  293.             catch (Exception e) {
  294.                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
  295.                     throw;
  296.                 }
  297.                
  298.                 if (throwOnError) {
  299.                     throw new CookieException(SR.GetString(SR.net_container_add_cookie), e);
  300.                 }
  301.             }
  302.             catch {
  303.                 if (throwOnError) {
  304.                     throw new CookieException(SR.GetString(SR.net_container_add_cookie), new Exception(SR.GetString(SR.net_nonClsCompliantException)));
  305.                 }
  306.             }
  307.         }
  308.        
  309.         //
  310.         // This function, once called, must delete at least one cookie
  311.         // If there are expired cookies in given scope they are cleaned up
  312.         // If nothing found the least used Collection will be found and removed
  313.         // from the container.
  314.         //
  315.         // Also note that expired cookies are also removed during request preparation
  316.         // (this.GetCookies method)
  317.         //
  318.         // Param. 'domain' == null means to age in the whole container
  319.         //
  320.         private bool AgeCookies(string domain)
  321.         {
  322.            
  323.             // border case => shrinked to zero
  324.             if (m_maxCookies == 0 || m_maxCookiesPerDomain == 0) {
  325.                 m_domainTable = new Hashtable();
  326.                 m_count = 0;
  327.                 return false;
  328.             }
  329.            
  330.             int removed = 0;
  331.             DateTime oldUsed = DateTime.MaxValue;
  332.             DateTime tempUsed;
  333.            
  334.             CookieCollection lruCc = null;
  335.             string lruDomain = null;
  336.             string tempDomain = null;
  337.            
  338.             PathList pathList;
  339.             int domain_count = 0;
  340.             int itemp = 0;
  341.             float remainingFraction = 1f;
  342.            
  343.             // the container was shrinked, might need additional cleanup for each domain
  344.             if (m_count > m_maxCookies) {
  345.                 // Means the fraction of the container to be left
  346.                 // Each domain will be cut accordingly
  347.                 remainingFraction = (float)m_maxCookies / (float)m_count;
  348.                
  349.             }
  350.            
  351.             foreach (DictionaryEntry entry in m_domainTable) {
  352.                 if (domain == null) {
  353.                     tempDomain = (string)entry.Key;
  354.                     pathList = (PathList)entry.Value;
  355.                     //aliasing to trick foreach
  356.                 }
  357.                 else {
  358.                     tempDomain = domain;
  359.                     pathList = (PathList)m_domainTable[domain];
  360.                 }
  361.                
  362.                 domain_count = 0;
  363.                 // cookies in the domain
  364.                 foreach (CookieCollection cc in pathList.Values) {
  365.                     itemp = ExpireCollection(cc);
  366.                     removed += itemp;
  367.                     m_count -= itemp;
  368.                     //update this container count;
  369.                     domain_count += cc.Count;
  370.                     // we also find the least used cookie collection in ENTIRE container
  371.                     // we count the collection as LRU only if it holds 1+ elements
  372.                     if (cc.Count > 0 && (tempUsed = cc.TimeStamp(CookieCollection.Stamp.Check)) < oldUsed) {
  373.                         lruDomain = tempDomain;
  374.                         lruCc = cc;
  375.                         oldUsed = tempUsed;
  376.                     }
  377.                 }
  378.                
  379.                 // Check if we have reduced to the limit of the domain by expiration only
  380.                 int min_count = Math.Min((int)(domain_count * remainingFraction), Math.Min(m_maxCookiesPerDomain, m_maxCookies) - 1);
  381.                 if (domain_count > min_count) {
  382.                     //That case require sorting all domain collections by timestamp
  383.                     Array cookies = Array.CreateInstance(typeof(CookieCollection), pathList.Count);
  384.                     Array stamps = Array.CreateInstance(typeof(DateTime), pathList.Count);
  385.                     foreach (CookieCollection cc in pathList.Values) {
  386.                         stamps.SetValue(cc.TimeStamp(CookieCollection.Stamp.Check), itemp);
  387.                         cookies.SetValue(cc, itemp);
  388.                         ++itemp;
  389.                     }
  390.                     Array.Sort(stamps, cookies);
  391.                    
  392.                     itemp = 0;
  393.                     for (int i = 0; i < pathList.Count; ++i) {
  394.                         CookieCollection cc = (CookieCollection)cookies.GetValue(i);
  395.                        
  396.                         lock (cc) {
  397.                             while (domain_count > min_count && cc.Count > 0) {
  398.                                 cc.RemoveAt(0);
  399.                                 --domain_count;
  400.                                 --m_count;
  401.                                 ++removed;
  402.                             }
  403.                         }
  404.                         if (domain_count <= min_count) {
  405.                             break;
  406.                         }
  407.                     }
  408.                    
  409.                     if (domain_count > min_count && domain != null) {
  410.                         //cannot complete aging of explicit domain (no cookie adding allowed)
  411.                         return false;
  412.                     }
  413.                 }
  414.                
  415.                 // we have completed aging of specific domain
  416.                 if (domain != null) {
  417.                     return true;
  418.                 }
  419.                
  420.             }
  421.            
  422.             // The rest is for entire container aging
  423.             // We must get at least one free slot.
  424.            
  425.             //Don't need to appy LRU if we already cleaned something
  426.             if (removed != 0) {
  427.                 return true;
  428.             }
  429.            
  430.             if (oldUsed == DateTime.MaxValue) {
  431.                 //Something strange. Either capacity is 0 or all collections are locked with cc.Used
  432.                 return false;
  433.             }
  434.            
  435.             // Remove oldest cookies from the least used collection
  436.             lock (lruCc) {
  437.                 while (m_count >= m_maxCookies && lruCc.Count > 0) {
  438.                     lruCc.RemoveAt(0);
  439.                     --m_count;
  440.                 }
  441.             }
  442.             return true;
  443.         }
  444.        
  445.         //return number of cookies removed from the collection
  446.         private int ExpireCollection(CookieCollection cc)
  447.         {
  448.             int oldCount = cc.Count;
  449.             int idx = oldCount - 1;
  450.            
  451.             // minor optimization by caching Now
  452.             DateTime now = DateTime.Now;
  453.            
  454.             lock (cc) {
  455.                 //Cannot use enumerator as we are going to alter collection
  456.                 while (idx >= 0) {
  457.                     Cookie cookie = cc[idx];
  458.                     if (cookie.Expires <= now && cookie.Expires != DateTime.MinValue) {
  459.                        
  460.                         cc.RemoveAt(idx);
  461.                     }
  462.                     --idx;
  463.                 }
  464.             }
  465.             return oldCount - cc.Count;
  466.         }
  467.        
  468.        
  469.        
  470.         public void Add(CookieCollection cookies)
  471.         {
  472.             if (cookies == null) {
  473.                 throw new ArgumentNullException("cookies");
  474.             }
  475.             foreach (Cookie c in cookies) {
  476.                 Add(c);
  477.             }
  478.         }
  479.        
  480.         //
  481.         // This will try (if needed) get the full domain name of the host given the Uri
  482.         // NEVER call this function from internal methods with 'fqdnRemote' == NULL
  483.         // Since this method counts security issue for DNS and hence will slow
  484.         // the performance
  485.         //
  486.         internal bool IsLocal(string host)
  487.         {
  488.            
  489.             int dot = host.IndexOf('.');
  490.             if (dot == -1) {
  491.                 // No choice but to treat it as a host on the local domain
  492.                 // This also covers 'localhost' and 'loopback'
  493.                 return true;
  494.             }
  495.            
  496.             // quick test for usual case
  497.             if (host == "127.0.0.1") {
  498.                 return true;
  499.             }
  500.            
  501.             // test domain membership
  502.             if (string.Compare(m_fqdnMyDomain, 0, host, dot, m_fqdnMyDomain.Length, StringComparison.OrdinalIgnoreCase) == 0) {
  503.                 return true;
  504.             }
  505.            
  506.             // test for "127.###.###.###" without using regex
  507.             string[] ipParts = host.Split('.');
  508.             if (ipParts != null && ipParts.Length == 4 && ipParts[0] == "127") {
  509.                 int i;
  510.                 for (i = 1; i < 4; i++) {
  511.                     switch (ipParts[i].Length) {
  512.                         case 3:
  513.                             if (ipParts[i][2] < '0' || ipParts[i][2] > '9') {
  514.                                 break;
  515.                             }
  516.                             goto case 2;
  517.                             break;
  518.                         case 2:
  519.                            
  520.                             if (ipParts[i][1] < '0' || ipParts[i][1] > '9') {
  521.                                 break;
  522.                             }
  523.                             goto case 1;
  524.                             break;
  525.                         case 1:
  526.                            
  527.                             if (ipParts[i][0] < '0' || ipParts[i][0] > '9') {
  528.                                 break;
  529.                             }
  530.                             continue;
  531.                     }
  532.                     break;
  533.                 }
  534.                 if (i == 4) {
  535.                     return true;
  536.                 }
  537.             }
  538.            
  539.             return false;
  540.         }
  541.        
  542.         /// <devdoc>
  543.         /// <para>[To be supplied.]</para>
  544.         /// </devdoc>
  545.         public void Add(Uri uri, Cookie cookie)
  546.         {
  547.             if (uri == null) {
  548.                 throw new ArgumentNullException("uri");
  549.             }
  550.             if (cookie == null) {
  551.                 throw new ArgumentNullException("cookie");
  552.             }
  553.             cookie.VerifySetDefaults(CookieVariant.Unknown, uri, IsLocal(uri.Host), m_fqdnMyDomain, true, true);
  554.            
  555.             Add(cookie, true);
  556.         }
  557.        
  558.         /// <devdoc>
  559.         /// <para>[To be supplied.]</para>
  560.         /// </devdoc>
  561.        
  562.         public void Add(Uri uri, CookieCollection cookies)
  563.         {
  564.             if (uri == null) {
  565.                 throw new ArgumentNullException("uri");
  566.             }
  567.             if (cookies == null) {
  568.                 throw new ArgumentNullException("cookies");
  569.             }
  570.             bool isLocalDomain = IsLocal(uri.Host);
  571.             foreach (Cookie c in cookies) {
  572.                 c.VerifySetDefaults(CookieVariant.Unknown, uri, isLocalDomain, m_fqdnMyDomain, true, true);
  573.                 Add(c, true);
  574.             }
  575.         }
  576.        
  577.         internal CookieCollection CookieCutter(Uri uri, string headerName, string setCookieHeader, bool isThrow)
  578.         {
  579.             GlobalLog.Print("CookieContainer#" + ValidationHelper.HashString(this) + "::CookieCutter() uri:" + uri + " headerName:" + headerName + " setCookieHeader:" + setCookieHeader + " isThrow:" + isThrow);
  580.             CookieCollection cookies = new CookieCollection();
  581.             CookieVariant variant = CookieVariant.Unknown;
  582.             if (headerName == null) {
  583.                 variant = CookieVariant.Default;
  584.             }
  585.             else
  586.                 for (int i = 0; i < HeaderInfo.Length; ++i) {
  587.                     if ((String.Compare(headerName, HeaderInfo[i].Name, StringComparison.OrdinalIgnoreCase) == 0)) {
  588.                         variant = HeaderInfo[i].Variant;
  589.                     }
  590.                 }
  591.             bool isLocalDomain = IsLocal(uri.Host);
  592.             try {
  593.                 CookieParser parser = new CookieParser(setCookieHeader);
  594.                 do {
  595.                     Cookie cookie = parser.Get();
  596.                     GlobalLog.Print("CookieContainer#" + ValidationHelper.HashString(this) + "::CookieCutter() CookieParser returned cookie:" + ValidationHelper.ToString(cookie));
  597.                     if (cookie == null) {
  598.                         break;
  599.                     }
  600.                    
  601.                     //Parser marks invalid cookies this way
  602.                     if (ValidationHelper.IsBlankString(cookie.Name)) {
  603.                         if (isThrow) {
  604.                             throw new CookieException(SR.GetString(SR.net_cookie_format));
  605.                         }
  606.                         //Otherwise, ignore (reject) cookie
  607.                         continue;
  608.                     }
  609.                    
  610.                     // this will set the default values from the response URI
  611.                     // AND will check for cookie validity
  612.                     if (!cookie.VerifySetDefaults(variant, uri, isLocalDomain, m_fqdnMyDomain, true, isThrow)) {
  613.                         continue;
  614.                     }
  615.                     // If many same cookies arrive we collapse them into just one, hence setting
  616.                     // parameter isStrict = true below
  617.                     cookies.InternalAdd(cookie, true);
  618.                    
  619.                 }
  620.                 while (true);
  621.             }
  622.             catch (Exception e) {
  623.                 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
  624.                     throw;
  625.                 }
  626.                
  627.                 if (isThrow) {
  628.                     throw new CookieException(SR.GetString(SR.net_cookie_parse_header, uri.AbsoluteUri), e);
  629.                 }
  630.             }
  631.             catch {
  632.                 if (isThrow) {
  633.                     throw new CookieException(SR.GetString(SR.net_cookie_parse_header, uri.AbsoluteUri), new Exception(SR.GetString(SR.net_nonClsCompliantException)));
  634.                 }
  635.             }
  636.            
  637.             foreach (Cookie c in cookies) {
  638.                 Add(c, isThrow);
  639.             }
  640.            
  641.             return cookies;
  642.         }
  643.        
  644.         /// <devdoc>
  645.         /// <para>[To be supplied.]</para>
  646.         /// </devdoc>
  647.         public CookieCollection GetCookies(Uri uri)
  648.         {
  649.             if (uri == null) {
  650.                 throw new ArgumentNullException("uri");
  651.             }
  652.             return InternalGetCookies(uri);
  653.         }
  654.        
  655.         internal CookieCollection InternalGetCookies(Uri uri)
  656.         {
  657.            
  658.             bool isSecure = (uri.Scheme == Uri.UriSchemeHttps);
  659.             int port = uri.Port;
  660.             CookieCollection cookies = new CookieCollection();
  661.             ArrayList nameKeys = new ArrayList();
  662.             int firstCompatibleVersion0SpecKey = 0;
  663.            
  664.             string fqdnRemote = uri.Host;
  665.            
  666.             int dot = fqdnRemote.IndexOf('.');
  667.             if (dot == -1) {
  668.                 // DNS.resolve may return short names even for other inet domains ;-(
  669.                 // We _don't_ know what the exact domain is, so try also grab short hostname cookies.
  670.                 nameKeys.Add(fqdnRemote);
  671.                 // grab long name from the local domain
  672.                 if (m_fqdnMyDomain != null && m_fqdnMyDomain.Length != 0) {
  673.                     nameKeys.Add(fqdnRemote + m_fqdnMyDomain);
  674.                     // grab the local domain itself
  675.                     nameKeys.Add(m_fqdnMyDomain);
  676.                     firstCompatibleVersion0SpecKey = 3;
  677.                 }
  678.                 else {
  679.                     firstCompatibleVersion0SpecKey = 1;
  680.                 }
  681.             }
  682.             else {
  683.                 // grab the host itself
  684.                 nameKeys.Add(fqdnRemote);
  685.                 // grab the host domain
  686.                 nameKeys.Add(fqdnRemote.Substring(dot));
  687.                 firstCompatibleVersion0SpecKey = 2;
  688.                 // The following block is only for compatibility with Version0 spec.
  689.                 // Still, we'll add only Plain-Variant cookies if found under below keys
  690.                 if (fqdnRemote.Length > 2) {
  691.                     // We ignore the '.' at the end on the name
  692.                     int last = fqdnRemote.LastIndexOf('.', fqdnRemote.Length - 2);
  693.                     //AND keys with <2 dots inside.
  694.                     if (last > 0) {
  695.                         last = fqdnRemote.LastIndexOf('.', last - 1);
  696.                     }
  697.                     if (last != -1) {
  698.                         while ((dot < last) && (dot = fqdnRemote.IndexOf('.', dot + 1)) != -1) {
  699.                             nameKeys.Add(fqdnRemote.Substring(dot));
  700.                         }
  701.                     }
  702.                 }
  703.             }
  704.            
  705.             foreach (string key in nameKeys) {
  706.                 bool found = false;
  707.                 bool defaultAdded = false;
  708.                 PathList pathList = (PathList)m_domainTable[key];
  709.                 --firstCompatibleVersion0SpecKey;
  710.                
  711.                 if (pathList == null) {
  712.                     continue;
  713.                 }
  714.                
  715.                 foreach (DictionaryEntry entry in pathList) {
  716.                     string path = (string)entry.Key;
  717.                     if (uri.AbsolutePath.StartsWith(CookieParser.CheckQuoted(path))) {
  718.                         found = true;
  719.                        
  720.                         CookieCollection cc = (CookieCollection)entry.Value;
  721.                         cc.TimeStamp(CookieCollection.Stamp.Set);
  722.                         MergeUpdateCollections(cookies, cc, port, isSecure, (firstCompatibleVersion0SpecKey < 0));
  723.                        
  724.                         if (path == "/") {
  725.                             defaultAdded = true;
  726.                         }
  727.                     }
  728.                     else if (found) {
  729.                         break;
  730.                     }
  731.                 }
  732.                
  733.                 if (!defaultAdded) {
  734.                     CookieCollection cc = (CookieCollection)pathList["/"];
  735.                    
  736.                     if (cc != null) {
  737.                         cc.TimeStamp(CookieCollection.Stamp.Set);
  738.                         MergeUpdateCollections(cookies, cc, port, isSecure, (firstCompatibleVersion0SpecKey < 0));
  739.                     }
  740.                 }
  741.                
  742.                 // Remove unused domain
  743.                 // (This is the only place that does domain removal)
  744.                 if (pathList.Count == 0) {
  745.                     AddRemoveDomain(key, null);
  746.                 }
  747.             }
  748.             return cookies;
  749.         }
  750.        
  751.         private void MergeUpdateCollections(CookieCollection destination, CookieCollection source, int port, bool isSecure, bool isPlainOnly)
  752.         {
  753.            
  754.             // we may change it
  755.             lock (source) {
  756.                
  757.                 //cannot use foreach as we going update 'source'
  758.                 for (int idx = 0; idx < source.Count; ++idx) {
  759.                     bool to_add = false;
  760.                    
  761.                     Cookie cookie = source[idx];
  762.                    
  763.                     if (cookie.Expired) {
  764.                         //If expired, remove from container and don't add to the destination
  765.                         source.RemoveAt(idx);
  766.                         --m_count;
  767.                         --idx;
  768.                     }
  769.                     else {
  770.                         //Add only if port does match to this request URI
  771.                         //or was not present in the original response
  772.                         if (isPlainOnly && cookie.Variant != CookieVariant.Plain) {
  773.                             ;
  774.                             //don;t add
  775.                         }
  776.                         else if (cookie.PortList != null) {
  777.                             foreach (int p in cookie.PortList) {
  778.                                 if (p == port) {
  779.                                     to_add = true;
  780.                                     break;
  781.                                 }
  782.                             }
  783.                         }
  784.                         else {
  785.                             //it was implicit Port, always OK to add
  786.                             to_add = true;
  787.                         }
  788.                        
  789.                         //refuse adding secure cookie into 'unsecure' destination
  790.                         if (cookie.Secure && !isSecure) {
  791.                             to_add = false;
  792.                         }
  793.                        
  794.                         if (to_add) {
  795.                             // In 'source' are already orederd.
  796.                             // If two same cookies come from dif 'source' then they
  797.                             // will follow (not replace) each other.
  798.                             destination.InternalAdd(cookie, false);
  799.                         }
  800.                        
  801.                     }
  802.                 }
  803.             }
  804.         }
  805.        
  806.         /// <devdoc>
  807.         /// <para>[To be supplied.]</para>
  808.         /// </devdoc>
  809.         public string GetCookieHeader(Uri uri)
  810.         {
  811.             if (uri == null) {
  812.                 throw new ArgumentNullException("uri");
  813.             }
  814.             string dummy;
  815.             return GetCookieHeader(uri, out dummy);
  816.            
  817.         }
  818.        
  819.         internal string GetCookieHeader(Uri uri, out string optCookie2)
  820.         {
  821.             CookieCollection cookies = InternalGetCookies(uri);
  822.             string cookieString = String.Empty;
  823.             string delimiter = String.Empty;
  824.            
  825.             foreach (Cookie cookie in cookies) {
  826.                 cookieString += delimiter + cookie.ToString();
  827.                 delimiter = "; ";
  828.             }
  829.             optCookie2 = cookies.IsOtherVersionSeen ? (Cookie.SpecialAttributeLiteral + Cookie.VersionAttributeName + Cookie.EqualsLiteral + Cookie.MaxSupportedVersion.ToString(NumberFormatInfo.InvariantInfo)) : String.Empty;
  830.            
  831.             return cookieString;
  832.         }
  833.        
  834.        
  835.        
  836.        
  837.        
  838.        
  839.        
  840.        
  841.        
  842.        
  843.        
  844.         public void SetCookies(Uri uri, string cookieHeader)
  845.         {
  846.             if (uri == null) {
  847.                 throw new ArgumentNullException("uri");
  848.             }
  849.             if (cookieHeader == null) {
  850.                 throw new ArgumentNullException("cookieHeader");
  851.             }
  852.             CookieCutter(uri, null, cookieHeader, true);
  853.             //will throw on error
  854.         }
  855.        
  856.         #if DEBUG
  857.         /// <devdoc>
  858.         /// <para>[To be supplied.]</para>
  859.         /// </devdoc>
  860.         internal void Dump()
  861.         {
  862.             GlobalLog.Print("CookieContainer:");
  863.             foreach (DictionaryEntry de in m_domainTable) {
  864.                 GlobalLog.Print("domain = \"" + de.Key + "\"");
  865.                 ((PathList)de.Value).Dump();
  866.             }
  867.         }
  868.         #endif
  869.        
  870.     }
  871.    
  872.     [Serializable()]
  873.     internal class PathList
  874.     {
  875.         SortedList m_list = (SortedList.Synchronized(new SortedList(PathListComparer.StaticInstance)));
  876.        
  877.         public PathList()
  878.         {
  879.         }
  880.        
  881.         public int Count {
  882.             get { return m_list.Count; }
  883.         }
  884.        
  885.         public int GetCookiesCount()
  886.         {
  887.             int count = 0;
  888.             foreach (CookieCollection cc in m_list.Values) {
  889.                 count += cc.Count;
  890.             }
  891.             return count;
  892.         }
  893.        
  894.         public ICollection Values {
  895.             get { return m_list.Values; }
  896.         }
  897.        
  898.         public object this[string s]
  899.         {
  900.             get { return m_list[s]; }
  901.             set { m_list[s] = value; }
  902.         }
  903.        
  904.         public IEnumerator GetEnumerator()
  905.         {
  906.             return m_list.GetEnumerator();
  907.         }
  908.        
  909.         [Serializable()]
  910.         class PathListComparer : IComparer
  911.         {
  912.             static internal readonly PathListComparer StaticInstance = new PathListComparer();
  913.            
  914.             int IComparer.Compare(object ol, object or)
  915.             {
  916.                
  917.                 string pathLeft = CookieParser.CheckQuoted((string)ol);
  918.                 string pathRight = CookieParser.CheckQuoted((string)or);
  919.                 int ll = pathLeft.Length;
  920.                 int lr = pathRight.Length;
  921.                 int length = Math.Min(ll, lr);
  922.                
  923.                 for (int i = 0; i < length; ++i) {
  924.                     if (pathLeft[i] != pathRight[i]) {
  925.                         return pathLeft[i] - pathRight[i];
  926.                     }
  927.                 }
  928.                 return lr - ll;
  929.             }
  930.         }
  931.        
  932.        
  933.         #if DEBUG
  934.         public void Dump()
  935.         {
  936.             GlobalLog.Print("PathList:");
  937.             foreach (DictionaryEntry cookies in this) {
  938.                 GlobalLog.Print("collection = \"" + cookies.Key + "\"");
  939.                 ((CookieCollection)cookies.Value).Dump();
  940.             }
  941.         }
  942.         #endif
  943.        
  944.     }
  945. }

Developer Fusion