一个.net Cookie组件的bug引发的题外话

时间:2023-03-09 16:35:00
一个.net Cookie组件的bug引发的题外话

在.net里,做过Http模拟发送请求的朋友们应该遇到过,有个时候无论怎么努力,都没办法让Cookie跟网页用浏览器所收集的一样,其中原因除了有些Cookie大概是ReadOnly之外,似乎另有隐情:那就是CookieContainer本身就有bug。无怪乎忙活了半天有些站点硬是无法搞掂。

那么CookieContainer的bug在哪呢?我们不妨先做个实验。

首先新建一个webService网站项目。在Page_Load加入带有如下特征的cookie:qm_sid=s57sdfsf,3243...;...

  HttpCookie cookie1 = new HttpCookie("qm_sid", "dsgs34dss,sgsg...");
HttpCookie cookie2 = new HttpCookie("s_list", "win7;wxp");
Response.Cookies.Add(cookie1);
Response.Cookies.Add(cookie2);
Response.Write(string.Format("当前Cookie有:<br />{0}:【{1}】<br />{2}:【{3}】", cookie1.Name, cookie1.Value, cookie2.Name, cookie2.Value));

这种Cookie不是没有,只是没遇到,而且还可能很多大网站故意这样设计的。

接着我们再添加一个项目,用于获取收集并显示Cookie。

我们先用以前自己写的HttpHelper来获取一下cookie看看。在这里附上全部代码,并做了适当的注释,可以获取Http和加密通道的Https

