WebConnectionStream.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479
  1. //
  2. // System.Net.WebConnectionStream
  3. //
  4. // Authors:
  5. // Gonzalo Paniagua Javier ([email protected])
  6. //
  7. // (C) 2003 Ximian, Inc (http://www.ximian.com)
  8. // (C) 2004 Novell, Inc (http://www.novell.com)
  9. //
  10. using System.IO;
  11. using System.Text;
  12. using System.Threading;
  13. namespace System.Net
  14. {
  15. class WebConnectionStream : Stream
  16. {
  17. static byte [] crlf = new byte [] { 13, 10 };
  18. bool isRead;
  19. WebConnection cnc;
  20. HttpWebRequest request;
  21. byte [] readBuffer;
  22. int readBufferOffset;
  23. int readBufferSize;
  24. int contentLength;
  25. int totalRead;
  26. bool nextReadCalled;
  27. int pendingReads;
  28. int pendingWrites;
  29. ManualResetEvent pending;
  30. bool allowBuffering;
  31. bool sendChunked;
  32. MemoryStream writeBuffer;
  33. bool requestWritten;
  34. byte [] headers;
  35. bool disposed;
  36. bool headersSent;
  37. public WebConnectionStream (WebConnection cnc)
  38. {
  39. isRead = true;
  40. pending = new ManualResetEvent (true);
  41. this.cnc = cnc;
  42. try {
  43. contentLength = Int32.Parse (cnc.Data.Headers ["Content-Length"]);
  44. } catch {
  45. contentLength = Int32.MaxValue;
  46. }
  47. }
  48. public WebConnectionStream (WebConnection cnc, HttpWebRequest request)
  49. {
  50. isRead = false;
  51. this.cnc = cnc;
  52. this.request = request;
  53. allowBuffering = request.InternalAllowBuffering;
  54. sendChunked = request.SendChunked;
  55. if (allowBuffering)
  56. writeBuffer = new MemoryStream ();
  57. if (sendChunked)
  58. pending = new ManualResetEvent (true);
  59. }
  60. internal bool SendChunked {
  61. set { sendChunked = value; }
  62. }
  63. internal byte [] ReadBuffer {
  64. set { readBuffer = value; }
  65. }
  66. internal int ReadBufferOffset {
  67. set { readBufferOffset = value;}
  68. }
  69. internal int ReadBufferSize {
  70. set { readBufferSize = value; }
  71. }
  72. internal byte[] WriteBuffer {
  73. get { return writeBuffer.GetBuffer (); }
  74. }
  75. internal int WriteBufferLength {
  76. get { return (int) writeBuffer.Length; }
  77. }
  78. internal void CheckComplete ()
  79. {
  80. if (!nextReadCalled && readBufferSize - readBufferOffset == contentLength) {
  81. nextReadCalled = true;
  82. cnc.NextRead ();
  83. }
  84. }
  85. internal void ReadAll ()
  86. {
  87. if (!isRead || totalRead >= contentLength || nextReadCalled)
  88. return;
  89. pending.WaitOne ();
  90. lock (this) {
  91. if (totalRead >= contentLength)
  92. return;
  93. byte [] b = null;
  94. int diff = readBufferSize - readBufferOffset;
  95. int new_size;
  96. if (contentLength == Int32.MaxValue) {
  97. MemoryStream ms = new MemoryStream ();
  98. if (readBuffer != null && diff > 0)
  99. ms.Write (readBuffer, readBufferOffset, diff);
  100. byte [] buffer = new byte [2048];
  101. int read;
  102. while ((read = cnc.Read (buffer, 0, 2048)) != 0)
  103. ms.Write (buffer, 0, read);
  104. b = ms.GetBuffer ();
  105. new_size = (int) ms.Length;
  106. contentLength = new_size;
  107. } else {
  108. new_size = contentLength - totalRead;
  109. b = new byte [new_size];
  110. if (readBuffer != null && diff > 0)
  111. Buffer.BlockCopy (readBuffer, readBufferOffset, b, 0, diff);
  112. int remaining = new_size - diff;
  113. int r = -1;
  114. while (remaining > 0 && r != 0) {
  115. r = cnc.Read (b, diff, remaining);
  116. remaining -= r;
  117. diff += r;
  118. }
  119. }
  120. readBuffer = b;
  121. readBufferOffset = 0;
  122. readBufferSize = new_size;
  123. totalRead = 0;
  124. nextReadCalled = true;
  125. }
  126. cnc.NextRead ();
  127. }
  128. static void CallbackWrapper (IAsyncResult r)
  129. {
  130. WebAsyncResult result = (WebAsyncResult) r.AsyncState;
  131. result.InnerAsyncResult = r;
  132. result.DoCallback ();
  133. }
  134. public override int Read (byte [] buffer, int offset, int size)
  135. {
  136. if (!isRead)
  137. throw new NotSupportedException ("this stream does not allow reading");
  138. if (totalRead >= contentLength)
  139. return 0;
  140. IAsyncResult res = BeginRead (buffer, offset, size, null, null);
  141. return EndRead (res);
  142. }
  143. public override IAsyncResult BeginRead (byte [] buffer, int offset, int size,
  144. AsyncCallback cb, object state)
  145. {
  146. if (!isRead)
  147. throw new NotSupportedException ("this stream does not allow reading");
  148. if (buffer == null)
  149. throw new ArgumentNullException ("buffer");
  150. int length = buffer.Length;
  151. if (size < 0 || offset < 0 || length < offset || length - offset < size)
  152. throw new ArgumentOutOfRangeException ();
  153. WebAsyncResult result = new WebAsyncResult (cb, state, buffer, offset, size);
  154. if (totalRead >= contentLength) {
  155. result.SetCompleted (true, -1);
  156. result.DoCallback ();
  157. return result;
  158. }
  159. int remaining = readBufferSize - readBufferOffset;
  160. if (remaining > 0) {
  161. int copy = (remaining > size) ? size : remaining;
  162. Buffer.BlockCopy (readBuffer, readBufferOffset, buffer, offset, copy);
  163. readBufferOffset += copy;
  164. offset += copy;
  165. size -= copy;
  166. totalRead += copy;
  167. if (size == 0 || totalRead >= contentLength) {
  168. result.SetCompleted (true, copy);
  169. result.DoCallback ();
  170. return result;
  171. }
  172. result.NBytes = copy;
  173. }
  174. lock (this) {
  175. pendingReads++;
  176. pending.Reset ();
  177. }
  178. if (cb != null)
  179. cb = new AsyncCallback (CallbackWrapper);
  180. if (contentLength != Int32.MaxValue && contentLength - totalRead < size)
  181. size = contentLength - totalRead;
  182. result.InnerAsyncResult = cnc.BeginRead (buffer, offset, size, cb, result);
  183. return result;
  184. }
  185. public override int EndRead (IAsyncResult r)
  186. {
  187. WebAsyncResult result = (WebAsyncResult) r;
  188. if (!result.IsCompleted) {
  189. int nbytes = cnc.EndRead (result.InnerAsyncResult);
  190. lock (this) {
  191. pendingReads--;
  192. if (pendingReads == 0)
  193. pending.Set ();
  194. }
  195. bool finished = (nbytes == -1);
  196. if (finished && result.NBytes > 0)
  197. nbytes = 0;
  198. result.SetCompleted (false, nbytes + result.NBytes);
  199. totalRead += nbytes;
  200. if (finished || nbytes == 0)
  201. contentLength = totalRead;
  202. }
  203. if (totalRead >= contentLength && !nextReadCalled) {
  204. nextReadCalled = true;
  205. cnc.NextRead ();
  206. }
  207. return result.NBytes;
  208. }
  209. public override IAsyncResult BeginWrite (byte [] buffer, int offset, int size,
  210. AsyncCallback cb, object state)
  211. {
  212. if (isRead)
  213. throw new NotSupportedException ("this stream does not allow writing");
  214. if (buffer == null)
  215. throw new ArgumentNullException ("buffer");
  216. int length = buffer.Length;
  217. if (size < 0 || offset < 0 || length < offset || length - offset < size)
  218. throw new ArgumentOutOfRangeException ();
  219. if (sendChunked) {
  220. lock (this) {
  221. pendingWrites++;
  222. pending.Reset ();
  223. }
  224. }
  225. WebAsyncResult result = new WebAsyncResult (cb, state);
  226. if (allowBuffering) {
  227. writeBuffer.Write (buffer, offset, size);
  228. if (!sendChunked) {
  229. result.SetCompleted (true, 0);
  230. result.DoCallback ();
  231. return result;
  232. }
  233. }
  234. AsyncCallback callback = null;
  235. if (cb != null)
  236. callback = new AsyncCallback (CallbackWrapper);
  237. if (sendChunked) {
  238. WriteRequest ();
  239. string cSize = String.Format ("{0:X}\r\n", size);
  240. byte [] head = Encoding.ASCII.GetBytes (cSize);
  241. int chunkSize = 2 + size + head.Length;
  242. byte [] newBuffer = new byte [chunkSize];
  243. Buffer.BlockCopy (head, 0, newBuffer, 0, head.Length);
  244. Buffer.BlockCopy (buffer, offset, newBuffer, head.Length, size);
  245. Buffer.BlockCopy (crlf, 0, newBuffer, head.Length + size, crlf.Length);
  246. buffer = newBuffer;
  247. offset = 0;
  248. size = chunkSize;
  249. }
  250. result.InnerAsyncResult = cnc.BeginWrite (buffer, offset, size, callback, result);
  251. return result;
  252. }
  253. public override void EndWrite (IAsyncResult r)
  254. {
  255. if (r == null)
  256. throw new ArgumentNullException ("r");
  257. if (allowBuffering && !sendChunked)
  258. return;
  259. WebAsyncResult result = r as WebAsyncResult;
  260. if (result == null)
  261. throw new ArgumentException ("Invalid IAsyncResult");
  262. if (result.GotException)
  263. throw result.Exception;
  264. cnc.EndWrite (result.InnerAsyncResult);
  265. if (sendChunked) {
  266. lock (this) {
  267. pendingWrites--;
  268. if (pendingWrites == 0)
  269. pending.Set ();
  270. }
  271. }
  272. }
  273. public override void Write (byte [] buffer, int offset, int size)
  274. {
  275. if (isRead)
  276. throw new NotSupportedException ("This stream does not allow writing");
  277. IAsyncResult res = BeginWrite (buffer, offset, size, null, null);
  278. EndWrite (res);
  279. }
  280. public override void Flush ()
  281. {
  282. }
  283. internal void SetHeaders (byte [] buffer, int offset, int size)
  284. {
  285. if (headersSent)
  286. return;
  287. if (!allowBuffering || sendChunked) {
  288. headersSent = true;
  289. try {
  290. cnc.Write (buffer, offset, size);
  291. } catch (IOException) {
  292. if (cnc.Connected)
  293. throw;
  294. if (!cnc.TryReconnect ())
  295. throw;
  296. cnc.Write (buffer, offset, size);
  297. }
  298. } else {
  299. headers = new byte [size];
  300. Buffer.BlockCopy (buffer, offset, headers, 0, size);
  301. }
  302. }
  303. internal void WriteRequest ()
  304. {
  305. if (requestWritten)
  306. return;
  307. if (sendChunked) {
  308. request.SendRequestHeaders ();
  309. requestWritten = true;
  310. return;
  311. }
  312. if (!allowBuffering || writeBuffer == null)
  313. return;
  314. byte [] bytes = writeBuffer.GetBuffer ();
  315. int length = (int) writeBuffer.Length;
  316. if (request.ContentLength != -1 && request.ContentLength < length) {
  317. throw new ProtocolViolationException ("Specified Content-Length is less than the " +
  318. "number of bytes to write");
  319. }
  320. request.InternalContentLength = length;
  321. request.SendRequestHeaders ();
  322. requestWritten = true;
  323. while (true) {
  324. cnc.WaitForContinue (headers, 0, headers.Length);
  325. if (!cnc.Connected) {
  326. if (!cnc.TryReconnect ())
  327. return;
  328. continue;
  329. }
  330. headersSent = true;
  331. if (cnc.Data.StatusCode != 0 && cnc.Data.StatusCode != 100)
  332. return;
  333. cnc.Write (bytes, 0, length);
  334. if (!cnc.Connected && cnc.TryReconnect ())
  335. continue;
  336. break;
  337. }
  338. }
  339. internal void InternalClose ()
  340. {
  341. disposed = true;
  342. }
  343. public override void Close ()
  344. {
  345. if (sendChunked) {
  346. pending.WaitOne ();
  347. byte [] chunk = Encoding.ASCII.GetBytes ("0\r\n\r\n");
  348. cnc.Write (chunk, 0, chunk.Length);
  349. return;
  350. }
  351. if (isRead || !allowBuffering || disposed)
  352. return;
  353. disposed = true;
  354. long length = request.ContentLength;
  355. if (length != -1 && length > writeBuffer.Length)
  356. throw new IOException ("Cannot close the stream until all bytes are written");
  357. WriteRequest ();
  358. }
  359. internal void ResetWriteBuffer ()
  360. {
  361. if (!allowBuffering)
  362. return;
  363. writeBuffer = new MemoryStream ();
  364. requestWritten = false;
  365. headersSent = false;
  366. }
  367. public override long Seek (long a, SeekOrigin b)
  368. {
  369. throw new NotSupportedException ();
  370. }
  371. public override void SetLength (long a)
  372. {
  373. throw new NotSupportedException ();
  374. }
  375. public override bool CanSeek {
  376. get { return false; }
  377. }
  378. public override bool CanRead {
  379. get { return isRead && (contentLength == Int32.MaxValue || totalRead < contentLength); }
  380. }
  381. public override bool CanWrite {
  382. get { return !isRead; }
  383. }
  384. public override long Length {
  385. get { throw new NotSupportedException (); }
  386. }
  387. public override long Position {
  388. get { throw new NotSupportedException (); }
  389. set { throw new NotSupportedException (); }
  390. }
  391. }
  392. }