Update Reference Sources to .NET Framework 4.6
[mono.git] / mcs / class / referencesource / System / net / System / Net / _ConnectStream.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="_ConnectStream.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 //------------------------------------------------------------------------------
6
7 namespace System.Net {
8     using System.Diagnostics;
9     using System.IO;
10     using System.Net.Security;
11     using System.Net.Sockets;
12     using System.Runtime.InteropServices;
13     using System.Threading;
14     using System.Security.Authentication.ExtendedProtection;
15     using System.Security.Permissions;
16     using System.ComponentModel;
17     using System.Threading.Tasks;
18     using System.Configuration;
19     using System.Globalization;
20
21     internal struct WriteHeadersCallbackState{
22         internal HttpWebRequest request;
23         internal ConnectStream stream;
24
25         internal WriteHeadersCallbackState(HttpWebRequest request, ConnectStream stream){
26             this.request = request;
27             this.stream = stream;
28         }
29     }
30
31     /*++
32
33         ConnectStream  - a stream interface to a Connection object.
34
35         This class implements the Stream interface, as well as a
36         WriteHeaders call. Inside this stream we handle details like
37         chunking, dechunking and tracking of ContentLength. To write
38         or read data, we call a method on the connection object. The
39         connection object is responsible for protocol stuff that happens
40         'below' the level of the HTTP request, for example MUX or SSL
41
42     --*/
43
44     internal class ConnectStream : Stream, ICloseEx, IRequestLifetimeTracker
45     {
46 #if DEBUG
47         internal IAsyncResult       _PendingResult;
48 #endif
49         // These would be defined in the IOControlCode enum but we don't want them to be public.
50         private const int ApplyTransportSetting = unchecked((int)0x98000013);
51         private const int QueryTransportSetting = unchecked((int)0x98000014);
52         
53         private static class Nesting {
54             public const int Idle           = 0;
55             public const int IoInProgress   = 1;    // we are doing read or write
56             public const int Closed         = 2;    // stream was closed if that is done in IoInProgress on write, the write will resume delayed close part.
57             public const int InError        = 3;    // IO is not allowed due to error stream state
58             public const int InternalIO     = 4;    // stream is used by us, this is internal error if public IO sees that value
59         }
60
61         private     int             m_CallNesting;          // see Nesting enum for details
62         private     ScatterGatherBuffers
63                                     m_BufferedData;        // list of sent buffers in case of resubmit (redirect/authentication).
64         private     bool            m_SuppressWrite;           // don't write data to the connection, only buffer it
65         private     bool            m_BufferOnly;           // don't write data to the connection, only buffer it
66         private     long            m_BytesLeftToWrite;     // Total bytes left to be written.
67         private     int             m_BytesAlreadyTransferred;  // Bytes already read/written in the current operation.
68         private     Connection      m_Connection;           // Connection for I/O.
69         private     byte[]          m_ReadBuffer;           // Read buffer for read stream.
70         private     int             m_ReadOffset;           // Offset into m_ReadBuffer.
71         private     int             m_ReadBufferSize;       // Bytes left in m_ReadBuffer.
72         private     long            m_ReadBytes;            // Total bytes to read on stream, -1 for read to end.
73         private     bool            m_Chunked;              // True if we're doing chunked read.
74         private     int             m_DoneCalled;           // 0 at init, 1 after we've called Read/Write Done
75         private     int             m_ShutDown;             // 0 at init, 1 after we've called Complete
76         private     Exception       m_ErrorException;       // non-null if we've seen an error on this connection.
77         private     bool            m_ChunkEofRecvd;        // True, if we've seen an EOF, or reached a EOF state for no more reads
78         private     ChunkParser     m_ChunkParser;          // Helper object used for parsing chunked responses.
79         
80         private     HttpWriteMode   m_HttpWriteMode;
81
82         private     int             m_ReadTimeout;          // timeout in ms for reads
83         private     int             m_WriteTimeout;         // timeout in ms for writes
84
85         private RequestLifetimeSetter m_RequestLifetimeSetter;
86
87         private const long c_MaxDrainBytes = 64 * 1024; // 64 K - greater than, we should just close the connection
88
89         // These two must not be static because the socket will use them when caching the user context.
90         private readonly AsyncCallback m_ReadCallbackDelegate;
91         private readonly AsyncCallback m_WriteCallbackDelegate;
92         private static readonly AsyncCallback m_WriteHeadersCallback = new AsyncCallback(WriteHeadersCallback);
93
94         // Special value indicating that an asynchronous read operation is intentionally zero-length.
95         private static readonly object ZeroLengthRead = new object();
96
97         private HttpWebRequest m_Request;
98
99         private static volatile int responseDrainTimeoutMilliseconds = Timeout.Infinite;
100         private const int defaultResponseDrainTimeoutMilliseconds = 500;
101         private const string responseDrainTimeoutAppSetting = "responseDrainTimeout";
102
103         //
104         // Timeout - timeout in ms for sync reads & writes, passed in HttpWebRequest
105         //
106
107         public override bool CanTimeout {
108             get {return true;}
109         }
110
111         public override int ReadTimeout {
112             get {
113                 return m_ReadTimeout;
114             }
115             set {
116                 if (value<=0 && value!=System.Threading.Timeout.Infinite) {
117                     throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_gt_zero));
118                 }
119                 m_ReadTimeout = value;
120             }
121         }
122
123         public override int WriteTimeout {
124             get {
125                 return m_WriteTimeout;
126
127             }
128             set {
129                 if (value<=0 && value!=System.Threading.Timeout.Infinite) {
130                     throw new ArgumentOutOfRangeException("value", SR.GetString(SR.net_io_timeout_use_gt_zero));
131                 }
132                 m_WriteTimeout = value;
133             }
134         }
135
136         // We will be done with this stream/connection after the user finishes uploading (redirected)
137         internal bool FinishedAfterWrite { get; set; }
138
139         //
140         // If IgnoreSocketErrors==true then no data will be sent to the wire
141         //
142         private bool m_IgnoreSocketErrors;
143         internal bool IgnoreSocketErrors {
144             get {
145                 return m_IgnoreSocketErrors;
146             }
147         }
148
149         //
150         // If the KeepAlive=true then we  must be prepares for a write socket errors trying to flush the body
151         // If the KeepAlive=false then we should cease body writing as the connection is probably dead
152         // If fatal=true then the connection is dead due to IO fault (discovered during read), throw IO exception
153         //
154         // m_IgnoreSocketErrors and m_ThrowSocketError are mostly for a write type of streams.
155         // However a response read stream may have this member set when draning a response on resubmit.
156         //
157         // This this isn't synchronized, we also check after receiving an exception from the transport whether these have been set
158         // and take them into account if they have (on writes).
159         private bool m_ErrorResponseStatus;
160         internal void ErrorResponseNotify(bool isKeepAlive) {
161             m_ErrorResponseStatus = true;
162             m_IgnoreSocketErrors |= !isKeepAlive;
163             GlobalLog.Print((WriteStream?"Write-":"Read-") + "ConnectStream#"+ ValidationHelper.HashString(this) + "::Got notification on an Error Response, m_IgnoreSocketErrors:" + m_IgnoreSocketErrors);
164         }
165
166         // This means we should throw a connection closed exception from now on (write only).
167         // It's unclear whether this needs to be better synchronized with m_ErrorResponseStatus, such as if ErrorResponseNotify
168         // were called (asynchronously) while a m_ErrorException was already set.
169         internal void FatalResponseNotify()
170         {
171             if (m_ErrorException == null)
172             {
173                 Interlocked.CompareExchange<Exception>(ref m_ErrorException, new IOException(SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed))), null);
174             }
175             m_ErrorResponseStatus = false;
176             GlobalLog.Print((WriteStream ? "Write-" : "Read-") + "ConnectStream#" + ValidationHelper.HashString(this) + "::Got notification on a Fatal Response");
177         }
178
179         /*++
180             Write Constructor for this class. This is the write constructor;
181             it takes as a parameter the amount of entity body data to be written,
182             with a value of -1 meaning to write it out as chunked. The other
183             parameter is the Connection of which we'll be writing.
184
185             Right now we use the DefaultBufferSize for the stream. In
186             the future we'd like to pass a 0 and have the stream be
187             unbuffered for write.
188
189             Input:
190
191                 Conn            - Connection for this stream.
192                 BytesToWrite    - Total bytes to be written, or -1
193                                     if we're doing chunked encoding.
194
195             Returns:
196
197                 Nothing.
198
199         --*/
200
201         public ConnectStream(Connection connection, HttpWebRequest request) {
202             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::.ctor(Write)");
203             m_Connection = connection;
204             m_ReadTimeout = m_WriteTimeout = System.Threading.Timeout.Infinite;
205             //
206             // we need to save a reference to the request for two things
207             // 1. In case of buffer-only we kick in actual submition when the stream is closed by a user
208             // 2. In case of write stream abort() we notify the request so the response stream is handled properly
209             //
210             m_Request = request;
211             m_HttpWriteMode = request.HttpWriteMode;
212
213             GlobalLog.Assert(m_HttpWriteMode != HttpWriteMode.Unknown, "ConnectStream#{0}::.ctor()|HttpWriteMode:{1}", ValidationHelper.HashString(this), m_HttpWriteMode);
214             m_BytesLeftToWrite = m_HttpWriteMode==HttpWriteMode.ContentLength ? request.ContentLength : -1;
215             if (request.HttpWriteMode==HttpWriteMode.Buffer) {
216                 m_BufferOnly = true;
217                 EnableWriteBuffering();
218             }
219             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::.ctor() Connection:" + ValidationHelper.HashString(m_Connection) + " BytesToWrite:" + BytesLeftToWrite);
220
221             m_ReadCallbackDelegate = new AsyncCallback(ReadCallback);
222             m_WriteCallbackDelegate = new AsyncCallback(WriteCallback);
223         }
224
225         /*++
226
227             Read constructor for this class. This constructor takes in
228             the connection and some information about a buffer that already
229             contains data. Reads from this stream will read first from the
230             buffer, and after that is exhausted will read from the connection.
231
232             We also take in a size of bytes to read, or -1 if we're to read
233             to connection close, and a flag indicating whether or now
234             we're chunked.
235
236             Input:
237
238                 Conn                - Connection for this stream.
239                 buffer              - Initial buffer to read from.
240                 offset              - offset into buffer to start reading.
241                 size               - number of bytes in buffer to read.
242                 readSize            - Number of bytes allowed to be read from
243                                         the stream, -1 for read to connection
244                                         close.
245                 chunked             - True if we're doing chunked decoding.
246
247             Returns:
248
249                 Nothing.
250
251         --*/
252
253         public ConnectStream(Connection connection, byte[] buffer, int offset, int bufferCount, long readCount, bool chunked, HttpWebRequest request)
254         {
255             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::.ctor(Read)");
256             if(Logging.On)Logging.PrintInfo(Logging.Web, this, "ConnectStream", SR.GetString(SR.net_log_buffered_n_bytes, readCount));
257
258             m_ReadBytes = readCount;
259             m_ReadTimeout = m_WriteTimeout = System.Threading.Timeout.Infinite;
260             m_Chunked = chunked;
261             m_Connection = connection;
262
263             if (m_Chunked)
264             {
265                 m_ChunkParser = new ChunkParser(m_Connection, buffer, offset, bufferCount, 
266                     request.MaximumResponseHeadersLength * 1024);
267             }
268             else
269             {
270                 m_ReadBuffer = buffer;
271                 m_ReadOffset = offset;
272                 m_ReadBufferSize = bufferCount;
273             }
274
275             //
276             // A request reference is used to verify (by the connection class) that this request should start a next one on Close.
277             //
278             m_Request = request;
279             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::.ctor() Connection:" + ValidationHelper.HashString(m_Connection) +
280                             " m_ReadOffset:"   + m_ReadOffset + " m_ReadBufferSize: " + m_ReadBufferSize +
281                             " ContentLength: " + m_ReadBytes  + " m_Chunked:" + m_Chunked.ToString());
282
283             m_ReadCallbackDelegate = new AsyncCallback(ReadCallback);
284             m_WriteCallbackDelegate = new AsyncCallback(WriteCallback);
285         }
286
287         internal void SwitchToContentLength(){
288             m_HttpWriteMode = HttpWriteMode.ContentLength;
289         }
290
291         internal bool SuppressWrite {
292             /* Consider Removing 
293             get {
294                 return m_SuppressWrite;
295             }
296             */
297             set{
298                 m_SuppressWrite = value;
299             }
300         }
301         
302         internal Connection Connection {
303             get {
304                 return m_Connection;
305             }
306         }
307
308         internal bool BufferOnly {
309             get {
310                 return m_BufferOnly;
311             }
312         }
313
314         internal ScatterGatherBuffers BufferedData {
315             get {
316                 return m_BufferedData;
317             }
318             set {
319                 m_BufferedData = value;
320             }
321         }
322
323         private bool WriteChunked {
324             get {
325                 return m_HttpWriteMode==HttpWriteMode.Chunked;
326             }
327         }
328
329         internal long BytesLeftToWrite {
330             get {
331                 return m_BytesLeftToWrite;
332             }
333             set {
334                 m_BytesLeftToWrite = value;
335             }
336         }
337
338         // True if this is a write stream.
339         bool WriteStream {
340             get {
341                 return m_HttpWriteMode != HttpWriteMode.Unknown;
342             }
343         }
344
345         internal bool IsPostStream {
346             get {
347                 return m_HttpWriteMode != HttpWriteMode.None;
348             }
349         }
350
351         /*++
352
353             ErrorInStream - indicates an exception was caught
354             internally due to a stream error, and that I/O
355             operations should not continue
356
357             Input: Nothing.
358
359             Returns: True if there is an error
360
361          --*/
362
363         internal bool ErrorInStream {
364             get {
365                 return m_ErrorException!=null;
366             }
367         }
368
369         /*++
370
371             CallDone - calls out to the Connection that spawned this
372             Stream (using the DoneRead/DoneWrite method).
373             If the Connection specified that we don't need to
374             do this, or if we've already done this already, then
375             we return silently.
376
377             Input: Nothing.
378
379             Returns: Nothing.
380
381          --*/
382         internal void CallDone()
383         {
384             CallDone(null);
385         }
386         private void CallDone(ConnectionReturnResult returnResult)
387         {
388             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::CallDone");
389             if ( Interlocked.Increment( ref m_DoneCalled) == 1 )
390             {
391                 if (!WriteStream)
392                 {
393 #if DEBUG
394                     GlobalLog.DebugRemoveRequest(m_Request);
395 #endif
396                     if (returnResult == null) {
397                         //readstartnextrequest will call setresponses internally.
398
399                         if (m_Chunked)
400                         {
401                             int leftoverBufferOffset;
402                             int leftoverBufferSize;
403                             byte[] leftoverBuffer;
404
405                             if (m_ChunkParser.TryGetLeftoverBytes(out leftoverBuffer, out leftoverBufferOffset, 
406                                 out leftoverBufferSize))
407                             {
408                                 m_Connection.SetLeftoverBytes(leftoverBuffer, leftoverBufferOffset, leftoverBufferSize);
409                             }
410                         }
411                         m_Connection.ReadStartNextRequest(m_Request, ref returnResult);
412                     }
413                     else{
414                         ConnectionReturnResult.SetResponses(returnResult);
415                     }
416                 }
417                 else
418                 {
419                     m_Request.WriteCallDone(this, returnResult);
420                 }
421             }
422             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::CallDone");
423         }
424
425         internal void ProcessWriteCallDone(ConnectionReturnResult returnResult)
426         {
427             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::ProcessWriteCallDone()");
428
429             try {
430                 if (returnResult == null) {
431                     m_Connection.WriteStartNextRequest(m_Request, ref returnResult);
432
433                     // If the request is Sync, then we do our Read here for data
434                     if (!m_Request.Async)
435                     {
436                         object syncReaderResult = m_Request.ConnectionReaderAsyncResult.InternalWaitForCompletion();
437
438                         //we should only do a syncread if we didn't already read the response
439                         //via poll when we handed back the request stream
440                         if (syncReaderResult == null && m_Request.NeedsToReadForResponse)
441 #if DEBUG
442                             // Remove once mixed sync/async requests are supported.
443                             using (GlobalLog.SetThreadKind(ThreadKinds.Sync))
444 #endif
445                         {
446                             m_Connection.SyncRead(m_Request, true, false);
447                         }
448                     }
449
450                     m_Request.NeedsToReadForResponse = true;
451                 }
452
453                 ConnectionReturnResult.SetResponses(returnResult);
454             }
455             finally {
456                 // This will decrement the response window on the write side AND may
457                 // result in either immediate or delayed processing of a response for the m_Request instance
458                 if (IsPostStream || m_Request.Async)
459                     m_Request.CheckWriteSideResponseProcessing();
460             }
461         }
462
463         internal bool IsClosed {
464             get {
465                 return m_ShutDown != 0;
466             }
467         }
468
469         /*++
470
471             Read property for this class. We return the readability of
472             this stream. This is a read only property.
473
474             Input: Nothing.
475
476             Returns: True if this is a read stream, false otherwise.
477
478          --*/
479
480         public override bool CanRead {
481             get {
482                 return !WriteStream && !IsClosed;
483             }
484         }
485
486         /*++
487
488             Seek property for this class. Since this stream is not
489             seekable, we just return false. This is a read only property.
490
491             Input: Nothing.
492
493             Returns: false
494
495          --*/
496
497         public override bool CanSeek {
498             get {
499                 return false;
500             }
501         }
502
503         /*++
504
505             CanWrite property for this class. We return the writeability of
506             this stream. This is a read only property.
507
508             Input: Nothing.
509
510             Returns: True if this is a write stream, false otherwise.
511
512          --*/
513
514         public override bool CanWrite {
515             get {
516                 return WriteStream && !IsClosed;
517             }
518         }
519
520
521         /*++
522
523             Length property for this class. Since we don't support seeking,
524             this property just throws a NotSupportedException.
525
526             Input: Nothing.
527
528             Returns: Throws exception.
529
530          --*/
531
532         public override long Length {
533             get {
534                 throw new NotSupportedException(SR.GetString(SR.net_noseek));
535             }
536         }
537
538         /*++
539
540             Position property for this class. Since we don't support seeking,
541             this property just throws a NotSupportedException.
542
543             Input: Nothing.
544
545             Returns: Throws exception.
546
547          --*/
548
549         public override long Position {
550             get {
551                 throw new NotSupportedException(SR.GetString(SR.net_noseek));
552             }
553
554             set {
555                 throw new NotSupportedException(SR.GetString(SR.net_noseek));
556             }
557         }
558
559
560         /*++
561
562             Eof property to indicate when the read is no longer allowed,
563             because all data has been already read from socket.
564
565             Input: Nothing.
566
567             Returns: true/false depending on whether we are complete
568
569          --*/
570
571         internal bool Eof {
572             get {
573                 if (ErrorInStream) {
574                     return true;
575                 }
576                 else if (m_Chunked) {
577                     return m_ChunkEofRecvd;
578                 }
579                 else if (m_ReadBytes == 0) {
580                     return true;
581                 }
582                 else if (m_ReadBytes == -1) {
583                     return(m_DoneCalled > 0 && m_ReadBufferSize <= 0);
584                 }
585                 else {
586                     return false;
587                 }
588             }
589         }
590
591         /*++
592             Uses an old Stream to resubmit buffered data using the current
593              stream, this is used in cases of POST, or authentication,
594              where we need to buffer upload data so that it can be resubmitted
595
596             Input:
597
598                 OldStream - Old Stream that was previously used
599
600             Returns:
601
602                 Nothing.
603
604         --*/
605
606         // 
607         internal void ResubmitWrite(ConnectStream oldStream, bool suppressWrite) {
608             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::ResubmitWrite", ValidationHelper.HashString(oldStream));
609             GlobalLog.ThreadContract(ThreadKinds.Sync, "ConnectStream#" + ValidationHelper.HashString(this) + "::ResubmitWrite");
610
611             //
612             // 
613
614
615
616
617             //
618             // we're going to resubmit
619             //
620             try {
621                 Interlocked.CompareExchange(ref m_CallNesting, Nesting.InternalIO, Nesting.Idle);
622                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::ResubmitWrite() Inc: " + m_CallNesting.ToString());
623
624                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::ResubmitWrite(), callNesting : " + m_CallNesting.ToString() + " IsClosed = " + IsClosed);
625                 //
626                 // no need to buffer here:
627                 // we're already resubmitting buffered data give it to the connection to put it on the wire again
628                 // we set BytesLeftToWrite to 0 'cause even on failure there can be no recovery,
629                 // so just leave it to IOError() to clean up and don't call ResubmitWrite()
630                 //
631                 ScatterGatherBuffers bufferedData = oldStream.BufferedData;
632                 SafeSetSocketTimeout(SocketShutdown.Send);
633                 if (!WriteChunked) {
634                     if (!suppressWrite)
635                         m_Connection.Write(bufferedData);
636                 }
637                 else {
638                     // we have the data buffered, but we still want to chunk.
639
640                     // first set this to disable Close() from sending a chunk terminator.
641                     GlobalLog.Assert(m_HttpWriteMode != HttpWriteMode.None, "ConnectStream#{0}::ResubmitWrite()|m_HttpWriteMode == HttpWriteMode.None", ValidationHelper.HashString(this));
642                     m_HttpWriteMode = HttpWriteMode.ContentLength;
643
644                     if (bufferedData.Length==0) {
645                         m_Connection.Write(NclConstants.ChunkTerminator, 0, NclConstants.ChunkTerminator.Length);
646                     }
647                     else {
648                         int chunkHeaderOffset = 0;
649                         byte[] chunkHeaderBuffer = GetChunkHeader(bufferedData.Length, out chunkHeaderOffset);
650                         BufferOffsetSize[] dataBuffers = bufferedData.GetBuffers();
651                         BufferOffsetSize[] buffers = new BufferOffsetSize[dataBuffers.Length + 3];
652                         buffers[0] = new BufferOffsetSize(chunkHeaderBuffer, chunkHeaderOffset, chunkHeaderBuffer.Length - chunkHeaderOffset, false);
653                         int index = 0;
654                         foreach (BufferOffsetSize buffer in dataBuffers) {
655                             buffers[++index] = buffer;
656                         }
657                         buffers[++index] = new BufferOffsetSize(NclConstants.CRLF, 0, NclConstants.CRLF.Length, false);
658                         buffers[++index] = new BufferOffsetSize(NclConstants.ChunkTerminator, 0, NclConstants.ChunkTerminator.Length, false);
659
660                         SplitWritesState splitState = new SplitWritesState(buffers);
661
662                         BufferOffsetSize[] sendBuffers = splitState.GetNextBuffers();
663                         while(sendBuffers != null){
664                             m_Connection.MultipleWrite(sendBuffers);
665                             sendBuffers = splitState.GetNextBuffers();
666                         }
667                     }
668                 }
669                 if(Logging.On && bufferedData.GetBuffers() != null) {
670                     foreach (BufferOffsetSize bufferOffsetSize in bufferedData.GetBuffers()) {
671                         if (bufferOffsetSize == null) {
672                             Logging.Dump(Logging.Web, this, "ResubmitWrite", null, 0, 0);
673                         }
674                         else {
675                             Logging.Dump(Logging.Web, this, "ResubmitWrite", bufferOffsetSize.Buffer, bufferOffsetSize.Offset, bufferOffsetSize.Size);
676                         }
677                     }
678                 }
679                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::ResubmitWrite() sent:" + bufferedData.Length.ToString() );
680             }
681             catch (Exception exception)
682             {
683                 if (NclUtilities.IsFatal(exception)) throw;
684
685                 // A Fatal error
686                 WebException we = new WebException(NetRes.GetWebStatusString("net_connclosed", WebExceptionStatus.SendFailure),
687                                                WebExceptionStatus.SendFailure,
688                                                WebExceptionInternalStatus.RequestFatal,
689                                                exception);
690                 IOError(we, false);
691             }
692             finally {
693                 Interlocked.CompareExchange(ref m_CallNesting, Nesting.Idle, Nesting.InternalIO);
694                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::ResubmitWrite(), callNesting : " + m_CallNesting.ToString() + " IsClosed = " + IsClosed);
695             }
696             m_BytesLeftToWrite = 0;
697             CallDone();
698             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::ResubmitWrite", BytesLeftToWrite.ToString());
699         }
700
701
702         //
703         // called by HttpWebRequest if AllowWriteStreamBuffering is true on that instance
704         //
705         internal void EnableWriteBuffering() {
706             GlobalLog.Assert(WriteStream, "ConnectStream#{0}::EnableWriteBuffering()|!WriteStream", ValidationHelper.HashString(this));
707             if (BufferedData==null) {
708                 // create stream on demand, only if needed
709                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::EnableWriteBuffering() Write() creating ScatterGatherBuffers WriteChunked:" + WriteChunked.ToString() + " BytesLeftToWrite:" + BytesLeftToWrite.ToString());
710                 if (WriteChunked)
711                 {
712                     BufferedData = new ScatterGatherBuffers();
713                 }
714                 else
715                 {
716                     BufferedData = new ScatterGatherBuffers(BytesLeftToWrite);
717                 }
718             }
719         }
720
721         /*++
722             FillFromBufferedData - This fills in a buffer from data that we have buffered.
723
724             This method pulls out the buffered data that may have been provided as
725             excess actual data from the header parsing
726
727             Input:
728
729                 buffer          - Buffer to read into.
730                 offset          - Offset in buffer to read into.
731                 size           - Size in bytes to read.
732
733             Returns:
734                 Number of bytes read.
735
736         --*/
737         internal int FillFromBufferedData(byte [] buffer, ref int offset, ref int size ) {
738             //
739             // if there's no stuff in our read buffer just return 0
740             //
741             if (m_ReadBufferSize == 0) {
742                 return 0;
743             }
744
745             //
746             // There's stuff in our read buffer. Figure out how much to take,
747             // which is the minimum of what we have and what we're to read,
748             // and copy it out.
749             //
750             int BytesTransferred = Math.Min(size, m_ReadBufferSize);
751
752             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::FillFromBufferedData() Filling bytes: " + BytesTransferred.ToString());
753
754             Buffer.BlockCopy(
755                 m_ReadBuffer,
756                 m_ReadOffset,
757                 buffer,
758                 offset,
759                 BytesTransferred);
760
761             // Update our internal read buffer state with what we took.
762
763             m_ReadOffset += BytesTransferred;
764             m_ReadBufferSize -= BytesTransferred;
765
766             // If the read buffer size has gone to 0, null out our pointer
767             // to it so maybe it'll be garbage-collected faster.
768
769             if (m_ReadBufferSize == 0) {
770                 m_ReadBuffer = null;
771             }
772
773             // Update what we're to read and the caller's offset.
774
775             size -= BytesTransferred;
776             offset += BytesTransferred;
777
778             return BytesTransferred;
779         }
780
781         /*++
782             Write
783
784             This function write data to the network. If we were given a definite
785             content length when constructed, we won't write more than that amount
786             of data to the network. If the caller tries to do that, we'll throw
787             an exception. If we're doing chunking, we'll chunk it up before
788             sending to the connection.
789
790
791             Input:
792
793                 buffer          - buffer to write.
794                 offset          - offset in buffer to write from.
795                 size           - size in bytes to write.
796
797             Returns:
798                 Nothing.
799
800         --*/
801         public override void Write(byte[] buffer, int offset, int size) {
802 #if DEBUG
803             using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync)) {
804 #endif
805             if (Logging.On) Logging.Enter(Logging.Web, this, "Write", "");
806             //
807             // Basic parameter validation
808             //
809             if (!WriteStream) {
810                 throw new NotSupportedException(SR.GetString(SR.net_readonlystream));
811             }
812             if (buffer==null) {
813                 throw new ArgumentNullException("buffer");
814             }
815             if (offset<0 || offset>buffer.Length) {
816                 throw new ArgumentOutOfRangeException("offset");
817             }
818             if (size<0 || size>buffer.Length-offset) {
819                 throw new ArgumentOutOfRangeException("size");
820             }
821
822             if(Logging.On) Logging.Dump(Logging.Web, this, "Write", buffer, offset, size);
823
824             InternalWrite(false, buffer, offset, size, null, null );
825
826             if(Logging.On)Logging.Exit(Logging.Web, this, "Write", "");
827 #if DEBUG
828             }
829 #endif
830         }
831
832
833
834         /*++
835             BeginWrite - Does async write to the Stream
836
837             Splits the operation into two outcomes, for the
838             non-chunking case, we calculate size to write,
839             then call BeginWrite on the Connection directly,
840             and then we're finish, for the Chunked case,
841             we procede with use two callbacks to continue the
842             chunking after the first write, and then second write.
843             In order that all of the Chunk data/header/terminator,
844             in the correct format are sent.
845
846             Input:
847
848                 buffer          - Buffer to write into.
849                 offset          - Offset in buffer to write into.
850                 size           - Size in bytes to write.
851                 callback        - the callback to be called on result
852                 state           - object to be passed to callback
853
854             Returns:
855                 IAsyncResult    - the async result
856
857         --*/
858
859
860         [HostProtection(ExternalThreading=true)]
861         public override IAsyncResult BeginWrite(byte[] buffer, int offset, int size, AsyncCallback callback, object state ) {
862 #if DEBUG
863             using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
864 #endif
865             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginWrite " + ValidationHelper.HashString(m_Connection) + ", " + offset.ToString() + ", " + size.ToString());
866             if(Logging.On)Logging.Enter(Logging.Web, this, "BeginWrite", "");
867             //
868             // Basic parameter validation
869             //
870             if (!WriteStream) {
871                 throw new NotSupportedException(SR.GetString(SR.net_readonlystream));
872             }
873             if (buffer==null) {
874                 throw new ArgumentNullException("buffer");
875             }
876             if (offset<0 || offset>buffer.Length) {
877                 throw new ArgumentOutOfRangeException("offset");
878             }
879             if (size<0 || size>buffer.Length-offset) {
880                 throw new ArgumentOutOfRangeException("size");
881             }
882
883             if (Logging.On) Logging.Dump(Logging.Web, this, "BeginWrite", buffer, offset, size);
884
885             IAsyncResult result = InternalWrite(true, buffer, offset, size, callback, state);
886             if(Logging.On)Logging.Exit(Logging.Web, this, "BeginWrite", result);
887             return result;
888 #if DEBUG
889             }
890 #endif
891         }
892
893         //
894         // Handles either async or sync Writing for *public* stream API
895         //
896         private IAsyncResult InternalWrite(bool async, byte[] buffer, int offset, int size, AsyncCallback callback, object state ) {
897             //
898             // if we have a stream error, or we've already shut down this socket
899             //  then we must prevent new BeginRead/BeginWrite's from getting
900             //  submited to the socket, since we've already closed the stream.
901             //
902             if (ErrorInStream) {
903                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing:" + m_ErrorException.ToString());
904                 throw m_ErrorException;
905             }
906
907             if (IsClosed && !IgnoreSocketErrors) {
908                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
909                 throw new WebException(
910                             NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.ConnectionClosed),
911                             WebExceptionStatus.ConnectionClosed);
912             }
913             
914             if (m_Request.Aborted && !IgnoreSocketErrors) {
915                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
916                 throw new WebException(
917                     NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
918                     WebExceptionStatus.RequestCanceled);
919             }             
920
921             int nesting = Interlocked.CompareExchange(ref m_CallNesting, Nesting.IoInProgress, Nesting.Idle);
922             GlobalLog.Print((async?"Async ":"") + "InternalWrite() In: callNesting : " + nesting.ToString());
923             if (nesting != Nesting.Idle && nesting != Nesting.Closed)
924             {
925                 throw new NotSupportedException(SR.GetString(SR.net_no_concurrent_io_allowed));
926             }
927
928             //
929             // buffer data to the ScatterGatherBuffers
930             // regardles of chunking, we buffer the data as if we were not chunking
931             // and on resubmit, we don't bother chunking.
932             //
933             if (BufferedData!=null && size != 0 && (m_Request.ContentLength != 0 || !IsPostStream || !m_Request.NtlmKeepAlive)) {
934                 //
935                 // if we don't need to, we shouldn't send data on the wire as well
936                 // but in this case we gave a stream to the user so we have transport
937                 //
938                 BufferedData.Write(buffer, offset, size);
939             }
940
941             LazyAsyncResult asyncResult = null;
942             bool completeSync = false;
943             try
944             {
945                 if (size == 0 || BufferOnly || m_SuppressWrite || IgnoreSocketErrors)
946                 {
947                     //
948                     // We're not putting this data on the wire, then we're done
949                     //
950                     if(m_SuppressWrite && m_BytesLeftToWrite > 0 && size > 0)
951                     {
952                         m_BytesLeftToWrite -= size;
953                     }
954
955                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() ----ing: size==0 || BufferOnly || IgnoreSocketErrors= " + (size==0) + BufferOnly + IgnoreSocketErrors);
956                     if (async) {
957                         asyncResult = new LazyAsyncResult(this, state, callback);
958                         completeSync = true;
959                     }
960                     return asyncResult;
961                 }
962                 else if (WriteChunked) {
963                     //
964                     // We're chunking. Write the chunk header out first,
965                     // then the data, then a CRLF.
966                     // for this we'll use BeginMultipleSend();
967                     //
968                     int chunkHeaderOffset = 0;
969                     byte[] chunkHeaderBuffer = GetChunkHeader(size, out chunkHeaderOffset);
970
971                     BufferOffsetSize[] buffers;
972                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() m_ErrorResponseStatus:" + m_ErrorResponseStatus);
973
974                     if (m_ErrorResponseStatus) {
975                         //if we already got a (>200) response, then just terminate chunking and
976                         //switch to simple buffering (if any)
977                         GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() setting m_IgnoreSocketErrors to True (was:" + m_IgnoreSocketErrors + ") sending chunk terminator");
978                         m_IgnoreSocketErrors = true;
979                         buffers = new BufferOffsetSize[1];
980                         buffers[0] = new BufferOffsetSize(NclConstants.ChunkTerminator, 0, NclConstants.ChunkTerminator.Length, false);
981                     }
982                     else {
983                         buffers = new BufferOffsetSize[3];
984                         buffers[0] = new BufferOffsetSize(chunkHeaderBuffer, chunkHeaderOffset, chunkHeaderBuffer.Length - chunkHeaderOffset, false);
985                         buffers[1] = new BufferOffsetSize(buffer, offset, size, false);
986                         buffers[2] = new BufferOffsetSize(NclConstants.CRLF, 0, NclConstants.CRLF.Length, false);
987                     }
988
989                     asyncResult = (async) ? new NestedMultipleAsyncResult(this, state, callback, buffers) : null;
990
991                     //
992                     // after setting up the buffers and error checking do the async Write Call
993                     //
994
995                     try {
996                         if (async) {
997                             m_Connection.BeginMultipleWrite(buffers, m_WriteCallbackDelegate, asyncResult);
998                         }
999                         else {
1000                             SafeSetSocketTimeout(SocketShutdown.Send);
1001                             m_Connection.MultipleWrite(buffers);
1002                         }
1003                     }
1004
1005                     catch (Exception exception) {
1006                         // IgnoreSocketErrors can be set at any time - need to check it again.
1007                         if (IgnoreSocketErrors && !NclUtilities.IsFatal(exception))
1008                         {
1009                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() ----ing: IgnoreSocketErrors set after throw.");
1010                             if (async)
1011                             {
1012                                 completeSync = true;
1013                             }
1014                             return asyncResult;
1015                         }
1016
1017                         if (m_Request.Aborted && (exception is IOException || exception is ObjectDisposedException)) {
1018                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
1019                             throw new WebException(
1020                                 NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
1021                                 WebExceptionStatus.RequestCanceled);
1022                         }
1023
1024                         nesting = Nesting.InError;
1025
1026                         if (NclUtilities.IsFatal(exception))
1027                         {
1028                             m_ErrorResponseStatus = false;
1029                             IOError(exception);
1030                             throw;
1031                         }
1032
1033                         if (m_ErrorResponseStatus) {
1034                             // We already got a error response, hence server could drop the connection,
1035                             // Here we are recovering for future (optional) resubmit ...
1036                             m_IgnoreSocketErrors = true;
1037                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() IGNORE write fault");
1038                             if (async)
1039                             {
1040                                 completeSync = true;
1041                             }
1042                         }
1043                         else {
1044                             // Note we could ---- this since receive callback is already posted and
1045                             // should give us similar failure
1046                             IOError(exception);
1047                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing:" + exception.ToString());
1048                             throw;
1049                         }
1050                     }
1051                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite chunked");
1052                     return asyncResult;
1053                 }
1054                 else {
1055                     //
1056                     // We're not chunking. See if we're sending too much; if not,
1057                     // go ahead and write it.
1058                     //
1059                     asyncResult = (async) ? new NestedSingleAsyncResult(this, state, callback, buffer, offset, size) : null;
1060
1061                     if (BytesLeftToWrite != -1) {
1062                         //
1063                         // but only check if we aren't writing to an unknown content-length size,
1064                         // as we can be buffering.
1065                         //
1066                         if (BytesLeftToWrite < (long)size) {
1067                             //
1068                             // writing too much data.
1069                             //
1070                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite()");
1071                             throw new ProtocolViolationException(SR.GetString(SR.net_entitytoobig));
1072                         }
1073
1074                         if (!async) {
1075                             //
1076                             // Otherwise update our bytes left to send and send it.
1077                             //
1078                             m_BytesLeftToWrite -= (long)size;
1079                         }
1080                     }
1081
1082                     //
1083                     // After doing, the m_WriteByte size calculations, and error checking
1084                     //  here doing the async Write Call
1085                     //
1086
1087                     try {
1088                         if (async) {
1089                             if(m_Request.ContentLength == 0 && IsPostStream) {
1090                                 m_BytesLeftToWrite -=size;
1091                                 completeSync = true;
1092                             }
1093                            else{
1094                                 m_BytesAlreadyTransferred = size;
1095                                 m_Connection.BeginWrite(buffer, offset, size, m_WriteCallbackDelegate, asyncResult);
1096                            }
1097                         }
1098                         else {
1099                             SafeSetSocketTimeout(SocketShutdown.Send);
1100                             //If we are doing the ntlm handshake,  contentlength
1101                             //could be 0 for the first part, even if there is data
1102                             //to write.
1103                             if (m_Request.ContentLength != 0 || !IsPostStream || !m_Request.NtlmKeepAlive) {
1104                                 m_Connection.Write(buffer, offset, size);
1105                             }
1106                         }
1107                     }
1108                     catch (Exception exception) {
1109                         // IgnoreSocketErrors can be set at any time - need to check it again.
1110                         if (IgnoreSocketErrors && !NclUtilities.IsFatal(exception))
1111                         {
1112                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() ----ing: IgnoreSocketErrors set after throw.");
1113                             if (async)
1114                             {
1115                                 completeSync = true;
1116                             }
1117                             return asyncResult;
1118                         }
1119
1120                         if (m_Request.Aborted && (exception is IOException || exception is ObjectDisposedException)) {
1121                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
1122                             throw new WebException(
1123                                 NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
1124                                 WebExceptionStatus.RequestCanceled);
1125                         }
1126
1127                         nesting = Nesting.InError;
1128
1129                         if (NclUtilities.IsFatal(exception))
1130                         {
1131                             m_ErrorResponseStatus = false;
1132                             IOError(exception);
1133                             throw;
1134                         }
1135
1136                         if (m_ErrorResponseStatus) {
1137                             // We already got a error response, hence server could drop the connection,
1138                             // Here we are recovering for future (optional) resubmit ...
1139                             m_IgnoreSocketErrors = true;
1140                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternWrite() IGNORE write fault");
1141                             if (async)
1142                             {
1143                                 completeSync = true;
1144                             }
1145                         }
1146                         else {
1147                             // Note we could ---- this since receive callback is already posted and
1148                             // should give us similar failure
1149                             IOError(exception);
1150                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing:" + exception.ToString());
1151                             throw;
1152                         }
1153                     }
1154                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite");
1155                     return asyncResult;
1156                 }
1157             }
1158             finally {
1159                 if (!async || nesting == Nesting.InError || completeSync)
1160                 {
1161                     nesting = Interlocked.CompareExchange(ref m_CallNesting, (nesting == Nesting.InError? Nesting.InError: Nesting.Idle), Nesting.IoInProgress);
1162                     GlobalLog.Print("InternalWrite() Out callNesting: " + nesting.ToString());
1163                     if (nesting == Nesting.Closed)
1164                     {
1165                         //send closing bytes
1166                         ResumeInternalClose(asyncResult);
1167                     }
1168                     else if (completeSync && asyncResult != null)
1169                     {
1170                         asyncResult.InvokeCallback();
1171                     }
1172                 }
1173             }
1174         }
1175
1176
1177         /*++
1178             WriteDataCallback
1179
1180             This is a callback, that is part of the main BeginWrite
1181             code, this is part of the normal transfer code.
1182
1183             Input:
1184
1185                asyncResult - IAsyncResult generated from BeginWrite
1186
1187             Returns:
1188
1189                None
1190
1191         --*/
1192         private void WriteCallback(IAsyncResult asyncResult)
1193         {
1194             LazyAsyncResult userResult = (LazyAsyncResult) asyncResult.AsyncState;
1195             ((ConnectStream) userResult.AsyncObject).ProcessWriteCallback(asyncResult, userResult);
1196         }
1197
1198         private void ProcessWriteCallback(IAsyncResult asyncResult, LazyAsyncResult userResult)
1199         {
1200             Exception userException = null;
1201
1202             try {
1203                 NestedSingleAsyncResult castedSingleAsyncResult = userResult as NestedSingleAsyncResult;
1204                 if (castedSingleAsyncResult != null)
1205                 {
1206                     try {
1207                         m_Connection.EndWrite(asyncResult);
1208                         if (BytesLeftToWrite != -1) {
1209                             // Update our bytes left to send.
1210                             m_BytesLeftToWrite -= m_BytesAlreadyTransferred;
1211                             m_BytesAlreadyTransferred = 0;
1212                         }
1213                     }
1214                     catch (Exception exception) {
1215
1216                         userException = exception;
1217
1218                         if (NclUtilities.IsFatal(exception))
1219                         {
1220                             m_ErrorResponseStatus = false;
1221                             IOError(exception);
1222                             throw;
1223                         }
1224                         if (m_ErrorResponseStatus) {
1225                             // We already got a error response, hence server could drop the connection,
1226                             // Here we are recovering for future (optional) resubmit ...
1227                             m_IgnoreSocketErrors = true;
1228                             userException = null;
1229                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::EndWrite() IGNORE write fault");
1230                         }
1231                     }
1232                 }
1233                 else {
1234                     NestedMultipleAsyncResult castedMultipleAsyncResult = (NestedMultipleAsyncResult) userResult;
1235                     try {
1236                         m_Connection.EndMultipleWrite(asyncResult);
1237                     }
1238                     catch (Exception exception) {
1239
1240                         userException = exception;
1241
1242                         if (NclUtilities.IsFatal(exception))
1243                         {
1244                             m_ErrorResponseStatus = false;
1245                             IOError(exception);
1246                             throw;
1247                         }
1248                         if (m_ErrorResponseStatus) {
1249                             // We already got a error response, hence server could drop the connection,
1250                             // Here we are recovering for future (optional) resubmit ...
1251                             m_IgnoreSocketErrors = true;
1252                             userException = null;
1253                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::EndWrite() IGNORE write fault");
1254                         }
1255                     }
1256                 }
1257             }
1258             finally {
1259
1260                 if (Nesting.Closed == ExchangeCallNesting((userException == null? Nesting.Idle: Nesting.InError), Nesting.IoInProgress))
1261                 {
1262                     if (userException != null && m_ErrorException == null)
1263                     {
1264                         Interlocked.CompareExchange<Exception>(ref m_ErrorException, userException, null);
1265                     }
1266                     ResumeInternalClose(userResult);
1267                 }
1268                 else
1269                 {
1270                     userResult.InvokeCallback(userException);
1271                 }
1272             }
1273         }
1274         //I need this because doing this within the static w/ "ref stream.m_Callnesting is getting an error.
1275         private int  ExchangeCallNesting(int value, int comparand) {
1276             int result = Interlocked.CompareExchange(ref m_CallNesting, value, comparand);
1277             GlobalLog.Print("an AsyncCallback Out callNesting: " + m_CallNesting.ToString());
1278             return result;
1279         }
1280
1281         /*++
1282             EndWrite - Finishes off async write of data, just calls into
1283                 m_Connection.EndWrite to get the result.
1284
1285             Input:
1286
1287                 asyncResult     - The AsyncResult returned by BeginWrite
1288
1289
1290         --*/
1291         public override void EndWrite(IAsyncResult asyncResult) {
1292 #if DEBUG
1293             using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
1294 #endif
1295             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::EndWrite");
1296             if(Logging.On)Logging.Enter(Logging.Web, this, "EndWrite", "");
1297             //
1298             // parameter validation
1299             //
1300             if (asyncResult==null) {
1301                 throw new ArgumentNullException("asyncResult");
1302             }
1303             LazyAsyncResult castedAsyncResult = asyncResult as LazyAsyncResult;
1304
1305             if (castedAsyncResult==null || castedAsyncResult.AsyncObject!=this) {
1306                 throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
1307             }
1308             if (castedAsyncResult.EndCalled) {
1309                 throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndWrite"));
1310             }
1311
1312             castedAsyncResult.EndCalled = true;
1313
1314             //
1315             // wait & then check for errors
1316             //
1317
1318             object returnValue = castedAsyncResult.InternalWaitForCompletion();
1319
1320             if (ErrorInStream) {
1321                 GlobalLog.LeaveException("ConnectStream#" + ValidationHelper.HashString(this) + "::EndWrite", m_ErrorException);
1322                 throw m_ErrorException;
1323             }
1324
1325             Exception exception = returnValue as Exception;
1326             if (exception!=null) {
1327
1328                 if (exception is IOException && m_Request.Aborted) {
1329                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
1330                     throw new WebException(
1331                         NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
1332                         WebExceptionStatus.RequestCanceled);
1333                 }
1334
1335                 IOError(exception);
1336                 GlobalLog.LeaveException("ConnectStream#" + ValidationHelper.HashString(this) + "::EndWrite", exception);
1337                 throw exception;
1338             }
1339
1340             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::EndWrite");
1341             if(Logging.On)Logging.Exit(Logging.Web, this, "EndWrite", "");
1342 #if DEBUG
1343             }
1344 #endif
1345         }
1346
1347
1348         /*++
1349             Read - Read from the connection.
1350             ReadWithoutValidation
1351
1352             This method reads from the network, or our internal buffer if there's
1353             data in that. If there's not, we'll read from the network. If we're
1354
1355             doing chunked decoding, we'll decode it before returning from this
1356             call.
1357
1358
1359             Input:
1360
1361                 buffer          - Buffer to read into.
1362                 offset          - Offset in buffer to read into.
1363                 size           - Size in bytes to read.
1364
1365             Returns:
1366                 Nothing.
1367
1368         --*/
1369         public override int Read([In, Out] byte[] buffer, int offset, int size) {
1370 #if DEBUG
1371             using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync)) {
1372 #endif
1373             if (Logging.On) Logging.Enter(Logging.Web, this, "Read", "");
1374             if (WriteStream) {
1375                 throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
1376             }
1377             if (buffer==null) {
1378                 throw new ArgumentNullException("buffer");
1379             }
1380             if (offset<0 || offset>buffer.Length) {
1381                 throw new ArgumentOutOfRangeException("offset");
1382             }
1383             if (size<0 || size>buffer.Length-offset) {
1384                 throw new ArgumentOutOfRangeException("size");
1385             }
1386             if (ErrorInStream) {
1387                 throw m_ErrorException;
1388             }
1389
1390             if (IsClosed) {
1391                 throw new WebException(
1392                             NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.ConnectionClosed),
1393                             WebExceptionStatus.ConnectionClosed);
1394             }
1395
1396             if (m_Request.Aborted) {
1397                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
1398                 throw new WebException(
1399                             NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
1400                             WebExceptionStatus.RequestCanceled);
1401             }
1402
1403             //
1404             // if we fail/hang this call for some reason,
1405             // this Nesting count we be non-0, so that when we
1406             // close this stream, we will abort the socket.
1407             //
1408             int nesting = Interlocked.CompareExchange(ref m_CallNesting, Nesting.IoInProgress, Nesting.Idle);
1409             GlobalLog.Print("Read() In: callNesting : " + m_CallNesting.ToString());
1410
1411             if (nesting != Nesting.Idle)
1412             {
1413                 throw new NotSupportedException(SR.GetString(SR.net_no_concurrent_io_allowed));
1414             }
1415
1416             int bytesRead = -1;
1417             try
1418             {
1419                 SafeSetSocketTimeout(SocketShutdown.Receive);
1420             }
1421             catch (Exception exception)
1422             {
1423                 IOError(exception);
1424                 throw;
1425             }
1426             try {
1427                 bytesRead = ReadWithoutValidation(buffer, offset, size);
1428             }
1429             catch (Exception exception)
1430             {
1431                 Win32Exception win32Exception = exception.InnerException as Win32Exception;
1432                 if (win32Exception != null && win32Exception.NativeErrorCode == (int)SocketError.TimedOut)
1433                     exception = new WebException(SR.GetString(SR.net_timeout), WebExceptionStatus.Timeout);
1434                 throw exception;
1435             }
1436
1437             Interlocked.CompareExchange(ref m_CallNesting, Nesting.Idle, Nesting.IoInProgress);
1438             GlobalLog.Print("Read() Out: callNesting: " + m_CallNesting.ToString());
1439
1440             if(Logging.On && bytesRead>0)Logging.Dump(Logging.Web, this, "Read", buffer, offset, bytesRead);
1441             if(Logging.On)Logging.Exit(Logging.Web, this, "Read", bytesRead);
1442             return bytesRead;
1443 #if DEBUG
1444             }
1445 #endif
1446         }
1447
1448
1449         /*++
1450             ReadWithoutValidation - Read from the connection.
1451
1452             Sync version of BeginReadWithoutValidation
1453
1454             This method reads from the network, or our internal buffer if there's
1455             data in that. If there's not, we'll read from the network. If we're
1456             doing chunked decoding, we'll decode it before returning from this
1457             call.
1458
1459         --*/
1460         private int ReadWithoutValidation(byte[] buffer, int offset, int size)
1461         {
1462             return ReadWithoutValidation(buffer, offset, size, true);
1463         }
1464
1465         //
1466         // abortOnError parameter is set to false when called from CloseInternal
1467         //
1468         private int ReadWithoutValidation([In, Out] byte[] buffer, int offset, int size, bool abortOnError)
1469         {
1470             GlobalLog.Print("int ConnectStream::ReadWithoutValidation()");
1471             GlobalLog.Print("(start)m_ReadBytes = "+m_ReadBytes);
1472
1473
1474 // ********** WARNING - updating logic below should also be updated in BeginReadWithoutValidation *****************
1475
1476             //
1477             // Figure out how much we should really read.
1478             //
1479
1480             int bytesToRead = 0;
1481
1482             if (m_Chunked) {
1483                 if (!m_ChunkEofRecvd) {
1484
1485                     try {
1486                         bytesToRead = m_ChunkParser.Read(buffer, offset, size);
1487
1488                         if (bytesToRead == 0) {
1489                             m_ChunkEofRecvd = true;
1490                             CallDone();
1491                         }
1492                     }
1493                     catch (Exception exception) {
1494                         if (abortOnError) {
1495                             IOError(exception);
1496                         }
1497                         throw;
1498                     }
1499
1500                     return bytesToRead;
1501                 }
1502             }
1503             else {
1504
1505                 //
1506                 // Not doing chunked, so don't read more than is left.
1507                 //
1508
1509                 if (m_ReadBytes != -1) {
1510                     bytesToRead = (int)Math.Min(m_ReadBytes, (long)size);
1511                 }
1512                 else {
1513                     bytesToRead = size;
1514                 }
1515             }
1516
1517             // If we're not going to read anything, either because they're
1518             // not asking for anything or there's nothing left, bail
1519             // out now.
1520
1521             if (bytesToRead == 0 || this.Eof) {
1522                 return 0;
1523             }
1524
1525             Debug.Assert(!m_Chunked, 
1526                 "Chunked responses should never get here: Either we go into the chunked-specific code path or the " + 
1527                 "response is complete (Eof is true).");
1528
1529             try {
1530                 bytesToRead = InternalRead(buffer, offset, bytesToRead);
1531             }
1532             catch (Exception exception) {
1533                 if (abortOnError) {
1534                     IOError(exception);
1535                 }
1536                 throw;
1537             }
1538
1539             GlobalLog.Print("bytesTransferred = "+bytesToRead);
1540             int bytesTransferred = bytesToRead;
1541
1542             bool doneReading = false;
1543
1544             if (bytesTransferred <= 0) {
1545                 bytesTransferred = 0;
1546
1547                 //
1548                 // We read 0 bytes from the connection, or got an error. This is OK if we're
1549                 // reading to end, it's an error otherwise.
1550                 //
1551                 if (m_ReadBytes != -1) {
1552                     // A Fatal error
1553                     if (abortOnError) {
1554                         IOError(null, false);   // request will be aborted but the user will see EOF on that stream read call
1555                     }
1556                     else {
1557                         throw m_ErrorException; // CloseInternal will process this case as abnormal
1558                     }
1559                 }
1560                 else {
1561                     //
1562                     // We're reading to end, and we found the end, by reading 0 bytes
1563                     //
1564                     doneReading = true;
1565                 }
1566             }
1567
1568             //
1569             // Not chunking. Update our read bytes state and return what we've read.
1570             //
1571
1572             if (m_ReadBytes != -1) {
1573                 m_ReadBytes -= bytesTransferred;
1574
1575                 GlobalLog.Assert(m_ReadBytes >= 0, "ConnectStream: Attempting to read more bytes than available.|m_ReadBytes < 0");
1576
1577                 GlobalLog.Print("m_ReadBytes = "+m_ReadBytes);
1578
1579                 if (m_ReadBytes < 0)
1580                     throw new InternalException(); // 
1581
1582             }
1583
1584             if (m_ReadBytes == 0 || doneReading) {
1585                 // We're all done reading, tell the connection that.
1586                 m_ReadBytes = 0;
1587
1588                 //
1589                 // indicate to cache that read completed OK
1590                 //
1591
1592                 CallDone();
1593             }
1594
1595             GlobalLog.Print("bytesTransferred = "+bytesToRead);
1596             GlobalLog.Print("(end)m_ReadBytes = "+m_ReadBytes);
1597 // ********** WARNING - updating logic above should also be updated in BeginReadWithoutValidation and EndReadWithoutValidation *****************
1598             return bytesTransferred;
1599         }
1600
1601
1602
1603         /*++
1604             BeginRead - Read from the connection.
1605             BeginReadWithoutValidation
1606
1607             This method reads from the network, or our internal buffer if there's
1608             data in that. If there's not, we'll read from the network. If we're
1609             doing chunked decoding, we'll decode it before returning from this
1610             call.
1611
1612
1613             Input:
1614
1615                 buffer          - Buffer to read into.
1616                 offset          - Offset in buffer to read into.
1617                 size           - Size in bytes to read.
1618
1619             Returns:
1620                 Nothing.
1621
1622         --*/
1623
1624
1625         [HostProtection(ExternalThreading=true)]
1626         public override IAsyncResult BeginRead(byte[] buffer, int offset, int size, AsyncCallback callback, object state) {
1627 #if DEBUG
1628             using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Async)) {
1629 #endif
1630             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginRead() " + ValidationHelper.HashString(m_Connection) + ", " + offset.ToString() + ", " + size.ToString());
1631             if(Logging.On)Logging.Enter(Logging.Web, this, "BeginRead", "");
1632
1633             //
1634             // parameter validation
1635             //
1636             if (WriteStream) {
1637                 throw new NotSupportedException(SR.GetString(SR.net_writeonlystream));
1638             }
1639             if (buffer==null) {
1640                 throw new ArgumentNullException("buffer");
1641             }
1642             if (offset<0 || offset>buffer.Length) {
1643                 throw new ArgumentOutOfRangeException("offset");
1644             }
1645             if (size<0 || size>buffer.Length-offset) {
1646                 throw new ArgumentOutOfRangeException("size");
1647             }
1648
1649             //
1650             // if we have a stream error, or we've already shut down this socket
1651             //  then we must prevent new BeginRead/BeginWrite's from getting
1652             //  submited to the socket, since we've already closed the stream.
1653             //
1654
1655             if (ErrorInStream) {
1656                 throw m_ErrorException;
1657             }
1658
1659             if (IsClosed) {
1660                 throw new WebException(
1661                             NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.ConnectionClosed),
1662                             WebExceptionStatus.ConnectionClosed);
1663             }
1664
1665             if (m_Request.Aborted) {
1666                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::InternalWrite() throwing");
1667                 throw new WebException(
1668                             NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
1669                             WebExceptionStatus.RequestCanceled);
1670             }         
1671
1672
1673             //
1674             // if we fail/hang this call for some reason,
1675             // this Nesting count we be non-0, so that when we
1676             // close this stream, we will abort the socket.
1677             //
1678
1679             int nesting = Interlocked.CompareExchange(ref m_CallNesting, Nesting.IoInProgress, Nesting.Idle);
1680             GlobalLog.Print("BeginRead() In: callNesting : " + m_CallNesting.ToString());
1681
1682             if (nesting != 0)
1683             {
1684                 throw new NotSupportedException(SR.GetString(SR.net_no_concurrent_io_allowed));
1685             }
1686
1687             IAsyncResult result =
1688                 BeginReadWithoutValidation(
1689                         buffer,
1690                         offset,
1691                         size,
1692                         callback,
1693                         state);
1694
1695             if(Logging.On)Logging.Exit(Logging.Web, this, "BeginRead", result);
1696             return result;
1697 #if DEBUG
1698             }
1699 #endif
1700         }
1701
1702
1703         /*++
1704             BeginReadWithoutValidation - Read from the connection.
1705
1706             internal version of BeginRead above, without validation
1707
1708             This method reads from the network, or our internal buffer if there's
1709             data in that. If there's not, we'll read from the network. If we're
1710             doing chunked decoding, we'll decode it before returning from this
1711             call.
1712
1713
1714             Input:
1715
1716                 buffer          - Buffer to read into.
1717                 offset          - Offset in buffer to read into.
1718                 size           - Size in bytes to read.
1719
1720             Returns:
1721                 Nothing.
1722
1723         --*/
1724
1725         private IAsyncResult BeginReadWithoutValidation(byte[] buffer, int offset, int size, AsyncCallback callback, object state) {
1726             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation", ValidationHelper.HashString(m_Connection) + ", " + offset.ToString() + ", " + size.ToString());
1727             GlobalLog.ThreadContract(ThreadKinds.Unknown, "ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation");
1728
1729             //
1730             // Figure out how much we should really read.
1731             //
1732
1733             int bytesToRead = 0;
1734
1735             if (m_Chunked) {
1736                 if (!m_ChunkEofRecvd) {
1737                     return m_ChunkParser.ReadAsync(this, buffer, offset, size, callback, state);
1738                 }
1739             }
1740             else {
1741
1742                 //
1743                 // Not doing chunked, so don't read more than is left.
1744                 //
1745
1746                 if (m_ReadBytes != -1) {
1747                     bytesToRead = (int)Math.Min(m_ReadBytes, (long)size);
1748                 }
1749                 else {
1750                     bytesToRead = size;
1751                 }
1752             }
1753
1754             // If we're not going to read anything, either because they're
1755             // not asking for anything or there's nothing left, bail
1756             // out now.
1757
1758             if (bytesToRead == 0 || this.Eof) {
1759                 NestedSingleAsyncResult completedResult = new NestedSingleAsyncResult(this, state, callback, ZeroLengthRead);
1760                 GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation() completed, bytesToRead: " + bytesToRead + " Eof: " + this.Eof.ToString());
1761                 return completedResult;
1762             }
1763
1764             try
1765             {
1766                 int bytesAlreadyRead = 0;
1767                 if (m_ReadBufferSize > 0)
1768                 {
1769                     bytesAlreadyRead = FillFromBufferedData(buffer, ref offset, ref bytesToRead);
1770                     if (bytesToRead == 0)
1771                     {
1772                         NestedSingleAsyncResult completedResult = new NestedSingleAsyncResult(this, state, callback, bytesAlreadyRead);
1773                         GlobalLog.Leave("ConnectStream::BeginReadWithoutValidation");
1774                         return completedResult;
1775                     }
1776                 }
1777
1778                 if (ErrorInStream)
1779                 {
1780                     GlobalLog.LeaveException("ConnectStream::BeginReadWithoutValidation", m_ErrorException);
1781                     throw m_ErrorException;
1782                 }
1783
1784                 GlobalLog.Assert(m_DoneCalled == 0 || m_ReadBytes != -1, "BeginRead: Calling BeginRead after ReadDone.|m_DoneCalled > 0 && m_ReadBytes == -1");
1785
1786                 // Keep track of this during the read so it can be added back at the end.
1787                 m_BytesAlreadyTransferred = bytesAlreadyRead;
1788
1789                 IAsyncResult asyncResult = m_Connection.BeginRead(buffer, offset, bytesToRead, callback, state);
1790
1791                 // a null return indicates that the connection was closed underneath us.
1792                 if (asyncResult == null)
1793                 {
1794                     m_BytesAlreadyTransferred = 0;
1795                     m_ErrorException = new WebException(
1796                         NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
1797                         WebExceptionStatus.RequestCanceled);
1798
1799                     GlobalLog.LeaveException("ConnectStream::BeginReadWithoutValidation", m_ErrorException);
1800                     throw m_ErrorException;
1801                 }
1802
1803                 GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation() called BeginRead");
1804                 return asyncResult;
1805             }
1806             catch (Exception exception) {
1807                 IOError(exception);
1808                 GlobalLog.LeaveException("ConnectStream#" + ValidationHelper.HashString(this) + "::BeginReadWithoutValidation", exception);
1809                 throw;
1810             }
1811         }
1812
1813
1814         /*++
1815             InternalRead
1816
1817             This is an interal version of Read without validation,
1818              that is called from the Chunked code as well the normal codepaths.
1819
1820         --*/
1821
1822         private int InternalRead(byte[] buffer, int offset, int size) {
1823             GlobalLog.ThreadContract(ThreadKinds.Sync, "ConnectStream#" + ValidationHelper.HashString(this) + "::InternalRead");
1824
1825             // Read anything first out of the buffer
1826             int bytesToRead = FillFromBufferedData(buffer, ref offset, ref size);
1827             if (bytesToRead>0) {
1828                 return bytesToRead;
1829             }
1830
1831             // otherwise, we need to read more data from the connection.
1832             if (ErrorInStream) {
1833                 GlobalLog.LeaveException("ConnectStream::InternalBeginRead", m_ErrorException);
1834                 throw m_ErrorException;
1835             }
1836
1837             bytesToRead = m_Connection.Read(
1838                     buffer,
1839                     offset,
1840                     size);
1841
1842             return bytesToRead;
1843         }
1844
1845
1846         /*++
1847             ReadCallback
1848
1849             This callback is only used by chunking as the last step of its multi-phase async operation.
1850
1851             Input:
1852
1853                asyncResult - IAsyncResult generated from BeginWrite
1854
1855             Returns:
1856
1857                None
1858
1859         --*/
1860         private void ReadCallback(IAsyncResult asyncResult) {
1861             GlobalLog.Enter("ConnectStream::ReadCallback", "asyncResult=#"+ValidationHelper.HashString(asyncResult));
1862             GlobalLog.ThreadContract(ThreadKinds.Unknown, "ConnectStream::ReadCallback");
1863
1864             //
1865             // we called m_Connection.BeginRead() previously that call
1866             // completed and called our internal callback
1867             // we passed the NestedSingleAsyncResult (that we then returned to the user)
1868             // as the state of this call, so build it back:
1869             //
1870             NestedSingleAsyncResult castedAsyncResult = (NestedSingleAsyncResult)asyncResult.AsyncState;
1871             ConnectStream thisConnectStream = (ConnectStream)castedAsyncResult.AsyncObject;
1872
1873             object result = null;
1874
1875             try {
1876                 int bytesTransferred = thisConnectStream.m_Connection.EndRead(asyncResult);
1877                 if(Logging.On)Logging.Dump(Logging.Web, thisConnectStream, "ReadCallback", castedAsyncResult.Buffer, castedAsyncResult.Offset, Math.Min(castedAsyncResult.Size, bytesTransferred));
1878
1879                 result = bytesTransferred;
1880             }
1881             catch (Exception exception) {
1882                 result = exception;
1883             }
1884
1885             castedAsyncResult.InvokeCallback(result);
1886             GlobalLog.Leave("ConnectStream::ReadCallback");
1887         }
1888
1889
1890         /*++
1891             EndRead - Finishes off the Read for the Connection
1892             EndReadWithoutValidation
1893
1894             This method completes the async call created from BeginRead,
1895             it attempts to determine how many bytes were actually read,
1896             and if any errors occured.
1897
1898             Input:
1899                 asyncResult - created by BeginRead
1900
1901             Returns:
1902                 int - size of bytes read, or < 0 on error
1903
1904         --*/
1905
1906         public override int EndRead(IAsyncResult asyncResult) {
1907 #if DEBUG
1908             using (GlobalLog.SetThreadKind(ThreadKinds.User)) {
1909 #endif
1910             if (Logging.On) Logging.Enter(Logging.Web, this, "EndRead", "");
1911
1912             //
1913             // parameter validation
1914             //
1915             if (asyncResult==null) {
1916                 throw new ArgumentNullException("asyncResult");
1917             }
1918
1919             int bytesTransferred;
1920             bool zeroLengthRead = false;
1921             if ((asyncResult.GetType() == typeof(NestedSingleAsyncResult)) || m_Chunked)
1922             {
1923                 LazyAsyncResult castedAsyncResult = (LazyAsyncResult)asyncResult;
1924                 if (castedAsyncResult.AsyncObject != this)
1925                 {
1926                     throw new ArgumentException(SR.GetString(SR.net_io_invalidasyncresult), "asyncResult");
1927                 }
1928                 if (castedAsyncResult.EndCalled)
1929                 {
1930                     throw new InvalidOperationException(SR.GetString(SR.net_io_invalidendcall, "EndRead"));
1931                 }
1932                 castedAsyncResult.EndCalled = true;
1933
1934                 if (ErrorInStream)
1935                 {
1936                     GlobalLog.LeaveException("ConnectStream::EndRead", m_ErrorException);
1937                     throw m_ErrorException;
1938                 }
1939
1940                 object result = castedAsyncResult.InternalWaitForCompletion();
1941
1942                 Exception errorException = result as Exception;
1943                 if (errorException != null)
1944                 {
1945                     IOError(errorException, false);
1946                     bytesTransferred = -1;
1947                 }
1948                 else
1949                 {
1950                     // If it's a NestedSingleAsyncResult, we completed it ourselves with our own result.
1951                     if (result == null)
1952                     {
1953                         bytesTransferred = 0;
1954                     }
1955                     else if (result == ZeroLengthRead)
1956                     {
1957                         bytesTransferred = 0;
1958                         zeroLengthRead = true;
1959                     }
1960                     else
1961                     {
1962                         try
1963                         {
1964                             bytesTransferred = (int) result;
1965
1966                             if (m_Chunked && (bytesTransferred == 0))
1967                             {
1968                                 m_ChunkEofRecvd = true;
1969                                 CallDone();
1970                             }
1971                         }
1972                         catch (InvalidCastException)
1973                         {
1974                             bytesTransferred = -1;
1975                         }
1976                     }
1977                 }
1978             }
1979             else
1980             {
1981                 // If it's not a NestedSingleAsyncResult, we forwarded directly to the Connection and need to call EndRead.
1982                 try
1983                 {
1984                     bytesTransferred = m_Connection.EndRead(asyncResult);
1985                 }
1986                 catch (Exception exception)
1987                 {
1988                     if (NclUtilities.IsFatal(exception)) throw;
1989
1990                     IOError(exception, false);
1991                     bytesTransferred = -1;
1992                 }
1993             }
1994
1995             bytesTransferred = EndReadWithoutValidation(bytesTransferred, zeroLengthRead);
1996
1997             Interlocked.CompareExchange(ref m_CallNesting, Nesting.Idle, Nesting.IoInProgress);
1998             GlobalLog.Print("EndRead() callNesting: " + m_CallNesting.ToString());
1999
2000             if(Logging.On)Logging.Exit(Logging.Web, this, "EndRead", bytesTransferred);
2001             if (m_ErrorException != null) {
2002                 throw (m_ErrorException);
2003             }
2004
2005             return bytesTransferred;
2006 #if DEBUG
2007             }
2008 #endif
2009         }
2010
2011
2012         /*++
2013             EndReadWithoutValidation - Finishes off the Read for the Connection
2014                 Called internally by EndRead.
2015
2016             This method completes the async call created from BeginRead,
2017             it attempts to determine how many bytes were actually read,
2018             and if any errors occured.
2019
2020             Input:
2021                 asyncResult - created by BeginRead
2022
2023             Returns:
2024                 int - size of bytes read, or < 0 on error
2025
2026         --*/
2027         private int EndReadWithoutValidation(int bytesTransferred, bool zeroLengthRead)
2028         {
2029             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::EndReadWithoutValidation", bytesTransferred.ToString());
2030
2031             int bytesAlreadyTransferred = m_BytesAlreadyTransferred;
2032             m_BytesAlreadyTransferred = 0;
2033
2034             if (!m_Chunked) {
2035
2036                 //
2037                 // we're not chunking, a note about error
2038                 //  checking here, in some cases due to 1.0
2039                 //  servers we need to read until 0 bytes,
2040                 //  or a server reset, therefore, we may need
2041                 //  ignore sockets errors
2042                 //
2043
2044                 bool doneReading = false;
2045
2046                 // if its finished without async, just use what was read already from the buffer,
2047                 // otherwise we call the Connection's EndRead to find out
2048                 if (bytesTransferred <= 0)
2049                 {
2050                     //
2051                     // We read 0 bytes from the connection, or it had an error. This is OK if we're
2052                     // reading to end, it's an error otherwise.
2053                     //
2054                     if (m_ReadBytes != -1 && (bytesTransferred < 0 || !zeroLengthRead))
2055                     {
2056                         IOError(null, false);
2057                     }
2058                     else {
2059                         //
2060                         // We're reading to end, and we found the end, by reading 0 bytes
2061                         //
2062                         doneReading = true;
2063                         bytesTransferred = 0;
2064                     }
2065                 }
2066
2067                 bytesTransferred += bytesAlreadyTransferred;
2068
2069                 //
2070                 // Not chunking. Update our read bytes state and return what we've read.
2071                 //
2072                 if (m_ReadBytes != -1) {
2073                     m_ReadBytes -= bytesTransferred;
2074
2075                     GlobalLog.Assert(m_ReadBytes >= 0, "ConnectStream: Attempting to read more bytes than available.|m_ReadBytes < 0");
2076
2077                     GlobalLog.Print("m_ReadBytes = "+m_ReadBytes);
2078                 }
2079
2080                 if (m_ReadBytes == 0 || doneReading) {
2081                     // We're all done reading, tell the connection that.
2082                     m_ReadBytes = 0;
2083
2084                     //
2085                     // indicate to cache that read completed OK
2086                     //
2087
2088                     CallDone();
2089                 }
2090             }
2091
2092             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::EndRead", bytesTransferred);
2093             return bytesTransferred;
2094         }
2095
2096
2097         private static void WriteHeadersCallback(IAsyncResult ar)
2098         {
2099             if(ar.CompletedSynchronously){
2100                 return;
2101             }
2102
2103             WriteHeadersCallbackState state = (WriteHeadersCallbackState)ar.AsyncState;
2104             ConnectStream stream = state.stream;
2105             HttpWebRequest request = state.request;
2106             WebExceptionStatus error = WebExceptionStatus.SendFailure;
2107
2108             //m_Request.writebuffer may be set to null on resubmit before method exits
2109
2110             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(stream) + "::WriteHeadersCallback", "Connection#" + ValidationHelper.HashString(stream.m_Connection) + ", " + request.WriteBufferLength.ToString()); 
2111             try{
2112                 stream.m_Connection.EndWrite(ar);
2113                 if (stream.m_Connection.m_InnerException != null)
2114                     throw stream.m_Connection.m_InnerException;
2115                 else
2116                     error = WebExceptionStatus.Success;
2117             }
2118             catch (Exception e){
2119                 stream.HandleWriteHeadersException(e, error);
2120             }
2121
2122             stream.ExchangeCallNesting(Nesting.Idle, Nesting.InternalIO);
2123
2124             if (error == WebExceptionStatus.Success && !stream.ErrorInStream) {
2125
2126                 error = WebExceptionStatus.ReceiveFailure;
2127
2128                 // Start checking async for responses.  This needs to happen outside of the Nesting.InternalIO lock 
2129                 // because it may receive, process, and start a resubmit.
2130                 try {
2131                     request.StartAsync100ContinueTimer();
2132                     stream.m_Connection.CheckStartReceive(request);
2133
2134                     if (stream.m_Connection.m_InnerException != null)
2135                         throw stream.m_Connection.m_InnerException;
2136                     else
2137                         error = WebExceptionStatus.Success;
2138                 }
2139                 catch (Exception e) {
2140                     stream.HandleWriteHeadersException(e, error);
2141                 }
2142             }
2143
2144             // Resend data, etc.
2145             request.WriteHeadersCallback(error, stream, true);
2146             request.FreeWriteBuffer();
2147             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(stream) + "::WriteHeadersCallback",request.WriteBufferLength.ToString());
2148         }
2149
2150
2151         /*++
2152             WriteHeaders
2153
2154             This function writes header data to the network. Headers are special
2155             in that they don't have any non-header transforms applied to them,
2156             and are not subject to content-length constraints. We just write them
2157             through, and if we're done writing headers we tell the connection that.
2158
2159             Returns:
2160                 WebExceptionStatus.Pending      - we don't have a stream yet.
2161                 WebExceptionStatus.SendFailure  - there was an error while writing to the wire.
2162                 WebExceptionStatus.Success      - success.
2163
2164         --*/
2165         internal void WriteHeaders(bool async) {
2166             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::WriteHeaders", "Connection#" + ValidationHelper.HashString(m_Connection) + ", headers buffer size = " + m_Request.WriteBufferLength.ToString());
2167
2168             WebExceptionStatus error =  WebExceptionStatus.SendFailure;
2169
2170             if (!ErrorInStream)
2171             {
2172                 //m_Request.WriteBuffer may be set to null on resubmit before method exits
2173                 byte[] writeBuffer = m_Request.WriteBuffer;
2174                 int writeBufferLength = m_Request.WriteBufferLength;
2175                 
2176                 try
2177                 {
2178                     Interlocked.CompareExchange(ref m_CallNesting, Nesting.InternalIO, Nesting.Idle);
2179                     GlobalLog.Print("WriteHeaders() callNesting: " + m_CallNesting.ToString());
2180
2181                     if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_sending_headers, m_Request.Headers.ToString(true)));
2182
2183                     if(async)
2184                     {
2185                         WriteHeadersCallbackState state = new WriteHeadersCallbackState(m_Request, this);
2186                         IAsyncResult ar = m_Connection.UnsafeBeginWrite(writeBuffer,0,writeBufferLength, m_WriteHeadersCallback, state);
2187                         if (ar.CompletedSynchronously) {
2188                             m_Connection.EndWrite(ar);
2189                             error = WebExceptionStatus.Success;
2190                         }
2191                         else {
2192                             error = WebExceptionStatus.Pending;
2193 #if DEBUG
2194                             _PendingResult = ar;
2195 #endif
2196                         }
2197                     }
2198                     else
2199                     {
2200                         SafeSetSocketTimeout(SocketShutdown.Send);
2201                         m_Connection.Write(writeBuffer, 0, writeBufferLength);
2202                         error = WebExceptionStatus.Success;
2203                     }
2204                 }
2205                 catch (Exception e) {
2206                     HandleWriteHeadersException(e, error);
2207                 }
2208                 finally {
2209                     if(error != WebExceptionStatus.Pending) {
2210                         Interlocked.CompareExchange(ref m_CallNesting, Nesting.Idle, Nesting.InternalIO);
2211                         GlobalLog.Print("WriteHeaders() callNesting: " + m_CallNesting.ToString());
2212                     }
2213                 }
2214             }
2215             else
2216             {
2217                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::WriteHeaders() ignoring since ErrorInStream = true");
2218             }
2219
2220             if (error == WebExceptionStatus.Pending) 
2221             {
2222                 return; // WriteHeadersCallback will finish this async
2223             }
2224
2225             if (error == WebExceptionStatus.Success && !ErrorInStream)
2226             {
2227                 error = WebExceptionStatus.ReceiveFailure;
2228
2229                 // Start checking for responses.  This needs to happen outside of the Nesting.InternalIO lock 
2230                 // because it may receive, process, and start a resubmit.
2231                 try
2232                 {
2233                     if (async)
2234                     {
2235                         m_Request.StartAsync100ContinueTimer();
2236                         m_Connection.CheckStartReceive(m_Request);
2237                     }
2238                     else 
2239                     {
2240                         m_Request.StartContinueWait();
2241                         m_Connection.CheckStartReceive(m_Request);
2242                         if (m_Request.ShouldWaitFor100Continue()) // Sync poll
2243                         {
2244                             PollAndRead(m_Request.UserRetrievedWriteStream);
2245                         }
2246                     }
2247                     error = WebExceptionStatus.Success;
2248                 }
2249                 catch (Exception e)
2250                 {
2251                     HandleWriteHeadersException(e, error);
2252                 }
2253             }
2254
2255             m_Request.WriteHeadersCallback(error, this, async);            
2256             m_Request.FreeWriteBuffer();
2257         }
2258
2259         private void HandleWriteHeadersException(Exception e, WebExceptionStatus error) {
2260             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::WriteHeaders Exception: " + e.ToString());
2261
2262             if (e is IOException || e is ObjectDisposedException)
2263             {
2264                 //new connection but reset from server on inital send
2265                 if (!m_Connection.AtLeastOneResponseReceived && !m_Request.BodyStarted) {
2266                     e = new WebException(
2267                         NetRes.GetWebStatusString("net_connclosed", error),
2268                         error,
2269                         WebExceptionInternalStatus.Recoverable,
2270                         e);
2271                 }
2272                 else {
2273                     e = new WebException(
2274                                     NetRes.GetWebStatusString("net_connclosed", error),
2275                                     error,
2276                                     m_Connection.AtLeastOneResponseReceived ? WebExceptionInternalStatus.Isolated : WebExceptionInternalStatus.RequestFatal,
2277                                     e);
2278                 }
2279             }
2280
2281             IOError(e, false);
2282         }
2283
2284         internal ChannelBinding GetChannelBinding(ChannelBindingKind kind)
2285         {
2286             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::GetChannelBinding", kind.ToString());
2287
2288             ChannelBinding binding = null;
2289             TlsStream tlsStream = m_Connection.NetworkStream as TlsStream;
2290
2291             if (tlsStream != null)
2292             {
2293                 binding = tlsStream.GetChannelBinding(kind);
2294             }
2295
2296             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::GetChannelBinding", ValidationHelper.HashString(binding));
2297             return binding;
2298         }
2299
2300         // Wrapper for Connection
2301         internal void PollAndRead(bool userRetrievedStream) {
2302             m_Connection.PollAndRead(m_Request, userRetrievedStream);
2303         }
2304
2305         private void SafeSetSocketTimeout(SocketShutdown mode) {
2306
2307             if(Eof){
2308                 return;
2309             }
2310
2311             int timeout;
2312             if (mode == SocketShutdown.Receive) {
2313                 timeout = ReadTimeout;
2314             } else /*if (mode == SocketShutdown.Send)*/ {
2315                 timeout = WriteTimeout;
2316             }
2317             Connection connection = m_Connection;
2318             if (connection!=null) {
2319                 NetworkStream networkStream = connection.NetworkStream;
2320                 if (networkStream!=null) {
2321                     networkStream.SetSocketTimeoutOption(mode, timeout, false);
2322                 }
2323             }
2324         }
2325
2326         internal int SetRtcOption(byte[] rtcInputSocketConfig, byte[] rtcOutputSocketResult)
2327         {
2328             Socket socket = InternalSocket;
2329             Debug.Assert(socket != null, "No Socket");
2330             try
2331             {
2332                 socket.IOControl(ApplyTransportSetting, rtcInputSocketConfig, null);
2333                 socket.IOControl(QueryTransportSetting, rtcInputSocketConfig, rtcOutputSocketResult);
2334             }
2335             catch (SocketException ex)
2336             {
2337                 IOError(ex, false);
2338                 // Report error to QuerySetting
2339                 return ex.ErrorCode;
2340             }
2341             return 0;
2342         }
2343
2344         private Socket InternalSocket {
2345             get {
2346                 Connection connection = m_Connection;
2347                 if (connection != null)
2348                 {
2349                     NetworkStream networkStream = connection.NetworkStream;
2350                     if (networkStream != null)
2351                     {
2352                         return networkStream.InternalSocket;
2353                     }
2354                 }
2355                 return null;
2356             }
2357         }
2358
2359         /*++
2360             Close - Close the stream
2361
2362             Called when the stream is closed. We close our parent stream first.
2363             Then if this is a write stream, we'll send the terminating chunk
2364             (if needed) and call the connection DoneWriting() method.
2365
2366             Input:
2367
2368                 Nothing.
2369
2370             Returns:
2371
2372                 Nothing.
2373
2374         --*/
2375
2376         protected override void Dispose(bool disposing) {
2377 #if DEBUG
2378             using (GlobalLog.SetThreadKind(ThreadKinds.User | ThreadKinds.Sync)) {
2379 #endif
2380                 try {
2381  
2382                     if (disposing) {                        
2383                         GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::Close()");
2384                         if(Logging.On)Logging.Enter(Logging.Web, this, "Close", "");
2385                         ((ICloseEx)this).CloseEx(CloseExState.Normal);
2386                         if(Logging.On)Logging.Exit(Logging.Web, this, "Close", "");
2387                     }
2388                 }
2389                 finally {
2390                     base.Dispose(disposing);
2391                 }
2392 #if DEBUG
2393             }
2394 #endif
2395         }
2396
2397         internal void CloseInternal(bool internalCall) {
2398             GlobalLog.ThreadContract(ThreadKinds.Unknown, "ConnectStream#" + ValidationHelper.HashString(this) + "::Abort");
2399             ((ICloseEx)this).CloseEx((internalCall ? CloseExState.Silent : CloseExState.Normal));
2400         }
2401
2402         void ICloseEx.CloseEx(CloseExState closeState) {
2403             GlobalLog.ThreadContract(ThreadKinds.Unknown, "ConnectStream#" + ValidationHelper.HashString(this) + "::Abort");
2404             CloseInternal(
2405                           (closeState & CloseExState.Silent) != 0,
2406                           (closeState & CloseExState.Abort) != 0
2407                           );
2408             GC.SuppressFinalize(this);
2409         }
2410
2411         //
2412         // Optionally sends chunk terminator and proceeds with close that was collided with pending user write IO
2413         //
2414         void ResumeInternalClose(LazyAsyncResult userResult)
2415         {
2416             GlobalLog.Print("ConnectStream##" + ValidationHelper.HashString(this) + "::ResumeInternalClose(), userResult:" + userResult);
2417             //
2418             // write stream. terminate our chunking if needed.
2419             //
2420             if (WriteChunked && !ErrorInStream && !m_IgnoreSocketErrors)
2421             {
2422                 m_IgnoreSocketErrors = true;
2423                 try {
2424                     if (userResult == null)
2425                     {
2426                         SafeSetSocketTimeout(SocketShutdown.Send);
2427                         m_Connection.Write(NclConstants.ChunkTerminator, 0, NclConstants.ChunkTerminator.Length);
2428                     }
2429                     else
2430                     {
2431                         m_Connection.BeginWrite(NclConstants.ChunkTerminator, 0, NclConstants.ChunkTerminator.Length, new AsyncCallback(ResumeClose_Part2_Wrapper), userResult);
2432                         return;
2433                     }
2434                 }
2435                 catch (Exception exception) {
2436                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() exceptionOnWrite:" + exception.Message);
2437                 }
2438             }
2439             ResumeClose_Part2(userResult); //never throws
2440         }
2441         
2442         void ResumeClose_Part2_Wrapper(IAsyncResult ar)
2443         {
2444             try {
2445                 m_Connection.EndWrite(ar);
2446             }
2447             catch (Exception exception) {
2448                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::ResumeClose_Part2_Wrapper() ignoring exceptionOnWrite:" + exception.Message);
2449             }
2450             ResumeClose_Part2((LazyAsyncResult)ar.AsyncState);
2451         }
2452
2453         private void ResumeClose_Part2(LazyAsyncResult userResult)
2454         {
2455             try
2456             {
2457                 try
2458                 {
2459                     if (ErrorInStream)
2460                     {
2461                         GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::ResumeClose_Part2() Aborting the connection");
2462                         m_Connection.AbortSocket(true);
2463                     }
2464                 }
2465                 finally
2466                 {
2467                     CallDone();
2468                     GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::ResumeClose_Part2", "Done");
2469                 }
2470             }
2471             catch { }
2472             finally
2473             {
2474                 if (userResult != null)
2475                 {
2476                     userResult.InvokeCallback();
2477                 }
2478             }
2479         }
2480
2481         // The number should be reasonalbly large
2482         private const int AlreadyAborted = 777777;
2483         // 
2484         private void CloseInternal(bool internalCall, bool aborting) {
2485             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal", internalCall.ToString());
2486             GlobalLog.ThreadContract(ThreadKinds.Unknown, "ConnectStream#" + ValidationHelper.HashString(this) + "::Abort");
2487
2488             bool normalShutDown = !aborting;
2489             Exception exceptionOnWrite = null;
2490
2491             //
2492             // We have to prevent recursion, because we'll call our parents, close,
2493             // which might try to flush data. If we're in an error situation, that
2494             // will cause an error on the write, which will cause Close to be called
2495             // again, etc.
2496             //
2497             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() m_ShutDown:" + m_ShutDown.ToString() + " m_CallNesting:" + m_CallNesting.ToString() + " m_DoneCalled:" + m_DoneCalled.ToString());
2498
2499             //If this is an abort (aborting == true) of a write stream then we will call request.Abort()
2500             //that will call us again. To prevent a recursion here, only one abort is allowed.
2501             //However, Abort must still override previous normal close if any.
2502             if (aborting) {
2503                 if (Interlocked.Exchange(ref m_ShutDown, AlreadyAborted) >= AlreadyAborted) {
2504                     GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal", "already has been Aborted");
2505                     return;
2506                 }
2507             }
2508             else {
2509                 //If m_ShutDown != 0, then this method has been already called before,
2510                 //Hence disregard this (presumably normal) extra close
2511                 if (Interlocked.Increment(ref m_ShutDown) > 1) {
2512                     GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal", "already has been closed");
2513                     return;
2514                 }
2515
2516                 RequestLifetimeSetter.Report(m_RequestLifetimeSetter);
2517             }
2518
2519             //
2520             // Since this should be the last call made, we should be at 0
2521             //  If not on the read side then it's an error so we should close the socket
2522             //  If not on the write side then MAY BE we want this write stream to ignore all
2523             //  further writes and optionally send chunk terminator.
2524             //
2525             int nesting = (IsPostStream  && internalCall && !IgnoreSocketErrors && !BufferOnly && normalShutDown && !NclUtilities.HasShutdownStarted)? Nesting.Closed: Nesting.InError;
2526             if (Interlocked.Exchange(ref m_CallNesting, nesting) == Nesting.IoInProgress)
2527             {
2528                 if (nesting == Nesting.Closed)
2529                 {
2530                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() PostStream, Internal call and m_CallNesting==1, defer closing until user write completes");
2531                     return;
2532                 }
2533                 normalShutDown &= !NclUtilities.HasShutdownStarted;
2534             }
2535             GlobalLog.Print("Close m_CallNesting: " + m_CallNesting.ToString());
2536
2537             // Questionable: Thsi is to avoid throwing on public Close() when IgnoreSocketErrors==true
2538             if (IgnoreSocketErrors && IsPostStream && !internalCall)
2539             {
2540                 m_BytesLeftToWrite = 0;
2541             }
2542
2543             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() normalShutDown:" + normalShutDown.ToString() + " m_CallNesting:" + m_CallNesting.ToString() + " m_DoneCalled:" + m_DoneCalled.ToString());
2544
2545
2546             if (IgnoreSocketErrors || !normalShutDown) {
2547                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() don't read/write on this, dead connection stream.");
2548             }
2549             else if (!WriteStream) {
2550                 //
2551                 // read stream
2552                 //
2553                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() callNesting: " + m_CallNesting.ToString());
2554                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() read stream, calling DrainSocket()");
2555 #if DEBUG
2556             using (GlobalLog.SetThreadKind(ThreadKinds.Sync)) {
2557 #endif
2558             //
2559             // A race condition still exists when a different thread calls HttpWebRequest.Abort().
2560             // This is expected and handled within DrainSocket().
2561             //
2562             Connection connection = m_Connection;
2563             if (connection != null)
2564             {
2565                 NetworkStream networkStream = connection.NetworkStream;
2566                 if (networkStream != null && networkStream.Connected)
2567                 {
2568                     normalShutDown = DrainSocket();
2569                 }
2570             }
2571 #if DEBUG
2572             }
2573 #endif
2574             }
2575             else {
2576                 //
2577                 // write stream. terminate our chunking if needed.
2578                 //
2579                 try {
2580                     if (!ErrorInStream) {
2581                         //
2582                         // if not error already, then...
2583                         // first handle chunking case
2584                         //
2585                         if (WriteChunked) {
2586                             //
2587                             // no need to buffer here:
2588                             // on resubmit, we won't be chunking anyway this will send 5 bytes on the wire
2589                             //
2590                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() Chunked, writing ChunkTerminator");
2591                             try {
2592                                 // The idea behind is that closed stream must not write anything to the wire
2593                                 // Still if we are chunking, the now buffering and future resubmit is possible
2594                                 if (!m_IgnoreSocketErrors) {
2595                                     m_IgnoreSocketErrors = true;
2596                                     SafeSetSocketTimeout(SocketShutdown.Send);
2597
2598 #if DEBUG
2599                                     // Until there is an async version of this, we have to assert Sync privileges here.
2600                                     using (GlobalLog.SetThreadKind(ThreadKinds.Sync)) {
2601 #endif
2602                                     m_Connection.Write(NclConstants.ChunkTerminator, 0, NclConstants.ChunkTerminator.Length);
2603 #if DEBUG
2604                                     }
2605 #endif
2606                                 }
2607                             }
2608                             catch {
2609                                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() IGNORE chunk write fault");
2610                             }
2611                             m_BytesLeftToWrite = 0;
2612                         }
2613                         else if (BytesLeftToWrite>0) {
2614                             if (internalCall) { // ContentLength, after a 401 without buffering we need to close the connection
2615                                 m_Connection.AbortSocket(true);
2616                             }
2617                             else { // The user called close before they wrote ContentLength number of bytes to the stream
2618                                 //
2619                                 // not enough bytes written to client
2620                                 //
2621                                 GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() BytesLeftToWrite:" + BytesLeftToWrite.ToString() + " throwing not enough bytes written");
2622                                 throw new IOException(SR.GetString(SR.net_io_notenoughbyteswritten));
2623                             }
2624                         }
2625                         else if (BufferOnly) {
2626                             //
2627                             // now we need to use the saved reference to the request the client
2628                             // closed the write stream. we need to wake up the request, so that it
2629                             // sends the headers and kick off resubmitting of buffered entity body
2630                             //
2631                             GlobalLog.Assert(m_Request != null, "ConnectStream#{0}::CloseInternal|m_Request == null", ValidationHelper.HashString(this));
2632                             m_BytesLeftToWrite = BufferedData.Length;
2633                             m_Request.SwitchToContentLength();
2634                             //
2635                             // writing the headers will kick off the whole request submission process
2636                             // (including waiting for the 100 Continue and writing the whole entity body)
2637                             //
2638                             SafeSetSocketTimeout(SocketShutdown.Send);
2639                             m_Request.NeedEndSubmitRequest();
2640                             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal", "Done");
2641                             return;
2642                         }
2643                     }
2644                     else {
2645                         normalShutDown = false;
2646                     }
2647                 }
2648                 catch (Exception exception) {
2649                     normalShutDown = false;
2650
2651                     if (NclUtilities.IsFatal(exception))
2652                     {
2653                         m_ErrorException = exception;
2654                         throw;
2655                     }
2656
2657                     exceptionOnWrite = new WebException(
2658                         NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
2659                         exception,
2660                         WebExceptionStatus.RequestCanceled,
2661                         null);
2662
2663                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() exceptionOnWrite:" + exceptionOnWrite.Message);
2664                 }
2665             }
2666
2667             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() normalShutDown:" + normalShutDown.ToString() + " m_CallNesting:" + m_CallNesting.ToString() + " m_DoneCalled:" + m_DoneCalled.ToString());
2668
2669             if (!normalShutDown && m_DoneCalled==0) {
2670                 // If a normal Close (aborting == false) has turned into Abort _inside_ this method,
2671                 // then check if another abort has been charged from other thread
2672                 if (!aborting && Interlocked.Exchange(ref m_ShutDown, AlreadyAborted) >= AlreadyAborted){
2673                     GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal", "other thread has charged Abort(), canceling that one");
2674                     return;
2675                 }
2676                 //
2677                 // then abort the connection if we finished in error
2678                 //   note: if m_DoneCalled != 0, then we no longer have
2679                 //   control of the socket, so closing would cause us
2680                 //   to close someone else's socket/connection.
2681                 //
2682                 m_ErrorException =
2683                     new WebException(
2684                         NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled),
2685                         WebExceptionStatus.RequestCanceled);
2686
2687                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() Aborting the connection");
2688
2689                 m_Connection.AbortSocket(true);
2690                 // For write stream Abort() we depend on either of two, i.e:
2691                 // 1. The connection BeginRead is curently posted (means there are no response headers received yet)
2692                 // 2. The response (read) stream must be closed as well if aborted this (write) stream.
2693                 // Next block takes care of (2) since otherwise, (1) is true.
2694                 if (WriteStream) {
2695                     HttpWebRequest req = m_Request;
2696                     if (req != null) {
2697                         req.Abort();
2698                     }
2699                 }
2700
2701                 if (exceptionOnWrite != null) {
2702                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() calling CallDone() on exceptionOnWrite:" + exceptionOnWrite.Message);
2703
2704                     CallDone();
2705
2706                     if (!internalCall) {
2707                         GlobalLog.LeaveException("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() throwing:", exceptionOnWrite);
2708                         throw exceptionOnWrite;
2709                     }
2710                 }
2711             }
2712             //
2713             // Let the connection know we're done writing or reading.
2714             //
2715             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal() calling CallDone()");
2716
2717             CallDone();
2718
2719             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::CloseInternal", "Done");
2720         }
2721
2722
2723         /*++
2724             Flush - Flush the stream
2725
2726             Called when the user wants to flush the stream. This is meaningless to
2727             us, so we just ignore it.
2728
2729             Input:
2730
2731                 Nothing.
2732
2733             Returns:
2734
2735                 Nothing.
2736
2737
2738
2739         --*/
2740         public override void Flush() {
2741         }
2742
2743         public override Task FlushAsync(CancellationToken cancellationToken)
2744         {
2745             return Task.CompletedTask;
2746         }
2747
2748         /*++
2749             Seek - Seek on the stream
2750
2751             Called when the user wants to seek the stream. Since we don't support
2752             seek, we'll throw an exception.
2753
2754             Input:
2755
2756                 offset      - offset to see
2757                 origin      - where to start seeking
2758
2759             Returns:
2760
2761                 Throws exception
2762
2763
2764
2765         --*/
2766         public override long Seek(long offset, SeekOrigin origin) {
2767             throw new NotSupportedException(SR.GetString(SR.net_noseek));
2768         }
2769
2770         /*++
2771             SetLength - Set the length on the stream
2772
2773             Called when the user wants to set the stream length. Since we don't
2774             support seek, we'll throw an exception.
2775
2776             Input:
2777
2778                 value       - length of stream to set
2779
2780             Returns:
2781
2782                 Throws exception
2783
2784
2785
2786         --*/
2787         public override void SetLength(long value) {
2788             throw new NotSupportedException(SR.GetString(SR.net_noseek));
2789         }
2790
2791         /*++
2792             DrainSocket - Reads data from the connection, till we'll ready
2793                 to finish off the stream, or close the connection for good.
2794
2795
2796             returns - bool true on success, false on failure
2797
2798         --*/
2799         private bool DrainSocket() {
2800             GlobalLog.Enter("ConnectStream::DrainSocket");
2801             GlobalLog.ThreadContract(ThreadKinds.Unknown, "ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket");
2802
2803             if (IgnoreSocketErrors)
2804             {
2805                 GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() IgnoreSocketErrors == true, stream is dead.", true);
2806                 return true;
2807             }
2808
2809             // Optimization for non-chunked responses: when data is already present in the buffer, skip parsing it.
2810             long ReadBytes = m_ReadBytes;
2811
2812             if (!m_Chunked) {
2813
2814                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() m_ReadBytes:" + m_ReadBytes.ToString() + " m_ReadBufferSize:" + m_ReadBufferSize.ToString());
2815
2816                 if (m_ReadBufferSize != 0) {
2817                     //
2818                     // There's stuff in our read buffer.
2819                     // Update our internal read buffer state with what we took.
2820                     //
2821                     m_ReadOffset += m_ReadBufferSize;
2822
2823                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() m_ReadBytes:" + m_ReadBytes.ToString() + " m_ReadOffset:" + m_ReadOffset.ToString());
2824
2825                     if (m_ReadBytes != -1) {
2826
2827                         m_ReadBytes -= m_ReadBufferSize;
2828
2829                         GlobalLog.Print("m_ReadBytes = "+m_ReadBytes);
2830
2831                         // error handling, we shouldn't hang here if trying to drain, and there
2832                         //  is a mismatch with Content-Length and actual bytes.
2833                         //
2834                         //  Note: I've seen this often happen with some sites where they return 204
2835                         //  in violation of HTTP/1.1 with a Content-Length > 0
2836
2837                         if (m_ReadBytes < 0) {
2838                             GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() m_ReadBytes:" + m_ReadBytes.ToString() + " incorrect Content-Length? setting m_ReadBytes to 0 and returning false.");
2839                             m_ReadBytes = 0;
2840                             GlobalLog.Leave("ConnectStream::DrainSocket", false);
2841                             return false;
2842                         }
2843                     }
2844                     m_ReadBufferSize = 0;
2845
2846                     // If the read buffer size has gone to 0, null out our pointer
2847                     // to it so maybe it'll be garbage-collected faster.
2848                     m_ReadBuffer = null;
2849                 }
2850
2851                 // exit out of drain Socket when there is no connection-length,
2852                 // it doesn't make sense to drain a possible empty socket,
2853                 // when we're just going to close it.
2854                 if (ReadBytes == -1) {
2855                     GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() ReadBytes==-1, returning true");
2856                     return true;
2857                 }
2858             }
2859
2860             //
2861             // in error or Eof, we may be in a weird state
2862             //  so we need return if we as if we don't have any more
2863             //  space to read, note Eof is true when there is an error
2864             //
2865
2866             if (this.Eof) {
2867                 GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() Eof, returning true");
2868                 return true;
2869             }
2870
2871             int drainTimeoutMilliseconds = GetResponseDrainTimeout();
2872
2873             //
2874             // If we're draining more than 64K, then we should
2875             //  just close the socket, since it would be costly to
2876             //  do this.
2877             //
2878
2879             if ((drainTimeoutMilliseconds == 0) || (m_ReadBytes > c_MaxDrainBytes)) {
2880                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() m_ReadBytes:" + m_ReadBytes.ToString() + " Closing the Connection");
2881                 m_Connection.AbortSocket(false);
2882                 GlobalLog.Leave("ConnectStream::DrainSocket", true);
2883                 return true;
2884             }
2885
2886             int bytesRead;
2887             int totalBytesRead = 0;
2888             Stopwatch sw = new Stopwatch();
2889
2890             try {
2891
2892                 // Override the receive timeout interval to correspond to the drainTimeoutMiliseconds.
2893                 NetworkStream networkStream = m_Connection.NetworkStream;
2894                 networkStream.SetSocketTimeoutOption(SocketShutdown.Receive, drainTimeoutMilliseconds, false);
2895                 sw.Start();
2896
2897                 do {
2898                     if (sw.ElapsedMilliseconds >= drainTimeoutMilliseconds) {
2899                         GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() drain timeout exceeded.");
2900                         bytesRead = -1;
2901                         break;
2902                     }
2903                     bytesRead = ReadWithoutValidation(s_DrainingBuffer, 0, s_DrainingBuffer.Length, false);
2904                     totalBytesRead += bytesRead;
2905                     GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() drained bytesRead:" + bytesRead.ToString() + " bytes");
2906                 } while ((bytesRead > 0) && (totalBytesRead <= c_MaxDrainBytes));
2907             }
2908             catch (IOException) {
2909                 //
2910                 // If SO_RCVTIMEO is set and we aborted the recv, the socket is in an indeterminate state.
2911                 // We need to close this socket.
2912                 //
2913
2914                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() drain timeout exceeded.");
2915                 bytesRead = -1;
2916             }
2917             catch (ObjectDisposedException)
2918             {
2919                 //
2920                 // A race condition exists when a different thread calls HttpWebRequest.Abort().
2921                 // In certain cases, the SocketHandle or NetworkStream object may have been already closed/disposed.
2922                 //
2923
2924                 GlobalLog.Print("ConnectStream#" + ValidationHelper.HashString(this) + "::DrainSocket() the socket was already closed.");
2925                 bytesRead = -1;
2926             }
2927             catch (Exception exception) {
2928                 if (NclUtilities.IsFatal(exception)) throw;
2929
2930                 GlobalLog.Print("exception" + exception.ToString());
2931                 bytesRead = -1;
2932             }
2933             finally {
2934                 sw.Stop();
2935             }
2936
2937             if (bytesRead != 0) {
2938                 //
2939                 // bytesRead > 0 means that we still have data from a chunked transfer but we have exceeded c_MaxDrainBytes.
2940                 // bytesRead = -1 indicates a failure or a time out (either SO_RCVTIMEO or total execution time exceeded).
2941                 // For appCompat reasons we must call AbortSocket(false) and return true below.
2942                 //
2943
2944                 m_Connection.AbortSocket(false);
2945             }
2946             else {
2947                 
2948                 // Drain succesful. Set the receive timeout interval back to the original value.
2949                 SafeSetSocketTimeout(SocketShutdown.Receive);
2950             }
2951
2952             GlobalLog.Leave("ConnectStream::DrainSocket", true);
2953             return true;
2954         }
2955
2956         private int GetResponseDrainTimeout() {
2957
2958             if (responseDrainTimeoutMilliseconds == Timeout.Infinite) { 
2959                 
2960                 // Check if the config setting for response drain timeout is set. If not, use the default value.
2961                 // Thread safety: It's OK to enter this if-block from multiple threads: We'll just initialize 
2962                 // responseDrainTimeoutMilliseconds multiple times.
2963
2964                 string appSetting = ConfigurationManager.AppSettings[responseDrainTimeoutAppSetting];
2965                 int timeoutMilliseconds;
2966                 if (int.TryParse(appSetting, NumberStyles.None, CultureInfo.InvariantCulture, 
2967                     out timeoutMilliseconds)) {
2968
2969                     responseDrainTimeoutMilliseconds = timeoutMilliseconds;
2970                 }
2971                 else {
2972                     responseDrainTimeoutMilliseconds = defaultResponseDrainTimeoutMilliseconds;
2973                 }
2974             }
2975
2976             return responseDrainTimeoutMilliseconds;
2977         }
2978
2979         internal static byte[] s_DrainingBuffer = new byte[4096];
2980
2981         /*++
2982             IOError - Handle an IOError on the stream.
2983
2984             Input:
2985
2986                 exception       - optional Exception that will be later thrown
2987
2988             Returns:
2989
2990                 Nothing or may throw
2991
2992         --*/
2993         private void IOError(Exception exception) {
2994             IOError(exception, true);
2995         }
2996
2997         // If willThrow=true, it means that the caller intends to throw an
2998         // exception. If there is already an earlier exception, then IO error
2999         // will throw that one instead. Otherwise IOError() will let the caller
3000         // throw the exception (typically the one passed in)
3001         // If willThrow = false, the user does not want an exception to be
3002         // thrown. So IOError should not throw an exception.
3003         private void IOError(Exception exception, bool willThrow)
3004         {
3005             GlobalLog.Enter("ConnectStream#" + ValidationHelper.HashString(this) + "::IOError", "Connection# " + ValidationHelper.HashString(m_Connection));
3006             GlobalLog.ThreadContract(ThreadKinds.Unknown, "ConnectStream#" + ValidationHelper.HashString(this) + "::IOError");
3007
3008             string Msg;
3009
3010             if (m_ErrorException == null)
3011             {
3012                 if ( exception == null ) {
3013                     if ( !WriteStream ) {
3014                         Msg = SR.GetString(SR.net_io_readfailure, SR.GetString(SR.net_io_connectionclosed));
3015                     }
3016                     else {
3017                         Msg = SR.GetString(SR.net_io_writefailure, SR.GetString(SR.net_io_connectionclosed));
3018                     }
3019
3020                     Interlocked.CompareExchange<Exception>(ref m_ErrorException, new IOException(Msg), null);
3021                 }
3022                 else
3023                 {
3024                     willThrow &= Interlocked.CompareExchange<Exception>(ref m_ErrorException, exception, null) != null;
3025                 }
3026             }
3027
3028             ConnectionReturnResult returnResult = null;
3029
3030             if (WriteStream)
3031                 m_Connection.HandleConnectStreamException(true, false, WebExceptionStatus.SendFailure, ref returnResult, m_ErrorException);
3032             else
3033                 m_Connection.HandleConnectStreamException(false, true, WebExceptionStatus.ReceiveFailure, ref returnResult, m_ErrorException);
3034
3035
3036             CallDone(returnResult);
3037
3038             GlobalLog.Leave("ConnectStream#" + ValidationHelper.HashString(this) + "::IOError");
3039
3040             if (willThrow)
3041             {
3042                 throw m_ErrorException;
3043             }
3044         }
3045
3046
3047         /*++
3048
3049             GetChunkHeader
3050
3051             A private utility routine to convert an integer to a chunk header,
3052             which is an ASCII hex number followed by a CRLF. The header is retuned
3053             as a byte array.
3054
3055             Input:
3056
3057                 size        - Chunk size to be encoded
3058                 offset      - Out parameter where we store offset into buffer.
3059
3060             Returns:
3061
3062                 A byte array with the header in int.
3063
3064         --*/
3065
3066         internal static byte[] GetChunkHeader(int size, out int offset) {
3067             GlobalLog.Enter("ConnectStream::GetChunkHeader", "size:" + size.ToString());
3068
3069             uint Mask = 0xf0000000;
3070             byte[] Header = new byte[10];
3071             int i;
3072             offset = -1;
3073
3074             //
3075             // Loop through the size, looking at each nibble. If it's not 0
3076             // convert it to hex. Save the index of the first non-zero
3077             // byte.
3078             //
3079             for (i = 0; i < 8; i++, size <<= 4) {
3080                 //
3081                 // offset == -1 means that we haven't found a non-zero nibble
3082                 // yet. If we haven't found one, and the current one is zero,
3083                 // don't do anything.
3084                 //
3085                 if (offset == -1) {
3086                     if ((size & Mask) == 0) {
3087                         continue;
3088                     }
3089                 }
3090
3091                 //
3092                 // Either we have a non-zero nibble or we're no longer skipping
3093                 // leading zeros. Convert this nibble to ASCII and save it.
3094                 //
3095                 uint Temp = (uint)size >> 28;
3096
3097                 if (Temp < 10) {
3098                     Header[i] = (byte)(Temp + '0');
3099                 }
3100                 else {
3101                     Header[i] = (byte)((Temp - 10) + 'A');
3102                 }
3103
3104                 //
3105                 // If we haven't found a non-zero nibble yet, we've found one
3106                 // now, so remember that.
3107                 //
3108                 if (offset == -1) {
3109                     offset = i;
3110                 }
3111             }
3112
3113             Header[8] = (byte)'\r';
3114             Header[9] = (byte)'\n';
3115
3116             GlobalLog.Leave("ConnectStream::GetChunkHeader");
3117             return Header;
3118         }
3119
3120         void IRequestLifetimeTracker.TrackRequestLifetime(long requestStartTimestamp)
3121         {
3122             GlobalLog.Assert(m_RequestLifetimeSetter == null, "TrackRequestLifetime called more than once.");
3123             m_RequestLifetimeSetter = new RequestLifetimeSetter(requestStartTimestamp);
3124         }
3125     }
3126     //
3127     // Base Memory stream does not overide BeginXXX and that will cause base Stream
3128     // to do async delegates and that is not thread safe on async Stream.Close()
3129     //
3130     // This class will always complete async requests synchronously
3131     //
3132     internal sealed class SyncMemoryStream: MemoryStream, IRequestLifetimeTracker {
3133         private int m_ReadTimeout;
3134         private int m_WriteTimeout;
3135         private RequestLifetimeSetter m_RequestLifetimeSetter;
3136         private bool m_Disposed;
3137
3138         internal SyncMemoryStream(byte[] bytes): base(bytes, false)
3139         {
3140             m_ReadTimeout = m_WriteTimeout = System.Threading.Timeout.Infinite;
3141         }
3142         //
3143         internal SyncMemoryStream(int initialCapacity): base(initialCapacity)
3144         {
3145             m_ReadTimeout = m_WriteTimeout = System.Threading.Timeout.Infinite;
3146         }
3147         public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, Object state)
3148         {
3149             int result = Read(buffer, offset, count);
3150             return new LazyAsyncResult(null, state, callback, result);
3151         }
3152         //
3153         public override int EndRead(IAsyncResult asyncResult)
3154         {
3155             LazyAsyncResult lazyResult = (LazyAsyncResult)asyncResult;
3156             return (int) lazyResult.InternalWaitForCompletion();
3157         }
3158         public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, Object state)
3159         {
3160             Write(buffer, offset, count);
3161             return new LazyAsyncResult(null, state, callback, null);
3162         }
3163         //
3164         public override void EndWrite(IAsyncResult asyncResult)
3165         {
3166             LazyAsyncResult lazyResult = (LazyAsyncResult)asyncResult;
3167             lazyResult.InternalWaitForCompletion();
3168         }
3169         //
3170         public override bool CanTimeout {
3171             get {
3172                 return true;
3173             }
3174         }
3175         public override int ReadTimeout {
3176             get {
3177                 return m_ReadTimeout;
3178             }
3179             set {
3180                 m_ReadTimeout = value;
3181             }
3182         }
3183         public override int WriteTimeout {
3184             get {
3185                 return m_WriteTimeout;
3186             }
3187             set {
3188                 m_WriteTimeout = value;
3189             }
3190         }
3191
3192         public void TrackRequestLifetime(long requestStartTimestamp)
3193         {
3194             GlobalLog.Assert(m_RequestLifetimeSetter == null, "TrackRequestLifetime called more than once.");
3195             m_RequestLifetimeSetter = new RequestLifetimeSetter(requestStartTimestamp);
3196         }
3197
3198         protected override void Dispose(bool disposing)
3199         {
3200             if (m_Disposed)
3201             {
3202                 return;
3203             }
3204
3205             m_Disposed = true;
3206
3207             if (disposing)
3208             {
3209                 RequestLifetimeSetter.Report(m_RequestLifetimeSetter);
3210             }
3211
3212             base.Dispose(disposing);
3213         }
3214     }
3215 }