using System;
using System.Collections;
using System.Collections.Generic;
using System.Drawing;
using System.IO;
using System.Net;
using System.Runtime.InteropServices;
using System.Text;
using System.Text.RegularExpressions; namespace Lingchen.Net.Utils
{
/// <summary>
/// www.uu102.com
/// 专门研究网络协议和群发技术的论坛
/// </summary>
[Serializable]
public class HttpHelper
{
public HttpHelper()
{
this.AllowAutoRedirect = true;
}
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
internal static extern bool InternetSetCookie(string lpszUrlName, string lbszCookieName, string lpszCookieData);
[DllImport("kernel32.dll")]
internal static extern Int32 GetLastError();
internal bool AllowAutoRedirect { get; set; }
/// <summary>
/// 请求并发限制数目
/// </summary>
private int DefaultConnectionLimit = ;
private string Accept = "text/html, application/xhtml+xml, */*";
public string UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ZHCN)";
private string ContentType = "application/x-www-form-urlencoded";
public Encoding MyEncoding = Encoding.UTF8;
public string Referer = "";
public string ExtendHeadData = "";
public string CookieString = "";
public int Timeout = * ;
public readonly object objlocker = new object();
#region GetHtml
public HttpWebResponse GetResponse(string url, string postString, CookieContainer cookieContainer)
{
HttpWebResponse httpWebResponse=null;
try
{
HttpWebRequest httpWebRequest = SetRequest(url, postString, cookieContainer);
if (httpWebRequest == null) return null;
httpWebResponse= httpWebRequest.GetResponse() as HttpWebResponse;
foreach (Cookie cookie in httpWebResponse.Cookies) //获取cookie
{
if (cookieContainer != null) cookieContainer.Add(cookie);
}
// CookieString = httpWebResponse.Headers["Set-Cookie"];
return httpWebResponse;
}
catch
{
if (httpWebResponse != null)
{
httpWebResponse = null;
}
return null;
}
}
public HttpWebResponse GetResponse(string url, CookieContainer cookieContainer)
{
return GetResponse(url, "", cookieContainer);
}
public string GetHtml(string url, CookieContainer cookieContainer)
{
return GetHtml(url, "", cookieContainer);
}
public HttpWebRequest SetRequest(string url, string postString, CookieContainer cookieContainer)
{
string html = string.Empty;
HttpWebRequest httpWebRequest = null;
try
{
ServicePointManager.Expect100Continue = false;
if (DefaultConnectionLimit > ) ServicePointManager.DefaultConnectionLimit = DefaultConnectionLimit;//设置并发连接数限制上额
httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
if (url.ToLower().StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
httpWebRequest.ProtocolVersion = HttpVersion.Version11;
}
else
httpWebRequest.ProtocolVersion = HttpVersion.Version10;
httpWebRequest.Method = !string.IsNullOrEmpty(postString) ? "POST" : "GET";
if (postString == " ") httpWebRequest.Method = "POST";
if (!string.IsNullOrEmpty(ExtendHeadData)) httpWebRequest.Headers.Add(ExtendHeadData);
if (cookieContainer != null) httpWebRequest.CookieContainer = cookieContainer;
httpWebRequest.AllowAutoRedirect = AllowAutoRedirect;
httpWebRequest.ContentType = ContentType;
httpWebRequest.Accept = Accept;
httpWebRequest.UserAgent = UserAgent;
httpWebRequest.Referer = string.IsNullOrEmpty(Referer) ? url : Referer;
httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip;
if (httpWebRequest.Method == "POST") //如果是Post递交数据,则写入传的字符串数据
{
byte[] byteRequest = MyEncoding.GetBytes(postString);
httpWebRequest.ContentLength = byteRequest.Length;
Stream stream = null;
stream = httpWebRequest.GetRequestStream();
stream.Write(byteRequest, , byteRequest.Length);
stream.Close(); }
return httpWebRequest;
}
catch
{
if (httpWebRequest != null)
httpWebRequest = null;
return null;
}
}
public string GetHtml(string url, string postString, CookieContainer cookieContainer)
{
HttpWebResponse httpWebResponse = null;
try
{
httpWebResponse = GetResponse(url, postString, cookieContainer);
if (httpWebResponse == null) return "";
Stream responseStream = httpWebResponse.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream, MyEncoding);
string html = streamReader.ReadToEnd();
streamReader.Close();
// this.CookieString = httpWebResponse.Headers["Set-Cookie"];
httpWebResponse.Close();
return html;
}
catch (Exception e)
{
return null;
}
finally
{
if (httpWebResponse != null)
{
httpWebResponse.Close();
httpWebResponse = null;
}
} }
public string GetHtml(HttpWebResponse httpWebResponse, CookieContainer cookieContainer)
{
Stream responseStream=null;
try
{
responseStream= httpWebResponse.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream, MyEncoding);
string html = streamReader.ReadToEnd();
streamReader.Close();
return html;
}
catch (Exception e)
{
return null;
}
finally
{
if (responseStream != null)
{
responseStream.Close();
responseStream.Dispose();
responseStream = null;
}
} }
public Stream GetStream(string url, CookieContainer cookieContainer)
{
HttpWebRequest httpWebRequest = null;
HttpWebResponse httpWebResponse = null; try
{
ServicePointManager.DefaultConnectionLimit = DefaultConnectionLimit;//设置并发连接数限制上额
httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
// httpWebRequest.Timeout = TimeOut;
if (cookieContainer != null) httpWebRequest.CookieContainer = cookieContainer;
httpWebResponse = (HttpWebResponse)httpWebRequest.GetResponse();
Stream responseStream = httpWebResponse.GetResponseStream();
foreach (Cookie cookie in httpWebResponse.Cookies) //获取cookie
{
if (cookieContainer != null) cookieContainer.Add(cookie);
}
return responseStream;
}
catch
{
if (httpWebRequest != null)
{
httpWebRequest.Abort();
}
throw;
}
}
public System.Drawing.Image GetImage(string url, CookieContainer cookieContainer)
{
Image image = null;
System.IO.Stream stream = GetStream(url, cookieContainer);
if (stream != null)
{
image = System.Drawing.Image.FromStream(stream);
}
return image;
}
#endregion public string GetHtml(HttpWebResponse httpWebResponse, ref CookieContainer cookieContainer)
{
try
{
Stream responseStream = httpWebResponse.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream, MyEncoding);
string html = streamReader.ReadToEnd();
streamReader.Close(); httpWebResponse.Close();
return html;
}
catch (Exception e)
{ if (httpWebResponse != null)
{
httpWebResponse.Close();
httpWebResponse = null;
}
return null;
} }
private HttpWebRequest SetRequest(string url, string postString, ref CookieContainer cc)
{
string html = string.Empty;
HttpWebRequest httpWebRequest = null;
try
{
ServicePointManager.Expect100Continue = false;
ServicePointManager.DefaultConnectionLimit = DefaultConnectionLimit;//设置并发连接数限制上额
httpWebRequest = (HttpWebRequest)HttpWebRequest.Create(url);
if (url.ToLower().StartsWith("https", StringComparison.OrdinalIgnoreCase))
{
ServicePointManager.ServerCertificateValidationCallback = new System.Net.Security.RemoteCertificateValidationCallback(CheckValidationResult);
httpWebRequest.ProtocolVersion = HttpVersion.Version11;
}
else
httpWebRequest.ProtocolVersion = HttpVersion.Version10;
httpWebRequest.Method = !string.IsNullOrEmpty(postString) ? "POST" : "GET";
if (postString == " ") httpWebRequest.Method = "POST";
if (!string.IsNullOrEmpty(ExtendHeadData)) httpWebRequest.Headers.Add(ExtendHeadData);
httpWebRequest.AllowAutoRedirect = AllowAutoRedirect;
httpWebRequest.Headers["Cookie"] = string.IsNullOrEmpty(CookieString)?GetCookieString(cc):CookieString;
httpWebRequest.ContentType = ContentType;
httpWebRequest.Accept = Accept;
httpWebRequest.UserAgent = UserAgent;
httpWebRequest.Referer = string.IsNullOrEmpty(Referer) ? url : Referer;
httpWebRequest.AutomaticDecompression = DecompressionMethods.GZip;
if (httpWebRequest.Method == "POST") //如果是Post递交数据,则写入传的字符串数据
{
byte[] byteRequest = MyEncoding.GetBytes(postString);
httpWebRequest.ContentLength = byteRequest.Length;
Stream stream = null;
stream = httpWebRequest.GetRequestStream();
stream.Write(byteRequest, , byteRequest.Length);
stream.Close(); }
return httpWebRequest;
}
catch (Exception e)
{
throw e;
} }
public HttpWebResponse GetResponse(string url, string postString, ref CookieContainer cc)
{
HttpWebRequest httpWebRequest = SetRequest(url, postString, ref cc);
HttpWebResponse httpWebResponse = httpWebRequest.GetResponse() as HttpWebResponse;
CookieString= GetAllCookie(httpWebResponse);
return httpWebResponse;
}
public string GetHtml(string url, string postString, ref CookieContainer cookieContainer)
{ string html = string.Empty; HttpWebResponse httpWebResponse = null;
try
{
httpWebResponse = GetResponse(url, postString, ref cookieContainer);
Stream responseStream = httpWebResponse.GetResponseStream();
StreamReader streamReader = new StreamReader(responseStream, MyEncoding);
html = streamReader.ReadToEnd();
/*string cookieString = httpWebResponse.Headers["Set-Cookie"];
foreach (Cookie cookie in httpWebResponse.Cookies) //获取cookie
{
if (cookieContainer != null) cookieContainer.Add(cookie);
}
*/
streamReader.Close();
httpWebResponse.Close();
return html;
}
catch
{ if (httpWebResponse != null)
{
httpWebResponse.Close();
httpWebResponse = null;
}
return null;
} } public Stream GetStream(string url, ref CookieContainer cc)
{
HttpWebResponse httpWebResponse = null; ServicePointManager.DefaultConnectionLimit = DefaultConnectionLimit;//设置并发连接数限制上额
DefaultConnectionLimit++;
httpWebResponse = GetResponse(url, "", ref cc);
Stream responseStream = httpWebResponse.GetResponseStream();
return responseStream; }
public System.Drawing.Image GetImage(string url, ref CookieContainer cc)
{
Image image = null;
System.IO.Stream stream = this.GetStream(url, ref cc);
if (stream != null)
{
image = System.Drawing.Image.FromStream(stream);
}
return image;
}
public string UploadImage(string file, string url, Dictionary<string, string> dic, string name, CookieContainer cookieContainer)
{
String time = DateTime.Now.Ticks.ToString("x"); string boundary = "----------" + DateTime.Now.Ticks.ToString("x");
HttpWebRequest httpWebRequest = (HttpWebRequest)WebRequest.Create(new Uri(url));
httpWebRequest.CookieContainer = cookieContainer;
httpWebRequest.ContentType = "multipart/form-data; boundary=" + boundary;
httpWebRequest.Method = "POST";
StringBuilder sb = new StringBuilder(); if (dic.Count != )
{
foreach (KeyValuePair<string, string> kvp in dic)
{
sb.Append("--");
sb.Append(boundary);
sb.Append("\r\n");
sb.Append("Content-Disposition: form-data; name=\"" + kvp.Key + "\"\r\n\r\n");
sb.Append(kvp.Value);
sb.Append("\r\n");
}
}
string shortfilename = file.Substring(file.LastIndexOf("\\") + , file.Length - file.LastIndexOf("\\") - );
sb.Append("--");
sb.Append(boundary);
sb.Append("\r\n");
sb.Append(string.Format("Content-Disposition: form-data; name=\"{0}\"; filename=\"", name));
sb.Append(shortfilename);
sb.Append("\"");
sb.Append("\r\n");
sb.Append("Content-Type: image/jpeg");
sb.Append("\r\n");
sb.Append("\r\n"); string postHeader = sb.ToString();
byte[] postHeaderBytes = Encoding.UTF8.GetBytes(postHeader);
byte[] boundaryBytes = Encoding.ASCII.GetBytes("\r\n--" + boundary + "--\r\n"); FileStream fileStream = new FileStream(file, FileMode.Open, FileAccess.Read);
long fileLength = fileStream.Length;
long length = postHeaderBytes.Length + fileStream.Length + boundaryBytes.Length;
httpWebRequest.Accept = "text/html, application/xhtml+xml, */*";
httpWebRequest.UserAgent = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0; BOIE9;ZHCN)";
httpWebRequest.AllowWriteStreamBuffering = false;
httpWebRequest.ServicePoint.Expect100Continue = false;
httpWebRequest.ContentLength = length;
Stream requestStream = httpWebRequest.GetRequestStream(); requestStream.Write(postHeaderBytes, , postHeaderBytes.Length); byte[] buffer = new Byte[checked((uint)Math.Min(, (int)fileStream.Length))];
long filebytes = fileStream.Length;
int bytesRead = ;
while ((bytesRead = fileStream.Read(buffer, , buffer.Length)) != )
requestStream.Write(buffer, , bytesRead);
requestStream.Write(boundaryBytes, , boundaryBytes.Length); WebResponse webResponse2 = httpWebRequest.GetResponse();
Stream stream = webResponse2.GetResponseStream();
StreamReader streamReader = new StreamReader(stream);
string html = streamReader.ReadToEnd();
requestStream.Close();
return html;
}
private bool CheckValidationResult(object sender, System.Security.Cryptography.X509Certificates.X509Certificate certificate,
System.Security.Cryptography.X509Certificates.X509Chain chain, System.Net.Security.SslPolicyErrors sslPolicyErrors)
{ // Always accept
return true;
}
/// <summary>
/// 切换客户端字符串
/// </summary>
/// <returns></returns>
public string ChangeUserAgent()
{
#region UserAgents
string[] UserAgents = {"Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)",
"Mozilla/4.0 (compatible; MSIE 7.0; Windows NT 6.0)",
"Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 5.1; Trident/4.0; EmbeddedWB 14.52 from: http://www.bsalsa.com/ EmbeddedWB 14.52; .NET4.0C; .NET4.0E; .NET CLR 2.0.50727; .NET CLR 3.0.4506.2152; .NET CLR 3.5.30729)",
"Mozilla/4.0 (compatible; MSIE 9.0; Windows NT 6.1)" ,
"Mozilla/5.0 (compatible; rv:1.9.1) Gecko/20090702 Firefox/3.5",
"Mozilla/5.0 (compatible; rv:1.9.2) Gecko/20100101 Firefox/3.6",
"Mozilla/5.0 (compatible; rv:2.0) Gecko/20110101 Firefox/4.0",
"Mozilla/5.0 (Windows NT 6.1; WOW64; rv:6.0.2) Gecko/20100101 Firefox/6.0.2",
"Mozilla/5.0 (compatible) AppleWebKit/534.21 (KHTML, like Gecko) Chrome/11.0.682.0 Safari/534.21",
"Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10_7) AppleWebKit/534.16+ (KHTML, like Gecko) Version/5.0.3 Safari/533.19.4",
"Opera/9.80 (compatible; U) Presto/2.7.39 Version/11.00",
"Mozilla/5.0 (compatible; U) AppleWebKit/533.1 (KHTML, like Gecko) Maxthon/3.0.8.2 Safari/533.1",
"Mozilla/5.0 (iPhone; U; CPU OS 4_2_1 like Mac OS X) AppleWebKit/532.9 (KHTML, like Gecko) Version/5.0.3 Mobile/8B5097d Safari/6531.22.7",
"Mozilla/5.0 (iPad; U; CPU OS 4_2_1 like Mac OS X) AppleWebKit/533.17.9 (KHTML, like Gecko) Version/4.0.2 Mobile/8C148 Safari/6533.18.5",
"Mozilla/5.0 (Linux; U; Android 2.2) AppleWebKit/533.1 (KHTML, like Gecko) Version/4.0 Mobile Safari/533.1",
"Mozilla/5.0 (compatible; Googlebot/2.1; +http://www.google.com/bot.html)",
"msnbot/1.1 (+http://search.msn.com/msnbot.htm)"};
#endregion
string userAgent = UserAgent;
UserAgent = UserAgents[new Random().Next(, UserAgents.Length)];
while (UserAgent == userAgent)
{
UserAgent = UserAgents[new Random().Next(, UserAgents.Length)];
}
return UserAgent;
} public List<Cookie> GetAllCookie(CookieContainer cookieContainer)
{
List<Cookie> cookieCollection = new List<Cookie>();
try
{ Hashtable m_domainTable = (Hashtable)cookieContainer.GetType().InvokeMember("m_domainTable",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, cookieContainer, new object[] { });
//通过反射CookieContainer类进入其内部对私有变量获取其值。m_domainTable为CookieContainer类中的私有字段,类型为Hashtable
foreach (object pathList in m_domainTable.Values)
{
//pathList为一个SortList类型的派生类
SortedList m_list = (SortedList)pathList.GetType().InvokeMember("m_list", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, pathList, new object[] { });
foreach (CookieCollection cookies in m_list.Values)
foreach (Cookie cookie in cookies)
{
if (!cookieCollection.Contains(cookie)) cookieCollection.Add(cookie);
}
} }
catch
{ }
return cookieCollection;
}
public string GetAllCookie(HttpWebResponse response)
{
StringBuilder sb = new StringBuilder();
string cookieString = response.Headers["Set-Cookie"];
if (!string.IsNullOrEmpty(cookieString))
{
Regex regex = new Regex(@"(?'name'[^\=,/;]+)=(?'value'[^;=]*);\s*", RegexOptions.IgnoreCase);
// Regex regex = new Regex(@"(httponly,)|(\s*domain=[^;]*;(,)?)|(\s*path=[^;]*;)|(\s*Expires=[^;=]*;)(,)?", RegexOptions.IgnoreCase);
//cookieString= regex.Replace(cookieString, "");
CookieContainer cookieContainer = new CookieContainer(); if (regex.IsMatch(cookieString))
{
MatchCollection matches = regex.Matches(cookieString);
foreach (Match match in matches)
{
string name = match.Groups["name"].Value.ToLower().Trim();
if (name == "domain" || name == "path" || name == "expires")
continue;
string value = match.Groups["value"].Value;
string domain = match.Groups["path"].Value; //sb.AppendFormat("{0}={1};", name, value);
if (!CookieList.ContainsKey(name))
{
CookieList.Add(name, value);
}
else
{
CookieList[name] = value;
}
}
}
}
foreach (KeyValuePair<string, string> kvp in CookieList)
{
sb.AppendFormat("{0}={1};", kvp.Key, kvp.Value);
}
return sb.ToString();
}
internal Dictionary<string, string> CookieList = new Dictionary<string, string>();
public void SetIECookies(CookieContainer cookieContainer, string url)
{
List<Cookie> cookies = GetAllCookie(cookieContainer);
foreach (Cookie cookie in cookies)
{
string timeStamp = DateTime.Now.AddYears().ToString("R");
if (!InternetSetCookie(url, cookie.Name, string.Format("{0};path=/;expires={1}", cookie.Value, timeStamp)))
{
System.Diagnostics.Debug.Print(GetLastError().ToString());
}
}
} } }

