2 // System.Net.WebConnectionStream
5 // Gonzalo Paniagua Javier (gonzalo@ximian.com)
7 // (C) 2003 Ximian, Inc (http://www.ximian.com)
8 // (C) 2004 Novell, Inc (http://www.novell.com)
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
34 using System.Threading;
38 class WebConnectionStream : Stream
40 static byte [] crlf = new byte [] { 13, 10 };
43 HttpWebRequest request;
52 ManualResetEvent pending;
55 MemoryStream writeBuffer;
61 public WebConnectionStream (WebConnection cnc)
64 pending = new ManualResetEvent (true);
66 string clength = cnc.Data.Headers ["Content-Length"];
67 if (clength != null && clength != "") {
69 contentLength = Int32.Parse (clength);
71 contentLength = Int32.MaxValue;
74 contentLength = Int32.MaxValue;
78 public WebConnectionStream (WebConnection cnc, HttpWebRequest request)
82 this.request = request;
83 allowBuffering = request.InternalAllowBuffering;
84 sendChunked = request.SendChunked;
86 writeBuffer = new MemoryStream ();
89 pending = new ManualResetEvent (true);
92 internal bool SendChunked {
93 set { sendChunked = value; }
96 internal byte [] ReadBuffer {
97 set { readBuffer = value; }
100 internal int ReadBufferOffset {
101 set { readBufferOffset = value;}
104 internal int ReadBufferSize {
105 set { readBufferSize = value; }
108 internal byte[] WriteBuffer {
109 get { return writeBuffer.GetBuffer (); }
112 internal int WriteBufferLength {
113 get { return (int) writeBuffer.Length; }
116 internal void ForceCompletion ()
118 nextReadCalled = true;
122 internal void CheckComplete ()
124 bool nrc = nextReadCalled;
125 if (!nrc && readBufferSize - readBufferOffset == contentLength) {
126 nextReadCalled = true;
131 internal void ReadAll ()
133 if (!isRead || totalRead >= contentLength || nextReadCalled) {
134 if (!nextReadCalled) {
135 nextReadCalled = true;
143 if (totalRead >= contentLength)
147 int diff = readBufferSize - readBufferOffset;
150 if (contentLength == Int32.MaxValue) {
151 MemoryStream ms = new MemoryStream ();
152 byte [] buffer = null;
153 if (readBuffer != null && diff > 0) {
154 ms.Write (readBuffer, readBufferOffset, diff);
155 if (readBufferSize >= 8192)
160 buffer = new byte [8192];
163 while ((read = cnc.Read (buffer, 0, buffer.Length)) != 0)
164 ms.Write (buffer, 0, read);
167 new_size = (int) ms.Length;
168 contentLength = new_size;
170 new_size = contentLength - totalRead;
171 b = new byte [new_size];
172 if (readBuffer != null && diff > 0) {
176 Buffer.BlockCopy (readBuffer, readBufferOffset, b, 0, diff);
179 int remaining = new_size - diff;
181 while (remaining > 0 && r != 0) {
182 r = cnc.Read (b, diff, remaining);
189 readBufferOffset = 0;
190 readBufferSize = new_size;
192 nextReadCalled = true;
198 static void CallbackWrapper (IAsyncResult r)
200 WebAsyncResult result = (WebAsyncResult) r.AsyncState;
201 result.InnerAsyncResult = r;
202 result.DoCallback ();
205 public override int Read (byte [] buffer, int offset, int size)
208 throw new NotSupportedException ("this stream does not allow reading");
210 if (totalRead >= contentLength)
213 IAsyncResult res = BeginRead (buffer, offset, size, null, null);
214 return EndRead (res);
217 public override IAsyncResult BeginRead (byte [] buffer, int offset, int size,
218 AsyncCallback cb, object state)
221 throw new NotSupportedException ("this stream does not allow reading");
224 throw new ArgumentNullException ("buffer");
226 int length = buffer.Length;
227 if (size < 0 || offset < 0 || length < offset || length - offset < size)
228 throw new ArgumentOutOfRangeException ();
235 WebAsyncResult result = new WebAsyncResult (cb, state, buffer, offset, size);
236 if (totalRead >= contentLength) {
237 result.SetCompleted (true, -1);
238 result.DoCallback ();
242 int remaining = readBufferSize - readBufferOffset;
244 int copy = (remaining > size) ? size : remaining;
245 Buffer.BlockCopy (readBuffer, readBufferOffset, buffer, offset, copy);
246 readBufferOffset += copy;
250 if (size == 0 || totalRead >= contentLength) {
251 result.SetCompleted (true, copy);
252 result.DoCallback ();
255 result.NBytes = copy;
259 cb = new AsyncCallback (CallbackWrapper);
261 if (contentLength != Int32.MaxValue && contentLength - totalRead < size)
262 size = contentLength - totalRead;
264 result.InnerAsyncResult = cnc.BeginRead (buffer, offset, size, cb, result);
268 public override int EndRead (IAsyncResult r)
270 WebAsyncResult result = (WebAsyncResult) r;
272 if (!result.IsCompleted) {
273 int nbytes = cnc.EndRead (result.InnerAsyncResult);
274 bool finished = (nbytes == -1);
275 if (finished && result.NBytes > 0)
278 result.SetCompleted (false, nbytes + result.NBytes);
280 if (finished || nbytes == 0)
281 contentLength = totalRead;
286 if (pendingReads == 0)
290 if (totalRead >= contentLength && !nextReadCalled)
293 return result.NBytes;
296 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int size,
297 AsyncCallback cb, object state)
300 throw new NotSupportedException ("this stream does not allow writing");
303 throw new ArgumentNullException ("buffer");
305 int length = buffer.Length;
306 if (size < 0 || offset < 0 || length < offset || length - offset < size)
307 throw new ArgumentOutOfRangeException ();
316 WebAsyncResult result = new WebAsyncResult (cb, state);
317 if (allowBuffering) {
318 writeBuffer.Write (buffer, offset, size);
320 result.SetCompleted (true, 0);
321 result.DoCallback ();
326 AsyncCallback callback = null;
328 callback = new AsyncCallback (CallbackWrapper);
333 string cSize = String.Format ("{0:X}\r\n", size);
334 byte [] head = Encoding.ASCII.GetBytes (cSize);
335 int chunkSize = 2 + size + head.Length;
336 byte [] newBuffer = new byte [chunkSize];
337 Buffer.BlockCopy (head, 0, newBuffer, 0, head.Length);
338 Buffer.BlockCopy (buffer, offset, newBuffer, head.Length, size);
339 Buffer.BlockCopy (crlf, 0, newBuffer, head.Length + size, crlf.Length);
346 result.InnerAsyncResult = cnc.BeginWrite (buffer, offset, size, callback, result);
350 public override void EndWrite (IAsyncResult r)
353 throw new ArgumentNullException ("r");
355 if (allowBuffering && !sendChunked)
358 WebAsyncResult result = r as WebAsyncResult;
360 throw new ArgumentException ("Invalid IAsyncResult");
362 if (result.GotException)
363 throw result.Exception;
365 cnc.EndWrite (result.InnerAsyncResult);
369 if (pendingWrites == 0)
375 public override void Write (byte [] buffer, int offset, int size)
378 throw new NotSupportedException ("This stream does not allow writing");
380 IAsyncResult res = BeginWrite (buffer, offset, size, null, null);
384 public override void Flush ()
388 internal void SetHeaders (byte [] buffer, int offset, int size)
393 if (!allowBuffering || sendChunked) {
396 throw new WebException ("Not connected", null, WebExceptionStatus.SendFailure, null);
398 cnc.Write (buffer, offset, size);
400 headers = new byte [size];
401 Buffer.BlockCopy (buffer, offset, headers, 0, size);
405 internal bool RequestWritten {
406 get { return requestWritten; }
409 internal void WriteRequest ()
415 request.SendRequestHeaders ();
416 requestWritten = true;
420 if (!allowBuffering || writeBuffer == null)
423 byte [] bytes = writeBuffer.GetBuffer ();
424 int length = (int) writeBuffer.Length;
425 if (request.ContentLength != -1 && request.ContentLength < length) {
426 throw new WebException ("Specified Content-Length is less than the number of bytes to write", null,
427 WebExceptionStatus.ServerProtocolViolation, null);
430 request.InternalContentLength = length;
431 request.SendRequestHeaders ();
432 requestWritten = true;
433 cnc.Write (headers, 0, headers.Length);
435 throw new WebException ("Error writing request.", null, WebExceptionStatus.SendFailure, null);
438 if (cnc.Data.StatusCode != 0 && cnc.Data.StatusCode != 100)
441 cnc.Write (bytes, 0, length);
444 internal void InternalClose ()
449 public override void Close ()
453 byte [] chunk = Encoding.ASCII.GetBytes ("0\r\n\r\n");
454 cnc.Write (chunk, 0, chunk.Length);
459 if (!nextReadCalled) {
461 // If we have not read all the contents
468 if (!allowBuffering || disposed)
473 long length = request.ContentLength;
474 if (length != -1 && length > writeBuffer.Length)
475 throw new IOException ("Cannot close the stream until all bytes are written");
480 public override long Seek (long a, SeekOrigin b)
482 throw new NotSupportedException ();
485 public override void SetLength (long a)
487 throw new NotSupportedException ();
490 public override bool CanSeek {
491 get { return false; }
494 public override bool CanRead {
495 get { return isRead; }
498 public override bool CanWrite {
499 get { return !isRead; }
502 public override long Length {
503 get { throw new NotSupportedException (); }
506 public override long Position {
507 get { throw new NotSupportedException (); }
508 set { throw new NotSupportedException (); }