HttpClient.cs 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. /* Name: Socket 实现 http 协议全功能版
  6. * Version: v0.6
  7. * Description: 此版本实现了 http 及 https 的 get 或 post 访问, 自动处理301,302跳转, 支持 gzip 解压, 分块传输.
  8. * 支持的操作: 获取文本,图片,文件形式的内容.
  9. * 使用方法: new HttpWebSocket(); 调用实例的 Get.... 方法.
  10. */
  11. using System.Net;
  12. using System.Net.Sockets;
  13. using System.Net.Security;
  14. using System.Text.RegularExpressions;
  15. using System.IO;
  16. using System.IO.Compression;
  17. using System.Web;
  18. using System.Security.Cryptography.X509Certificates;
  19. namespace CommonNetwork.Http
  20. {
  21. public class HttpWebSocket
  22. {
  23. /// <summary>
  24. /// 获取或设置请求与回应的超时时间,默认3秒.
  25. /// </summary>
  26. public int TimeOut
  27. {
  28. get;
  29. set;
  30. }
  31. /// <summary>
  32. /// 获取或设置请求cookie
  33. /// </summary>
  34. public List<string> Cookies
  35. {
  36. get;
  37. set;
  38. }
  39. /// <summary>
  40. /// 获取请求返回的 HTTP 头部内容
  41. /// </summary>
  42. public HttpHeader HttpHeaders
  43. {
  44. get;
  45. internal set;
  46. }
  47. /// <summary>
  48. /// 获取或设置错误信息分隔符
  49. /// </summary>
  50. private string ErrorMessageSeparate;
  51. public HttpWebSocket()
  52. {
  53. this.TimeOut = 3;
  54. this.Cookies = new List<string>();
  55. this.ErrorMessageSeparate = ";;";
  56. this.HttpHeaders = new HttpHeader();
  57. }
  58. /// <summary>
  59. /// get或post方式请求一个 http 或 https 地址.使用 Socket 方式
  60. /// </summary>
  61. /// <param name="url">请求绝对地址</param>
  62. /// <param name="referer">请求来源地址,可为空</param>
  63. /// <param name="postData">post请求参数. 设置空值为get方式请求</param>
  64. /// <returns>返回图像</returns>
  65. public static MemoryStream GetImageUseSocket(string url, string referer, string postData = null)
  66. {
  67. HttpWebSocket www = new HttpWebSocket();
  68. //Image result = null;
  69. MemoryStream result = www.GetSocketResult(url, referer, postData);
  70. return result;
  71. }
  72. public static string GetTextUseSocket(string url)
  73. {
  74. HttpWebSocket www = new HttpWebSocket();
  75. string txt = www.GetHtmlUseSocket(url, null, null);
  76. return txt;
  77. }
  78. /// <summary>
  79. /// get或post方式请求一个 http 或 https 地址.使用 Socket 方式
  80. /// </summary>
  81. /// <param name="url">请求绝对地址</param>
  82. /// <param name="postData">post请求参数. 设置空值为get方式请求</param>
  83. /// <returns>返回 html 内容,如果发生异常将返回上次http状态码及异常信息</returns>
  84. public string GetHtmlUseSocket(string url, string postData = null)
  85. {
  86. return this.GetHtmlUseSocket(url, null, postData);
  87. }
  88. /// <summary>
  89. /// get或post方式请求一个 http 或 https 地址.使用 Socket 方式
  90. /// </summary>
  91. /// <param name="url">请求绝对地址</param>
  92. /// <param name="referer">请求来源地址,可为空</param>
  93. /// <param name="postData">post请求参数. 设置空值为get方式请求</param>
  94. /// <returns>返回 html 内容,如果发生异常将返回上次http状态码及异常信息</returns>
  95. public string GetHtmlUseSocket(string url, string referer, string postData = null)
  96. {
  97. string result = string.Empty;
  98. try
  99. {
  100. MemoryStream ms = this.GetSocketResult(url, referer, postData);
  101. if (ms != null)
  102. {
  103. result = Encoding.GetEncoding(string.IsNullOrEmpty(this.HttpHeaders.Charset) ? "UTF-8" : this.HttpHeaders.Charset).GetString(ms.ToArray());
  104. }
  105. }
  106. catch (SocketException se)
  107. {
  108. result = this.HttpHeaders.ResponseStatusCode + this.ErrorMessageSeparate + se.ErrorCode.ToString() + this.ErrorMessageSeparate + se.SocketErrorCode.ToString("G") + this.ErrorMessageSeparate + se.Message;
  109. }
  110. catch (Exception e)
  111. {
  112. result = this.HttpHeaders.ResponseStatusCode + this.ErrorMessageSeparate + e.Message;
  113. }
  114. return result;
  115. }
  116. /// <summary>
  117. /// get或post方式请求一个 http 或 https 地址.
  118. /// </summary>
  119. /// <param name="url">请求绝对地址</param>
  120. /// <param name="referer">请求来源地址,可为空</param>
  121. /// <param name="postData">post请求参数. 设置空值为get方式请求</param>
  122. /// <returns>返回的已解压的数据内容</returns>
  123. private MemoryStream GetSocketResult(string url, string referer, string postData)
  124. {
  125. if (string.IsNullOrEmpty(url))
  126. {
  127. throw new UriFormatException("'Url' cannot be empty.");
  128. }
  129. MemoryStream result = null;
  130. Uri uri = new Uri(url);
  131. if (uri.Scheme == "http")
  132. {
  133. result = this.GetHttpResult(uri, referer, postData);
  134. }
  135. else if (uri.Scheme == "https")
  136. {
  137. result = this.GetSslResult(uri, referer, postData);
  138. }
  139. else
  140. {
  141. throw new ArgumentException("url must start with HTTP or HTTPS.", "url");
  142. }
  143. if (!string.IsNullOrEmpty(this.HttpHeaders.Location))
  144. {
  145. result = GetSocketResult(this.HttpHeaders.Location, uri.AbsoluteUri, null);
  146. }
  147. else
  148. {
  149. result = unGzip(result);
  150. }
  151. return result;
  152. }
  153. /// <summary>
  154. /// get或post方式请求一个 http 地址.
  155. /// </summary>
  156. /// <param name="uri">请求绝对地址</param>
  157. /// <param name="referer">请求来源地址,可为空</param>
  158. /// <param name="postData">post请求参数. 设置空值为get方式请求</param>
  159. /// <param name="headText">输出包含头部内容的StringBuilder</param>
  160. /// <returns>返回未解压的数据流</returns>
  161. private MemoryStream GetHttpResult(Uri uri, string referer, string postData)
  162. {
  163. MemoryStream result = new MemoryStream(10240);
  164. Socket HttpSocket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
  165. HttpSocket.SendTimeout = this.TimeOut * 1000;
  166. HttpSocket.ReceiveTimeout = this.TimeOut * 1000;
  167. try
  168. {
  169. byte[] send = GetSendHeaders(uri, referer, postData);
  170. HttpSocket.Connect(uri.Host, uri.Port);
  171. if (HttpSocket.Connected)
  172. {
  173. HttpSocket.Send(send, SocketFlags.None);
  174. this.ProcessData(HttpSocket, ref result);
  175. }
  176. result.Flush();
  177. }
  178. finally
  179. {
  180. HttpSocket.Shutdown(SocketShutdown.Both);
  181. HttpSocket.Close();
  182. }
  183. result.Seek(0, SeekOrigin.Begin);
  184. return result;
  185. }
  186. /// <summary>
  187. /// get或post方式请求一个 https 地址.
  188. /// </summary>
  189. /// <param name="uri">请求绝对地址</param>
  190. /// <param name="referer">请求来源地址,可为空</param>
  191. /// <param name="postData">post请求参数. 设置空值为get方式请求</param>
  192. /// <param name="headText">输出包含头部内容的StringBuilder</param>
  193. /// <returns>返回未解压的数据流</returns>
  194. private MemoryStream GetSslResult(Uri uri, string referer, string postData)
  195. {
  196. MemoryStream result = new MemoryStream(10240);
  197. StringBuilder sb = new StringBuilder(1024);
  198. byte[] send = GetSendHeaders(uri, referer, postData);
  199. TcpClient client = new TcpClient(uri.Host, uri.Port);
  200. try
  201. {
  202. SslStream sslStream = new SslStream(client.GetStream(), true
  203. , new RemoteCertificateValidationCallback((sender, certificate, chain, sslPolicyErrors)
  204. =>
  205. {
  206. return sslPolicyErrors == SslPolicyErrors.None;
  207. }
  208. ), null);
  209. sslStream.ReadTimeout = this.TimeOut * 1000;
  210. sslStream.WriteTimeout = this.TimeOut * 1000;
  211. X509Store store = new X509Store(StoreName.My);
  212. sslStream.AuthenticateAsClient(uri.Host, store.Certificates, System.Security.Authentication.SslProtocols.Default, false);
  213. if (sslStream.IsAuthenticated)
  214. {
  215. sslStream.Write(send, 0, send.Length);
  216. sslStream.Flush();
  217. this.ProcessData(sslStream, ref result);
  218. }
  219. result.Flush();
  220. }
  221. finally
  222. {
  223. client.Close();
  224. }
  225. result.Seek(0, SeekOrigin.Begin);
  226. return result;
  227. }
  228. /// <summary>
  229. /// 返回请求的头部内容
  230. /// </summary>
  231. /// <param name="uri">请求绝对地址</param>
  232. /// <param name="referer">请求来源地址,可为空</param>
  233. /// <param name="postData">post请求参数. 设置空值为get方式请求</param>
  234. /// <returns>请求头部数据</returns>
  235. private byte[] GetSendHeaders(Uri uri, string referer, string postData)
  236. {
  237. string sendString = @"{0} {1} HTTP/1.1
  238. Accept: text/html, application/xhtml+xml, */*
  239. Referer: {2}
  240. Accept-Language: zh-CN
  241. User-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)
  242. Accept-Encoding: gzip, deflate
  243. Host: {3}
  244. Connection: Keep-Alive
  245. Cache-Control: no-cache
  246. ";
  247. sendString = string.Format(sendString,
  248. string.IsNullOrEmpty(postData) ? "GET" : "POST",
  249. uri.PathAndQuery ,
  250. string.IsNullOrEmpty(referer) ? uri.AbsoluteUri : referer,
  251. uri.Host);
  252. if (this.Cookies != null && this.Cookies.Count > 0)
  253. {
  254. sendString += string.Format("Cookie: {0}\r\n", string.Join("; ", this.Cookies.ToArray()));
  255. }
  256. if (string.IsNullOrEmpty(postData))
  257. {
  258. sendString += "\r\n";
  259. }
  260. else
  261. {
  262. int dlength = Encoding.UTF8.GetBytes(postData).Length;
  263. sendString += string.Format(@"Content-Type: application/x-www-form-urlencoded
  264. Content-Length: {0}
  265. {1}
  266. ", postData.Length, postData);
  267. }
  268. return Encoding.UTF8.GetBytes(sendString);
  269. ;
  270. }
  271. /// <summary>
  272. /// 设置此类的字段
  273. /// </summary>
  274. /// <param name="headText">头部文本</param>
  275. private void SetThisHeaders(string headText)
  276. {
  277. if (string.IsNullOrEmpty(headText))
  278. {
  279. throw new ArgumentNullException("'WithHeadersText' cannot be empty.");
  280. }
  281. //Match m = Regex.Match( withHeadersText,@".*(?=\r\n\r\n)", RegexOptions.Singleline | RegexOptions.IgnoreCase );
  282. //if ( m == null || string.IsNullOrWhiteSpace( m.Value ) )
  283. //{
  284. // throw new HttpParseException( "'SetThisHeaders' method has bug." );
  285. //}
  286. string[] headers = headText.Split(new string[] { "\r\n" }, StringSplitOptions.RemoveEmptyEntries);
  287. if (headers == null || headers.Length == 0)
  288. {
  289. throw new ArgumentException("'WithHeadersText' param format error.");
  290. }
  291. this.HttpHeaders = new HttpHeader();
  292. foreach (string head in headers)
  293. {
  294. if (head.StartsWith("HTTP", StringComparison.OrdinalIgnoreCase))
  295. {
  296. string[] ts = head.Split(' ');
  297. if (ts.Length > 1)
  298. {
  299. this.HttpHeaders.ResponseStatusCode = ts[1];
  300. }
  301. }
  302. else if (head.StartsWith("Set-Cookie:", StringComparison.OrdinalIgnoreCase))
  303. {
  304. this.Cookies = this.Cookies ?? new List<string>();
  305. string tCookie = head.Substring(11, head.IndexOf(";") < 0 ? head.Length - 11 : head.IndexOf(";") - 10).Trim();
  306. if (!this.Cookies.Exists(f => f.Split('=')[0] == tCookie.Split('=')[0]))
  307. {
  308. this.Cookies.Add(tCookie);
  309. }
  310. }
  311. else if (head.StartsWith("Location:", StringComparison.OrdinalIgnoreCase))
  312. {
  313. this.HttpHeaders.Location = head.Substring(9).Trim();
  314. }
  315. else if (head.StartsWith("Content-Encoding:", StringComparison.OrdinalIgnoreCase))
  316. {
  317. if (head.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) >= 0)
  318. {
  319. this.HttpHeaders.IsGzip = true;
  320. }
  321. }
  322. else if (head.StartsWith("Content-Type:", StringComparison.OrdinalIgnoreCase))
  323. {
  324. string[] types = head.Substring(13).Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
  325. foreach (string t in types)
  326. {
  327. if (t.IndexOf("charset=", StringComparison.OrdinalIgnoreCase) >= 0)
  328. {
  329. this.HttpHeaders.Charset = t.Trim().Substring(8);
  330. }
  331. else if (t.IndexOf('/') >= 0)
  332. {
  333. this.HttpHeaders.ContentType = t.Trim();
  334. }
  335. }
  336. }
  337. else if (head.StartsWith("Content-Length:", StringComparison.OrdinalIgnoreCase))
  338. {
  339. this.HttpHeaders.ContentLength = long.Parse(head.Substring(15).Trim());
  340. }
  341. else if (head.StartsWith("Transfer-Encoding:", StringComparison.OrdinalIgnoreCase) && head.EndsWith("chunked", StringComparison.OrdinalIgnoreCase))
  342. {
  343. this.HttpHeaders.IsChunk = true;
  344. }
  345. }
  346. }
  347. /// <summary>
  348. /// 解压数据流
  349. /// </summary>
  350. /// <param name="data">数据流, 压缩或未压缩的.</param>
  351. /// <returns>返回解压缩的数据流</returns>
  352. private MemoryStream unGzip(MemoryStream data)
  353. {
  354. if (data == null)
  355. {
  356. throw new ArgumentNullException("data cannot be null.", "data");
  357. }
  358. data.Seek(0, SeekOrigin.Begin);
  359. MemoryStream result = data;
  360. if (this.HttpHeaders.IsGzip)
  361. {
  362. GZipStream gs = new GZipStream(data, CompressionMode.Decompress);
  363. result = new MemoryStream(1024);
  364. try
  365. {
  366. byte[] buffer = new byte[1024];
  367. int length = -1;
  368. do
  369. {
  370. length = gs.Read(buffer, 0, buffer.Length);
  371. result.Write(buffer, 0, length);
  372. }
  373. while (length != 0);
  374. gs.Flush();
  375. result.Flush();
  376. }
  377. finally
  378. {
  379. gs.Close();
  380. }
  381. }
  382. return result;
  383. }
  384. /// <summary>
  385. /// 处理请求返回的数据.
  386. /// </summary>
  387. /// <typeparam name="T">数据源类型</typeparam>
  388. /// <param name="reader">数据源实例</param>
  389. /// <param name="body">保存数据的流</param>
  390. private void ProcessData<T>(T reader, ref MemoryStream body)
  391. {
  392. byte[] data = new byte[10240];
  393. int bodyStart = -1;//数据部分起始位置
  394. int readLength = 0;
  395. bodyStart = GetHeaders(reader, ref data, ref readLength);
  396. if (bodyStart >= 0)
  397. {
  398. if (this.HttpHeaders.IsChunk)
  399. {
  400. GetChunkData(reader, ref data, ref bodyStart, ref readLength, ref body);
  401. }
  402. else
  403. {
  404. GetBodyData(reader, ref data, bodyStart, readLength, ref body);
  405. }
  406. }
  407. }
  408. /// <summary>
  409. /// 取得返回的http头部内容,并设置相关属性.
  410. /// </summary>
  411. /// <typeparam name="T">数据源类型</typeparam>
  412. /// <param name="reader">数据源实例</param>
  413. /// <param name="data">待处理的数据</param>
  414. /// <param name="readLength">读取的长度</param>
  415. /// <returns>数据内容的起始位置,返回-1表示未读完头部内容</returns>
  416. private int GetHeaders<T>(T reader, ref byte[] data, ref int readLength)
  417. {
  418. int result = -1;
  419. StringBuilder sb = new StringBuilder(1024);
  420. do
  421. {
  422. readLength = this.ReadData(reader, ref data);
  423. if (result < 0)
  424. {
  425. for (int i = 0; i < data.Length; i++)
  426. {
  427. char c = (char)data[i];
  428. sb.Append(c);
  429. if (c == '\n' && string.Concat(sb[sb.Length - 4], sb[sb.Length - 3], sb[sb.Length - 2], sb[sb.Length - 1]).Contains("\r\n\r\n"))
  430. {
  431. result = i + 1;
  432. this.SetThisHeaders(sb.ToString());
  433. break;
  434. }
  435. }
  436. }
  437. if (result >= 0)
  438. {
  439. break;
  440. }
  441. }
  442. while (readLength > 0);
  443. return result;
  444. }
  445. /// <summary>
  446. /// 取得未分块数据的内容
  447. /// </summary>
  448. /// <typeparam name="T">数据源类型</typeparam>
  449. /// <param name="reader">数据源实例</param>
  450. /// <param name="data">已读取未处理的字节数据</param>
  451. /// <param name="startIndex">起始位置</param>
  452. /// <param name="readLength">读取的长度</param>
  453. /// <param name="body">保存块数据的流</param>
  454. private void GetBodyData<T>(T reader, ref byte[] data, int startIndex, int readLength, ref MemoryStream body)
  455. {
  456. int contentTotal = 0;
  457. if (startIndex < data.Length)
  458. {
  459. int count = readLength - startIndex;
  460. body.Write(data, startIndex, count);
  461. contentTotal += count;
  462. }
  463. int tlength = 0;
  464. do
  465. {
  466. tlength = this.ReadData(reader, ref data);
  467. contentTotal += tlength;
  468. body.Write(data, 0, tlength);
  469. if (this.HttpHeaders.ContentLength > 0 && contentTotal >= this.HttpHeaders.ContentLength)
  470. {
  471. break;
  472. }
  473. }
  474. while (tlength > 0);
  475. }
  476. /// <summary>
  477. /// 取得分块数据
  478. /// </summary>
  479. /// <typeparam name="T">数据源类型</typeparam>
  480. /// <param name="reader">Socket实例</param>
  481. /// <param name="data">已读取未处理的字节数据</param>
  482. /// <param name="startIndex">起始位置</param>
  483. /// <param name="readLength">读取的长度</param>
  484. /// <param name="body">保存块数据的流</param>
  485. private void GetChunkData<T>(T reader, ref byte[] data, ref int startIndex, ref int readLength, ref MemoryStream body)
  486. {
  487. int chunkSize = -1;//每个数据块的长度,用于分块数据.当长度为0时,说明读到数据末尾.
  488. while (true)
  489. {
  490. chunkSize = this.GetChunkHead(reader, ref data, ref startIndex, ref readLength);
  491. this.GetChunkBody(reader, ref data, ref startIndex, ref readLength, ref body, chunkSize);
  492. if (chunkSize <= 0)
  493. {
  494. break;
  495. }
  496. }
  497. }
  498. /// <summary>
  499. /// 取得分块数据的数据长度
  500. /// </summary>
  501. /// <typeparam name="T">数据源类型</typeparam>
  502. /// <param name="reader">Socket实例</param>
  503. /// <param name="data">已读取未处理的字节数据</param>
  504. /// <param name="startIndex">起始位置</param>
  505. /// <param name="readLength">读取的长度</param>
  506. /// <returns>块长度,返回0表示已到末尾.</returns>
  507. private int GetChunkHead<T>(T reader, ref byte[] data, ref int startIndex, ref int readLength)
  508. {
  509. int chunkSize = -1;
  510. List<char> tChars = new List<char>();//用于临时存储块长度字符
  511. if (startIndex >= data.Length || startIndex >= readLength)
  512. {
  513. readLength = this.ReadData(reader, ref data);
  514. startIndex = 0;
  515. }
  516. do
  517. {
  518. for (int i = startIndex; i < readLength; i++)
  519. {
  520. char c = (char)data[i];
  521. if (c == '\n')
  522. {
  523. try
  524. {
  525. chunkSize = Convert.ToInt32(new string(tChars.ToArray()).TrimEnd('\r'), 16);
  526. startIndex = i + 1;
  527. }
  528. catch (Exception e)
  529. {
  530. throw new Exception("Maybe exists 'chunk-ext' field.", e);
  531. }
  532. break;
  533. }
  534. tChars.Add(c);
  535. }
  536. if (chunkSize >= 0)
  537. {
  538. break;
  539. }
  540. startIndex = 0;
  541. readLength = this.ReadData(reader, ref data);
  542. }
  543. while (readLength > 0);
  544. return chunkSize;
  545. }
  546. /// <summary>
  547. /// 取得分块传回的数据内容
  548. /// </summary>
  549. /// <typeparam name="T">数据源类型</typeparam>
  550. /// <param name="reader">Socket实例</param>
  551. /// <param name="data">已读取未处理的字节数据</param>
  552. /// <param name="startIndex">起始位置</param>
  553. /// <param name="readLength">读取的长度</param>
  554. /// <param name="body">保存块数据的流</param>
  555. /// <param name="chunkSize">块长度</param>
  556. private void GetChunkBody<T>(T reader, ref byte[] data, ref int startIndex, ref int readLength, ref MemoryStream body, int chunkSize)
  557. {
  558. if (chunkSize <= 0)
  559. {
  560. return;
  561. }
  562. int chunkReadLength = 0;//每个数据块已读取长度
  563. if (startIndex >= data.Length || startIndex >= readLength)
  564. {
  565. readLength = this.ReadData(reader, ref data);
  566. startIndex = 0;
  567. }
  568. do
  569. {
  570. int owing = chunkSize - chunkReadLength;
  571. int count = Math.Min(readLength - startIndex, owing);
  572. body.Write(data, startIndex, count);
  573. chunkReadLength += count;
  574. if (owing <= count)
  575. {
  576. startIndex += count + 2;
  577. break;
  578. }
  579. startIndex = 0;
  580. readLength = this.ReadData(reader, ref data);
  581. }
  582. while (readLength > 0);
  583. }
  584. /// <summary>
  585. /// 从数据源读取数据
  586. /// </summary>
  587. /// <typeparam name="T">数据源类型</typeparam>
  588. /// <param name="reader">数据源</param>
  589. /// <param name="data">用于存储读取的数据</param>
  590. /// <returns>读取的数据长度,无数据为-1</returns>
  591. private int ReadData<T>(T reader, ref byte[] data)
  592. {
  593. int result = -1;
  594. if (reader is Socket)
  595. {
  596. result = (reader as Socket).Receive(data, SocketFlags.None);
  597. }
  598. else if (reader is SslStream)
  599. {
  600. result = (reader as SslStream).Read(data, 0, data.Length);
  601. }
  602. return result;
  603. }
  604. }
  605. public class HttpHeader
  606. {
  607. /// <summary>
  608. /// 获取请求回应状态码
  609. /// </summary>
  610. public string ResponseStatusCode
  611. {
  612. get;
  613. internal set;
  614. }
  615. /// <summary>
  616. /// 获取跳转url
  617. /// </summary>
  618. public string Location
  619. {
  620. get;
  621. internal set;
  622. }
  623. /// <summary>
  624. /// 获取是否由Gzip压缩
  625. /// </summary>
  626. public bool IsGzip
  627. {
  628. get;
  629. internal set;
  630. }
  631. /// <summary>
  632. /// 获取返回的文档类型
  633. /// </summary>
  634. public string ContentType
  635. {
  636. get;
  637. internal set;
  638. }
  639. /// <summary>
  640. /// 获取内容使用的字符集
  641. /// </summary>
  642. public string Charset
  643. {
  644. get;
  645. internal set;
  646. }
  647. /// <summary>
  648. /// 获取内容长度
  649. /// </summary>
  650. public long ContentLength
  651. {
  652. get;
  653. internal set;
  654. }
  655. /// <summary>
  656. /// 获取是否分块传输
  657. /// </summary>
  658. public bool IsChunk
  659. {
  660. get;
  661. internal set;
  662. }
  663. }
  664. }