在此基础上,我们在后面的控制台项目中敲入如下代码:

using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Net; namespace CookieContainer_BugRefix
{
class Program
{
static void Main(string[] args)
{
CookieContainer cookieContainer=new CookieContainer();
HttpHelper httpHelper=new HttpHelper();
string url="http://localhost:7807/";
string html = httpHelper.GetHtml(url, cookieContainer);
string cookieString = cookieContainer.GetCookieHeader(new Uri(url));
Console.Write(cookieString);
Console.Read(); }
}
}

这里我们选择性的只显示cookie,运行后做了如下对比:

一个.net Cookie组件的bug引发的题外话

仔细观察一下,发现了吧,由于cookie中出现了英文逗号(,)和分号(;),由于.net中的cookie类处理失败,导致获取出错。

由此网上出现了很多版本用于修复这个漏洞。

比较常见的就是下面这种方法,他是用发射获取Cookie类里的私有字段m_domainTable里的值。别人有没有用我们不清楚,至少我是没有成功过,究其原因,恐怕再明显不过了,既然HttpWebRequest是先获取的Cookie字符串之后才收集进去Cookie对象去的,然而Cookie类在处理这个类的时候已经出了问题,怎么可能收集到了全部?

      public List<Cookie> GetAllCookie(CookieContainer cookieContainer)
{
List<Cookie> cookieCollection = new List<Cookie>();
try
{ Hashtable m_domainTable = (Hashtable)cookieContainer.GetType().InvokeMember("m_domainTable",
System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, cookieContainer, new object[] { });
//通过反射CookieContainer类进入其内部对私有变量获取其值。m_domainTable为CookieContainer类中的私有字段,类型为Hashtable
foreach (object pathList in m_domainTable.Values)
{
//pathList为一个SortList类型的派生类
SortedList m_list = (SortedList)pathList.GetType().InvokeMember("m_list", System.Reflection.BindingFlags.NonPublic | System.Reflection.BindingFlags.GetField | System.Reflection.BindingFlags.Instance, null, pathList, new object[] { });
foreach (CookieCollection cookies in m_list.Values)
foreach (Cookie cookie in cookies)
{
if (!cookieCollection.Contains(cookie)) cookieCollection.Add(cookie);
}
} }
catch
{ }
return cookieCollection;
}

