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
;
62 public WebConnectionStream (WebConnection cnc
)
65 pending
= new ManualResetEvent (true);
67 string clength
= cnc
.Data
.Headers
["Content-Length"];
68 if (clength
!= null && clength
!= "") {
70 contentLength
= Int32
.Parse (clength
);
72 contentLength
= Int32
.MaxValue
;
75 contentLength
= Int32
.MaxValue
;
79 public WebConnectionStream (WebConnection cnc
, HttpWebRequest request
)
83 this.request
= request
;
84 allowBuffering
= request
.InternalAllowBuffering
;
85 sendChunked
= request
.SendChunked
;
87 writeBuffer
= new MemoryStream ();
90 pending
= new ManualResetEvent (true);
93 internal bool SendChunked
{
94 set { sendChunked = value; }
97 internal byte [] ReadBuffer
{
98 set { readBuffer = value; }
101 internal int ReadBufferOffset
{
102 set { readBufferOffset = value;}
105 internal int ReadBufferSize
{
106 set { readBufferSize = value; }
109 internal byte[] WriteBuffer
{
110 get { return writeBuffer.GetBuffer (); }
113 internal int WriteBufferLength
{
114 get { return (int) writeBuffer.Length; }
117 internal void ForceCompletion ()
119 forceCompletion
= true;
122 internal void CheckComplete ()
124 bool nrc
= nextReadCalled
;
125 if (forceCompletion
|| (!nrc
&& readBufferSize
- readBufferOffset
== contentLength
)) {
126 nextReadCalled
= true;
131 internal void ReadAll ()
133 if (!isRead
|| totalRead
>= contentLength
|| nextReadCalled
)
138 if (totalRead
>= contentLength
)
142 int diff
= readBufferSize
- readBufferOffset
;
145 if (contentLength
== Int32
.MaxValue
) {
146 MemoryStream ms
= new MemoryStream ();
147 if (readBuffer
!= null && diff
> 0)
148 ms
.Write (readBuffer
, readBufferOffset
, diff
);
150 byte [] buffer
= new byte [2048];
152 while ((read
= cnc
.Read (buffer
, 0, 2048)) != 0)
153 ms
.Write (buffer
, 0, read
);
156 new_size
= (int) ms
.Length
;
157 contentLength
= new_size
;
159 new_size
= contentLength
- totalRead
;
160 b
= new byte [new_size
];
161 if (readBuffer
!= null && diff
> 0) {
165 Buffer
.BlockCopy (readBuffer
, readBufferOffset
, b
, 0, diff
);
168 int remaining
= new_size
- diff
;
170 while (remaining
> 0 && r
!= 0) {
171 r
= cnc
.Read (b
, diff
, remaining
);
178 readBufferOffset
= 0;
179 readBufferSize
= new_size
;
181 nextReadCalled
= true;
187 static void CallbackWrapper (IAsyncResult r
)
189 WebAsyncResult result
= (WebAsyncResult
) r
.AsyncState
;
190 result
.InnerAsyncResult
= r
;
191 result
.DoCallback ();
194 public override int Read (byte [] buffer
, int offset
, int size
)
197 throw new NotSupportedException ("this stream does not allow reading");
199 if (totalRead
>= contentLength
)
202 IAsyncResult res
= BeginRead (buffer
, offset
, size
, null, null);
203 return EndRead (res
);
206 public override IAsyncResult
BeginRead (byte [] buffer
, int offset
, int size
,
207 AsyncCallback cb
, object state
)
210 throw new NotSupportedException ("this stream does not allow reading");
213 throw new ArgumentNullException ("buffer");
215 int length
= buffer
.Length
;
216 if (size
< 0 || offset
< 0 || length
< offset
|| length
- offset
< size
)
217 throw new ArgumentOutOfRangeException ();
219 WebAsyncResult result
= new WebAsyncResult (cb
, state
, buffer
, offset
, size
);
220 if (totalRead
>= contentLength
) {
221 result
.SetCompleted (true, -1);
222 result
.DoCallback ();
226 int remaining
= readBufferSize
- readBufferOffset
;
228 int copy
= (remaining
> size
) ? size
: remaining
;
229 Buffer
.BlockCopy (readBuffer
, readBufferOffset
, buffer
, offset
, copy
);
230 readBufferOffset
+= copy
;
234 if (size
== 0 || totalRead
>= contentLength
) {
235 result
.SetCompleted (true, copy
);
236 result
.DoCallback ();
239 result
.NBytes
= copy
;
248 cb
= new AsyncCallback (CallbackWrapper
);
250 if (contentLength
!= Int32
.MaxValue
&& contentLength
- totalRead
< size
)
251 size
= contentLength
- totalRead
;
253 result
.InnerAsyncResult
= cnc
.BeginRead (buffer
, offset
, size
, cb
, result
);
257 public override int EndRead (IAsyncResult r
)
259 WebAsyncResult result
= (WebAsyncResult
) r
;
261 if (!result
.IsCompleted
) {
262 int nbytes
= cnc
.EndRead (result
.InnerAsyncResult
);
265 if (pendingReads
== 0)
269 bool finished
= (nbytes
== -1);
270 if (finished
&& result
.NBytes
> 0)
273 result
.SetCompleted (false, nbytes
+ result
.NBytes
);
275 if (finished
|| nbytes
== 0)
276 contentLength
= totalRead
;
279 if (totalRead
>= contentLength
&& !nextReadCalled
) {
280 nextReadCalled
= true;
284 return result
.NBytes
;
287 public override IAsyncResult
BeginWrite (byte [] buffer
, int offset
, int size
,
288 AsyncCallback cb
, object state
)
291 throw new NotSupportedException ("this stream does not allow writing");
294 throw new ArgumentNullException ("buffer");
296 int length
= buffer
.Length
;
297 if (size
< 0 || offset
< 0 || length
< offset
|| length
- offset
< size
)
298 throw new ArgumentOutOfRangeException ();
307 WebAsyncResult result
= new WebAsyncResult (cb
, state
);
308 if (allowBuffering
) {
309 writeBuffer
.Write (buffer
, offset
, size
);
311 result
.SetCompleted (true, 0);
312 result
.DoCallback ();
317 AsyncCallback callback
= null;
319 callback
= new AsyncCallback (CallbackWrapper
);
324 string cSize
= String
.Format ("{0:X}\r\n", size
);
325 byte [] head
= Encoding
.ASCII
.GetBytes (cSize
);
326 int chunkSize
= 2 + size
+ head
.Length
;
327 byte [] newBuffer
= new byte [chunkSize
];
328 Buffer
.BlockCopy (head
, 0, newBuffer
, 0, head
.Length
);
329 Buffer
.BlockCopy (buffer
, offset
, newBuffer
, head
.Length
, size
);
330 Buffer
.BlockCopy (crlf
, 0, newBuffer
, head
.Length
+ size
, crlf
.Length
);
337 result
.InnerAsyncResult
= cnc
.BeginWrite (buffer
, offset
, size
, callback
, result
);
341 public override void EndWrite (IAsyncResult r
)
344 throw new ArgumentNullException ("r");
346 if (allowBuffering
&& !sendChunked
)
349 WebAsyncResult result
= r
as WebAsyncResult
;
351 throw new ArgumentException ("Invalid IAsyncResult");
353 if (result
.GotException
)
354 throw result
.Exception
;
356 cnc
.EndWrite (result
.InnerAsyncResult
);
360 if (pendingWrites
== 0)
366 public override void Write (byte [] buffer
, int offset
, int size
)
369 throw new NotSupportedException ("This stream does not allow writing");
371 IAsyncResult res
= BeginWrite (buffer
, offset
, size
, null, null);
375 public override void Flush ()
379 internal void SetHeaders (byte [] buffer
, int offset
, int size
)
384 if (!allowBuffering
|| sendChunked
) {
387 cnc
.Write (buffer
, offset
, size
);
388 } catch (IOException
) {
392 if (!cnc
.TryReconnect ())
395 cnc
.Write (buffer
, offset
, size
);
398 headers
= new byte [size
];
399 Buffer
.BlockCopy (buffer
, offset
, headers
, 0, size
);
403 internal void WriteRequest ()
409 request
.SendRequestHeaders ();
410 requestWritten
= true;
414 if (!allowBuffering
|| writeBuffer
== null)
417 byte [] bytes
= writeBuffer
.GetBuffer ();
418 int length
= (int) writeBuffer
.Length
;
419 if (request
.ContentLength
!= -1 && request
.ContentLength
< length
) {
420 throw new ProtocolViolationException ("Specified Content-Length is less than the " +
421 "number of bytes to write");
424 request
.InternalContentLength
= length
;
425 request
.SendRequestHeaders ();
426 requestWritten
= true;
428 cnc
.Write (headers
, 0, headers
.Length
);
429 if (!cnc
.Connected
) {
430 if (!cnc
.TryReconnect ())
437 if (cnc
.Data
.StatusCode
!= 0 && cnc
.Data
.StatusCode
!= 100)
440 cnc
.Write (bytes
, 0, length
);
441 if (!cnc
.Connected
&& cnc
.TryReconnect ())
448 internal void InternalClose ()
453 public override void Close ()
457 byte [] chunk
= Encoding
.ASCII
.GetBytes ("0\r\n\r\n");
458 cnc
.Write (chunk
, 0, chunk
.Length
);
462 if (isRead
|| !allowBuffering
|| disposed
)
467 long length
= request
.ContentLength
;
468 if (length
!= -1 && length
> writeBuffer
.Length
)
469 throw new IOException ("Cannot close the stream until all bytes are written");
474 public override long Seek (long a
, SeekOrigin b
)
476 throw new NotSupportedException ();
479 public override void SetLength (long a
)
481 throw new NotSupportedException ();
484 public override bool CanSeek
{
485 get { return false; }
488 public override bool CanRead
{
489 get { return isRead; }
492 public override bool CanWrite
{
493 get { return !isRead; }
496 public override long Length
{
497 get { throw new NotSupportedException (); }
500 public override long Position
{
501 get { throw new NotSupportedException (); }
502 set { throw new NotSupportedException (); }