WebConnection.cs 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917
  1. //
  2. // System.Net.WebConnection
  3. //
  4. // Authors:
  5. // Gonzalo Paniagua Javier ([email protected])
  6. //
  7. // (C) 2003 Ximian, Inc (http://www.ximian.com)
  8. //
  9. //
  10. // Permission is hereby granted, free of charge, to any person obtaining
  11. // a copy of this software and associated documentation files (the
  12. // "Software"), to deal in the Software without restriction, including
  13. // without limitation the rights to use, copy, modify, merge, publish,
  14. // distribute, sublicense, and/or sell copies of the Software, and to
  15. // permit persons to whom the Software is furnished to do so, subject to
  16. // the following conditions:
  17. //
  18. // The above copyright notice and this permission notice shall be
  19. // included in all copies or substantial portions of the Software.
  20. //
  21. // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
  22. // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
  23. // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
  24. // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
  25. // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
  26. // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
  27. // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
  28. //
  29. using System.IO;
  30. using System.Collections;
  31. using System.Net.Sockets;
  32. using System.Reflection;
  33. using System.Security.Cryptography.X509Certificates;
  34. using System.Text;
  35. using System.Threading;
  36. namespace System.Net
  37. {
  38. enum ReadState
  39. {
  40. None,
  41. Status,
  42. Headers,
  43. Content
  44. }
  45. class WebConnection
  46. {
  47. ServicePoint sPoint;
  48. Stream nstream;
  49. Socket socket;
  50. object socketLock = new object ();
  51. WebExceptionStatus status;
  52. WaitCallback initConn;
  53. bool keepAlive;
  54. byte [] buffer;
  55. static AsyncCallback readDoneDelegate = new AsyncCallback (ReadDone);
  56. EventHandler abortHandler;
  57. ReadState readState;
  58. internal WebConnectionData Data;
  59. bool chunkedRead;
  60. ChunkStream chunkStream;
  61. Queue queue;
  62. bool reused;
  63. int position;
  64. bool busy;
  65. bool ssl;
  66. bool certsAvailable;
  67. static object classLock = new object ();
  68. static Type sslStream;
  69. static PropertyInfo piClient;
  70. static PropertyInfo piServer;
  71. static PropertyInfo piTrustFailure;
  72. public WebConnection (WebConnectionGroup group, ServicePoint sPoint)
  73. {
  74. this.sPoint = sPoint;
  75. buffer = new byte [4096];
  76. readState = ReadState.None;
  77. Data = new WebConnectionData ();
  78. initConn = new WaitCallback (InitConnection);
  79. abortHandler = new EventHandler (Abort);
  80. queue = group.Queue;
  81. }
  82. bool CanReuse ()
  83. {
  84. // The real condition is !(socket.Poll (0, SelectMode.SelectRead) || socket.Available != 0)
  85. // but if there's data pending to read (!) we won't reuse the socket.
  86. return (socket.Poll (0, SelectMode.SelectRead) == false);
  87. }
  88. void Connect ()
  89. {
  90. lock (socketLock) {
  91. if (socket != null && socket.Connected && status == WebExceptionStatus.Success) {
  92. // Take the chunked stream to the expected state (State.None)
  93. if (CanReuse () && CompleteChunkedRead ()) {
  94. reused = true;
  95. return;
  96. }
  97. }
  98. reused = false;
  99. if (socket != null) {
  100. socket.Close();
  101. socket = null;
  102. }
  103. chunkStream = null;
  104. IPHostEntry hostEntry = sPoint.HostEntry;
  105. if (hostEntry == null) {
  106. status = sPoint.UsesProxy ? WebExceptionStatus.ProxyNameResolutionFailure :
  107. WebExceptionStatus.NameResolutionFailure;
  108. return;
  109. }
  110. foreach (IPAddress address in hostEntry.AddressList) {
  111. socket = new Socket (address.AddressFamily, SocketType.Stream, ProtocolType.Tcp);
  112. try {
  113. socket.Connect (new IPEndPoint(address, sPoint.Address.Port));
  114. status = WebExceptionStatus.Success;
  115. break;
  116. } catch (SocketException) {
  117. // This might be null if the request is aborted
  118. if (socket != null) {
  119. socket.Close();
  120. socket = null;
  121. status = WebExceptionStatus.ConnectFailure;
  122. }
  123. }
  124. }
  125. }
  126. }
  127. static void EnsureSSLStreamAvailable ()
  128. {
  129. lock (classLock) {
  130. if (sslStream != null)
  131. return;
  132. // HttpsClientStream is an internal glue class in Mono.Security.dll
  133. sslStream = Type.GetType ("Mono.Security.Protocol.Tls.HttpsClientStream, " +
  134. Consts.AssemblyMono_Security, false);
  135. if (sslStream == null) {
  136. string msg = "Missing Mono.Security.dll assembly. " +
  137. "Support for SSL/TLS is unavailable.";
  138. throw new NotSupportedException (msg);
  139. }
  140. piClient = sslStream.GetProperty ("SelectedClientCertificate");
  141. piServer = sslStream.GetProperty ("ServerCertificate");
  142. piTrustFailure = sslStream.GetProperty ("TrustFailure");
  143. }
  144. }
  145. bool CreateTunnel (HttpWebRequest request, Stream stream, out byte [] buffer)
  146. {
  147. StringBuilder sb = new StringBuilder ();
  148. sb.Append ("CONNECT ");
  149. sb.Append (request.Address.Host);
  150. sb.Append (':');
  151. sb.Append (request.Address.Port);
  152. sb.Append (" HTTP/");
  153. if (request.ServicePoint.ProtocolVersion == HttpVersion.Version11)
  154. sb.Append ("1.1");
  155. else
  156. sb.Append ("1.0");
  157. sb.Append ("\r\nHost: ");
  158. sb.Append (request.Address.Authority);
  159. if (request.Headers ["Proxy-Authorization"] != null) {
  160. sb.Append ("\r\nProxy-Authorization: ");
  161. sb.Append (request.Headers ["Proxy-Authorization"]);
  162. }
  163. sb.Append ("\r\n\r\n");
  164. byte [] connectBytes = Encoding.Default.GetBytes (sb.ToString ());
  165. stream.Write (connectBytes, 0, connectBytes.Length);
  166. return ReadHeaders (request, stream, out buffer);
  167. }
  168. bool ReadHeaders (HttpWebRequest request, Stream stream, out byte [] retBuffer)
  169. {
  170. retBuffer = null;
  171. byte [] buffer = new byte [256];
  172. MemoryStream ms = new MemoryStream ();
  173. bool gotStatus = false;
  174. while (true) {
  175. int n = stream.Read (buffer, 0, 256);
  176. if (n == 0) {
  177. HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadHeders");
  178. return false;
  179. }
  180. ms.Write (buffer, 0, n);
  181. int start = 0;
  182. string str = null;
  183. while (ReadLine (ms.GetBuffer (), ref start, (int) ms.Length, ref str)) {
  184. if (str == null) {
  185. if (ms.Length - start > 0) {
  186. retBuffer = new byte [ms.Length - start];
  187. Buffer.BlockCopy (ms.GetBuffer (), start, retBuffer, 0, retBuffer.Length);
  188. }
  189. return true;
  190. }
  191. if (gotStatus)
  192. continue;
  193. int spaceidx = str.IndexOf (' ');
  194. if (spaceidx == -1)
  195. throw new Exception ();
  196. int resultCode = Int32.Parse (str.Substring (spaceidx + 1, 3));
  197. if (resultCode != 200)
  198. throw new Exception ();
  199. gotStatus = true;
  200. }
  201. }
  202. }
  203. bool CreateStream (HttpWebRequest request)
  204. {
  205. try {
  206. NetworkStream serverStream = new NetworkStream (socket, false);
  207. if (request.Address.Scheme == Uri.UriSchemeHttps) {
  208. ssl = true;
  209. EnsureSSLStreamAvailable ();
  210. if (!reused || nstream == null || nstream.GetType () != sslStream) {
  211. byte [] buffer = null;
  212. if (sPoint.UseConnect) {
  213. bool ok = CreateTunnel (request, serverStream, out buffer);
  214. if (!ok)
  215. return false;
  216. }
  217. object[] args = new object [4] { serverStream,
  218. request.ClientCertificates,
  219. request, buffer};
  220. nstream = (Stream) Activator.CreateInstance (sslStream, args);
  221. certsAvailable = false;
  222. }
  223. // we also need to set ServicePoint.Certificate
  224. // and ServicePoint.ClientCertificate but this can
  225. // only be done later (after handshake - which is
  226. // done only after a read operation).
  227. } else {
  228. ssl = false;
  229. nstream = serverStream;
  230. }
  231. } catch (Exception) {
  232. status = WebExceptionStatus.ConnectFailure;
  233. return false;
  234. }
  235. return true;
  236. }
  237. void HandleError (WebExceptionStatus st, Exception e, string where)
  238. {
  239. status = st;
  240. lock (this) {
  241. if (st == WebExceptionStatus.RequestCanceled)
  242. Data = new WebConnectionData ();
  243. }
  244. if (e == null) { // At least we now where it comes from
  245. try {
  246. throw new Exception (new System.Diagnostics.StackTrace ().ToString ());
  247. } catch (Exception e2) {
  248. e = e2;
  249. }
  250. }
  251. HttpWebRequest req = null;
  252. if (Data != null && Data.request != null)
  253. req = Data.request;
  254. Close (true);
  255. if (req != null)
  256. req.SetResponseError (st, e, where);
  257. }
  258. static void ReadDone (IAsyncResult result)
  259. {
  260. WebConnection cnc = (WebConnection) result.AsyncState;
  261. WebConnectionData data = cnc.Data;
  262. Stream ns = cnc.nstream;
  263. if (ns == null) {
  264. cnc.Close (true);
  265. return;
  266. }
  267. int nread = -1;
  268. try {
  269. nread = ns.EndRead (result);
  270. } catch (Exception e) {
  271. cnc.HandleError (WebExceptionStatus.ReceiveFailure, e, "ReadDone1");
  272. return;
  273. }
  274. if (nread == 0) {
  275. cnc.HandleError (WebExceptionStatus.ReceiveFailure, null, "ReadDone2");
  276. return;
  277. }
  278. if (nread < 0) {
  279. cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, null, "ReadDone3");
  280. return;
  281. }
  282. int pos = -1;
  283. nread += cnc.position;
  284. if (cnc.readState == ReadState.None) {
  285. Exception exc = null;
  286. try {
  287. pos = cnc.GetResponse (cnc.buffer, nread);
  288. } catch (Exception e) {
  289. exc = e;
  290. }
  291. if (exc != null) {
  292. cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, exc, "ReadDone4");
  293. return;
  294. }
  295. }
  296. if (cnc.readState != ReadState.Content) {
  297. int est = nread * 2;
  298. int max = (est < cnc.buffer.Length) ? cnc.buffer.Length : est;
  299. byte [] newBuffer = new byte [max];
  300. Buffer.BlockCopy (cnc.buffer, 0, newBuffer, 0, nread);
  301. cnc.buffer = newBuffer;
  302. cnc.position = nread;
  303. cnc.readState = ReadState.None;
  304. InitRead (cnc);
  305. return;
  306. }
  307. cnc.position = 0;
  308. WebConnectionStream stream = new WebConnectionStream (cnc);
  309. string contentType = data.Headers ["Transfer-Encoding"];
  310. cnc.chunkedRead = (contentType != null && contentType.ToLower ().IndexOf ("chunked") != -1);
  311. if (!cnc.chunkedRead) {
  312. stream.ReadBuffer = cnc.buffer;
  313. stream.ReadBufferOffset = pos;
  314. stream.ReadBufferSize = nread;
  315. } else if (cnc.chunkStream == null) {
  316. try {
  317. cnc.chunkStream = new ChunkStream (cnc.buffer, pos, nread, data.Headers);
  318. } catch (Exception e) {
  319. cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, e, "ReadDone5");
  320. return;
  321. }
  322. } else {
  323. cnc.chunkStream.ResetBuffer ();
  324. try {
  325. cnc.chunkStream.Write (cnc.buffer, pos, nread);
  326. } catch (Exception e) {
  327. cnc.HandleError (WebExceptionStatus.ServerProtocolViolation, e, "ReadDone6");
  328. return;
  329. }
  330. }
  331. data.stream = stream;
  332. if (!ExpectContent (data.StatusCode))
  333. stream.ForceCompletion ();
  334. data.request.SetResponseData (data);
  335. }
  336. static bool ExpectContent (int statusCode)
  337. {
  338. return (statusCode >= 200 && statusCode != 204 && statusCode != 304);
  339. }
  340. internal void GetCertificates ()
  341. {
  342. // here the SSL negotiation have been done
  343. X509Certificate client = (X509Certificate) piClient.GetValue (nstream, null);
  344. X509Certificate server = (X509Certificate) piServer.GetValue (nstream, null);
  345. sPoint.SetCertificates (client, server);
  346. certsAvailable = (server != null);
  347. }
  348. delegate void MyDelegate (object o);
  349. internal static void InitRead (object state)
  350. {
  351. WebConnection cnc = (WebConnection) state;
  352. if (!cnc.ssl) {
  353. InitRead2 (state);
  354. } else {
  355. MyDelegate d = new MyDelegate (InitRead2);
  356. d.BeginInvoke (state, null, null);
  357. }
  358. }
  359. static void InitRead2 (object state)
  360. {
  361. WebConnection cnc = (WebConnection) state;
  362. Stream ns = cnc.nstream;
  363. try {
  364. int size = cnc.buffer.Length - cnc.position;
  365. ns.BeginRead (cnc.buffer, cnc.position, size, readDoneDelegate, cnc);
  366. } catch (Exception e) {
  367. cnc.HandleError (WebExceptionStatus.ReceiveFailure, e, "InitRead");
  368. }
  369. }
  370. int GetResponse (byte [] buffer, int max)
  371. {
  372. int pos = 0;
  373. string line = null;
  374. bool lineok = false;
  375. bool isContinue = false;
  376. bool emptyFirstLine = false;
  377. do {
  378. if (readState == ReadState.None) {
  379. lineok = ReadLine (buffer, ref pos, max, ref line);
  380. if (!lineok)
  381. return -1;
  382. if (line == null) {
  383. emptyFirstLine = true;
  384. continue;
  385. }
  386. emptyFirstLine = false;
  387. readState = ReadState.Status;
  388. string [] parts = line.Split (' ');
  389. if (parts.Length < 2)
  390. return -1;
  391. if (String.Compare (parts [0], "HTTP/1.1", true) == 0) {
  392. Data.Version = HttpVersion.Version11;
  393. sPoint.SetVersion (HttpVersion.Version11);
  394. } else {
  395. Data.Version = HttpVersion.Version10;
  396. sPoint.SetVersion (HttpVersion.Version10);
  397. }
  398. Data.StatusCode = (int) UInt32.Parse (parts [1]);
  399. if (parts.Length >= 3)
  400. Data.StatusDescription = String.Join (" ", parts, 2, parts.Length - 2);
  401. else
  402. Data.StatusDescription = "";
  403. if (pos >= max)
  404. return pos;
  405. }
  406. emptyFirstLine = false;
  407. if (readState == ReadState.Status) {
  408. readState = ReadState.Headers;
  409. Data.Headers = new WebHeaderCollection ();
  410. ArrayList headers = new ArrayList ();
  411. bool finished = false;
  412. while (!finished) {
  413. if (ReadLine (buffer, ref pos, max, ref line) == false)
  414. break;
  415. if (line == null) {
  416. // Empty line: end of headers
  417. finished = true;
  418. continue;
  419. }
  420. if (line.Length > 0 && (line [0] == ' ' || line [0] == '\t')) {
  421. int count = headers.Count - 1;
  422. if (count < 0)
  423. break;
  424. string prev = (string) headers [count] + line;
  425. headers [count] = prev;
  426. } else {
  427. headers.Add (line);
  428. }
  429. }
  430. if (!finished)
  431. return -1;
  432. foreach (string s in headers)
  433. Data.Headers.SetInternal (s);
  434. if (Data.StatusCode == (int) HttpStatusCode.Continue) {
  435. sPoint.SendContinue = true;
  436. if (pos >= max)
  437. return pos;
  438. if (Data.request.ExpectContinue) {
  439. Data.request.DoContinueDelegate (Data.StatusCode, Data.Headers);
  440. // Prevent double calls when getting the
  441. // headers in several packets.
  442. Data.request.ExpectContinue = false;
  443. }
  444. readState = ReadState.None;
  445. isContinue = true;
  446. }
  447. else {
  448. readState = ReadState.Content;
  449. return pos;
  450. }
  451. }
  452. } while (emptyFirstLine || isContinue);
  453. return -1;
  454. }
  455. void InitConnection (object state)
  456. {
  457. HttpWebRequest request = (HttpWebRequest) state;
  458. if (status == WebExceptionStatus.RequestCanceled) {
  459. lock (this) {
  460. busy = false;
  461. Data = new WebConnectionData ();
  462. SendNext ();
  463. }
  464. return;
  465. }
  466. keepAlive = request.KeepAlive;
  467. Data = new WebConnectionData ();
  468. Data.request = request;
  469. Connect ();
  470. if (status != WebExceptionStatus.Success) {
  471. if (status != WebExceptionStatus.RequestCanceled) {
  472. request.SetWriteStreamError (status);
  473. Close (true);
  474. }
  475. return;
  476. }
  477. if (!CreateStream (request)) {
  478. request.SetWriteStreamError (status);
  479. Close (true);
  480. return;
  481. }
  482. readState = ReadState.None;
  483. request.SetWriteStream (new WebConnectionStream (this, request));
  484. }
  485. internal EventHandler SendRequest (HttpWebRequest request)
  486. {
  487. lock (this) {
  488. if (!busy) {
  489. busy = true;
  490. status = WebExceptionStatus.Success;
  491. ThreadPool.QueueUserWorkItem (initConn, request);
  492. } else {
  493. lock (queue) {
  494. queue.Enqueue (request);
  495. }
  496. }
  497. }
  498. return abortHandler;
  499. }
  500. void SendNext ()
  501. {
  502. lock (queue) {
  503. if (queue.Count > 0) {
  504. SendRequest ((HttpWebRequest) queue.Dequeue ());
  505. }
  506. }
  507. }
  508. internal void NextRead ()
  509. {
  510. lock (this) {
  511. string header = (sPoint.UsesProxy) ? "Proxy-Connection" : "Connection";
  512. string cncHeader = (Data.Headers != null) ? Data.Headers [header] : null;
  513. bool keepAlive = (Data.Version == HttpVersion.Version11 && this.keepAlive);
  514. if (cncHeader != null) {
  515. cncHeader = cncHeader.ToLower ();
  516. keepAlive = (this.keepAlive && cncHeader.IndexOf ("keep-alive") != -1);
  517. }
  518. if ((socket != null && !socket.Connected) ||
  519. (!keepAlive || (cncHeader != null && cncHeader.IndexOf ("close") != -1))) {
  520. Close (false);
  521. }
  522. busy = false;
  523. SendNext ();
  524. }
  525. }
  526. static bool ReadLine (byte [] buffer, ref int start, int max, ref string output)
  527. {
  528. bool foundCR = false;
  529. StringBuilder text = new StringBuilder ();
  530. int c = 0;
  531. while (start < max) {
  532. c = (int) buffer [start++];
  533. if (c == '\n') { // newline
  534. if ((text.Length > 0) && (text [text.Length - 1] == '\r'))
  535. text.Length--;
  536. foundCR = false;
  537. break;
  538. } else if (foundCR) {
  539. text.Length--;
  540. break;
  541. }
  542. if (c == '\r')
  543. foundCR = true;
  544. text.Append ((char) c);
  545. }
  546. if (c != '\n' && c != '\r')
  547. return false;
  548. if (text.Length == 0) {
  549. output = null;
  550. return (c == '\n' || c == '\r');
  551. }
  552. if (foundCR)
  553. text.Length--;
  554. output = text.ToString ();
  555. return true;
  556. }
  557. internal IAsyncResult BeginRead (byte [] buffer, int offset, int size, AsyncCallback cb, object state)
  558. {
  559. if (nstream == null)
  560. return null;
  561. IAsyncResult result = null;
  562. if (!chunkedRead || chunkStream.WantMore) {
  563. try {
  564. result = nstream.BeginRead (buffer, offset, size, cb, state);
  565. cb = null;
  566. } catch (Exception) {
  567. HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked BeginRead");
  568. throw;
  569. }
  570. }
  571. if (chunkedRead) {
  572. WebAsyncResult wr = new WebAsyncResult (cb, state, buffer, offset, size);
  573. wr.InnerAsyncResult = result;
  574. if (result == null) {
  575. // Will be completed from the data in ChunkStream
  576. wr.SetCompleted (true, (Exception) null);
  577. wr.DoCallback ();
  578. }
  579. return wr;
  580. }
  581. return result;
  582. }
  583. internal int EndRead (IAsyncResult result)
  584. {
  585. if (nstream == null)
  586. return 0;
  587. int nbytes = 0;
  588. WebAsyncResult wr = null;
  589. IAsyncResult nsAsync = ((WebAsyncResult) result).InnerAsyncResult;
  590. if (chunkedRead && (nsAsync is WebAsyncResult)) {
  591. wr = (WebAsyncResult) nsAsync;
  592. IAsyncResult inner = wr.InnerAsyncResult;
  593. if (inner != null && !(inner is WebAsyncResult))
  594. nbytes = nstream.EndRead (inner);
  595. } else if (!(nsAsync is WebAsyncResult)) {
  596. nbytes = nstream.EndRead (nsAsync);
  597. wr = (WebAsyncResult) result;
  598. }
  599. if (chunkedRead) {
  600. bool done = (nbytes == 0);
  601. try {
  602. chunkStream.WriteAndReadBack (wr.Buffer, wr.Offset, wr.Size, ref nbytes);
  603. if (!done && nbytes == 0 && chunkStream.WantMore)
  604. nbytes = EnsureRead (wr.Buffer, wr.Offset, wr.Size);
  605. } catch (Exception e) {
  606. if (e is WebException)
  607. throw e;
  608. throw new WebException ("Invalid chunked data.", e,
  609. WebExceptionStatus.ServerProtocolViolation, null);
  610. }
  611. if ((done || nbytes == 0) && chunkStream.ChunkLeft != 0) {
  612. HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked EndRead");
  613. throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null);
  614. }
  615. }
  616. return (nbytes != 0) ? nbytes : -1;
  617. }
  618. // To be called on chunkedRead when we can read no data from the ChunkStream yet
  619. int EnsureRead (byte [] buffer, int offset, int size)
  620. {
  621. byte [] morebytes = null;
  622. int nbytes = 0;
  623. while (nbytes == 0 && chunkStream.WantMore) {
  624. int localsize = chunkStream.ChunkLeft;
  625. if (localsize <= 0) // not read chunk size yet
  626. localsize = 1024;
  627. else if (localsize > 16384)
  628. localsize = 16384;
  629. if (morebytes == null || morebytes.Length < localsize)
  630. morebytes = new byte [localsize];
  631. int nread = nstream.Read (morebytes, 0, localsize);
  632. if (nread <= 0)
  633. return 0; // Error
  634. chunkStream.Write (morebytes, 0, nread);
  635. nbytes += chunkStream.Read (buffer, offset + nbytes, size - nbytes);
  636. }
  637. return nbytes;
  638. }
  639. bool CompleteChunkedRead()
  640. {
  641. if (!chunkedRead || chunkStream == null)
  642. return true;
  643. while (chunkStream.WantMore) {
  644. int nbytes = nstream.Read (buffer, 0, buffer.Length);
  645. if (nbytes <= 0)
  646. return false; // Socket was disconnected
  647. chunkStream.Write (buffer, 0, nbytes);
  648. }
  649. return true;
  650. }
  651. internal IAsyncResult BeginWrite (byte [] buffer, int offset, int size, AsyncCallback cb, object state)
  652. {
  653. IAsyncResult result = null;
  654. if (nstream == null)
  655. return null;
  656. try {
  657. result = nstream.BeginWrite (buffer, offset, size, cb, state);
  658. } catch (Exception) {
  659. status = WebExceptionStatus.SendFailure;
  660. throw;
  661. }
  662. return result;
  663. }
  664. internal bool EndWrite (IAsyncResult result)
  665. {
  666. if (nstream == null)
  667. return false;
  668. try {
  669. nstream.EndWrite (result);
  670. return true;
  671. } catch (Exception e) {
  672. status = WebExceptionStatus.SendFailure;
  673. return false;
  674. }
  675. }
  676. internal int Read (byte [] buffer, int offset, int size)
  677. {
  678. if (nstream == null)
  679. return 0;
  680. int result = 0;
  681. try {
  682. bool done = false;
  683. if (!chunkedRead) {
  684. result = nstream.Read (buffer, offset, size);
  685. done = (result == 0);
  686. }
  687. if (chunkedRead) {
  688. try {
  689. chunkStream.WriteAndReadBack (buffer, offset, size, ref result);
  690. if (!done && result == 0 && chunkStream.WantMore)
  691. result = EnsureRead (buffer, offset, size);
  692. } catch (Exception e) {
  693. HandleError (WebExceptionStatus.ReceiveFailure, e, "chunked Read1");
  694. throw;
  695. }
  696. if ((done || result == 0) && chunkStream.WantMore) {
  697. HandleError (WebExceptionStatus.ReceiveFailure, null, "chunked Read2");
  698. throw new WebException ("Read error", null, WebExceptionStatus.ReceiveFailure, null);
  699. }
  700. }
  701. } catch (Exception e) {
  702. HandleError (WebExceptionStatus.ReceiveFailure, e, "Read");
  703. }
  704. return result;
  705. }
  706. internal void Write (byte [] buffer, int offset, int size)
  707. {
  708. if (nstream == null)
  709. return;
  710. try {
  711. nstream.Write (buffer, offset, size);
  712. // here SSL handshake should have been done
  713. if (ssl && !certsAvailable)
  714. GetCertificates ();
  715. } catch (Exception e) {
  716. WebExceptionStatus wes = WebExceptionStatus.SendFailure;
  717. string msg = "Write";
  718. if (e is WebException) {
  719. HandleError (wes, e, msg);
  720. return;
  721. }
  722. // if SSL is in use then check for TrustFailure
  723. if (ssl && (bool) piTrustFailure.GetValue (nstream, null)) {
  724. wes = WebExceptionStatus.TrustFailure;
  725. msg = "Trust failure";
  726. }
  727. HandleError (wes, e, msg);
  728. }
  729. }
  730. internal void Close (bool sendNext)
  731. {
  732. lock (this) {
  733. if (nstream != null) {
  734. try {
  735. nstream.Close ();
  736. } catch {}
  737. nstream = null;
  738. }
  739. if (socket != null) {
  740. try {
  741. socket.Close ();
  742. } catch {}
  743. socket = null;
  744. }
  745. busy = false;
  746. if (sendNext)
  747. SendNext ();
  748. }
  749. }
  750. void Abort (object sender, EventArgs args)
  751. {
  752. lock (this) {
  753. if (Data.request == sender) {
  754. HandleError (WebExceptionStatus.RequestCanceled, null, "Abort");
  755. return;
  756. }
  757. lock (queue) {
  758. if (queue.Count > 0 && queue.Peek () == sender) {
  759. queue.Dequeue ();
  760. return;
  761. }
  762. object [] old = queue.ToArray ();
  763. queue.Clear ();
  764. for (int i = old.Length - 1; i >= 0; i--) {
  765. if (old [i] != sender)
  766. queue.Enqueue (old [i]);
  767. }
  768. }
  769. }
  770. }
  771. internal bool Busy {
  772. get { lock (this) return busy; }
  773. }
  774. internal bool Connected {
  775. get {
  776. lock (this) {
  777. return (socket != null && socket.Connected);
  778. }
  779. }
  780. }
  781. }
  782. }