既然是在cookie字符串转换成Cookie对象的过程中出现了差错,所以很简单,只要我们自己去处理这个字符串不就行了?而这个字符串在获取后保存在了HttpWebResponse.Headers["Set-Cookie"]
这是字符串处理那是一个痛苦啊。最后只好用正则表达式勉强搞定。

   public string GetAllCookie(HttpWebResponse response)
{
StringBuilder sb = new StringBuilder();
string cookieString = response.Headers["Set-Cookie"];
if (!string.IsNullOrEmpty(cookieString))
{
Regex regex = new Regex(@"(?'name'[^\=,/;]+)=(?'value'[^;=]*);\s*", RegexOptions.IgnoreCase);
CookieContainer cookieContainer = new CookieContainer(); if (regex.IsMatch(cookieString))
{
MatchCollection matches = regex.Matches(cookieString);
foreach (Match match in matches)
{
string name = match.Groups["name"].Value.ToLower().Trim();
if (name == "domain" || name == "path" || name == "expires")
continue;
string value = match.Groups["value"].Value;
string domain = match.Groups["path"].Value; if (!CookieList.ContainsKey(name))
{
CookieList.Add(name, value);
}
else
{
CookieList[name] = value;
}
}
}
}
foreach (KeyValuePair<string, string> kvp in CookieList)
{
sb.AppendFormat("{0}={1};", kvp.Key, kvp.Value);
}
return sb.ToString();
}

