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)
13 using System.Threading;
17 class WebConnectionStream : Stream
19 static byte [] crlf = new byte [] { 13, 10 };
22 HttpWebRequest request;
31 ManualResetEvent pending;
34 MemoryStream writeBuffer;
40 public WebConnectionStream (WebConnection cnc)
43 pending = new ManualResetEvent (true);
46 contentLength = Int32.Parse (cnc.Data.Headers ["Content-Length"]);
48 contentLength = Int32.MaxValue;
52 public WebConnectionStream (WebConnection cnc, HttpWebRequest request)
56 this.request = request;
57 allowBuffering = request.InternalAllowBuffering;
58 sendChunked = request.SendChunked;
60 writeBuffer = new MemoryStream ();
63 pending = new ManualResetEvent (true);
66 internal bool SendChunked {
67 set { sendChunked = value; }
70 internal byte [] ReadBuffer {
71 set { readBuffer = value; }
74 internal int ReadBufferOffset {
75 set { readBufferOffset = value;}
78 internal int ReadBufferSize {
79 set { readBufferSize = value; }
82 internal byte[] WriteBuffer {
83 get { return writeBuffer.GetBuffer (); }
86 internal int WriteBufferLength {
87 get { return (int) writeBuffer.Length; }
90 internal void CheckComplete ()
92 if (!nextReadCalled && readBufferSize - readBufferOffset == contentLength) {
93 nextReadCalled = true;
98 internal void ReadAll ()
100 if (!isRead || totalRead >= contentLength || nextReadCalled)
105 if (totalRead >= contentLength)
109 int diff = readBufferSize - readBufferOffset;
112 if (contentLength == Int32.MaxValue) {
113 MemoryStream ms = new MemoryStream ();
114 if (readBuffer != null && diff > 0)
115 ms.Write (readBuffer, readBufferOffset, diff);
117 byte [] buffer = new byte [2048];
119 while ((read = cnc.Read (buffer, 0, 2048)) != 0)
120 ms.Write (buffer, 0, read);
123 new_size = (int) ms.Length;
124 contentLength = new_size;
126 new_size = contentLength - totalRead;
127 b = new byte [new_size];
128 if (readBuffer != null && diff > 0)
129 Buffer.BlockCopy (readBuffer, readBufferOffset, b, 0, diff);
131 int remaining = new_size - diff;
133 while (remaining > 0 && r != 0) {
134 r = cnc.Read (b, diff, remaining);
141 readBufferOffset = 0;
142 readBufferSize = new_size;
144 nextReadCalled = true;
150 static void CallbackWrapper (IAsyncResult r)
152 WebAsyncResult result = (WebAsyncResult) r.AsyncState;
153 result.InnerAsyncResult = r;
154 result.DoCallback ();
157 public override int Read (byte [] buffer, int offset, int size)
160 throw new NotSupportedException ("this stream does not allow reading");
162 if (totalRead >= contentLength)
165 IAsyncResult res = BeginRead (buffer, offset, size, null, null);
166 return EndRead (res);
169 public override IAsyncResult BeginRead (byte [] buffer, int offset, int size,
170 AsyncCallback cb, object state)
173 throw new NotSupportedException ("this stream does not allow reading");
176 throw new ArgumentNullException ("buffer");
178 int length = buffer.Length;
179 if (size < 0 || offset < 0 || length < offset || length - offset < size)
180 throw new ArgumentOutOfRangeException ();
182 WebAsyncResult result = new WebAsyncResult (cb, state, buffer, offset, size);
183 if (totalRead >= contentLength) {
184 result.SetCompleted (true, -1);
185 result.DoCallback ();
189 int remaining = readBufferSize - readBufferOffset;
191 int copy = (remaining > size) ? size : remaining;
192 Buffer.BlockCopy (readBuffer, readBufferOffset, buffer, offset, copy);
193 readBufferOffset += copy;
197 if (size == 0 || totalRead >= contentLength) {
198 result.SetCompleted (true, copy);
199 result.DoCallback ();
202 result.NBytes = copy;
211 cb = new AsyncCallback (CallbackWrapper);
213 if (contentLength != Int32.MaxValue && contentLength - totalRead < size)
214 size = contentLength - totalRead;
216 result.InnerAsyncResult = cnc.BeginRead (buffer, offset, size, cb, result);
220 public override int EndRead (IAsyncResult r)
222 WebAsyncResult result = (WebAsyncResult) r;
224 if (!result.IsCompleted) {
225 int nbytes = cnc.EndRead (result.InnerAsyncResult);
228 if (pendingReads == 0)
232 bool finished = (nbytes == -1);
233 if (finished && result.NBytes > 0)
236 result.SetCompleted (false, nbytes + result.NBytes);
238 if (finished || nbytes == 0)
239 contentLength = totalRead;
242 if (totalRead >= contentLength && !nextReadCalled) {
243 nextReadCalled = true;
247 return result.NBytes;
250 public override IAsyncResult BeginWrite (byte [] buffer, int offset, int size,
251 AsyncCallback cb, object state)
254 throw new NotSupportedException ("this stream does not allow writing");
257 throw new ArgumentNullException ("buffer");
259 int length = buffer.Length;
260 if (size < 0 || offset < 0 || length < offset || length - offset < size)
261 throw new ArgumentOutOfRangeException ();
270 WebAsyncResult result = new WebAsyncResult (cb, state);
271 if (allowBuffering) {
272 writeBuffer.Write (buffer, offset, size);
274 result.SetCompleted (true, 0);
275 result.DoCallback ();
280 AsyncCallback callback = null;
282 callback = new AsyncCallback (CallbackWrapper);
287 string cSize = String.Format ("{0:X}\r\n", size);
288 byte [] head = Encoding.ASCII.GetBytes (cSize);
289 int chunkSize = 2 + size + head.Length;
290 byte [] newBuffer = new byte [chunkSize];
291 Buffer.BlockCopy (head, 0, newBuffer, 0, head.Length);
292 Buffer.BlockCopy (buffer, offset, newBuffer, head.Length, size);
293 Buffer.BlockCopy (crlf, 0, newBuffer, head.Length + size, crlf.Length);
300 result.InnerAsyncResult = cnc.BeginWrite (buffer, offset, size, callback, result);
304 public override void EndWrite (IAsyncResult r)
307 throw new ArgumentNullException ("r");
309 if (allowBuffering && !sendChunked)
312 WebAsyncResult result = r as WebAsyncResult;
314 throw new ArgumentException ("Invalid IAsyncResult");
316 if (result.GotException)
317 throw result.Exception;
319 cnc.EndWrite (result.InnerAsyncResult);
323 if (pendingWrites == 0)
329 public override void Write (byte [] buffer, int offset, int size)
332 throw new NotSupportedException ("This stream does not allow writing");
334 IAsyncResult res = BeginWrite (buffer, offset, size, null, null);
338 public override void Flush ()
342 internal void SetHeaders (byte [] buffer, int offset, int size)
347 if (!allowBuffering || sendChunked) {
350 cnc.Write (buffer, offset, size);
351 } catch (IOException) {
355 if (!cnc.TryReconnect ())
358 cnc.Write (buffer, offset, size);
361 headers = new byte [size];
362 Buffer.BlockCopy (buffer, offset, headers, 0, size);
366 internal void WriteRequest ()
372 request.SendRequestHeaders ();
373 requestWritten = true;
377 if (!allowBuffering || writeBuffer == null)
380 byte [] bytes = writeBuffer.GetBuffer ();
381 int length = (int) writeBuffer.Length;
382 if (request.ContentLength != -1 && request.ContentLength < length) {
383 throw new ProtocolViolationException ("Specified Content-Length is less than the " +
384 "number of bytes to write");
387 request.InternalContentLength = length;
388 request.SendRequestHeaders ();
389 requestWritten = true;
391 cnc.Write (headers, 0, headers.Length);
392 if (!cnc.Connected) {
393 if (!cnc.TryReconnect ())
400 if (cnc.Data.StatusCode != 0 && cnc.Data.StatusCode != 100)
403 cnc.Write (bytes, 0, length);
404 if (!cnc.Connected && cnc.TryReconnect ())
411 internal void InternalClose ()
416 public override void Close ()
420 byte [] chunk = Encoding.ASCII.GetBytes ("0\r\n\r\n");
421 cnc.Write (chunk, 0, chunk.Length);
425 if (isRead || !allowBuffering || disposed)
430 long length = request.ContentLength;
431 if (length != -1 && length > writeBuffer.Length)
432 throw new IOException ("Cannot close the stream until all bytes are written");
437 internal void ResetWriteBuffer ()
442 writeBuffer = new MemoryStream ();
443 requestWritten = false;
447 public override long Seek (long a, SeekOrigin b)
449 throw new NotSupportedException ();
452 public override void SetLength (long a)
454 throw new NotSupportedException ();
457 public override bool CanSeek {
458 get { return false; }
461 public override bool CanRead {
462 get { return isRead && (contentLength == Int32.MaxValue || totalRead < contentLength); }
465 public override bool CanWrite {
466 get { return !isRead; }
469 public override long Length {
470 get { throw new NotSupportedException (); }
473 public override long Position {
474 get { throw new NotSupportedException (); }
475 set { throw new NotSupportedException (); }