The Labs \ Source Viewer \ SSCLI \ System.Net \ Nesting

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

Developer Fusion