说道这里,bug算是解决了,然而我们还是有理由继续回顾一下Http基础。

Http协议是一种特殊的基于80端口的套接字通讯协议,事实上也就是一种普通的套接字通讯过程。即便你用TCP类型的Socket还是UDP类型的Socket,只要遵循Http约定的规则,就能实现。但是显然自己亲自去用套接字实现Http是一种已经算完美的封装类功能,不觉得多此一举吗。

那么Http发给服务器有哪些东西呢?

一个.net Cookie组件的bug引发的题外话

这里就是HttpAnalizer捕获到的请求头部。如果你用Socket往服务器80端口发送这样的字符串,服务器照样会返回给你你需要的东西。够简单的吧。所谓协议,不要把它们想象得国语复杂深奥难懂,他们不过是一些IT巨头开几个会议,约好一些规则让大家去遵循,这样大家就能通用。仅此而已。我们平时使用的QQ,封装了好多种复杂的规则,我们也可以把他们称作是腾讯规定的协议,虽然不是大家都通用的协议,但是也是协议!

我们给服务器服务器发送了这些字符串,服务器也会给我们返回的一些规则和我们发过去的字符串的规则一样的字符串,这样一个接头暗号是所有协议必须遵循的。

Cookie类的bug在.net 3.5版本以后也就是4.0的时候已经修复了,具体我也没用4.0的去试过,大家感兴趣的不妨试一下。