iPad上的Cookie到底有多長?


【故事背景】:

公司某個站點,特別依賴Cookie的使用,而且用的比較狠。在設計之初想當然地以為到達Cookie上限是猴年馬月的事兒,沒想到時過境遷,這個上限真的來了。

着手改吧,也不想投入太多。於是下面的思路就涌上心頭:

【問題】

目前遇到的瓶頸主要是單個Cookie的尺寸超大,在IE下,沒有問題,在Firefox下和Chrome下均出現單個Cookie過大,以至於被丟棄的現象。

【思路】

因此為了不侵入舊代碼,打算在站點前加一個HttpModule來切分和拼裝Cookie。為了保證一次成型,減少投入,仔細閱讀了RFC2109文檔/RFC6265(http://www.w3.org/Protocols/rfc2109/rfc2109 或http://www.rfc-editor.org/rfc/rfc6265.txt),下面是文檔里關於對瀏覽器Cookie實現的一些說明:

6.3  Implementation Limits

   Practical user agent implementations have limits on the number and
   size of cookies that they can store.  In general, user agents' cookie
   support should have no fixed limits.  They should strive to store as
   many frequently-used cookies as possible.  Furthermore, general-use
   user agents should provide each of the following minimum capabilities
   individually, although not necessarily simultaneously:

      * at least 300 cookies

      * at least 4096 bytes per cookie (as measured by the size of the
        characters that comprise the cookie non-terminal in the syntax
        description of the Set-Cookie header)

      * at least 20 cookies per unique host or domain name
   User agents created for specific purposes or for limited-capacity
   devices should provide at least 20 cookies of 4096 bytes, to ensure
   that the user can interact with a session-based origin server.

   The information in a Set-Cookie response header must be retained in
   its entirety.  If for some reason there is inadequate space to store
   the cookie, it must be discarded, not truncated.

   Applications should use as few and as small cookies as possible, and
   they should cope gracefully with the loss of a cookie.

文檔中,要求:

至少有300個Cookie總量

每個Cookie至少4096kb

每個域/主機下至少能有20個Cookie

並且還要求:

如果沒有什么特殊原因,Cookie就別做啥限制啦!

【實現】

  • 難點1:單個Cookie的尺寸限制是包括“Set-Cookie:”以后的部分,而不是Cookie.Value的部分。作為全局Cookie過濾,包括Path/Domain等值事先都是不知道的。我的做法是將.net類庫中的生成Cookie的方法翻出來抄一遍。詳見“GetSetCookieHeaderString”方法。然后就可以在輸出之前先檢查一下哪些值出現超長的現象。
  • 難點2:不同的瀏覽器對Cookie的實現不盡相同。經過簡單的測試,
    • 在Win8.1下:
      • IE11:暫時沒發現上限
      • FireFox(30.0):4145kb
      • Chrome(版本 36.0.1985.125 m):4096kb
    • 這個世界發展地太快,其實這個問題的最初版本是因為在iOS下發現了兼容性問題,所以不可忽視的是iOS下的容量限制。
      • iPad-Safari(iOSversion:7.1.2(11D257)):4125kb?
      • iPad-Chrome(iOSversion:7.1.2(11D257)):4125kb?
      • iPad-QQ瀏覽器(iOSversion:7.1.2(11D257)):4125kb?

為什么帶問號呢?因為這個地方其實最讓人頭疼。在測試的過程中,我發現Windows下,對Cookie標准理解最正確的是IE,其他瀏覽器也中規中矩地在做事,頂多是讓你做事的時候不得不畏手畏腳。但是iPad則似乎比較調皮,讓人捉摸不定。

iPad下的Cookie問題:

  • 不論哪個瀏覽器,似乎都表現出了相同的行為,它們的Cookie是共享的?但其實不是,他們似乎只是遵循了這樣的規則,因為清除Cookie和數據后,只有Safari會受到影響。
  • 單個Cookie長度是4125kb,但有時候似乎是4123kb,不知道是不是我腦子糊塗后錯誤地計算了邊界值。但是另一個問題,讓我放棄了繼續追蹤這個問題。當一個Cookie超過這個長度后,后續的Cookie就凌亂了。假設你有3個Cookie,如果有一個超長了,后續的Cookie就會丟失。不忙着下結論,繼續試驗,因此得出了下面的規律。
  • 總的Cookie長度,不再像標准所述,每個域至少30個,且每個至少4096kb,總的容量相加的長度似乎也是有限的。我循環了30次,每個Cookie使用了相同的名稱,然后控制Cookie的長度,測量出的結果有9510kb這樣的長度(所有Cookie相加),但是這不是最終的值,因為一旦后續發生丟失后,不會丟失部分Cookie,要么單個Cookie全部丟失,要么全部保留,因此這個值很難被推算出來。但這個結論讓我開始糾結。因為即便所有Cookie都沒有超過限制,也沒有至少30個Cookie讓我存放。

現在講一下我Windows版本的基本實現思路:

  • 在HttpModule.EndRequest事件中,將所有Response的Cookie進行逐一檢查,發現太長的,就將其拆成多個,並且在名稱上用原始名稱+標識符+遞增數字的方式進行顯示。然后將其添加到Response的Cookie中。為了保持類似IE這樣的瀏覽器仍然能夠使用較長的Cookie,而不使用拼拆的(IE仍然是我們的主流客戶,任何一次代碼調整,在遇到大規模發布的時候,盡量不要改變原來的實現方式,以防止問題大面積爆發),我仍然將原來較長的Cookie放進輸出流中。而Chrome這種存儲較短Cookie的瀏覽器,在遇到較長Cookie的時候,會自動丟棄,因此實現了較好的瀏覽器兼容。
  • 在HttpModule.BeginRequest事件中,將所有從瀏覽器返回的Cookie進行檢查,然后將可能發生拼接的進行拼接后供后續程序使用。那么在IE下,如果存在原來名稱的Cookie就直接用了,忽略通過拼接回來的Cookie。在Chrome下,發現原本傳下去較長的Cookie已經丟失,因此只能使用拼接后的結果,因此也避免的Cookie丟失的問題。

但這一切在iPad下,就令人頭疼了。因為只有發現超長的時候,才進行拆分,而且同時為了向上兼容,那么兩個4096就已經接近其上限了,那么在這樣的場景下,再去使用,就會發生Cookie莫名其妙隨機丟失的情況,而實驗的結果也是如此。因此,可能就需要(還沒時間去測試)首先判斷瀏覽器是不是來自於移動設備?iOS?iPad?等,然后再進行Cookie實現,那么這個方案就會更復雜,但這個上限給人的感覺是,跑得了初一跑不了十五。很快這個Cookie值就會穿過枷鎖,讓你頭疼欲裂。

真是一失足成千古恨,任何一個依賴客戶端的場景都會因為時間地推移而變得無奈。關於iPad Cookie相關的問題,如果有園友知情的也請不吝留言告知。

附上代碼:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Globalization;
  4 using System.Linq;
  5 using System.Text;
  6 using System.Threading.Tasks;
  7 using System.Web;
  8 
  9 namespace LargeCookieModule
 10 {
 11     public class CookieHelper
 12     {
 13         /// <summary>
 14         /// firefox.total:4145
 15         /// chrome.total:4096
 16         /// </summary>
 17         private const int CONST_MAX_COOKIE_BYTES = 4096;
 18         private const string CONST_BEYOND_SEPARATOR = "_C_SPO_";
 19         /// <summary>
 20         /// http://www.rfc-editor.org/rfc/rfc6265.txt
 21         /// At least 4096 bytes per cookie (as measured by the sum of the length of the cookie's name, value, and attributes).
 22         /// At least 50 cookies per domain.
 23         /// At least 3000 cookies total.
 24         /// 目前考慮最多2位數來解決cookie溢出的問題。
 25         /// 另CONST_BEYOND_SEPARATOR.Length位用來解決SEPARATOR的問題。
 26         /// </summary>
 27         private const int CONST_BEYOND_CHARS = (2 + 7);
 28         private static System.Text.Encoding defaultEncoding
 29         {
 30             get
 31             {
 32                 return System.Text.Encoding.UTF8;
 33             }
 34         }
 35 
 36         public static string GetSetCookieHeaderString(HttpContext context, HttpCookie cookie)
 37         {
 38             string name = cookie.Name;
 39             string value = cookie.Value;
 40             string domain = cookie.Domain;
 41             DateTime expires = cookie.Expires;
 42             string path = cookie.Path;
 43             bool secure = cookie.Secure;
 44             bool httpOnly = cookie.HttpOnly;
 45 
 46             StringBuilder stringBuilder = new StringBuilder();
 47             if (!string.IsNullOrEmpty(name))
 48             {
 49                 stringBuilder.Append(name);
 50                 stringBuilder.Append('=');
 51             }
 52             if (!string.IsNullOrEmpty(value))
 53             {
 54                 stringBuilder.Append(value);
 55             }
 56             if (!string.IsNullOrEmpty(domain))
 57             {
 58                 stringBuilder.Append("; domain=");
 59                 stringBuilder.Append(domain);
 60             }
 61             if (expires != DateTime.MinValue)
 62             {
 63                 stringBuilder.Append("; expires=");
 64                 stringBuilder.Append(FormatHttpCookieDateTime(expires));
 65             }
 66             if (!string.IsNullOrEmpty(path))
 67             {
 68                 stringBuilder.Append("; path=");
 69                 stringBuilder.Append(path);
 70             }
 71             if (secure)
 72             {
 73                 stringBuilder.Append("; secure");
 74             }
 75             if (httpOnly && SupportsHttpOnly(context))
 76             {
 77                 stringBuilder.Append("; HttpOnly");
 78             }
 79             return stringBuilder.ToString();
 80         }
 81 
 82         private static bool SupportsHttpOnly(HttpContext context)
 83         {
 84             if (context != null && context.Request != null)
 85             {
 86                 HttpBrowserCapabilities browser = context.Request.Browser;
 87                 return browser != null && (browser.Type != "IE5" || browser.Platform != "MacPPC");
 88             }
 89             return false;
 90         }
 91 
 92         /// <summary>
 93         /// equals to HttpUtility.FormatHttpCookieDateTime
 94         /// for java system to rewrite code.
 95         /// </summary>
 96         /// <param name="dt"></param>
 97         /// <returns></returns>
 98         private static string FormatHttpCookieDateTime(DateTime dt)
 99         {
100             if (dt < DateTime.MaxValue.AddDays(-1.0) && dt > DateTime.MinValue.AddDays(1.0))
101             {
102                 dt = dt.ToUniversalTime();
103             }
104             return dt.ToString("ddd, dd-MMM-yyyy HH':'mm':'ss 'GMT'", DateTimeFormatInfo.InvariantInfo);
105         }
106 
107 
108         /// <summary>
109         /// 是否是合法的Cookies(目前只考慮長度)
110         /// 目前暫時只知道用System.Text.Encoding.UTF8來解決。無法獲取客戶端存儲的情況。
111         /// </summary>
112         /// <param name="context"></param>
113         /// <param name="cookie"></param>
114         /// <returns></returns>
115         public static bool IsLengthLegalCookie(HttpContext context, HttpCookie cookie)
116         {
117             if (cookie == null)
118                 return false;
119             string cookieString = CookieHelper.GetSetCookieHeaderString(context, cookie);
120 
121             if (cookieString != null && defaultEncoding.GetByteCount(cookieString) <= CONST_MAX_COOKIE_BYTES)
122             {
123                 return true;
124             }
125                         
126             return false;
127         }
128 
129         /// <summary>
130         /// 獲取除了Value(name+value)值以外的字符長度
131         /// </summary>
132         /// <param name="context"></param>
133         /// <param name="cookie"></param>
134         /// <returns></returns>
135         private static int GetLengthExceptValue(HttpContext context, HttpCookie cookie)
136         {
137             if (cookie == null)
138                 return 0;
139             string cookieString = CookieHelper.GetSetCookieHeaderString(context, cookie);
140             if (!string.IsNullOrEmpty(cookieString))
141             {
142                 int index = cookieString.IndexOf(';');
143                 // if index equals to -1 result is - (-1) - 1.
144                 return cookieString.Length - index - 1;
145             }
146             return 0;
147         }
148 
149         public static List<HttpCookie> GetCookies(HttpCookieCollection cookies)
150         {
151             Dictionary<string, HttpCookie> resultCookies = new Dictionary<string, HttpCookie>();
152             SeparatorSortedDictionary<HttpCookie> separatorCookies = new SeparatorSortedDictionary<HttpCookie>();
153 
154             if (cookies != null && cookies.AllKeys != null && cookies.AllKeys.Length > 0)
155             {
156                 foreach (string key in cookies.AllKeys)
157                 {
158                     HttpCookie cookie = cookies[key];
159                     if (cookie != null && !string.IsNullOrEmpty(cookie.Name))
160                     {
161                         int separatorIndex = cookie.Name.IndexOf(CONST_BEYOND_SEPARATOR);
162                         if (separatorIndex != -1)
163                         {
164                             string prefixName = cookie.Name.Substring(0, separatorIndex);
165                             string sOrder = cookie.Name.Substring(prefixName.Length + CONST_BEYOND_SEPARATOR.Length);
166                             int iOrder = 0;
167                             if (int.TryParse(sOrder, out iOrder))
168                             {
169                                 separatorCookies.Add(prefixName, iOrder, cookie);
170                             }
171                         }
172                         else
173                         {
174                             resultCookies[cookie.Name] = cookie;
175                         }
176                     }
177                 }
178                 List<string> separatorKeys = separatorCookies.Keys;
179                 foreach (string key in separatorKeys)
180                 {
181                     if(resultCookies.ContainsKey(key))
182                         continue;
183                     HttpCookie joinCookie = separatorCookies.Join(key,
184                         (c) => c.Value,
185                         (s, c) => { c.Value = s; return c; },
186                         CloneHttpCookie);
187                     resultCookies[key] = joinCookie;
188                 }
189             }
190             return resultCookies.Values.ToList();
191         }
192 
193         public static List<HttpCookie> GetSetCookies(HttpContext context, HttpCookie cookie)
194         {
195             if (IsLengthLegalCookie(context, cookie))
196             {
197                 List<HttpCookie> cookies = new List<HttpCookie>();
198                 cookies.Add(cookie);
199                 return cookies;
200             }
201             else
202             {
203                 string name = cookie.Name;
204                 string value = cookie.Value;
205                 int maxValueCount = CONST_MAX_COOKIE_BYTES - CONST_BEYOND_CHARS - GetLengthExceptValue(context, cookie) - name.Length - 1/*name=value后面的分號*/;
206                 if (maxValueCount > 0)
207                 {
208                     byte[] bValue = defaultEncoding.GetBytes(value);
209                     int iEachChunk = 0, iChunkNum, iChunkCount = 0;
210                     SortedList<int, byte[]> chunks = new SortedList<int, byte[]>();
211                     iChunkCount = bValue.Length / maxValueCount + 1;
212                     int iChunkLastIndex = iChunkCount-1;
213                     for (iChunkNum = 0; iChunkNum < iChunkCount; ++iChunkNum)
214                     {
215                         byte[] chunk = null;
216                         if (iChunkNum != iChunkLastIndex)
217                         {
218                             chunk = new byte[maxValueCount];
219                         }
220                         else 
221                         {
222                             chunk = new byte[bValue.Length % maxValueCount];
223                         }
224                         for (iEachChunk = 0; iEachChunk < chunk.Length; ++iEachChunk)
225                         {
226                             chunk[iEachChunk] = bValue[iChunkNum * maxValueCount + iEachChunk];
227                         }
228                         chunks.Add(iChunkNum, chunk);
229                     }
230                     List<HttpCookie> cookies = new List<HttpCookie>();
231                     foreach (KeyValuePair<int, byte[]> item in chunks)
232                     {
233                         string itemName = name + CONST_BEYOND_SEPARATOR + item.Key.ToString();
234                         string itemValue = defaultEncoding.GetString(item.Value);
235                         HttpCookie cloneCookie = CloneHttpCookie(cookie);
236                         cloneCookie.Name = itemName;
237                         cloneCookie.Value = itemValue;
238                         cookies.Add(cloneCookie);
239                     }
240                     return cookies;
241                 }
242             }
243             return null;
244         }
245 
246         public static bool IsSeparatorCookie(HttpCookie cookie)
247         {
248             if (cookie == null)
249                 return false;
250             if (!string.IsNullOrEmpty(cookie.Name) && cookie.Name.Contains(CONST_BEYOND_SEPARATOR))
251             {
252                 return true;
253             }
254             return false;
255         }
256 
257         private static HttpCookie CloneHttpCookie(HttpCookie cookie)
258         {
259             HttpCookie cloneCookie = new HttpCookie(cookie.Name);
260             cloneCookie.Value = cookie.Value;
261             cloneCookie.Path = cookie.Path;
262             cloneCookie.Secure = cookie.Secure;
263             cloneCookie.HttpOnly = cookie.HttpOnly;
264             cloneCookie.Domain = cookie.Domain;
265             cloneCookie.Expires = cookie.Expires;
266             return cloneCookie;
267         }
268 
269         public static void FilterRequestCookies(HttpContext context)
270         {
271             if (context != null)
272             {
273                 IList<HttpCookie> cookies = CookieHelper.GetCookies(context.Request.Cookies);
274                 if (cookies != null)
275                 {
276                     foreach (HttpCookie subCookie in cookies)
277                     {
278                         if (context.Request.Cookies[subCookie.Name] == null)
279                         {
280                             context.Request.Cookies.Add(subCookie);
281                         }
282                     }
283                 }
284             }
285         }
286 
287         public static void FilterResponseCookies(HttpContext context)
288         {
289             if (context != null && context.Response.Cookies != null && context.Response.Cookies.Keys.Count > 0)
290             {
291                 int keyCount = context.Response.Cookies.Keys.Count;
292                 if (keyCount > 0)
293                 {
294                     string[] keys = new string[keyCount];
295                     for (int i = 0; i < keyCount; ++i)
296                     {
297                         keys[i] = context.Response.Cookies.Keys[i];
298                     }
299 
300                     foreach (string key in keys)
301                     {
302                         HttpCookie everyCookie = context.Response.Cookies[key];
303 
304                         IList<HttpCookie> cookies = CookieHelper.GetSetCookies(context, everyCookie);
305                         if (cookies != null && cookies.Count > 1 /*means it is separator cookie or the raw cookie is not legal in length.*/)
306                         {
307                             foreach (HttpCookie subCookie in cookies)
308                             {
309                                 if (IsSeparatorCookie(subCookie))
310                                 {
311                                     context.Response.AppendCookie(subCookie);
312                                 }
313                             }
314                         }
315                     }
316                 }
317             }
318         }
319 
320         internal class SeparatorSortedDictionary<T> : Dictionary<string, SortedList<int, T>>
321         {
322             Dictionary<string, SortedList<int, T>> _innerStore = new Dictionary<string, SortedList<int, T>>();
323             public void Add(string name, int index, T value)
324             {
325                 if (_innerStore != null)
326                 {
327                     if (!_innerStore.ContainsKey(name))
328                         _innerStore[name] = new SortedList<int, T>();
329                     SortedList<int, T> itemCollection = _innerStore[name];
330                     if (!itemCollection.ContainsKey(index))
331                     {
332                         itemCollection.Add(index, value);
333                     }
334                 }
335             }
336 
337             public T Join(string name, Func<T, string> toStringFunc, Func<string, T, T> fromStringFunc, Func<T, T> cloneFunc)
338             {
339                 if (_innerStore != null)
340                 {
341                     if (_innerStore.ContainsKey(name))
342                     {
343                         SortedList<int, T> itemCollection = _innerStore[name];
344                         string value = string.Empty;
345                         T firstElement = default(T);
346                         bool getFirstElement = false;
347                         foreach (T itemValue in itemCollection.Values)
348                         {
349                             if (!getFirstElement)
350                             {
351                                 firstElement = itemValue;
352                                 getFirstElement = true;
353                             }
354                             value += toStringFunc(itemValue);
355                         }
356                         T cloneItem = cloneFunc(firstElement);
357                         cloneItem = fromStringFunc(value, cloneItem);
358                         return cloneItem;
359                     }
360                 }
361                 return default(T);
362             }
363 
364             public List<string> Keys
365             {
366                 get
367                 {
368                     List<string> result = new List<string>();
369                     if (_innerStore != null)
370                     {
371                         foreach (string key in _innerStore.Keys)
372                         {
373                             result.Add(key);
374                         }
375                     }
376                     return result;
377                 }
378             }
379         }
380     }
381 }

 調用代碼:

  1 using System;
  2 using System.Collections.Generic;
  3 using System.Linq;
  4 using System.Text;
  5 using System.Threading.Tasks;
  6 using System.Web;
  7 
  8 namespace LargeCookieModule
  9 {
 10     public class LargeCookieModule : IHttpModule
 11     {
 12         public void Dispose()
 13         {
 14             throw new NotImplementedException();
 15         }
 16 
 17         public void Init(HttpApplication context)
 18         {
 19             context.BeginRequest += context_BeginRequest;
 20             context.EndRequest += context_EndRequest;
 21         }
 22 
 23         void context_BeginRequest(object sender, EventArgs e)
 24         {
 25             HttpApplication app = sender as HttpApplication;
 26             if (app != null)
 27             {
 28                 if (app.Context.Request.ContentEncoding != null)
 29                     app.Context.Response.Write("app.Context.Request.ContentEncoding:" + app.Context.Request.ContentEncoding.ToString() + "<br />");
 30 
 31                 app.Context.Response.Write("<br />-----------(start)raw request cookies, context_BeginRequest:--------------<br />");
 32 
 33                 foreach (string key in app.Request.Cookies.AllKeys)
 34                 {
 35                     HttpCookie cookie = app.Request.Cookies[key];
 36                     if (cookie != null)
 37                     {
 38                         string cookieString = CookieHelper.GetSetCookieHeaderString(app.Context, cookie);
 39                         app.Context.Response.Write("context_BeginRequest:" + cookieString + "<br />");
 40                         app.Context.Response.Write("context_BeginRequest.cookieLength:" + cookieString.Length + "<br />");
 41                         app.Context.Response.Write("context_BeginRequest.GetByteCount:" + System.Text.Encoding.UTF8.GetByteCount(cookieString) + "<br />");
 42                     }
 43                 }
 44 
 45                 app.Context.Response.Write("<br />-----------(end)raw request cookies, context_BeginRequest:--------------<br /><br />");
 46 
 47                 app.Context.Response.Write("<br />-----------(start)merged by CookieHelper.cs, context_BeginRequest:--------------<br />");
 48                 // 唯一一句用來過濾
 49                 CookieHelper.FilterRequestCookies(app.Context);
 50                 foreach (string key in app.Request.Cookies.AllKeys)
 51                 {
 52                     HttpCookie cookie = app.Request.Cookies[key];
 53                     if (cookie != null)
 54                     {
 55                         string cookieString = CookieHelper.GetSetCookieHeaderString(app.Context, cookie);
 56                         app.Context.Response.Write("context_BeginRequest:" + cookieString + "<br />");
 57                         app.Context.Response.Write("context_BeginRequest.cookieLength:" + cookieString.Length + "<br />");
 58                         app.Context.Response.Write("context_BeginRequest.GetByteCount:" + System.Text.Encoding.UTF8.GetByteCount(cookieString) + "<br />");
 59                     }
 60                 }
 61                 app.Context.Response.Write("<br />-----------(end)merged by CookieHelper.cs, context_BeginRequest:--------------<br /><br />");
 62             }
 63         }
 64 
 65         void context_EndRequest(object sender, EventArgs e)
 66         {
 67             //HttpApplication app = sender as HttpApplication;
 68             //if (app != null)
 69             //{
 70             //    // CookieHelper.FilterResponseCookies(app.Context);
 71             //    app.Context.Response.Write("<br />-----------(start)response the merged cookies to the client, context_EndRequest:--------------<br />");
 72             //    foreach (string key in app.Response.Cookies.AllKeys)
 73             //    {
 74             //        HttpCookie cookie = app.Response.Cookies[key];
 75             //        if (cookie != null)
 76             //        {
 77             //            IList<HttpCookie> cookies = CookieHelper.GetSetCookies(app.Context, cookie);
 78             //            if (cookies != null)
 79             //            {
 80             //                foreach (HttpCookie subCookie in cookies)
 81             //                {
 82             //                    string cookieString = CookieHelper.GetSetCookieHeaderString(app.Context, subCookie);
 83             //                    app.Context.Response.AppendCookie(subCookie);
 84             //                    app.Context.Response.Write("context_EndRequest:" + cookieString + "<br />");
 85             //                    app.Context.Response.Write("context_EndRequest.cookieLength:" + cookieString.Length + "<br />");
 86             //                    app.Context.Response.Write("context_EndRequest.GetByteCount:" + System.Text.Encoding.UTF8.GetByteCount(cookieString) + "<br />");
 87             //                }
 88             //            }
 89             //        }
 90             //    }
 91             //    app.Context.Response.Write("<br />-----------(end)response the merged cookies to the client, context_EndRequest:--------------<br /><br />");
 92             //}
 93 
 94             HttpApplication app = sender as HttpApplication;
 95             if (app != null)
 96             {
 97                 app.Context.Response.Write("<br />-----------(start)response the all cookies to the client, context_EndRequest:--------------<br />");
 98 
 99                 CookieHelper.FilterResponseCookies(app.Context);
100 
101                 List<HttpCookie> reverseList = new List<HttpCookie>();
102                 List<string> keys = app.Response.Cookies.AllKeys.ToList();
103                 foreach (string key in keys)
104                 {
105                     HttpCookie cookie = app.Response.Cookies[key];
106                     if (cookie != null)
107                     {
108                         reverseList.Add(cookie);
109                         string cookieString = CookieHelper.GetSetCookieHeaderString(app.Context, cookie);
110                         app.Context.Response.Write("context_EndRequest:" + cookieString + "<br />");
111                         app.Context.Response.Write("context_EndRequest.cookieLength:" + cookieString.Length + "<br />");
112                         app.Context.Response.Write("context_EndRequest.GetByteCount:" + System.Text.Encoding.UTF8.GetByteCount(cookieString) + "<br />");
113                     }
114                 }
115 
116                 app.Context.Response.Write("<br />-----------(end)response the all cookies to the client, context_EndRequest:--------------<br />");
117             }
118         }
119     }
120 }

其中BeginRequest和EndRequest中其實只需要調用:

CookieHelper.FilterRequestCookies(app.Context);

CookieHelper.FilterResponseCookies(app.Context);

以上其余代碼都是用來測試的。

另外附上iPad測試的代碼:

 

 1 using System;
 2 using System.Collections.Generic;
 3 using System.Linq;
 4 using System.Web;
 5 using System.Web.UI;
 6 using System.Web.UI.WebControls;
 7 
 8 namespace TestIPadCookie
 9 {
10     public partial class Default : System.Web.UI.Page
11     {
12         protected void Page_Load(object sender, EventArgs e)
13         {
14             for (int j = 0; j < 30; ++j)
15             {
16                 string text = string.Empty;
17                 string cookieString = string.Empty;
18                 HttpCookie cookie = new HttpCookie("ipadTest" + j.ToString().PadLeft(2, '0'));
19                 cookie.Expires = DateTime.Now.AddDays(1);
20                 for (int i = 0; i < 10000; ++i)
21                 {
22                     text += 'c';
23                     cookie.Value = text;
24                     cookieString = LargeCookieModule.CookieHelper.GetSetCookieHeaderString(HttpContext.Current, cookie);
25                     if (System.Text.Encoding.UTF8.GetByteCount(cookieString) >= 317)
26                     {
27                         break;
28                     }
29                 }
30                 HttpContext.Current.Response.AppendCookie(cookie);
31                 HttpContext.Current.Response.Write("cookieLength=" + cookieString.Length.ToString() + "<br />");
32                 HttpContext.Current.Response.Write("cookieValueLength=" + text.Length.ToString()
33                     + ", gap = " + Math.Abs(cookieString.Length - text.Length) + "<br />");
34                 HttpContext.Current.Response.Write("cookieString=" + cookieString + "<br />");
35                 HttpContext.Current.Response.Write("<br />");
36             }
37         }
38     }
39 }

 


源代碼打包下載:(點擊這里

http://files.cnblogs.com/volnet/WebAppLargeCookieModule.zip


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM