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

  1. //------------------------------------------------------------------------------
  2. // <copyright file="_Connection.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.    
  18.     using System.Collections;
  19.     using System.Collections.Generic;
  20.     using System.Diagnostics;
  21.     using System.IO;
  22.     using System.Net.Sockets;
  23.     using System.Text;
  24.     using System.Threading;
  25.     using System.Security;
  26.     using System.Globalization;
  27.     using System.Net.Configuration;
  28.    
  29.     internal enum ReadState
  30.     {
  31.         Start,
  32.         StatusLine,
  33.         // about to parse status line
  34.         Headers,
  35.         // reading headers
  36.         Data
  37.         // now read data
  38.     }
  39.    
  40.     internal enum DataParseStatus
  41.     {
  42.         NeedMoreData = 0,
  43.         // need more data
  44.         ContinueParsing,
  45.         // continue parsing
  46.         Done,
  47.         // done
  48.         Invalid,
  49.         // bad data format
  50.         DataTooBig
  51.         // data exceeds the allowed size
  52.     }
  53.    
  54.     internal enum WriteBufferState
  55.     {
  56.         Disabled,
  57.         Headers,
  58.         Buffer,
  59.         Playback
  60.     }
  61.    
  62.     // The enum lietrals will be displayed to the user in the exception message
  63.     internal enum WebParseErrorSection
  64.     {
  65.         Generic,
  66.         ResponseHeader,
  67.         ResponseStatusLine,
  68.         ResponseBody
  69.     }
  70.    
  71.     // The enum literal will be used to look up an error string in the resource file
  72.     internal enum WebParseErrorCode
  73.     {
  74.         Generic,
  75.         InvalidHeaderName,
  76.         InvalidContentLength,
  77.         IncompleteHeaderLine,
  78.         CrLfError,
  79.         InvalidChunkFormat,
  80.         UnexpectedServerResponse
  81.     }
  82.    
  83.     // Only defined for DataParseStatus.Invalid
  84.     struct WebParseError
  85.     {
  86.         public WebParseErrorSection Section;
  87.         public WebParseErrorCode Code;
  88.     }
  89.    
  90.    
  91.     struct TunnelStateObject
  92.     {
  93.         internal TunnelStateObject(HttpWebRequest r, Connection c)
  94.         {
  95.             Connection = c;
  96.             OriginalRequest = r;
  97.         }
  98.        
  99.         internal Connection Connection;
  100.         internal HttpWebRequest OriginalRequest;
  101.     }
  102.    
  103. /*++
  104.         BufferChunkBytes - A class to read a chunk stream from a buffer.
  105.         A simple little value class that implements the IReadChunkBytes
  106.         interface.
  107.     --*/   
  108.     internal struct BufferChunkBytes : IReadChunkBytes
  109.     {
  110.        
  111.         public byte[] Buffer;
  112.         public int Offset;
  113.         public int Count;
  114.        
  115.         public int NextByte {
  116.             get {
  117.                 if (Count != 0) {
  118.                     Count--;
  119.                     return (int)Buffer[Offset++];
  120.                 }
  121.                 return -1;
  122.             }
  123.             set {
  124.                 Count++;
  125.                 Offset--;
  126.                 Buffer[Offset] = (byte)value;
  127.             }
  128.         }
  129.     }
  130.    
  131.     //
  132.     // ConnectionReturnResult - used to spool requests that have been completed,
  133.     // and need to be notified.
  134.     //
  135.    
  136.     internal class ConnectionReturnResult
  137.     {
  138.        
  139.         private static readonly WaitCallback s_InvokeConnectionCallback = new WaitCallback(InvokeConnectionCallback);
  140.        
  141.         private struct RequestContext
  142.         {
  143.             internal HttpWebRequest Request;
  144.             internal object CoreResponse;
  145.            
  146.             internal RequestContext(HttpWebRequest request, object coreResponse)
  147.             {
  148.                 Request = request;
  149.                 CoreResponse = coreResponse;
  150.             }
  151.         }
  152.        
  153.         private List<RequestContext> m_Context;
  154.        
  155.         internal ConnectionReturnResult()
  156.         {
  157.             m_Context = new List<RequestContext>(5);
  158.         }
  159.        
  160.         internal ConnectionReturnResult(int capacity)
  161.         {
  162.             m_Context = new List<RequestContext>(capacity);
  163.         }
  164.        
  165.         internal bool IsNotEmpty {
  166.             get { return m_Context.Count != 0; }
  167.         }
  168.        
  169.         static internal void Add(ref ConnectionReturnResult returnResult, HttpWebRequest request, CoreResponseData coreResponseData)
  170.         {
  171.             if (coreResponseData == null)
  172.                 throw new InternalException();
  173.             //This may cause duplicate requests if we let it through in retail
  174.             if (returnResult == null) {
  175.                 returnResult = new ConnectionReturnResult();
  176.             }
  177.            
  178.             #if DEBUG
  179.             //This may cause duplicate requests if we let it through in retail but it's may be expensive to catch here
  180.             for (int j = 0; j < returnResult.m_Context.Count; ++j)
  181.                 if ((object)returnResult.m_Context[j].Request == (object)request)
  182.                     throw new InternalException();
  183.             #endif
  184.            
  185.             returnResult.m_Context.Add(new RequestContext(request, coreResponseData));
  186.         }
  187.        
  188.         static internal void AddExceptionRange(ref ConnectionReturnResult returnResult, HttpWebRequest[] requests, Exception exception)
  189.         {
  190.             AddExceptionRange(ref returnResult, requests, exception, exception);
  191.         }
  192.         static internal void AddExceptionRange(ref ConnectionReturnResult returnResult, HttpWebRequest[] requests, Exception exception, Exception firstRequestException)
  193.         {
  194.            
  195.             //This may cause duplicate requests if we let it through in retail
  196.             if (exception == null)
  197.                 throw new InternalException();
  198.            
  199.             if (returnResult == null) {
  200.                 returnResult = new ConnectionReturnResult(requests.Length);
  201.             }
  202.             // "abortedRequestExeption" is assigned to the "abortedRequest" or to the very first request if the latest is null
  203.             // Everyone else will get "exception"
  204.             for (int i = 0; i < requests.Length; ++i) {
  205.                 #if DEBUG
  206.                 //This may cause duplicate requests if we let it through in retail but it's may be expensive to catch here
  207.                 for (int j = 0; j < returnResult.m_Context.Count; ++j)
  208.                     if ((object)returnResult.m_Context[j].Request == (object)requests[i])
  209.                         throw new InternalException();
  210.                 #endif
  211.                
  212.                 if (i == 0)
  213.                     returnResult.m_Context.Add(new RequestContext(requests[i], firstRequestException));
  214.                 else
  215.                     returnResult.m_Context.Add(new RequestContext(requests[i], exception));
  216.             }
  217.         }
  218.        
  219.         static internal void SetResponses(ConnectionReturnResult returnResult)
  220.         {
  221.             if (returnResult == null) {
  222.                 return;
  223.             }
  224.            
  225.             GlobalLog.Print("ConnectionReturnResult#" + ValidationHelper.HashString(returnResult) + "::SetResponses() count=" + returnResult.m_Context.Count.ToString());
  226.             for (int i = 0; i < returnResult.m_Context.Count; i++) {
  227.                 try {
  228.                     HttpWebRequest request = returnResult.m_Context[i].Request;
  229.                     #if DEBUG
  230.                     CoreResponseData coreResponseData = returnResult.m_Context[i].CoreResponse as CoreResponseData;
  231.                     if (coreResponseData == null)
  232.                         GlobalLog.DebugRemoveRequest(request);
  233.                     #endif
  234.                     request.SetAndOrProcessResponse(returnResult.m_Context[i].CoreResponse);
  235.                 }
  236.                 catch (Exception e) {
  237.                     //ASYNCISSUE
  238.                     // on error, with more than one callback need to queue others off to another thread
  239.                    
  240.                     GlobalLog.Print("ConnectionReturnResult#" + ValidationHelper.HashString(returnResult) + "::Exception" + e);
  241.                     returnResult.m_Context.RemoveRange(0, (i + 1));
  242.                     if (returnResult.m_Context.Count > 0) {
  243.                         ThreadPool.UnsafeQueueUserWorkItem(s_InvokeConnectionCallback, returnResult);
  244.                     }
  245.                     throw;
  246.                 }
  247.             }
  248.            
  249.             returnResult.m_Context.Clear();
  250.         }
  251.        
  252.         private static void InvokeConnectionCallback(object objectReturnResult)
  253.         {
  254.             ConnectionReturnResult returnResult = (ConnectionReturnResult)objectReturnResult;
  255.             SetResponses(returnResult);
  256.         }
  257.     }
  258.    
  259.     //
  260.     // Connection - this is the Connection used to parse
  261.     // server responses, queue requests, and pipeline requests
  262.     //
  263.     internal class Connection : PooledStream
  264.     {
  265.        
  266.         //
  267.         // thread statics - these values must be per thread, because
  268.         // other requests and operations can take place concurrently on this Connection.
  269.         // Our concern is to make sure that a nested call does not get confused with an
  270.         // operation on another thread. Parameter passing cannot be used, because
  271.         // the call stack may exit and then reenter the same Connection object.
  272.         //
  273.         [ThreadStatic()]
  274.         private static int t_SyncReadNesting;
  275.        
  276.         private const int CRLFSize = 2;
  277.         private const long c_InvalidContentLength = -2l;
  278.         //
  279.         // Little status line holder.
  280.         //
  281.         private class StatusLineValues
  282.         {
  283.             internal int MajorVersion;
  284.             internal int MinorVersion;
  285.             internal int StatusCode;
  286.             internal string StatusDescription;
  287.         }
  288.        
  289.         //
  290.         // class members
  291.         //
  292.         private WebExceptionStatus m_Error;
  293.         internal Exception m_InnerException;
  294.        
  295.        
  296.         internal int m_IISVersion = -1;
  297.         //-1 means unread
  298.         private byte[] m_ReadBuffer;
  299.         private int m_BytesRead;
  300.         private int m_BytesScanned;
  301.         private int m_TotalResponseHeadersLength;
  302.         private int m_MaximumResponseHeadersLength;
  303.         private long m_MaximumUnauthorizedUploadLength;
  304.         private CoreResponseData m_ResponseData;
  305.         private ReadState m_ReadState;
  306.         private StatusLineValues m_StatusLineValues;
  307.         private int m_StatusState;
  308.         private ArrayList m_WaitList;
  309.         private ArrayList m_WriteList;
  310.         private IAsyncResult m_LastAsyncResult;
  311.         private TimerThread.Timer m_RecycleTimer;
  312.         private WebParseError m_ParseError;
  313.         private bool m_AtLeastOneResponseReceived;
  314.        
  315.         private static readonly WaitCallback m_PostReceiveDelegate = new WaitCallback(PostReceiveWrapper);
  316.         private static readonly AsyncCallback m_ReadCallback = new AsyncCallback(ReadCallbackWrapper);
  317.         private static readonly AsyncCallback m_TunnelCallback = new AsyncCallback(TunnelThroughProxyWrapper);
  318.         private static byte[] s_NullBuffer = new byte[0];
  319.        
  320.         //
  321.         // Abort handling variables. When trying to abort the
  322.         // connection, we set Aborted = true, and close m_AbortSocket
  323.         // if its non-null. m_AbortDelegate, is returned to every
  324.         // request from our SubmitRequest method. Calling m_AbortDelegate
  325.         // drives us into Abort mode.
  326.         //
  327.         private HttpAbortDelegate m_AbortDelegate;
  328.         private ConnectionGroup m_ConnectionGroup;
  329.        
  330.         private UnlockConnectionDelegate m_ConnectionUnlock;
  331.        
  332.         //
  333.         // ReadDone and m_Write - no two vars are so complicated,
  334.         // as these two. Used for m_WriteList managment, most be under crit
  335.         // section when accessing.
  336.         //
  337.         // ReadDone tracks the item at the end or
  338.         // just recenlty removed from the m_WriteList. While a
  339.         // pending BeginRead is in place, we need this to be false, in
  340.         // order to indicate to tell the WriteDone callback, that we can
  341.         // handle errors/resets. The only exception is when the m_WriteList
  342.         // is empty, and there are no outstanding requests, then all it can
  343.         // be true.
  344.         //
  345.         // WriteDone tracks the item just added at the begining of the m_WriteList.
  346.         // this needs to be false while we about to write something, but have not
  347.         // yet begin or finished the write. Upon completion, its set to true,
  348.         // so that DoneReading/ReadStartNextRequest can close the socket, without fear
  349.         // of a errand writer still banging away on another thread.
  350.         //
  351.        
  352.         private DateTime m_IdleSinceUtc;
  353.         private HttpWebRequest m_LockedRequest;
  354.         private HttpWebRequest m_CurrentRequest;
  355.         // This is the request whose response is being parsed, same as WriteList[0] but could be different if request was aborted.
  356.         private bool m_CanPipeline;
  357.         private bool m_Free = true;
  358.         private bool m_Idle = true;
  359.         private bool m_KeepAlive = true;
  360.         private bool m_Pipelining;
  361.         private bool m_ReadDone;
  362.         private bool m_WriteDone;
  363.         private bool m_RemovedFromConnectionList;
  364.        
  365.         // Pipeline Throttling: m_IsPipelinePaused==true when we stopped and false when it's ok to add to the pipeline.
  366.         private bool m_IsPipelinePaused;
  367.         private static int s_MaxPipelinedCount = 10;
  368.         private static int s_MinPipelinedCount = 5;
  369.        
  370.        
  371.         internal override ServicePoint ServicePoint {
  372.             get { return ConnectionGroup.ServicePoint; }
  373.         }
  374.        
  375.         private ConnectionGroup ConnectionGroup {
  376.             get { return m_ConnectionGroup; }
  377.         }
  378.        
  379.         //
  380.         // LockedRequest is the request that needs exclusive access to this connection
  381.         // the ConnectionGroup should proctect the Connection object from any new
  382.         // Requests being queued, until this m_LockedRequest is finished.
  383.         //
  384.         internal HttpWebRequest LockedRequest {
  385.             get { return m_LockedRequest; }
  386.             set {
  387.                 HttpWebRequest myLock = m_LockedRequest;
  388.                
  389.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::LockedRequest_set() old#" + ((myLock != null) ? myLock.GetHashCode().ToString() : "null") + " new#" + ((value != null) ? value.GetHashCode().ToString() : "null"));
  390.                
  391.                 if ((object)value == (object)myLock) {
  392.                     if (value != null && (object)value.UnlockConnectionDelegate != (object)m_ConnectionUnlock) {
  393.                         throw new InternalException();
  394.                     }
  395.                     return;
  396.                 }
  397.                
  398.                 object myDelegate = myLock == null ? null : myLock.UnlockConnectionDelegate;
  399.                 if (myDelegate != null && (value != null || (object)m_ConnectionUnlock != (object)myDelegate))
  400.                     throw new InternalException();
  401.                
  402.                 if (value == null) {
  403.                     m_LockedRequest = null;
  404.                     myLock.UnlockConnectionDelegate = null;
  405.                     return;
  406.                 }
  407.                
  408.                 UnlockConnectionDelegate chkDelegate = value.UnlockConnectionDelegate;
  409.                 //
  410.                 // If "value" request was already locking a connection that is not "this", unlock that other connection
  411.                 //
  412.                 if ((object)chkDelegate != null) {
  413.                     if ((object)chkDelegate == (object)m_ConnectionUnlock)
  414.                         throw new InternalException();
  415.                    
  416.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::LockedRequest_set() Unlocking old request Connection");
  417.                     chkDelegate();
  418.                 }
  419.                
  420.                 value.UnlockConnectionDelegate = m_ConnectionUnlock;
  421.                 m_LockedRequest = value;
  422.             }
  423.         }
  424.        
  425.        
  426.         /// <devdoc>
  427.         /// <para>
  428.         /// Delegate called when the request is finished using this Connection
  429.         /// exclusively. Called in Abort cases and after NTLM authenticaiton completes.
  430.         /// </para>
  431.         /// </devdoc>
  432.         private void UnlockRequest()
  433.         {
  434.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::UnlockRequest() LockedRequest#" + ValidationHelper.HashString(LockedRequest));
  435.             LockedRequest = null;
  436.            
  437.             if (ConnectionGroup != null) {
  438.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::UnlockRequest() - forcing call to ConnectionGoneIdle()");
  439.                 ConnectionGroup.ConnectionGoneIdle();
  440.             }
  441.         }
  442.        
  443.        
  444.        
  445.         internal Connection(ConnectionGroup connectionGroup) : base(null)
  446.         {
  447.             //
  448.             // add this Connection to the pool in the connection group,
  449.             // keep a weak reference to it
  450.             //
  451.             m_MaximumUnauthorizedUploadLength = SettingsSectionInternal.Section.MaximumUnauthorizedUploadLength;
  452.             if (m_MaximumUnauthorizedUploadLength > 0) {
  453.                 m_MaximumUnauthorizedUploadLength *= 1024;
  454.             }
  455.             m_ResponseData = new CoreResponseData();
  456.             m_ConnectionGroup = connectionGroup;
  457.             m_ReadBuffer = new byte[4096];
  458.             // Using a fixed 4k read buffer.
  459.             m_ReadState = ReadState.Start;
  460.             m_WaitList = new ArrayList();
  461.             m_WriteList = new ArrayList();
  462.             m_AbortDelegate = new HttpAbortDelegate(AbortOrDisassociate);
  463.             m_ConnectionUnlock = new UnlockConnectionDelegate(UnlockRequest);
  464.            
  465.             // for status line parsing
  466.             m_StatusLineValues = new StatusLineValues();
  467.             m_RecycleTimer = ConnectionGroup.ServicePoint.ConnectionLeaseTimerQueue.CreateTimer();
  468.             // the following line must be the last line of the constructor
  469.             ConnectionGroup.Associate(this);
  470.             m_ReadDone = true;
  471.             m_WriteDone = true;
  472.             m_Error = WebExceptionStatus.Success;
  473.         }
  474.        
  475.         ~Connection()
  476.         {
  477.             #if DEBUG
  478.             GlobalLog.SetThreadSource(ThreadKinds.Finalization);
  479.             using (GlobalLog.SetThreadKind(ThreadKinds.System | ThreadKinds.Async)) {
  480.                 #endif
  481.                
  482.                 try {
  483.                     AbortSocket(true);
  484.                 }
  485.                 catch (SocketException) {
  486.                 }
  487.                 catch (ObjectDisposedException) {
  488.                 }
  489.                
  490.                 #if DEBUG
  491.             }
  492.             #endif
  493.         }
  494.        
  495.         internal int BusyCount {
  496.             get { return (m_ReadDone ? 0 : 1) + 2 * (m_WaitList.Count + m_WriteList.Count); }
  497.         }
  498.        
  499.         internal int IISVersion {
  500.             get { return m_IISVersion; }
  501.         }
  502.        
  503.         internal bool AtLeastOneResponseReceived {
  504.             get { return m_AtLeastOneResponseReceived; }
  505.         }
  506.        
  507. /*++
  508.             SubmitRequest      - Submit a request for sending.
  509.             The core submit handler. This is called when a request needs to be
  510.             submitted to the network. This routine is asynchronous; the caller
  511.             passes in an HttpSubmitDelegate that we invoke when the caller
  512.             can use the underlying network. The delegate is invoked with the
  513.             stream that it can right to.
  514.             On the Sync path, we work by attempting to gain control of the Connection
  515.             for writing and reading.  If some other thread is using the Connection,
  516.             We wait inside of a LazyAsyncResult until it is availble.
  517.             Input:
  518.                     request                - request that's being submitted.
  519.                     SubmitDelegate          - Delegate to be invoked.
  520.             Returns:
  521.                     true when the request was correctly submitted
  522.         --*/       
  523.         // userReqeustThread says whether we can post IO from this thread or not.
  524.         internal bool SubmitRequest(HttpWebRequest request)
  525.         {
  526.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest", "request#" + ValidationHelper.HashString(request));
  527.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest");
  528.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest() Free:" + m_Free.ToString() + " m_WaitList.Count:" + m_WaitList.Count.ToString());
  529.            
  530.             TriState startRequestResult = TriState.Unspecified;
  531.             ConnectionReturnResult returnResult = null;
  532.             bool expiredIdleConnection = false;
  533.            
  534.             // See if the connection is free, and if the underlying socket or
  535.             // stream is set up. If it is, we can assign this connection to the
  536.             // request right now. Otherwise we'll have to put this request on
  537.             // on the wait list until it its turn.
  538.            
  539.             lock (this) {
  540.                 request.AbortDelegate = m_AbortDelegate;
  541.                
  542.                 if (request.Aborted) {
  543.                     // Note that request is not on the connection list yet and Abort() will push the response on the request
  544.                     GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest - (Request was aborted before being submitted)", true);
  545.                     return true;
  546.                 }
  547.                 //
  548.                 // There is a race condition between FindConnection and PrepareCloseConnectionSocket
  549.                 // Some request may already try to submit themselves while the connection is dying.
  550.                 //
  551.                 // Retry if that's the case
  552.                 //
  553.                 if (!CanBePooled) {
  554.                     GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest", "false - can't be pooled");
  555.                     return false;
  556.                 }
  557.                
  558.                 // See if our timer still matches the SerivcePoint. If not, get rid of it.
  559.                 if (m_RecycleTimer.Duration != ServicePoint.ConnectionLeaseTimerQueue.Duration) {
  560.                     m_RecycleTimer.Cancel();
  561.                     m_RecycleTimer = ServicePoint.ConnectionLeaseTimerQueue.CreateTimer();
  562.                 }
  563.                
  564.                 if (m_RecycleTimer.HasExpired) {
  565.                     request.KeepAlive = false;
  566.                 }
  567.                
  568.                 //
  569.                 // If the connection has already been locked by another request, then
  570.                 // we fail the submission on this Connection.
  571.                 //
  572.                
  573.                 if (LockedRequest != null && LockedRequest != request) {
  574.                     GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest", "false");
  575.                     return false;
  576.                 }
  577.                
  578.                
  579.                 //free means no one in the wait list. We should only add a request
  580.                 //if the request can pipeline, or pipelining isn't available
  581.                
  582.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest WriteDone:" + m_WriteDone.ToString() + ", ReadDone:" + m_ReadDone.ToString() + ", m_WriteList.Count:" + m_WriteList.Count.ToString());
  583.                
  584.                 if (m_Free && m_WriteDone && (m_WriteList.Count == 0 || (request.Pipelined && !request.RequireBody && m_CanPipeline && m_Pipelining && !m_IsPipelinePaused))) {
  585.                    
  586.                     // Connection is free. Mark it as busy and see if underlying
  587.                     // socket is up.
  588.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest - Free ");
  589.                     m_Free = false;
  590.                    
  591.                     startRequestResult = StartRequest(request);
  592.                     if (startRequestResult == TriState.Unspecified) {
  593.                         expiredIdleConnection = true;
  594.                         PrepareCloseConnectionSocket(ref returnResult);
  595.                         // Hard Close the socket.
  596.                         Close(0);
  597.                     }
  598.                 }
  599.                 else {
  600.                     m_WaitList.Add(request);
  601.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest - Request added to WaitList#" + ValidationHelper.HashString(request));
  602.                    
  603.                     CheckNonIdle();
  604.                 }
  605.             }
  606.            
  607.             if (expiredIdleConnection) {
  608.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest(), expired idle connection", false);
  609.                 ConnectionReturnResult.SetResponses(returnResult);
  610.                 return false;
  611.             }
  612.            
  613.             GlobalLog.DebugAddRequest(request, this, 0);
  614.             if (Logging.On)
  615.                 Logging.Associate(Logging.Web, this, request);
  616.            
  617.             if (startRequestResult != TriState.Unspecified) {
  618.                 CompleteStartRequest(true, request, startRequestResult);
  619.             }
  620.             // On Sync, we wait for the Connection to be come availble here,
  621.             if (!request.Async) {
  622.                 object responseObject = request.ConnectionAsyncResult.InternalWaitForCompletion();
  623.                 ConnectStream writeStream = responseObject as ConnectStream;
  624.                 AsyncTriState triStateAsync = null;
  625.                 if (writeStream == null)
  626.                     triStateAsync = responseObject as AsyncTriState;
  627.                
  628.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest() Pipelining:" + m_Pipelining);
  629.                
  630.                 if (startRequestResult == TriState.Unspecified && triStateAsync != null) {
  631.                     // May need to recreate Connection here (i.e. call Socket.Connect)
  632.                     CompleteStartRequest(true, request, triStateAsync.Value);
  633.                 }
  634.                 else if (writeStream != null) {
  635.                     // return the Stream to the Request
  636.                     request.SetRequestSubmitDone(writeStream);
  637.                 }
  638.                 #if DEBUG
  639.                 else if (responseObject is Exception) {
  640.                     Exception exception = responseObject as Exception;
  641.                     WebException webException = responseObject as WebException;
  642.                     GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest (SYNC) - Error waiting for a connection: " + exception.Message, "Status:" + (webException == null ? exception.GetType().FullName : (webException.Status.ToString() + " Internal Status: " + webException.InternalStatus.ToString())));
  643.                     return true;
  644.                 }
  645.                 #endif
  646.             }
  647.            
  648.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::SubmitRequest", true);
  649.             return true;
  650.         }
  651.        
  652.         // Wrapper for TriState for marhshalling across Thread boundaries
  653.         private class AsyncTriState
  654.         {
  655.             public TriState Value;
  656.             public AsyncTriState(TriState newValue)
  657.             {
  658.                 Value = newValue;
  659.             }
  660.         }
  661.        
  662. /*++
  663.             StartRequest      - Start a request going.
  664.             Routine to start a request. Called when we know the connection is
  665.             free and we want to get a request going. This routine initializes
  666.             some state, adds the request to the write queue, and checks to
  667.             see whether or not the underlying connection is alive. If it's
  668.             not, it queues a request to get it going. If the connection
  669.             was alive we call the callback delegate of the request.
  670.             This routine MUST BE called with the critcal section held.
  671.             Input:
  672.                     request                - request that's being started.
  673.                     SubmitDelegate          - Delegate to be invoked.
  674.             Returns:
  675.                     True if request was started, false otherwise.
  676.         --*/       
  677.        
  678.         private TriState StartRequest(HttpWebRequest request)
  679.         {
  680.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::StartRequest", "HttpWebRequest#" + ValidationHelper.HashString(request) + " WriteDone:" + m_WriteDone + " ReadDone: " + m_ReadDone + " WaitList:" + m_WaitList.Count + " WriteList:" + m_WriteList.Count);
  681.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::StartRequest");
  682.            
  683.             if (m_WriteList.Count == 0 && ServicePoint.MaxIdleTime != -1 && m_IdleSinceUtc != DateTime.MinValue && m_IdleSinceUtc + TimeSpan.FromMilliseconds(ServicePoint.MaxIdleTime) < DateTime.UtcNow) {
  684.                 // This actually means that this connection was done with reading last response more than MaxIdle milliseconds ago.
  685.                 // We want to renew this connection in fear that the server has sent as a TCP FIN by closing an idle connection.
  686.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::StartRequest() Expired connection was idle for (sec) " + (int)((DateTime.UtcNow - m_IdleSinceUtc).TotalSeconds) + ", request will be retried: #" + ValidationHelper.HashString(request));
  687.                 return TriState.Unspecified;
  688.                 // The caller has close this one and find another Connection
  689.             }
  690.            
  691.             TriState needReConnect = TriState.False;
  692.             // Starting a request means the connection is not idle anymore
  693.             m_IdleSinceUtc = DateTime.MinValue;
  694.            
  695.             // Initialze state, and add the request to the write queue.
  696.            
  697.             //
  698.             // Note that m_Pipelining shold be only set here but the sanity check is made by the caller
  699.             // means if the caller has found that it is safe to pipeline the below result must be true as well
  700.             //
  701.             if (!m_IsPipelinePaused)
  702.                 m_IsPipelinePaused = m_WriteList.Count >= s_MaxPipelinedCount;
  703.            
  704.             m_Pipelining = m_CanPipeline && request.Pipelined && (!request.RequireBody);
  705.             m_KeepAlive &= (request.KeepAlive || request.NtlmKeepAlive);
  706.            
  707.             // start of write process, disable done-ness flag
  708.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::StartRequest() setting WriteDone:" + m_WriteDone.ToString() + " to false");
  709.             m_WriteDone = false;
  710.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::StartRequest() m_WriteList adding HttpWebRequest#" + ValidationHelper.HashString(request));
  711.             m_WriteList.Add(request);
  712.            
  713.             GlobalLog.Print(m_WriteList.Count + " requests queued");
  714.             CheckNonIdle();
  715.            
  716.             // with no network stream around, we will have to create one, therefore, we can't have
  717.             // the possiblity to even have a DoneReading().
  718.            
  719.             if (IsInitalizing)
  720.                 needReConnect = TriState.True;
  721.            
  722.            
  723.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::StartRequest", needReConnect.ToString());
  724.             return needReConnect;
  725.         }
  726.        
  727.         private void CompleteStartRequest(bool onSubmitThread, HttpWebRequest request, TriState needReConnect)
  728.         {
  729.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartRequest", ValidationHelper.HashString(request));
  730.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::CompleteStartRequest");
  731.            
  732.             if (needReConnect == TriState.True) {
  733.                 // Socket is not alive.
  734.                
  735.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartRequest() Queue StartConnection Delegate ");
  736.                 try {
  737.                     if (request.Async) {
  738.                         CompleteStartConnection(true, request);
  739.                     }
  740.                     else if (onSubmitThread) {
  741.                         CompleteStartConnection(false, request);
  742.                     }
  743.                     // else - fall through and wake up other thread
  744.                 }
  745.                 catch (Exception exception) {
  746.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartRequest(): exception: " + exception.ToString());
  747.                     if (NclUtilities.IsFatal(exception))
  748.                         throw;
  749.                     //
  750.                     // Should not be here because CompleteStartConnection and below tries to catch everything
  751.                     //
  752.                     GlobalLog.Assert(exception.ToString());
  753.                 }
  754.                
  755.                 // If neeeded wake up other thread where SubmitRequest was called
  756.                 if (!request.Async) {
  757.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartRequest() Invoking Async Result");
  758.                     request.ConnectionAsyncResult.InvokeCallback(new AsyncTriState(needReConnect));
  759.                 }
  760.                
  761.                
  762.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartRequest", "needReConnect");
  763.                 return;
  764.             }
  765.            
  766.            
  767.             //
  768.             // From now on the request.SetRequestSubmitDone must be called or it may hang
  769.             // For a sync request the write side reponse windowwas opened in HttpWebRequest.SubmitRequest
  770.             if (request.Async)
  771.                 request.OpenWriteSideResponseWindow();
  772.            
  773.            
  774.             ConnectStream writeStream = new ConnectStream(this, request);
  775.            
  776.             // Call the request to let them know that we have a write-stream, this might invoke Send() call
  777.             if (request.Async || onSubmitThread) {
  778.                 request.SetRequestSubmitDone(writeStream);
  779.             }
  780.             else {
  781.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartRequest() Invoking Async Result");
  782.                 request.ConnectionAsyncResult.InvokeCallback(writeStream);
  783.             }
  784.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartRequest");
  785.         }
  786.        
  787. /*++
  788.             CheckNextRequest
  789.             Gets the next request from the wait queue, if there is one.
  790.             Must be called with the crit sec held.
  791.         --*/       
  792.         private HttpWebRequest CheckNextRequest()
  793.         {
  794.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::CheckNextRequest");
  795.            
  796.             if (m_WaitList.Count == 0) {
  797.                 // We're free now, if we're not going to close the connection soon.
  798.                 m_Free = m_KeepAlive;
  799.                 return null;
  800.             }
  801.             if (!CanBePooled) {
  802.                 return null;
  803.             }
  804.             HttpWebRequest nextRequest = m_WaitList[0] as HttpWebRequest;
  805.            
  806.             if (m_IsPipelinePaused)
  807.                 m_IsPipelinePaused = m_WriteList.Count > s_MinPipelinedCount;
  808.            
  809.             if (!nextRequest.Pipelined || nextRequest.RequireBody || !m_CanPipeline || !m_Pipelining || m_IsPipelinePaused) {
  810.                 if (m_WriteList.Count != 0) {
  811.                     nextRequest = null;
  812.                 }
  813.             }
  814.             if (nextRequest != null) {
  815.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CheckNextRequest() Removing request#" + ValidationHelper.HashString(nextRequest) + " from m_WaitList. New Count:" + (m_WaitList.Count - 1).ToString());
  816.                 m_WaitList.RemoveAt(0);
  817.                 CheckIdle();
  818.             }
  819.             return nextRequest;
  820.         }
  821.        
  822.         private void CompleteStartConnection(bool async, HttpWebRequest httpWebRequest)
  823.         {
  824.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartConnection", "async:" + async.ToString() + " httpWebRequest:" + ValidationHelper.HashString(httpWebRequest));
  825.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::CompleteStartConnection");
  826.            
  827.             WebExceptionStatus ws = WebExceptionStatus.ConnectFailure;
  828.             m_InnerException = null;
  829.             bool success = true;
  830.            
  831.             try {
  832.                 #if DEBUG
  833.                 lock (this) {
  834.                     // m_WriteList can be empty if request got aborted. In that case no new requests can come in so it should remain zero.
  835.                     if (m_WriteList.Count != 0) {
  836.                         GlobalLog.Assert(m_WriteList.Count == 1, "Connection#{0}::CompleteStartConnection()|WriteList is not sized 1.", ValidationHelper.HashString(this));
  837.                         GlobalLog.Assert((m_WriteList[0] as HttpWebRequest) == httpWebRequest, "Connection#{0}::CompleteStartConnection()|Last request on write list does not match.", ValidationHelper.HashString(this));
  838.                     }
  839.                 }
  840.                 #endif
  841.                
  842.                 //
  843.                 // we will create a tunnel through a proxy then create
  844.                 // and connect the socket we will use for the connection
  845.                 // otherwise we will just create a socket and use it
  846.                 //
  847.                 if (httpWebRequest.Address.Scheme == Uri.UriSchemeHttps && ServicePoint.InternalProxyServicePoint) {
  848.                     if (!TunnelThroughProxy(ServicePoint.InternalAddress, httpWebRequest, async)) {
  849.                         ws = WebExceptionStatus.ConnectFailure;
  850.                         success = false;
  851.                     }
  852.                     if (async) {
  853.                         return;
  854.                     }
  855.                 }
  856.                 else {
  857.                     TimerThread.Timer timer = httpWebRequest.RequestTimer;
  858.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartConnection() Calling Activate(). TimeRemaining:" + (timer == null ? "<null timer>" : timer.TimeRemaining.ToString()));
  859.                    
  860.                     if (!Activate(httpWebRequest, async, ((timer != null) ? timer.TimeRemaining : 0), new GeneralAsyncDelegate(CompleteConnectionWrapper))) {
  861.                         return;
  862.                     }
  863.                 }
  864.             }
  865.             catch (Exception exception) {
  866.                 if (m_InnerException == null)
  867.                     m_InnerException = exception;
  868.                
  869.                 if (exception is WebException) {
  870.                     ws = ((WebException)exception).Status;
  871.                 }
  872.                 success = false;
  873.             }
  874.             if (!success) {
  875.                 ConnectionReturnResult returnResult = null;
  876.                 HandleError(false, false, ws, ref returnResult);
  877.                 ConnectionReturnResult.SetResponses(returnResult);
  878.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartConnection Failed to connect.");
  879.                 return;
  880.             }
  881.            
  882.             // Getting here means we connected synchronously. Continue with the next step.
  883.            
  884.             CompleteConnection(async, httpWebRequest);
  885.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::CompleteStartConnection");
  886.         }
  887.        
  888.         private void CompleteConnectionWrapper(object request, object state)
  889.         {
  890.             #if DEBUG
  891.             using (GlobalLog.SetThreadKind(ThreadKinds.System | ThreadKinds.Async)) {
  892.                 #endif
  893.                 GlobalLog.Enter("Connection#" + ValidationHelper.HashString(state) + "::CompleteConnectionWrapper", "request:" + ValidationHelper.HashString(request));
  894.                
  895.                 Exception stateException = state as Exception;
  896.                 if (stateException != null) {
  897.                     GlobalLog.Print("CompleteConnectionWrapper() Request#" + ValidationHelper.HashString(request) + " Connection is in error: " + stateException.ToString());
  898.                     ConnectionReturnResult returnResult = null;
  899.                    
  900.                     if (m_InnerException == null)
  901.                         m_InnerException = stateException;
  902.                    
  903.                     HandleError(false, false, WebExceptionStatus.ConnectFailure, ref returnResult);
  904.                     ConnectionReturnResult.SetResponses(returnResult);
  905.                 }
  906.                 CompleteConnection(true, (HttpWebRequest)request);
  907.                
  908.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(state) + "::CompleteConnectionWrapper" + (stateException == null ? string.Empty : " failed"));
  909.                 #if DEBUG
  910.             }
  911.             #endif
  912.         }
  913.        
  914.         private void CompleteConnection(bool async, HttpWebRequest request)
  915.         {
  916.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::CompleteConnection", "async:" + async.ToString() + " request:" + ValidationHelper.HashString(request));
  917.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::CompleteConnection");
  918.            
  919.             WebExceptionStatus ws = WebExceptionStatus.ConnectFailure;
  920.             //
  921.             // From now on the request.SetRequestSubmitDone must be called or it may hang
  922.             // For a sync request the write side reponse windowwas opened in HttpWebRequest.SubmitRequest
  923.             if (request.Async)
  924.                 request.OpenWriteSideResponseWindow();
  925.            
  926.             try {
  927.                 try {
  928.                 }
  929.                 finally {
  930.                     m_ReadState = ReadState.Start;
  931.                     ClearReaderState();
  932.                    
  933.                     request.SetRequestSubmitDone(new ConnectStream(this, request));
  934.                     ws = WebExceptionStatus.Success;
  935.                 }
  936.                
  937.             }
  938.             catch (Exception exception) {
  939.                 if (m_InnerException == null)
  940.                     m_InnerException = exception;
  941.                 WebException webException = exception as WebException;
  942.                 if (webException != null) {
  943.                     ws = webException.Status;
  944.                 }
  945.             }
  946.            
  947.             if (ws != WebExceptionStatus.Success) {
  948.                 ConnectionReturnResult returnResult = null;
  949.                 HandleError(false, false, ws, ref returnResult);
  950.                 ConnectionReturnResult.SetResponses(returnResult);
  951.                
  952.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::CompleteConnection", "on error");
  953.             }
  954.             else {
  955.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::CompleteConnection");
  956.             }
  957.         }
  958.        
  959.         private void InternalWriteStartNextRequest(HttpWebRequest request, ref bool calledCloseConnection, ref TriState startRequestResult, ref HttpWebRequest nextRequest, ref ConnectionReturnResult returnResult)
  960.         {
  961.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::InternalWriteStartNextRequest");
  962.            
  963.             lock (this) {
  964.                
  965.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest() setting WriteDone:" + m_WriteDone.ToString() + " to true");
  966.                 m_WriteDone = true;
  967.                
  968.                 //
  969.                 // If we're not doing keep alive, and the read on this connection
  970.                 // has already completed, now is the time to close the
  971.                 // connection.
  972.                 //
  973.                 //need to wait for read to set the error
  974.                 if (!m_KeepAlive || m_Error != WebExceptionStatus.Success || !CanBePooled) {
  975.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest() m_WriteList.Count:" + m_WriteList.Count);
  976.                     if (m_ReadDone) {
  977.                         // We could be closing because of an unexpected keep-alive
  978.                         // failure, ie we pipelined a few requests and in the middle
  979.                         // the remote server stopped doing keep alive. In this
  980.                         // case m_Error could be success, which would be misleading.
  981.                         // So in that case we'll set it to connection closed.
  982.                        
  983.                         if (m_Error == WebExceptionStatus.Success) {
  984.                             // Only reason we could have gotten here is because
  985.                             // we're not keeping the connection alive.
  986.                             m_Error = WebExceptionStatus.KeepAliveFailure;
  987.                         }
  988.                        
  989.                         // PrepareCloseConnectionSocket is called with the critical section
  990.                         // held. Note that we know since it's not a keep-alive
  991.                         // connection the read half wouldn't have posted a receive
  992.                         // for this connection, so it's OK to call PrepareCloseConnectionSocket now.
  993.                         PrepareCloseConnectionSocket(ref returnResult);
  994.                         calledCloseConnection = true;
  995.                         Close();
  996.                     }
  997.                     else {
  998.                         if (m_Error != WebExceptionStatus.Success) {
  999.                             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest() a Failure, m_Error = " + m_Error.ToString());
  1000.                         }
  1001.                     }
  1002.                 }
  1003.                 else {
  1004.                     // If we're pipelining, we get get the next request going
  1005.                     // as soon as the write is done. Otherwise we have to wait
  1006.                     // until both read and write are done.
  1007.                    
  1008.                    
  1009.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest() Non-Error m_WriteList.Count:" + m_WriteList.Count + " m_WaitList.Count:" + m_WaitList.Count);
  1010.                    
  1011.                     if (m_Pipelining || m_ReadDone) {
  1012.                         nextRequest = CheckNextRequest();
  1013.                     }
  1014.                     if (nextRequest != null) {
  1015.                         startRequestResult = StartRequest(nextRequest);
  1016.                     }
  1017.                 }
  1018.             }
  1019.             // lock
  1020.         }
  1021.        
  1022.         internal void WriteStartNextRequest(HttpWebRequest request, ref ConnectionReturnResult returnResult)
  1023.         {
  1024.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest" + " WriteDone:" + m_WriteDone + " ReadDone:" + m_ReadDone + " WaitList:" + m_WaitList.Count + " WriteList:" + m_WriteList.Count);
  1025.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest");
  1026.            
  1027.             TriState startRequestResult = TriState.Unspecified;
  1028.             HttpWebRequest nextRequest = null;
  1029.             bool calledCloseConnection = false;
  1030.            
  1031.             InternalWriteStartNextRequest(request, ref calledCloseConnection, ref startRequestResult, ref nextRequest, ref returnResult);
  1032.            
  1033.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest: Pipelining:" + m_Pipelining + " nextRequest#" + ValidationHelper.HashString(nextRequest));
  1034.            
  1035.             if (!calledCloseConnection && startRequestResult != TriState.Unspecified) {
  1036.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest calling CompleteStartRequest");
  1037.                 CompleteStartRequest(false, nextRequest, startRequestResult);
  1038.             }
  1039.            
  1040.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::WriteStartNextRequest");
  1041.         }
  1042.        
  1043.        
  1044. /*++
  1045.             ReadStartNextRequest
  1046.             This method is called by a stream interface when it's done reading.
  1047.             We might possible free up the connection for another request here.
  1048.             Called when we think we might need to start another request because
  1049.             a read completed.
  1050.         --*/       
  1051.         internal void ReadStartNextRequest(WebRequest currentRequest, ref ConnectionReturnResult returnResult)
  1052.         {
  1053.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest" + " WriteDone:" + m_WriteDone + " ReadDone:" + m_ReadDone + " WaitList:" + m_WaitList.Count + " WriteList:" + m_WriteList.Count);
  1054.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest");
  1055.            
  1056.             HttpWebRequest nextRequest = null;
  1057.             TriState startRequestResult = TriState.Unspecified;
  1058.             bool calledCloseConnection = false;
  1059.             bool mustExit = false;
  1060.            
  1061.             try {
  1062.                 lock (this) {
  1063.                     if (m_WriteList.Count > 0 && (object)currentRequest == m_WriteList[0]) {
  1064.                         // advance back to state 0
  1065.                         m_ReadState = ReadState.Start;
  1066.                         m_WriteList.RemoveAt(0);
  1067.                        
  1068.                         // Must reset ConnectStream here to prevent a leak through the stream of the last request on each connection.
  1069.                         m_ResponseData.m_ConnectStream = null;
  1070.                        
  1071.                         GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest() Removed request#" + ValidationHelper.HashString(currentRequest) + " from m_WriteList. New m_WriteList.Count:" + m_WriteList.Count.ToString());
  1072.                     }
  1073.                     else {
  1074.                         GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest() The request#" + ValidationHelper.HashString(currentRequest) + " was disassociated so do nothing. m_WriteList.Count:" + m_WriteList.Count.ToString());
  1075.                         mustExit = true;
  1076.                         ;
  1077.                     }
  1078.                    
  1079.                     //
  1080.                     // Since this is called after we're done reading the current
  1081.                     // request, if we're not doing keepalive and we're done
  1082.                     // writing we can close the connection now.
  1083.                     //
  1084.                     if (!mustExit) {
  1085.                         //
  1086.                         // m_ReadDone==true is implied because we just finished a request but really the value must still be false here
  1087.                         //
  1088.                         if (m_ReadDone)
  1089.                             throw new InternalException();
  1090.                         // other requests may already started reading on this connection, need a QFE
  1091.                         if (!m_KeepAlive || m_Error != WebExceptionStatus.Success || !CanBePooled) {
  1092.                             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest() KeepAlive:" + m_KeepAlive + " WriteDone:" + m_WriteDone);
  1093.                             // Finished one request and connection is closing.
  1094.                             // We will not read from this connection so set readDone = true
  1095.                             m_ReadDone = true;
  1096.                             if (m_WriteDone) {
  1097.                                
  1098.                                 // We could be closing because of an unexpected keep-alive
  1099.                                 // failure, ie we pipelined a few requests and in the middle
  1100.                                 // the remote server stopped doing keep alive. In this
  1101.                                 // case m_Error could be success, which would be misleading.
  1102.                                 // So in that case we'll set it to KeepAliveFailure.
  1103.                                
  1104.                                 if (m_Error == WebExceptionStatus.Success) {
  1105.                                     // Only reason we could have gotten here is because
  1106.                                     // we're not keeping the connection alive.
  1107.                                     m_Error = WebExceptionStatus.KeepAliveFailure;
  1108.                                 }
  1109.                                
  1110.                                 // PrepareCloseConnectionSocket has to be called with the critical section held.
  1111.                                 PrepareCloseConnectionSocket(ref returnResult);
  1112.                                 calledCloseConnection = true;
  1113.                                 Close();
  1114.                             }
  1115.                         }
  1116.                         else {
  1117.                             // We try to sort out KeepAliveFailure thing (search by context)
  1118.                             m_AtLeastOneResponseReceived = true;
  1119.                            
  1120.                             if (m_WriteList.Count != 0) {
  1121.                                 // If a *pipelined* request that is being submitted has finished with the headers, post a receive
  1122.                                 nextRequest = m_WriteList[0] as HttpWebRequest;
  1123.                                 // If the active request has not finished its headers we can set m_ReadDone = true
  1124.                                 // and that will be changed when said request will call CheckStartReceive
  1125.                                 if (!nextRequest.HeadersCompleted) {
  1126.                                     nextRequest = null;
  1127.                                     m_ReadDone = true;
  1128.                                 }
  1129.                             }
  1130.                             // If there are no requests left to write (means pipeline),
  1131.                             // we can get the next request from wait list going now.
  1132.                             else {
  1133.                                 m_ReadDone = true;
  1134.                                
  1135.                                 // Sometime we get a response before completing the body in which case
  1136.                                 // we defer next request to WriteStartNextRequest
  1137.                                 if (m_WriteDone) {
  1138.                                     nextRequest = CheckNextRequest();
  1139.                                    
  1140.                                     if (nextRequest != null) {
  1141.                                         // We cannot have HeadersCompleted on the request that was not placed yet on the write list
  1142.                                         if (nextRequest.HeadersCompleted)
  1143.                                             throw new InternalException();
  1144.                                        
  1145.                                         startRequestResult = StartRequest(nextRequest);
  1146.                                     }
  1147.                                     else {
  1148.                                         //There are no other requests to process, so make connection avaliable for all
  1149.                                         m_Free = true;
  1150.                                     }
  1151.                                 }
  1152.                             }
  1153.                         }
  1154.                     }
  1155.                 }
  1156.             }
  1157.             finally {
  1158.                 CheckIdle();
  1159.                 //set result here to prevent nesting of readstartnextrequest.
  1160.                 if (returnResult != null) {
  1161.                     ConnectionReturnResult.SetResponses(returnResult);
  1162.                 }
  1163.             }
  1164.            
  1165.             if (!mustExit && !calledCloseConnection) {
  1166.                 if (startRequestResult != TriState.Unspecified) {
  1167.                     CompleteStartRequest(false, nextRequest, startRequestResult);
  1168.                 }
  1169.                 else if (nextRequest != null) {
  1170.                     // Handling receive, note that is for pipelinning case only !
  1171.                     if (!nextRequest.Async) {
  1172.                         nextRequest.ConnectionReaderAsyncResult.InvokeCallback();
  1173.                     }
  1174.                     else {
  1175.                         if (m_BytesScanned < m_BytesRead) {
  1176.                             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest() Calling ReadComplete, bytes unparsed = " + (m_BytesRead - m_BytesScanned));
  1177.                             ReadComplete(0, WebExceptionStatus.Success);
  1178.                         }
  1179.                         else if (Thread.CurrentThread.IsThreadPoolThread) {
  1180.                             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest() Calling PostReceive().");
  1181.                             PostReceive();
  1182.                         }
  1183.                         else {
  1184.                             // Offload to the threadpool to protect against the case where one request's thread posts IO that another request
  1185.                             // depends on, but the first thread dies in the mean time.
  1186.                             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest() ThreadPool.UnsafeQueueUserWorkItem(m_PostReceiveDelegate, this)");
  1187.                             ThreadPool.UnsafeQueueUserWorkItem(m_PostReceiveDelegate, this);
  1188.                         }
  1189.                     }
  1190.                 }
  1191.             }
  1192.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ReadStartNextRequest");
  1193.         }
  1194.        
  1195.         //
  1196.         //
  1197.         //
  1198.         internal void CheckStartReceive(HttpWebRequest request)
  1199.         {
  1200.             lock (this) {
  1201.                 request.HeadersCompleted = true;
  1202.                 if (m_WriteList.Count == 0) {
  1203.                     // aborted request, was already dispatched.
  1204.                     // Note it could have been aborted softly if not the first one in the pipeline
  1205.                     return;
  1206.                 }
  1207.                
  1208.                 // Note we do NOT allow receive if pipelining and the passed request is not the first one on the write queue
  1209.                 if (!m_ReadDone || m_WriteList[0] != (object)request) {
  1210.                     // ReadStartNextRequest should take care of these cases
  1211.                     return;
  1212.                 }
  1213.                 // Start a receive
  1214.                 m_ReadDone = false;
  1215.             }
  1216.            
  1217.             if (!request.Async) {
  1218.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CheckStartReceive() SYNC request, calling ConnectionReaderAsyncResult.InvokeCallback()");
  1219.                 request.ConnectionReaderAsyncResult.InvokeCallback();
  1220.             }
  1221.             else if (m_BytesScanned < m_BytesRead) {
  1222.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CheckStartReceive() Calling ReadComplete, bytes unparsed = " + (m_BytesRead - m_BytesScanned));
  1223.                 ReadComplete(0, WebExceptionStatus.Success);
  1224.             }
  1225.             else if (Thread.CurrentThread.IsThreadPoolThread) {
  1226.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CheckStartReceive() Calling PostReceive().");
  1227.                 PostReceive();
  1228.             }
  1229.             else {
  1230.                 // Offload to the threadpool to protect against the case where one request's thread posts IO that another request
  1231.                 // depends on, but the first thread dies in the mean time.
  1232.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CheckStartReceive() ThreadPool.UnsafeQueueUserWorkItem(m_PostReceiveDelegate, this)");
  1233.                 ThreadPool.UnsafeQueueUserWorkItem(m_PostReceiveDelegate, this);
  1234.             }
  1235.         }
  1236.        
  1237. /*++
  1238.         Routine Description:
  1239.           Clears out common member vars used for Status Line parsing
  1240.         Arguments:
  1241.           None.
  1242.         Return Value:
  1243.           None.
  1244.         --*/       
  1245.        
  1246.         private void InitializeParseStatusLine()
  1247.         {
  1248.             m_StatusState = BeforeVersionNumbers;
  1249.             m_StatusLineValues.MajorVersion = 0;
  1250.             m_StatusLineValues.MinorVersion = 0;
  1251.             m_StatusLineValues.StatusCode = 0;
  1252.             m_StatusLineValues.StatusDescription = null;
  1253.         }
  1254.        
  1255. /*++
  1256.         Routine Description:
  1257.           Performs status line parsing on incomming server responses
  1258.         Arguments:
  1259.           statusLine - status line that we wish to parse
  1260.           statusLineLength - length of the array
  1261.           statusLineInts - array of ints contanes result
  1262.           statusDescription - string with discription
  1263.           statusStatus    - state stored between parse attempts
  1264.         Return Value:
  1265.           bool - Success true/false
  1266.         --*/       
  1267.        
  1268.         private const int BeforeVersionNumbers = 0;
  1269.         private const int MajorVersionNumber = 1;
  1270.         private const int MinorVersionNumber = 2;
  1271.         private const int StatusCodeNumber = 3;
  1272.         private const int AfterStatusCode = 4;
  1273.         private const int AfterCarriageReturn = 5;
  1274.        
  1275.         private const string BeforeVersionNumberBytes = "HTTP/";
  1276.        
  1277.         private DataParseStatus ParseStatusLine(byte[] statusLine, int statusLineLength, ref int bytesParsed, ref int[] statusLineInts, ref string statusDescription, ref int statusState, ref WebParseError parseError)
  1278.         {
  1279.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::ParseStatusLine", statusLineLength.ToString(NumberFormatInfo.InvariantInfo) + ", " + bytesParsed.ToString(NumberFormatInfo.InvariantInfo) + ", " + statusState.ToString(NumberFormatInfo.InvariantInfo));
  1280.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::ParseStatusLine");
  1281.             GlobalLog.Assert((statusLineLength - bytesParsed) >= 0, "Connection#{0}::ParseStatusLine()|(statusLineLength - bytesParsed) < 0", ValidationHelper.HashString(this));
  1282.             //GlobalLog.Dump(statusLine, bytesParsed, statusLineLength);
  1283.            
  1284.             DataParseStatus parseStatus = DataParseStatus.Done;
  1285.             int statusLineSize = 0;
  1286.             int startIndexStatusDescription = -1;
  1287.             int lastUnSpaceIndex = 0;
  1288.            
  1289.             //
  1290.             // While walking the Status Line looking for terminating \r\n,
  1291.             // we extract the Major.Minor Versions and Status Code in that order.
  1292.             // text and spaces will lie between/before/after the three numbers
  1293.             // but the idea is to remember which number we're calculating based on a numeric state
  1294.             // If all goes well the loop will churn out an array with the 3 numbers plugged in as DWORDs
  1295.             //
  1296.            
  1297.             while ((bytesParsed < statusLineLength) && (statusLine[bytesParsed] != '\r') && (statusLine[bytesParsed] != '\n')) {
  1298.                
  1299.                 // below should be wrapped in while (response[i] != ' ') to be more robust???
  1300.                 switch (statusState) {
  1301.                     case BeforeVersionNumbers:
  1302.                         if (statusLine[bytesParsed] == '/') {
  1303.                             //INET_ASSERT(statusState == BeforeVersionNumbers);
  1304.                             statusState++;
  1305.                             // = MajorVersionNumber
  1306.                         }
  1307.                         else if (statusLine[bytesParsed] == ' ') {
  1308.                             statusState = StatusCodeNumber;
  1309.                         }
  1310.                        
  1311.                         break;
  1312.                     case MajorVersionNumber:
  1313.                        
  1314.                        
  1315.                         if (statusLine[bytesParsed] == '.') {
  1316.                             //INET_ASSERT(statusState == MajorVersionNumber);
  1317.                             statusState++;
  1318.                             // = MinorVersionNumber
  1319.                             break;
  1320.                         }
  1321.                         // fall through
  1322.                         goto case MinorVersionNumber;
  1323.                         break;
  1324.                     case MinorVersionNumber:
  1325.                        
  1326.                        
  1327.                         if (statusLine[bytesParsed] == ' ') {
  1328.                             //INET_ASSERT(statusState == MinorVersionNumber);
  1329.                             statusState++;
  1330.                             // = StatusCodeNumber
  1331.                             break;
  1332.                         }
  1333.                         // fall through
  1334.                         goto case StatusCodeNumber;
  1335.                         break;
  1336.                     case StatusCodeNumber:
  1337.                        
  1338.                        
  1339.                         if (Char.IsDigit((char)statusLine[bytesParsed])) {
  1340.                             int val = statusLine[bytesParsed] - '0';
  1341.                             statusLineInts[statusState] = statusLineInts[statusState] * 10 + val;
  1342.                         }
  1343.                         else if (statusLineInts[StatusCodeNumber] > 0) {
  1344.                             //
  1345.                             // we eat spaces before status code is found,
  1346.                             // once we have the status code we can go on to the next
  1347.                             // state on the next non-digit. This is done
  1348.                             // to cover cases with several spaces between version
  1349.                             // and the status code number.
  1350.                             //
  1351.                            
  1352.                             statusState++;
  1353.                             // = AfterStatusCode
  1354.                             break;
  1355.                         }
  1356.                         else if (!Char.IsWhiteSpace((char)statusLine[bytesParsed])) {
  1357.                             statusLineInts[statusState] = (int)-1;
  1358.                         }
  1359.                        
  1360.                         break;
  1361.                     case AfterStatusCode:
  1362.                        
  1363.                         if (statusLine[bytesParsed] != ' ') {
  1364.                             lastUnSpaceIndex = bytesParsed;
  1365.                             if (startIndexStatusDescription == -1) {
  1366.                                 startIndexStatusDescription = bytesParsed;
  1367.                             }
  1368.                         }
  1369.                         break;
  1370.                    
  1371.                 }
  1372.                 ++bytesParsed;
  1373.                 if (m_MaximumResponseHeadersLength >= 0 && ++m_TotalResponseHeadersLength >= m_MaximumResponseHeadersLength) {
  1374.                     parseStatus = DataParseStatus.DataTooBig;
  1375.                     goto quit;
  1376.                 }
  1377.             }
  1378.            
  1379.             statusLineSize = bytesParsed;
  1380.            
  1381.             // add to Description if already partialy parsed
  1382.             if (startIndexStatusDescription != -1) {
  1383.                 statusDescription += WebHeaderCollection.HeaderEncoding.GetString(statusLine, startIndexStatusDescription, lastUnSpaceIndex - startIndexStatusDescription + 1);
  1384.             }
  1385.            
  1386.             if (bytesParsed == statusLineLength) {
  1387.                 //
  1388.                 // response now points one past the end of the buffer. We may be looking
  1389.                 // over the edge...
  1390.                 //
  1391.                 // if we're at the end of the connection then the server sent us an
  1392.                 // incorrectly formatted response. Probably an error.
  1393.                 //
  1394.                 // Otherwise its a partial response. We need more
  1395.                 //
  1396.                 parseStatus = DataParseStatus.NeedMoreData;
  1397.                 //
  1398.                 // if we really hit the end of the response then update the amount of
  1399.                 // headers scanned
  1400.                 //
  1401.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ParseStatusLine", parseStatus.ToString());
  1402.                 return parseStatus;
  1403.             }
  1404.            
  1405.             while ((bytesParsed < statusLineLength) && ((statusLine[bytesParsed] == '\r') || (statusLine[bytesParsed] == ' '))) {
  1406.                 ++bytesParsed;
  1407.                 if (m_MaximumResponseHeadersLength >= 0 && ++m_TotalResponseHeadersLength >= m_MaximumResponseHeadersLength) {
  1408.                     parseStatus = DataParseStatus.DataTooBig;
  1409.                     goto quit;
  1410.                 }
  1411.             }
  1412.            
  1413.             if (bytesParsed == statusLineLength) {
  1414.                
  1415.                 //
  1416.                 // hit end of buffer without finding LF
  1417.                 //
  1418.                
  1419.                 parseStatus = DataParseStatus.NeedMoreData;
  1420.                 goto quit;
  1421.                
  1422.             }
  1423.             else if (statusLine[bytesParsed] == '\n') {
  1424.                 ++bytesParsed;
  1425.                 if (m_MaximumResponseHeadersLength >= 0 && ++m_TotalResponseHeadersLength >= m_MaximumResponseHeadersLength) {
  1426.                     parseStatus = DataParseStatus.DataTooBig;
  1427.                     goto quit;
  1428.                 }
  1429.                 //
  1430.                 // if we found the empty line then we are done
  1431.                 //
  1432.                 parseStatus = DataParseStatus.Done;
  1433.             }
  1434.             quit:
  1435.            
  1436.            
  1437.             //
  1438.             // Now we have our parsed header to add to the array
  1439.             //
  1440.            
  1441.             if (parseStatus == DataParseStatus.Done && statusState != AfterStatusCode) {
  1442.                 // need to handle the case where we parse the StatusCode,
  1443.                 // but didn't get a status Line, and there was no space afer it.
  1444.                 if (statusState != StatusCodeNumber || statusLineInts[StatusCodeNumber] <= 0) {
  1445.                     //
  1446.                     // we're done with the status line, if we didn't parse all the
  1447.                     // numbers needed this is invalid protocol on the server
  1448.                     //
  1449.                     parseStatus = DataParseStatus.Invalid;
  1450.                 }
  1451.             }
  1452.            
  1453.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseStatusLine() StatusCode:" + statusLineInts[StatusCodeNumber] + " MajorVersionNumber:" + statusLineInts[MajorVersionNumber] + " MinorVersionNumber:" + statusLineInts[MinorVersionNumber] + " StatusDescription:" + ValidationHelper.ToString(statusDescription));
  1454.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ParseStatusLine", parseStatus.ToString());
  1455.            
  1456.             if (parseStatus == DataParseStatus.Invalid) {
  1457.                 parseError.Section = WebParseErrorSection.ResponseStatusLine;
  1458.                 parseError.Code = WebParseErrorCode.Generic;
  1459.             }
  1460.            
  1461.             return parseStatus;
  1462.         }
  1463.        
  1464.         // Must all start with a different first character.
  1465.         private static readonly string[] s_ShortcutStatusDescriptions = new string[] {"OK", "Continue", "Unauthorized"};
  1466.        
  1467.         //
  1468.         // Updated version of ParseStatusLine() - secure and fast
  1469.         //
  1470.         unsafe private static DataParseStatus ParseStatusLineStrict(byte[] statusLine, int statusLineLength, ref int bytesParsed, ref int statusState, StatusLineValues statusLineValues, int maximumHeaderLength, ref int totalBytesParsed, ref WebParseError parseError)
  1471.         {
  1472.             GlobalLog.Enter("Connection::ParseStatusLineStrict", statusLineLength.ToString(NumberFormatInfo.InvariantInfo) + ", " + bytesParsed.ToString(NumberFormatInfo.InvariantInfo) + ", " + statusState.ToString(NumberFormatInfo.InvariantInfo));
  1473.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection::ParseStatusLineStrict");
  1474.             GlobalLog.Assert((statusLineLength - bytesParsed) >= 0, "Connection::ParseStatusLineStrict()|(statusLineLength - bytesParsed) < 0");
  1475.             GlobalLog.Assert(maximumHeaderLength <= 0 || totalBytesParsed <= maximumHeaderLength, "Connection::ParseStatusLineStrict()|Headers already read exceeds limit.");
  1476.            
  1477.             // Remember where we started.
  1478.             int initialBytesParsed = bytesParsed;
  1479.            
  1480.             // Set up parsing status with what will happen if we exceed the buffer.
  1481.             DataParseStatus parseStatus = DataParseStatus.DataTooBig;
  1482.             int effectiveMax = maximumHeaderLength <= 0 ? int.MaxValue : (maximumHeaderLength - totalBytesParsed + bytesParsed);
  1483.             if (statusLineLength < effectiveMax) {
  1484.                 parseStatus = DataParseStatus.NeedMoreData;
  1485.                 effectiveMax = statusLineLength;
  1486.             }
  1487.            
  1488.             // sanity check
  1489.             if (bytesParsed >= effectiveMax)
  1490.                 goto quit;
  1491.            
  1492.             fixed (byte* byteBuffer = statusLine) {
  1493.                 // Use this switch to jump midway into the action. They all fall through until the end of the buffer is reached or
  1494.                 // the status line is fully parsed.
  1495.                 switch (statusState) {
  1496.                     case BeforeVersionNumbers:
  1497.                         // This takes advantage of the fact that this token must be the very first thing in the response.
  1498.                         while (totalBytesParsed - initialBytesParsed + bytesParsed < BeforeVersionNumberBytes.Length) {
  1499.                             if ((byte)BeforeVersionNumberBytes[totalBytesParsed - initialBytesParsed + bytesParsed] != byteBuffer[bytesParsed]) {
  1500.                                 parseStatus = DataParseStatus.Invalid;
  1501.                                 goto quit;
  1502.                             }
  1503.                            
  1504.                             if (++bytesParsed == effectiveMax)
  1505.                                 goto quit;
  1506.                         }
  1507.                        
  1508.                         // When entering the MajorVersionNumber phase, make sure at least one digit is present.
  1509.                         if (byteBuffer[bytesParsed] == '.') {
  1510.                             parseStatus = DataParseStatus.Invalid;
  1511.                             goto quit;
  1512.                         }
  1513.                        
  1514.                         statusState = MajorVersionNumber;
  1515.                         goto case MajorVersionNumber;
  1516.                         break;
  1517.                     case MajorVersionNumber:
  1518.                        
  1519.                         while (byteBuffer[bytesParsed] != '.') {
  1520.                             if (byteBuffer[bytesParsed] < '0' || byteBuffer[bytesParsed] > '9') {
  1521.                                 parseStatus = DataParseStatus.Invalid;
  1522.                                 goto quit;
  1523.                             }
  1524.                            
  1525.                             statusLineValues.MajorVersion = statusLineValues.MajorVersion * 10 + byteBuffer[bytesParsed] - '0';
  1526.                            
  1527.                             if (++bytesParsed == effectiveMax)
  1528.                                 goto quit;
  1529.                         }
  1530.                        
  1531.                         // Need visibility past the dot.
  1532.                         if (bytesParsed + 1 == effectiveMax)
  1533.                             goto quit;
  1534.                         bytesParsed++;
  1535.                        
  1536.                         // When entering the MinorVersionNumber phase, make sure at least one digit is present.
  1537.                         if (byteBuffer[bytesParsed] == ' ') {
  1538.                             parseStatus = DataParseStatus.Invalid;
  1539.                             goto quit;
  1540.                         }
  1541.                        
  1542.                         statusState = MinorVersionNumber;
  1543.                         goto case MinorVersionNumber;
  1544.                         break;
  1545.                     case MinorVersionNumber:
  1546.                        
  1547.                         // Only a single SP character is allowed to delimit fields in the status line.
  1548.                         while (byteBuffer[bytesParsed] != ' ') {
  1549.                             if (byteBuffer[bytesParsed] < '0' || byteBuffer[bytesParsed] > '9') {
  1550.                                 parseStatus = DataParseStatus.Invalid;
  1551.                                 goto quit;
  1552.                             }
  1553.                            
  1554.                             statusLineValues.MinorVersion = statusLineValues.MinorVersion * 10 + byteBuffer[bytesParsed] - '0';
  1555.                            
  1556.                             if (++bytesParsed == effectiveMax)
  1557.                                 goto quit;
  1558.                         }
  1559.                        
  1560.                         statusState = StatusCodeNumber;
  1561.                        
  1562.                         // Start the status code out as "1". This will effectively add 1000 to the code. It's used to count
  1563.                         // the number of digits to make sure it's three. At the end, subtract 1000.
  1564.                         statusLineValues.StatusCode = 1;
  1565.                        
  1566.                         // Move past the space.
  1567.                         if (++bytesParsed == effectiveMax)
  1568.                             goto quit;
  1569.                        
  1570.                         goto case StatusCodeNumber;
  1571.                         break;
  1572.                     case StatusCodeNumber:
  1573.                        
  1574.                         // RFC2616 says codes with an unrecognized first digit
  1575.                         // should be rejected. We're allowing the application to define their own "understanding" of
  1576.                         // 0, 6, 7, 8, and 9xx codes.
  1577.                         while (byteBuffer[bytesParsed] >= '0' && byteBuffer[bytesParsed] <= '9') {
  1578.                             // Make sure it isn't too big. The leading '1' will be removed after three digits are read.
  1579.                             if (statusLineValues.StatusCode >= 1000) {
  1580.                                 parseStatus = DataParseStatus.Invalid;
  1581.                                 goto quit;
  1582.                             }
  1583.                            
  1584.                             statusLineValues.StatusCode = statusLineValues.StatusCode * 10 + byteBuffer[bytesParsed] - '0';
  1585.                            
  1586.                             if (++bytesParsed == effectiveMax)
  1587.                                 goto quit;
  1588.                         }
  1589.                        
  1590.                         // Make sure there was enough, and exactly one space.
  1591.                         if (byteBuffer[bytesParsed] != ' ' || statusLineValues.StatusCode < 1000) {
  1592.                             if (byteBuffer[bytesParsed] == '\r' && statusLineValues.StatusCode >= 1000) {
  1593.                                 statusLineValues.StatusCode -= 1000;
  1594.                                 statusState = AfterCarriageReturn;
  1595.                                 if (++bytesParsed == effectiveMax)
  1596.                                     goto quit;
  1597.                                 goto case AfterCarriageReturn;
  1598.                             }
  1599.                             parseStatus = DataParseStatus.Invalid;
  1600.                             goto quit;
  1601.                         }
  1602.                        
  1603.                         // Remove the extra leading 1.
  1604.                         statusLineValues.StatusCode -= 1000;
  1605.                        
  1606.                         statusState = AfterStatusCode;
  1607.                        
  1608.                         // Move past the space.
  1609.                         if (++bytesParsed == effectiveMax)
  1610.                             goto quit;
  1611.                        
  1612.                         goto case AfterStatusCode;
  1613.                         break;
  1614.                     case AfterStatusCode:
  1615.                        
  1616.                        
  1617.                         {
  1618.                             // Check for shortcuts.
  1619.                             if (statusLineValues.StatusDescription == null) {
  1620.                                 foreach (string s in s_ShortcutStatusDescriptions) {
  1621.                                     if (bytesParsed < effectiveMax - s.Length && byteBuffer[bytesParsed] == (byte)s[0]) {
  1622.                                         int i;
  1623.                                         byte* pBuffer = byteBuffer + bytesParsed + 1;
  1624.                                         for (i = 1; i < s.Length; i++)
  1625.                                             if (*(pBuffer++) != (byte)s[i])
  1626.                                                 break;
  1627.                                         if (i == s.Length) {
  1628.                                             statusLineValues.StatusDescription = s;
  1629.                                             bytesParsed += s.Length;
  1630.                                         }
  1631.                                         break;
  1632.                                     }
  1633.                                 }
  1634.                             }
  1635.                            
  1636.                             int beginning = bytesParsed;
  1637.                            
  1638.                             while (byteBuffer[bytesParsed] != '\r') {
  1639.                                 if (byteBuffer[bytesParsed] < ' ' || byteBuffer[bytesParsed] == 127) {
  1640.                                     parseStatus = DataParseStatus.Invalid;
  1641.                                     goto quit;
  1642.                                 }
  1643.                                
  1644.                                 if (++bytesParsed == effectiveMax) {
  1645.                                     string s = WebHeaderCollection.HeaderEncoding.GetString(byteBuffer + beginning, bytesParsed - beginning);
  1646.                                     if (statusLineValues.StatusDescription == null)
  1647.                                         statusLineValues.StatusDescription = s;
  1648.                                     else
  1649.                                         statusLineValues.StatusDescription += s;
  1650.                                    
  1651.                                     goto quit;
  1652.                                 }
  1653.                             }
  1654.                            
  1655.                             if (bytesParsed > beginning) {
  1656.                                 string s = WebHeaderCollection.HeaderEncoding.GetString(byteBuffer + beginning, bytesParsed - beginning);
  1657.                                 if (statusLineValues.StatusDescription == null)
  1658.                                     statusLineValues.StatusDescription = s;
  1659.                                 else
  1660.                                     statusLineValues.StatusDescription += s;
  1661.                             }
  1662.                             else if (statusLineValues.StatusDescription == null) {
  1663.                                 statusLineValues.StatusDescription = "";
  1664.                             }
  1665.                            
  1666.                             statusState = AfterCarriageReturn;
  1667.                            
  1668.                             // Move past the CR.
  1669.                             if (++bytesParsed == effectiveMax)
  1670.                                 goto quit;
  1671.                            
  1672.                             goto case AfterCarriageReturn;
  1673.                         }
  1674.                         break;
  1675.                     case AfterCarriageReturn:
  1676.                        
  1677.                         if (byteBuffer[bytesParsed] != '\n') {
  1678.                             parseStatus = DataParseStatus.Invalid;
  1679.                             goto quit;
  1680.                         }
  1681.                        
  1682.                         parseStatus = DataParseStatus.Done;
  1683.                         bytesParsed++;
  1684.                         break;
  1685.                 }
  1686.             }
  1687.             quit:
  1688.            
  1689.             totalBytesParsed += bytesParsed - initialBytesParsed;
  1690.            
  1691.             GlobalLog.Print("Connection::ParseStatusLineStrict() StatusCode:" + statusLineValues.StatusCode + " MajorVersionNumber:" + statusLineValues.MajorVersion + " MinorVersionNumber:" + statusLineValues.MinorVersion + " StatusDescription:" + ValidationHelper.ToString(statusLineValues.StatusDescription));
  1692.             GlobalLog.Leave("Connection::ParseStatusLineStrict", parseStatus.ToString());
  1693.            
  1694.             if (parseStatus == DataParseStatus.Invalid) {
  1695.                 parseError.Section = WebParseErrorSection.ResponseStatusLine;
  1696.                 parseError.Code = WebParseErrorCode.Generic;
  1697.             }
  1698.            
  1699.             return parseStatus;
  1700.         }
  1701.        
  1702.        
  1703. /*++
  1704.         Routine Description:
  1705.           SetStatusLineParsed - processes the result of status line,
  1706.             after it has been parsed, reads vars and formats result of parsing
  1707.         Arguments:
  1708.           None - uses member vars
  1709.         Return Value:
  1710.           None
  1711.         --*/       
  1712.        
  1713.         private void SetStatusLineParsed()
  1714.         {
  1715.             // transfer this to response data
  1716.             m_ResponseData.m_StatusCode = (HttpStatusCode)m_StatusLineValues.StatusCode;
  1717.             m_ResponseData.m_StatusDescription = m_StatusLineValues.StatusDescription;
  1718.             m_ResponseData.m_IsVersionHttp11 = m_StatusLineValues.MajorVersion >= 1 && m_StatusLineValues.MinorVersion >= 1;
  1719.             if (ServicePoint.HttpBehaviour == HttpBehaviour.Unknown || ServicePoint.HttpBehaviour == HttpBehaviour.HTTP11 && !m_ResponseData.m_IsVersionHttp11) {
  1720.                 // it's only safe to start doing HTTP/1.1 behaviour if the server's version was unknown
  1721.                 // or if we need to downgrade
  1722.                 ServicePoint.HttpBehaviour = m_ResponseData.m_IsVersionHttp11 ? HttpBehaviour.HTTP11 : HttpBehaviour.HTTP10;
  1723.             }
  1724.             m_CanPipeline = ServicePoint.SupportsPipelining;
  1725.         }
  1726.        
  1727. /*++
  1728.             ProcessHeaderData - Pulls out Content-length, and other critical
  1729.                 data from the newly parsed headers
  1730.             Input:
  1731.                 Nothing.
  1732.             Returns:
  1733.                 long - size of contentLength that we are to use
  1734.         --*/       
  1735.         private long ProcessHeaderData(ref bool fHaveChunked, HttpWebRequest request, out bool dummyResponseStream)
  1736.         {
  1737.             long contentLength = -1;
  1738.             fHaveChunked = false;
  1739.             //
  1740.             // Check for the "Transfer-Encoding" header to contain the "chunked" string
  1741.             //
  1742.             string transferEncodingString = m_ResponseData.m_ResponseHeaders[HttpKnownHeaderNames.TransferEncoding];
  1743.             if (transferEncodingString != null) {
  1744.                 transferEncodingString = transferEncodingString.ToLower(CultureInfo.InvariantCulture);
  1745.                 fHaveChunked = transferEncodingString.IndexOf(HttpWebRequest.ChunkedHeader) != -1;
  1746.             }
  1747.            
  1748.             if (!fHaveChunked) {
  1749.                 //
  1750.                 // If the response is not chunked, parse the "Content-Length" into a long for data size.
  1751.                 //
  1752.                 string contentLengthString = m_ResponseData.m_ResponseHeaders.ContentLength;
  1753.                 if (contentLengthString != null) {
  1754.                     int index = contentLengthString.IndexOf(':');
  1755.                     if (index != -1) {
  1756.                         contentLengthString = contentLengthString.Substring(index + 1);
  1757.                     }
  1758.                     bool success = long.TryParse(contentLengthString, NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out contentLength);
  1759.                     if (!success) {
  1760.                         contentLength = -1;
  1761.                         // in some very rare cases, a proxy server may
  1762.                         // send us a pair of numbers in comma delimated
  1763.                         // fashion, so we need to handle this case
  1764.                         index = contentLengthString.LastIndexOf(',');
  1765.                         if (index != -1) {
  1766.                             contentLengthString = contentLengthString.Substring(index + 1);
  1767.                             success = long.TryParse(contentLengthString, NumberStyles.None, CultureInfo.InvariantCulture.NumberFormat, out contentLength);
  1768.                             if (!success) {
  1769.                                 contentLength = -1;
  1770.                             }
  1771.                         }
  1772.                     }
  1773.                     if (contentLength < 0) {
  1774.                         GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ProcessHeaderData - ContentLength value in header: " + contentLengthString + ", HttpWebRequest#" + ValidationHelper.HashString(m_CurrentRequest));
  1775.                         contentLength = c_InvalidContentLength;
  1776.                         // This will indicate a CL error to the caller
  1777.                     }
  1778.                 }
  1779.             }
  1780.            
  1781.             // ** else ** signal no content-length present??? or error out?
  1782.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ProcessHeaderData() Content-Length parsed:" + contentLength.ToString(NumberFormatInfo.InvariantInfo));
  1783.            
  1784.             dummyResponseStream = !request.CanGetResponseStream || m_ResponseData.m_StatusCode < HttpStatusCode.OK || m_ResponseData.m_StatusCode == HttpStatusCode.NoContent || (m_ResponseData.m_StatusCode == HttpStatusCode.NotModified && contentLength < 0);
  1785.            
  1786.             if (m_KeepAlive) {
  1787.                 //
  1788.                 // Deciding on KEEP ALIVE
  1789.                 //
  1790.                 bool resetKeepAlive = false;
  1791.                
  1792.                 //(1) if no content-length and no chunked, then turn off keep-alive
  1793.                 // In some cases, though, Content-Length should be assumed to be 0 based on HTTP RFC 2616
  1794.                 if (!dummyResponseStream && contentLength < 0 && !fHaveChunked) {
  1795.                     resetKeepAlive = true;
  1796.                 }
  1797.                 //(2) A workaround for a failed client ssl session on IIS6
  1798.                 // The problem is that we cannot change the connection group name after it gets created.
  1799.                 // IIS6 does not close the connection on 403 so all subsequent requests will fail to be authorized on THAT connection.
  1800.                 else if (m_ResponseData.m_StatusCode == HttpStatusCode.Forbidden) {
  1801.                     resetKeepAlive = true;
  1802.                 }
  1803.                 // (3) Possibly cease posting a big body on the connection, was invented mainly for the very first 401 response
  1804.                 //
  1805.                 // This optimization is for the discovery legs only. For ntlm this is fine, because the 1st actual authleg
  1806.                 // is always sent w/ content-length = 0.
  1807.                 // For Kerberos preauth, it there could be 1 or 2 auth legs, but we don't know how many there are in advance,
  1808.                 // so we don't have a way of eliminating the 1st auth leg.
  1809.                 else if (m_ResponseData.m_StatusCode > HttpWebRequest.MaxOkStatus && ((request.CurrentMethod == KnownHttpVerb.Post || request.CurrentMethod == KnownHttpVerb.Put) && m_MaximumUnauthorizedUploadLength >= 0 && request.ContentLength > m_MaximumUnauthorizedUploadLength && (request.CurrentAuthenticationState == null || request.CurrentAuthenticationState.Module == null))) {
  1810.                     resetKeepAlive = true;
  1811.                 }
  1812.                 //(4) for Http/1.0 servers, we can't be sure what their behavior
  1813.                 // in this case, so the best thing is to disable KeepAlive unless explicitly set
  1814.                 //
  1815.                 else {
  1816.                     bool haveClose = false;
  1817.                     bool haveKeepAlive = false;
  1818.                     string connection = m_ResponseData.m_ResponseHeaders[HttpKnownHeaderNames.Connection];
  1819.                     if (connection == null && ServicePoint.InternalProxyServicePoint) {
  1820.                         connection = m_ResponseData.m_ResponseHeaders[HttpKnownHeaderNames.ProxyConnection];
  1821.                     }
  1822.                    
  1823.                     if (connection != null) {
  1824.                         connection = connection.ToLower(CultureInfo.InvariantCulture);
  1825.                         if (connection.IndexOf("keep-alive") != -1) {
  1826.                             haveKeepAlive = true;
  1827.                         }
  1828.                         else if (connection.IndexOf("close") != -1) {
  1829.                             haveClose = true;
  1830.                         }
  1831.                     }
  1832.                    
  1833.                     if ((haveClose && ServicePoint.HttpBehaviour == HttpBehaviour.HTTP11) || (!haveKeepAlive && ServicePoint.HttpBehaviour <= HttpBehaviour.HTTP10)) {
  1834.                         resetKeepAlive = true;
  1835.                     }
  1836.                 }
  1837.                
  1838.                
  1839.                 if (resetKeepAlive) {
  1840.                     lock (this) {
  1841.                         m_KeepAlive = false;
  1842.                         m_Free = false;
  1843.                     }
  1844.                 }
  1845.             }
  1846.            
  1847.             return contentLength;
  1848.         }
  1849.        
  1850. /*++
  1851.             ParseStreamData
  1852.             Handles parsing of the blocks of data received after buffer,
  1853.             distributes the data to stream constructors as needed
  1854.             returnResult - contains a object containing Requests
  1855.                 that must be notified upon return from callback
  1856.         --*/       
  1857.         private DataParseStatus ParseStreamData(ref ConnectionReturnResult returnResult)
  1858.         {
  1859.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::ParseStreamData");
  1860.            
  1861.             if (m_CurrentRequest == null) {
  1862.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ParseStreamData - Aborted Request, return DataParseStatus.Invalid");
  1863.                 m_ParseError.Section = WebParseErrorSection.Generic;
  1864.                 m_ParseError.Code = WebParseErrorCode.UnexpectedServerResponse;
  1865.                 return DataParseStatus.Invalid;
  1866.             }
  1867.            
  1868.             bool fHaveChunked = false;
  1869.             bool dummyResponseStream;
  1870.             // content-length if there is one
  1871.             long contentLength = ProcessHeaderData(ref fHaveChunked, m_CurrentRequest, out dummyResponseStream);
  1872.            
  1873.             GlobalLog.Assert(!fHaveChunked || contentLength == -1, "Connection#{0}::ParseStreamData()|fHaveChunked but contentLength != -1", ValidationHelper.HashString(this));
  1874.            
  1875.             if (contentLength == c_InvalidContentLength) {
  1876.                 m_ParseError.Section = WebParseErrorSection.ResponseHeader;
  1877.                 m_ParseError.Code = WebParseErrorCode.InvalidContentLength;
  1878.                 return DataParseStatus.Invalid;
  1879.             }
  1880.            
  1881.             // bytes left over that have not been parsed
  1882.             int bufferLeft = (m_BytesRead - m_BytesScanned);
  1883.            
  1884.             if (m_ResponseData.m_StatusCode > HttpWebRequest.MaxOkStatus) {
  1885.                 // This will tell the request to be prepared for possible connection drop
  1886.                 // Also that will stop writing on the wire if the connection is not kept alive
  1887.                 m_CurrentRequest.ErrorStatusCodeNotify(this, m_KeepAlive, false);
  1888.             }
  1889.            
  1890.             int bytesToCopy;
  1891.             //
  1892.             // If pipelining, then look for extra data that could
  1893.             // be part of of another stream, if its there,
  1894.             // then we need to copy it, add it to a stream,
  1895.             // and then continue with the next headers
  1896.             //
  1897.            
  1898.             if (dummyResponseStream) {
  1899.                 bytesToCopy = 0;
  1900.                 fHaveChunked = false;
  1901.             }
  1902.             else {
  1903.                 if (fHaveChunked) {
  1904.                     bytesToCopy = FindChunkEntitySize(m_ReadBuffer, m_BytesScanned, bufferLeft);
  1905.                    
  1906.                     if (bytesToCopy == 0) {
  1907.                         // If we get a 0 back, we had some sort of error in
  1908.                         // parsing the chunk.
  1909.                         GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ParseStreamData - Invalid Chunk in the resposne, HttpWebRequest#" + ValidationHelper.HashString(m_CurrentRequest));
  1910.                         m_ParseError.Section = WebParseErrorSection.ResponseBody;
  1911.                         m_ParseError.Code = WebParseErrorCode.InvalidChunkFormat;
  1912.                         return DataParseStatus.Invalid;
  1913.                     }
  1914.                 }
  1915.                 else {
  1916.                     if (contentLength > (long)Int32.MaxValue)
  1917.                         bytesToCopy = -1;
  1918.                     else
  1919.                         bytesToCopy = (int)contentLength;
  1920.                 }
  1921.             }
  1922.            
  1923.             DataParseStatus result;
  1924.            
  1925.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseStreamData() bytesToCopy:" + bytesToCopy + " bufferLeft:" + bufferLeft);
  1926.            
  1927.             if (bytesToCopy != -1 && bytesToCopy <= bufferLeft) {
  1928.                 m_ResponseData.m_ConnectStream = new ConnectStream(this, m_ReadBuffer, m_BytesScanned, bytesToCopy, dummyResponseStream ? 0 : contentLength, fHaveChunked, m_CurrentRequest);
  1929.                
  1930.                 // The parsing will be resumed from m_BytesScanned when response stream is closed.
  1931.                 result = DataParseStatus.ContinueParsing;
  1932.                 m_BytesScanned += bytesToCopy;
  1933.             }
  1934.             else {
  1935.                 m_ResponseData.m_ConnectStream = new ConnectStream(this, m_ReadBuffer, m_BytesScanned, bufferLeft, dummyResponseStream ? 0 : contentLength, fHaveChunked, m_CurrentRequest);
  1936.                
  1937.                 // This is the default case where we have a buffer with no more streams except the last one to create so we create it.
  1938.                 // Note the buffer is fully consumed so we can reset the buffer offests.
  1939.                 result = DataParseStatus.Done;
  1940.                 ClearReaderState();
  1941.             }
  1942.            
  1943.             m_ResponseData.m_ContentLength = contentLength;
  1944.             ConnectionReturnResult.Add(ref returnResult, m_CurrentRequest, m_ResponseData.Clone());
  1945.            
  1946.             #if DEBUG
  1947.             GlobalLog.DebugUpdateRequest(m_CurrentRequest, this, GlobalLog.WaitingForReadDoneFlag);
  1948.             #endif
  1949.            
  1950.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ParseStreamData");
  1951.             return result;
  1952.             // response stream is taking over the reading
  1953.         }
  1954.        
  1955.         // Called before restarting Read operations
  1956.         private void ClearReaderState()
  1957.         {
  1958.             m_BytesRead = 0;
  1959.             m_BytesScanned = 0;
  1960.         }
  1961.        
  1962. /*++
  1963.             ParseResponseData - Parses the incomming headers, and handles
  1964.               creation of new streams that are found while parsing, and passes
  1965.               extra data the new streams
  1966.             Input:
  1967.                 returnResult - returns an object containing items that need to be called
  1968.                     at the end of the read callback
  1969.             Returns:
  1970.                 bool - true if one should continue reading more data
  1971.         --*/       
  1972.         private DataParseStatus ParseResponseData(ref ConnectionReturnResult returnResult, out bool requestDone, out CoreResponseData continueResponseData)
  1973.         {
  1974.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData()");
  1975.            
  1976.             DataParseStatus parseStatus = DataParseStatus.NeedMoreData;
  1977.             DataParseStatus parseSubStatus;
  1978.            
  1979.             // Indicates whether or not at least one whole request was processed in this loop.
  1980.             // (i.e. Whether ParseStreamData() was called.
  1981.             requestDone = false;
  1982.             continueResponseData = null;
  1983.            
  1984.             // loop in case of multiple sets of headers or streams,
  1985.             // that may be generated due to a pipelined response
  1986.            
  1987.             // Invariants: at the start of this loop, m_BytesRead
  1988.             // is the number of bytes in the buffer, and m_BytesScanned
  1989.             // is how many bytes of the buffer we've consumed so far.
  1990.             // and the m_ReadState var will be updated at end of
  1991.             // each code path, call to this function to reflect,
  1992.             // the state, or error condition of the parsing of data
  1993.             //
  1994.             // We use the following variables in the code below:
  1995.             //
  1996.             // m_ReadState - tracks the current state of our Parsing in a
  1997.             // response. z.B.
  1998.             // Start - initial start state and begining of response
  1999.             // StatusLine - the first line sent in response, include status code
  2000.             // Headers - \r\n delimiated Header parsing until we find entity body
  2001.             // Data - Entity Body parsing, if we have all data, we create stream directly
  2002.             //
  2003.             // m_ResponseData - An object used to gather Stream, Headers, and other
  2004.             // tidbits so that a request/Response can receive this data when
  2005.             // this code is finished processing
  2006.             //
  2007.             // m_ReadBuffer - Of course the buffer of data we are parsing from
  2008.             //
  2009.             // m_BytesScanned - The bytes scanned in this buffer so far,
  2010.             // since its always assumed that parse to completion, this
  2011.             // var becomes ended of known data at the end of this function,
  2012.             // based off of 0
  2013.             //
  2014.             // m_BytesRead - The total bytes read in buffer, should be const,
  2015.             // till its updated at end of function.
  2016.             //
  2017.            
  2018.             //
  2019.             // Now attempt to parse the data,
  2020.             // we first parse status line,
  2021.             // then read headers,
  2022.             // and finally transfer results to a new stream, and tell request
  2023.             //
  2024.            
  2025.             switch (m_ReadState) {
  2026.                 case ReadState.Start:
  2027.                    
  2028.                    
  2029.                    
  2030.                     if (m_CurrentRequest == null) {
  2031.                         lock (this) {
  2032.                             if (m_WriteList.Count == 0 || ((m_CurrentRequest = m_WriteList[0] as HttpWebRequest) == null)) {
  2033.                                 m_ParseError.Section = WebParseErrorSection.Generic;
  2034.                                 m_ParseError.Code = WebParseErrorCode.Generic;
  2035.                                 parseStatus = DataParseStatus.Invalid;
  2036.                                 break;
  2037.                             }
  2038.                         }
  2039.                     }
  2040.                     m_MaximumResponseHeadersLength = m_CurrentRequest.MaximumResponseHeadersLength * 1024;
  2041.                     m_ResponseData = new CoreResponseData();
  2042.                     m_ReadState = ReadState.StatusLine;
  2043.                     m_TotalResponseHeadersLength = 0;
  2044.                    
  2045.                     InitializeParseStatusLine();
  2046.                     goto case ReadState.StatusLine;
  2047.                     break;
  2048.                 case ReadState.StatusLine:
  2049.                    
  2050.                     //
  2051.                     // Reads HTTP status response line
  2052.                     //
  2053.                     if (SettingsSectionInternal.Section.UseUnsafeHeaderParsing) {
  2054.                         // This one uses an array to store the parsed values in. Marshal between this legacy way.
  2055.                         int[] statusInts = new int[] {0, m_StatusLineValues.MajorVersion, m_StatusLineValues.MinorVersion, m_StatusLineValues.StatusCode};
  2056.                         if (m_StatusLineValues.StatusDescription == null)
  2057.                             m_StatusLineValues.StatusDescription = "";
  2058.                        
  2059.                             // buffer we're working with
  2060.                             // total bytes read so far
  2061.                             // index off of what we've scanned
  2062.                         parseSubStatus = ParseStatusLine(m_ReadBuffer, m_BytesRead, ref m_BytesScanned, ref statusInts, ref m_StatusLineValues.StatusDescription, ref m_StatusState, ref m_ParseError);
  2063.                        
  2064.                         m_StatusLineValues.MajorVersion = statusInts[1];
  2065.                         m_StatusLineValues.MinorVersion = statusInts[2];
  2066.                         m_StatusLineValues.StatusCode = statusInts[3];
  2067.                     }
  2068.                     else {
  2069.                             // buffer we're working with
  2070.                             // total bytes read so far
  2071.                             // index off of what we've scanned
  2072.                         parseSubStatus = ParseStatusLineStrict(m_ReadBuffer, m_BytesRead, ref m_BytesScanned, ref m_StatusState, m_StatusLineValues, m_MaximumResponseHeadersLength, ref m_TotalResponseHeadersLength, ref m_ParseError);
  2073.                     }
  2074.                    
  2075.                     if (parseSubStatus == DataParseStatus.Done) {
  2076.                         if (Logging.On)
  2077.                             Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_received_status_line, m_StatusLineValues.MajorVersion + "." + m_StatusLineValues.MinorVersion, m_StatusLineValues.StatusCode, m_StatusLineValues.StatusDescription));
  2078.                         SetStatusLineParsed();
  2079.                         m_ReadState = ReadState.Headers;
  2080.                         m_ResponseData.m_ResponseHeaders = new WebHeaderCollection(WebHeaderCollectionType.HttpWebResponse);
  2081.                         goto case ReadState.Headers;
  2082.                     }
  2083.                     else if (parseSubStatus != DataParseStatus.NeedMoreData) {
  2084.                         //
  2085.                         // report error - either Invalid or DataTooBig
  2086.                         //
  2087.                         GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() ParseStatusLine() parseSubStatus:" + parseSubStatus.ToString());
  2088.                         parseStatus = parseSubStatus;
  2089.                         break;
  2090.                     }
  2091.                    
  2092.                     break;
  2093.                 case ReadState.Headers:
  2094.                     // read more data
  2095.                     //
  2096.                     // Parse additional lines of header-name: value pairs
  2097.                     //
  2098.                     if (m_BytesScanned >= m_BytesRead) {
  2099.                         //
  2100.                         // we already can tell we need more data
  2101.                         //
  2102.                         break;
  2103.                     }
  2104.                    
  2105.                     if (SettingsSectionInternal.Section.UseUnsafeHeaderParsing) {
  2106.                         parseSubStatus = m_ResponseData.m_ResponseHeaders.ParseHeaders(m_ReadBuffer, m_BytesRead, ref m_BytesScanned, ref m_TotalResponseHeadersLength, m_MaximumResponseHeadersLength, ref m_ParseError);
  2107.                     }
  2108.                     else {
  2109.                         parseSubStatus = m_ResponseData.m_ResponseHeaders.ParseHeadersStrict(m_ReadBuffer, m_BytesRead, ref m_BytesScanned, ref m_TotalResponseHeadersLength, m_MaximumResponseHeadersLength, ref m_ParseError);
  2110.                     }
  2111.                    
  2112.                     if (parseSubStatus == DataParseStatus.Invalid || parseSubStatus == DataParseStatus.DataTooBig) {
  2113.                         //
  2114.                         // report error
  2115.                         //
  2116.                         GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() ParseHeaders() parseSubStatus:" + parseSubStatus.ToString());
  2117.                         parseStatus = parseSubStatus;
  2118.                         break;
  2119.                     }
  2120.                     else if (parseSubStatus == DataParseStatus.Done) {
  2121.                         if (Logging.On)
  2122.                             Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_received_headers, m_ResponseData.m_ResponseHeaders.ToString(true)));
  2123.                        
  2124.                         GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() DataParseStatus.Done StatusCode:" + (int)m_ResponseData.m_StatusCode + " m_BytesRead:" + m_BytesRead + " m_BytesScanned:" + m_BytesScanned);
  2125.                        
  2126.                         //get the IIS server version
  2127.                         if (m_IISVersion == -1) {
  2128.                             string server = m_ResponseData.m_ResponseHeaders.Server;
  2129.                             if (server != null && server.ToLower(CultureInfo.InvariantCulture).Contains("microsoft-iis")) {
  2130.                                 int i = server.IndexOf("/");
  2131.                                 if (i++ > 0 && i < server.Length) {
  2132.                                     m_IISVersion = server[i++] - '0';
  2133.                                     while (i < server.Length && Char.IsDigit(server[i])) {
  2134.                                         m_IISVersion = m_IISVersion * 10 + server[i++] - '0';
  2135.                                     }
  2136.                                 }
  2137.                             }
  2138.                             //we got a response,so if we don't know the server by now and it wasn't a 100 continue,
  2139.                             //we can't assume we will ever know it. IIS5 sends its server header w/ the continue
  2140.                            
  2141.                             if (m_IISVersion == -1 && m_ResponseData.m_StatusCode != HttpStatusCode.Continue) {
  2142.                                 m_IISVersion = 0;
  2143.                             }
  2144.                         }
  2145.                        
  2146.                         if (m_ResponseData.m_StatusCode == HttpStatusCode.Continue || m_ResponseData.m_StatusCode == HttpStatusCode.BadRequest) {
  2147.                            
  2148.                             GlobalLog.Assert(m_CurrentRequest != null, "Connection#{0}::ParseResponseData()|m_CurrentRequest == null", ValidationHelper.HashString(this));
  2149.                             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() HttpWebRequest#" + ValidationHelper.HashString(m_CurrentRequest));
  2150.                            
  2151.                             if (m_ResponseData.m_StatusCode == HttpStatusCode.BadRequest) {
  2152.                                 // If we have a 400 and we were sending a chunked request going through to a proxy with a chunked upload,
  2153.                                 // this proxy is a partially compliant so shut off fancy features (pipelining and chunked uploads)
  2154.                                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() got a 400 StatusDescription:" + m_ResponseData.m_StatusDescription);
  2155.                                 if (ServicePoint.HttpBehaviour == HttpBehaviour.HTTP11 && m_CurrentRequest.HttpWriteMode == HttpWriteMode.Chunked && m_ResponseData.m_ResponseHeaders.Via != null && string.Compare(m_ResponseData.m_StatusDescription, "Bad Request ( The HTTP request includes a non-supported header. Contact the Server administrator. )", StringComparison.OrdinalIgnoreCase) == 0) {
  2156.                                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() downgrading server to HTTP11PartiallyCompliant.");
  2157.                                     ServicePoint.HttpBehaviour = HttpBehaviour.HTTP11PartiallyCompliant;
  2158.                                 }
  2159.                             }
  2160.                             else {
  2161.                                 // If we have an HTTP continue, eat these headers and look
  2162.                                 // for the 200 OK
  2163.                                 //
  2164.                                 // we got a 100 Continue. set this on the HttpWebRequest
  2165.                                 //
  2166.                                 m_CurrentRequest.Saw100Continue = true;
  2167.                                 if (!ServicePoint.Understands100Continue) {
  2168.                                     //
  2169.                                     // and start expecting it again if this behaviour was turned off
  2170.                                     //
  2171.                                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() HttpWebRequest#" + ValidationHelper.HashString(m_CurrentRequest) + " ServicePoint#" + ValidationHelper.HashString(ServicePoint) + " sent UNexpected 100 Continue");
  2172.                                     ServicePoint.Understands100Continue = true;
  2173.                                 }
  2174.                                
  2175.                                 //
  2176.                                 // set Continue Ack on request.
  2177.                                 //
  2178.                                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() calling SetRequestContinue()");
  2179.                                 continueResponseData = m_ResponseData;
  2180.                                
  2181.                                 //if we got a 100continue we swallow it and start looking for a final response
  2182.                                 goto case ReadState.Start;
  2183.                             }
  2184.                         }
  2185.                        
  2186.                         m_ReadState = ReadState.Data;
  2187.                         goto case ReadState.Data;
  2188.                     }
  2189.                    
  2190.                     // need more data,
  2191.                     break;
  2192.                 case ReadState.Data:
  2193.                    
  2194.                    
  2195.                     // (check status code for continue handling)
  2196.                     // 1. Figure out if its Chunked, content-length, or encoded
  2197.                     // 2. Takes extra data, place in stream(s)
  2198.                     // 3. Wake up blocked stream requests
  2199.                     //
  2200.                    
  2201.                     // Got through one entire response
  2202.                     requestDone = true;
  2203.                    
  2204.                     // parse and create a stream if needed
  2205.                     parseStatus = ParseStreamData(ref returnResult);
  2206.                    
  2207.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() result:" + parseStatus);
  2208.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData()" + " WriteDone:" + m_WriteDone + " ReadDone:" + m_ReadDone + " WaitList:" + m_WaitList.Count + " WriteList:" + m_WriteList.Count);
  2209.                     break;
  2210.             }
  2211.            
  2212.             if (m_BytesScanned == m_BytesRead)
  2213.                 ClearReaderState();
  2214.            
  2215.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() m_ReadState:" + m_ReadState);
  2216.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData()", parseStatus.ToString());
  2217.             return parseStatus;
  2218.         }
  2219.        
  2220.         /// <devdoc>
  2221.         /// <para>
  2222.         /// Cause the Connection to Close and Abort its socket,
  2223.         /// after the next request is completed. If the Connection
  2224.         /// is already idle, then Aborts the socket immediately.
  2225.         /// </para>
  2226.         /// </devdoc>
  2227.         internal void CloseOnIdle()
  2228.         {
  2229.             // The timer thread is allowed to call this. (It doesn't call user code and doesn't block.)
  2230.             GlobalLog.ThreadContract(ThreadKinds.Unknown, ThreadKinds.SafeSources | ThreadKinds.Timer, "Connection#" + ValidationHelper.HashString(this) + "::CloseOnIdle");
  2231.            
  2232.             lock (this) {
  2233.                 m_KeepAlive = false;
  2234.                 m_RemovedFromConnectionList = true;
  2235.                 if (!m_Idle) {
  2236.                     CheckIdle();
  2237.                 }
  2238.                 if (m_Idle) {
  2239.                     AbortSocket(false);
  2240.                     GC.SuppressFinalize(this);
  2241.                 }
  2242.             }
  2243.         }
  2244.        
  2245.         internal bool AbortOrDisassociate(HttpWebRequest request, WebException webException)
  2246.         {
  2247.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::AbortOrDisassociate", "request#" + ValidationHelper.HashString(request));
  2248.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::AbortOrDisassociate()");
  2249.            
  2250.             ConnectionReturnResult result = null;
  2251.             lock (this) {
  2252.                 int idx = m_WriteList.IndexOf(request);
  2253.                 // If the request is in the submission AND this is the first request we have to abort the connection,
  2254.                 // Otheriwse we simply disassociate it from the current connection.
  2255.                 if (idx == -1) {
  2256.                     idx = m_WaitList.IndexOf(request);
  2257.                     // If idx == -1 then the request must be already dispatched and the response stream is drained
  2258.                     // If so then we let request.Abort() to deal with this situation.
  2259.                     //
  2260.                     if (idx != -1) {
  2261.                         m_WaitList.RemoveAt(idx);
  2262.                     }
  2263.                     GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::AbortOrDisassociate()", "Request was wisassociated");
  2264.                     return true;
  2265.                 }
  2266.                 else if (idx != 0) {
  2267.                     // Make this connection Keep-Alive=false, remove the request and do not close the connection
  2268.                     // When the active request completes, the rest of the pipeline (minus aborted request) will be resubmitted.
  2269.                     m_WriteList.RemoveAt(idx);
  2270.                     m_KeepAlive = false;
  2271.                     GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::AbortOrDisassociate()", "Request was Disassociated from the Write List, idx = " + idx);
  2272.                     return true;
  2273.                 }
  2274.                
  2275.                 #if DEBUG
  2276.                 try {
  2277.                     #endif
  2278.                     m_KeepAlive = false;
  2279.                     if (webException != null && m_InnerException == null) {
  2280.                         m_InnerException = webException;
  2281.                         m_Error = webException.Status;
  2282.                     }
  2283.                     else {
  2284.                         m_Error = WebExceptionStatus.RequestCanceled;
  2285.                     }
  2286.                    
  2287.                     PrepareCloseConnectionSocket(ref result);
  2288.                     // Hard Close the socket.
  2289.                     Close(0);
  2290.                     #if DEBUG
  2291.                 }
  2292.                 catch (Exception exception) {
  2293.                     t_LastStressException = exception;
  2294.                     if (!NclUtilities.IsFatal(exception)) {
  2295.                         GlobalLog.Assert("Connection#" + ValidationHelper.HashString(this) + "::AbortOrDisassociate()", exception.Message);
  2296.                     }
  2297.                 }
  2298.                 #endif
  2299.             }
  2300.             ConnectionReturnResult.SetResponses(result);
  2301.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::AbortOrDisassociate()", "Connection Aborted");
  2302.             return false;
  2303.         }
  2304.        
  2305.         #if DEBUG
  2306.         [ThreadStatic()]
  2307.         private static Exception t_LastStressException;
  2308.         #endif
  2309.        
  2310.         internal void AbortSocket(bool isAbortState)
  2311.         {
  2312.             // The timer/finalization thread is allowed to call this. (It doesn't call user code and doesn't block.)
  2313.             GlobalLog.ThreadContract(ThreadKinds.Unknown, ThreadKinds.SafeSources | ThreadKinds.Timer | ThreadKinds.Finalization, "Connection#" + ValidationHelper.HashString(this) + "::AbortSocket");
  2314.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::Abort", "isAbortState:" + isAbortState.ToString());
  2315.            
  2316.             if (isAbortState) {
  2317.                 UnlockRequest();
  2318.                 CheckIdle();
  2319.             }
  2320.             else {
  2321.                 // This one is recoverable, set it to keep Read/Write StartNextRequest happy.
  2322.                 m_Error = WebExceptionStatus.KeepAliveFailure;
  2323.             }
  2324.            
  2325.             // Stream close isn't threadsafe.
  2326.             lock (this) {
  2327.                 Close(0);
  2328.             }
  2329.            
  2330.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::Abort", "isAbortState:" + isAbortState.ToString());
  2331.         }
  2332.        
  2333.        
  2334. /*++
  2335.             PrepareCloseConnectionSocket - reset the connection requests list.
  2336.             This method is called when we want to close the conection.
  2337.             It must be called with the critical section held.
  2338.             The caller must call this.Close if decided to call this method.
  2339.             All connection closes (either ours or server initiated) eventually go through here.
  2340.             As to what we do: we loop through our write and wait list and pull requests
  2341.             off it, and give each request an error failure. Then the caller will
  2342.             dispatch the responses.
  2343.         --*/       
  2344.        
  2345.         private void PrepareCloseConnectionSocket(ref ConnectionReturnResult returnResult)
  2346.         {
  2347.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::PrepareCloseConnectionSocket", m_Error.ToString());
  2348.            
  2349.             // Effectivelly, closing a connection makes it exempted from the "Idling" logic
  2350.             m_IdleSinceUtc = DateTime.MinValue;
  2351.             CanBePooled = false;
  2352.            
  2353.             if (m_WriteList.Count != 0 || m_WaitList.Count != 0) {
  2354.                
  2355.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::PrepareCloseConnectionSocket() m_WriteList.Count:" + m_WriteList.Count);
  2356.                 DebugDumpArrayListEntries(m_WriteList, "WriteList");
  2357.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::PrepareCloseConnectionSocket() m_WaitList.Count:" + m_WaitList.Count);
  2358.                 DebugDumpArrayListEntries(m_WaitList, "WaitList");
  2359.                
  2360.                 HttpWebRequest lockedRequest = LockedRequest;
  2361.                
  2362.                 if (lockedRequest != null) {
  2363.                     bool callUnlockRequest = false;
  2364.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::PrepareCloseConnectionSocket() looking for HttpWebRequest#" + ValidationHelper.HashString(lockedRequest));
  2365.                    
  2366.                     foreach (HttpWebRequest request in m_WriteList) {
  2367.                         if (request == lockedRequest) {
  2368.                             callUnlockRequest = true;
  2369.                         }
  2370.                     }
  2371.                    
  2372.                     if (!callUnlockRequest) {
  2373.                         foreach (HttpWebRequest request in m_WaitList) {
  2374.                             if (request == lockedRequest) {
  2375.                                 callUnlockRequest = true;
  2376.                                 break;
  2377.                             }
  2378.                         }
  2379.                     }
  2380.                     if (callUnlockRequest) {
  2381.                         UnlockRequest();
  2382.                     }
  2383.                 }
  2384.                
  2385.                 HttpWebRequest[] requestArray = null;
  2386.                
  2387.                 // WaitList gets Isolated exception status, free to retry multiple times
  2388.                 if (m_WaitList.Count != 0) {
  2389.                     requestArray = new HttpWebRequest[m_WaitList.Count];
  2390.                     m_WaitList.CopyTo(requestArray, 0);
  2391.                     ConnectionReturnResult.AddExceptionRange(ref returnResult, requestArray, ExceptionHelper.IsolatedException);
  2392.                 }
  2393.                
  2394.                 //
  2395.                 // WriteList (except for single request list) gets Recoverable exception status, may be retired if not failed once
  2396.                 // For a single request list the exception is computed here
  2397.                 // InnerExeption if any may tell more details in both cases
  2398.                 //
  2399.                 if (m_WriteList.Count != 0) {
  2400.                     Exception theException = m_InnerException;
  2401.                    
  2402.                    
  2403.                     if (theException != null)
  2404.                         GlobalLog.Print(theException.ToString());
  2405.                    
  2406.                     GlobalLog.Print("m_Error = " + m_Error.ToString());
  2407.                    
  2408.                     if (!(theException is WebException) && !(theException is SecurityException)) {
  2409.                         if (m_Error == WebExceptionStatus.ServerProtocolViolation) {
  2410.                             string errorString = NetRes.GetWebStatusString(m_Error);
  2411.                            
  2412.                             string detailedInfo = "";
  2413.                             if (m_ParseError.Section != WebParseErrorSection.Generic)
  2414.                                 detailedInfo += " Section=" + m_ParseError.Section.ToString();
  2415.                             if (m_ParseError.Code != WebParseErrorCode.Generic) {
  2416.                                 detailedInfo += " Detail=" + SR.GetString("net_WebResponseParseError_" + m_ParseError.Code.ToString());
  2417.                             }
  2418.                             if (detailedInfo.Length != 0)
  2419.                                 errorString += "." + detailedInfo;
  2420.                            
  2421.                             theException = new WebException(errorString, theException, m_Error, null, WebExceptionInternalStatus.RequestFatal);
  2422.                         }
  2423.                         else if (m_Error == WebExceptionStatus.SecureChannelFailure) {
  2424.                             theException = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.SecureChannelFailure), WebExceptionStatus.SecureChannelFailure);
  2425.                         }
  2426.                        
  2427.                         else if (m_Error == WebExceptionStatus.Timeout) {
  2428.                             theException = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.Timeout), WebExceptionStatus.Timeout);
  2429.                         }
  2430.                         else if (m_Error == WebExceptionStatus.RequestCanceled) {
  2431.                             theException = new WebException(NetRes.GetWebStatusString("net_requestaborted", WebExceptionStatus.RequestCanceled), WebExceptionStatus.RequestCanceled, WebExceptionInternalStatus.RequestFatal, theException);
  2432.                         }
  2433.                         else if (m_Error == WebExceptionStatus.MessageLengthLimitExceeded || m_Error == WebExceptionStatus.TrustFailure) {
  2434.                             theException = new WebException(NetRes.GetWebStatusString("net_connclosed", m_Error), m_Error, WebExceptionInternalStatus.RequestFatal, theException);
  2435.                         }
  2436.                         else {
  2437.                             if (m_Error == WebExceptionStatus.Success) {
  2438.                                 throw new InternalException();
  2439.                             }
  2440.                            
  2441.                             bool retry = false;
  2442.                             bool isolatedKeepAliveFailure = false;
  2443.                            
  2444.                             if (m_WriteList.Count != 1) {
  2445.                                 // Real scenario: SSL against IIS-5 would fail if pipelinning.
  2446.                                 // retry = true will cover a general case when >>the server<< aborts a pipeline
  2447.                                 // Basically all pipelined requests are marked with recoverable error including the very active request.
  2448.                                 retry = true;
  2449.                             }
  2450.                             else if (m_Error == WebExceptionStatus.KeepAliveFailure) {
  2451.                                 HttpWebRequest request = (HttpWebRequest)m_WriteList[0];
  2452.                                 // Check that the active request did not start the body yet
  2453.                                 if (!request.BodyStarted)
  2454.                                     isolatedKeepAliveFailure = true;
  2455.                             }
  2456.                             else {
  2457.                                 retry = (!AtLeastOneResponseReceived && !((HttpWebRequest)m_WriteList[0]).BodyStarted);
  2458.                             }
  2459.                             theException = new WebException(NetRes.GetWebStatusString("net_connclosed", m_Error), m_Error, (isolatedKeepAliveFailure ? WebExceptionInternalStatus.Isolated : retry ? WebExceptionInternalStatus.Recoverable : WebExceptionInternalStatus.RequestFatal), theException);
  2460.                         }
  2461.                     }
  2462.                    
  2463.                     WebException pipelineException = new WebException(NetRes.GetWebStatusString("net_connclosed", WebExceptionStatus.PipelineFailure), WebExceptionStatus.PipelineFailure, WebExceptionInternalStatus.Recoverable, theException);
  2464.                    
  2465.                     requestArray = new HttpWebRequest[m_WriteList.Count];
  2466.                     m_WriteList.CopyTo(requestArray, 0);
  2467.                     ConnectionReturnResult.AddExceptionRange(ref returnResult, requestArray, pipelineException, theException);
  2468.                 }
  2469.                
  2470.                
  2471.                 m_WriteList.Clear();
  2472.                 m_WaitList.Clear();
  2473.             }
  2474.            
  2475.             CheckIdle();
  2476.            
  2477.             if (m_Idle) {
  2478.                 GC.SuppressFinalize(this);
  2479.             }
  2480.             if (!m_RemovedFromConnectionList && ConnectionGroup != null) {
  2481.                 m_RemovedFromConnectionList = true;
  2482.                 ConnectionGroup.Disassociate(this);
  2483.             }
  2484.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::PrepareCloseConnectionSocket");
  2485.         }
  2486.        
  2487.        
  2488. /*++
  2489.             HandleError - Handle a protocol error from the server.
  2490.             This method is called when we've detected some sort of fatal protocol
  2491.             violation while parsing a response, receiving data from the server,
  2492.             or failing to connect to the server. We'll fabricate
  2493.             a WebException and then call CloseConnection which closes the
  2494.             connection as well as informs the request through a callback.
  2495.             Input:
  2496.                     webExceptionStatus -
  2497.                     connectFailure -
  2498.                     readFailure -
  2499.             Returns: Nothing
  2500.         --*/       
  2501.         internal void HandleConnectStreamException(bool writeDone, bool readDone, WebExceptionStatus webExceptionStatus, ref ConnectionReturnResult returnResult, Exception e)
  2502.         {
  2503.             if (m_InnerException == null) {
  2504.                 m_InnerException = e;
  2505.                 if (e is ObjectDisposedException) {
  2506.                     webExceptionStatus = WebExceptionStatus.RequestCanceled;
  2507.                 }
  2508.             }
  2509.             HandleError(writeDone, readDone, webExceptionStatus, ref returnResult);
  2510.         }
  2511.         //
  2512.         private void HandleErrorWithReadDone(WebExceptionStatus webExceptionStatus, ref ConnectionReturnResult returnResult)
  2513.         {
  2514.             HandleError(false, true, webExceptionStatus, ref returnResult);
  2515.         }
  2516.         //
  2517.         private void HandleError(bool writeDone, bool readDone, WebExceptionStatus webExceptionStatus, ref ConnectionReturnResult returnResult)
  2518.         {
  2519.             lock (this) {
  2520.                 if (writeDone)
  2521.                     m_WriteDone = true;
  2522.                 if (readDone)
  2523.                     m_ReadDone = true;
  2524.                
  2525.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::HandleError() m_WriteList.Count:" + m_WriteList.Count + " m_WaitList.Count:" + m_WaitList.Count + " new WriteDone:" + m_WriteDone + " new ReadDone:" + m_ReadDone);
  2526.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::HandleError() current HttpWebRequest#" + ValidationHelper.HashString(m_CurrentRequest));
  2527.                
  2528.                 if (webExceptionStatus == WebExceptionStatus.Success)
  2529.                     throw new InternalException();
  2530.                 //consider making an assert later.
  2531.                 m_Error = webExceptionStatus;
  2532.                
  2533.                 PrepareCloseConnectionSocket(ref returnResult);
  2534.                 // This will kill the socket
  2535.                 // Must be done inside the lock. (Stream Close() isn't threadsafe.)
  2536.                 Close(0);
  2537.             }
  2538.         }
  2539.        
  2540.         private static void ReadCallbackWrapper(IAsyncResult asyncResult)
  2541.         {
  2542.             if (asyncResult.CompletedSynchronously) {
  2543.                 return;
  2544.             }
  2545.            
  2546.             ((Connection)asyncResult.AsyncState).ReadCallback(asyncResult);
  2547.         }
  2548.        
  2549.         /// <devdoc>
  2550.         /// <para>Performs read callback processing on connection
  2551.         /// handles getting headers parsed and streams created</para>
  2552.         /// </devdoc>
  2553.         private void ReadCallback(IAsyncResult asyncResult)
  2554.         {
  2555.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::ReadCallback", ValidationHelper.HashString(asyncResult));
  2556.            
  2557.             int bytesRead = -1;
  2558.             WebExceptionStatus errorStatus = WebExceptionStatus.ReceiveFailure;
  2559.            
  2560.             //
  2561.             // parameter validation
  2562.             //
  2563.             GlobalLog.Assert(asyncResult != null, "Connection#{0}::ReadCallback()|asyncResult == null", ValidationHelper.HashString(this));
  2564.             GlobalLog.Assert((asyncResult is OverlappedAsyncResult || asyncResult is LazyAsyncResult), "Connection#{0}::ReadCallback()|asyncResult is not OverlappedAsyncResult.", ValidationHelper.HashString(this));
  2565.            
  2566.             try {
  2567.                 bytesRead = EndRead(asyncResult);
  2568.                 if (bytesRead == 0)
  2569.                     bytesRead = -1;
  2570.                 // 0 is reserved for re-entry on already buffered data
  2571.                 errorStatus = WebExceptionStatus.Success;
  2572.             }
  2573.             catch (Exception exception) {
  2574.                
  2575.                 if (m_InnerException == null)
  2576.                     m_InnerException = exception;
  2577.                
  2578.                 if (exception.GetType() == typeof(ObjectDisposedException))
  2579.                     errorStatus = WebExceptionStatus.RequestCanceled;
  2580.                
  2581.                 errorStatus = WebExceptionStatus.ReceiveFailure;
  2582.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadCallback() EndRead() errorStatus:" + errorStatus.ToString() + " caught exception:" + exception);
  2583.             }
  2584.            
  2585.             ReadComplete(bytesRead, errorStatus);
  2586.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ReadCallback");
  2587.         }
  2588.        
  2589.        
  2590.         /// <devdoc>
  2591.         /// <para>Attempts to poll the socket, to see if data is waiting to be read,
  2592.         /// if there is data there, then a read is started</para>
  2593.         /// </devdoc>
  2594.         internal void PollAndRead(HttpWebRequest request, bool userRetrievedStream)
  2595.         {
  2596.             GlobalLog.ThreadContract(ThreadKinds.Unknown, "Connection#" + ValidationHelper.HashString(this) + "::PollAndRead");
  2597.            
  2598.             // Ensure that we don't already have a response for this request, before we attempt to read the socket.
  2599.             request.SawInitialResponse = false;
  2600.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::PollAndRead() InternalPeekCompleted:" + request.ConnectionReaderAsyncResult.InternalPeekCompleted.ToString() + " Result:" + ValidationHelper.ToString(request.ConnectionReaderAsyncResult.Result));
  2601.             if (request.ConnectionReaderAsyncResult.InternalPeekCompleted && request.ConnectionReaderAsyncResult.Result == null && CanBePooled) {
  2602.                 SyncRead(request, userRetrievedStream, true);
  2603.             }
  2604.         }
  2605.         //
  2606.         // Peforms a Sync Read and calls the ReadComplete to process the result
  2607.         // The reads are done iteratively, until the Request has received enough
  2608.         // data to contruct a response, or a 100-Continue is read, allowing the HttpWebRequest
  2609.         // to return a write stream
  2610.         //
  2611.         // probeRead = true only for POST request and when the caller needs to wait for 100-continue
  2612.         //
  2613.         internal void SyncRead(HttpWebRequest request, bool userRetrievedStream, bool probeRead)
  2614.         {
  2615.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::SyncRead(byte[]) request#" + ValidationHelper.HashString(request) + (probeRead ? ", Probe read = TRUE" : string.Empty));
  2616.             GlobalLog.ThreadContract(ThreadKinds.Sync, "Connection#" + ValidationHelper.HashString(this) + "::SyncRead");
  2617.            
  2618.             // prevent recursive calls to this function
  2619.             if (t_SyncReadNesting > 0) {
  2620.                 GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::SyncRead() - nesting");
  2621.                 return;
  2622.             }
  2623.            
  2624.             bool pollSuccess = probeRead ? false : true;
  2625.            
  2626.             try {
  2627.                 t_SyncReadNesting++;
  2628.                
  2629.                 // grab a counter to tell us whenever the SetRequestContinue is called
  2630.                 int requestContinueCount = probeRead ? request.RequestContinueCount : 0;
  2631.                
  2632.                 bool requestDone;
  2633.                 int bytesRead = -1;
  2634.                 WebExceptionStatus errorStatus = WebExceptionStatus.ReceiveFailure;
  2635.                
  2636.                 //Ensures that we'll timeout eventually on an appdomain unload.
  2637.                 //Will be a no-op if the timeout doesn't change from request to request.
  2638.                 ReadTimeout = request.Timeout;
  2639.                
  2640.                 if (m_BytesScanned < m_BytesRead) {
  2641.                     // left over from previous read
  2642.                     pollSuccess = true;
  2643.                     bytesRead = 0;
  2644.                     //tell it we want to use buffered data on the first iteration
  2645.                     errorStatus = WebExceptionStatus.Success;
  2646.                 }
  2647.                
  2648.                 do {
  2649.                     requestDone = true;
  2650.                    
  2651.                     try {
  2652.                         if (bytesRead != 0) {
  2653.                             errorStatus = WebExceptionStatus.ReceiveFailure;
  2654.                            
  2655.                             if (!pollSuccess) {
  2656.                                 pollSuccess = Poll(HttpWebRequest.DefaultContinueTimeout * 1000, SelectMode.SelectRead);
  2657.                                 // Timeout is in microseconds
  2658.                                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SyncRead() PollSuccess : " + pollSuccess);
  2659.                             }
  2660.                            
  2661.                             if (pollSuccess) {
  2662.                                 bytesRead = Read(m_ReadBuffer, m_BytesRead, m_ReadBuffer.Length - m_BytesRead);
  2663.                                 errorStatus = WebExceptionStatus.Success;
  2664.                                 if (bytesRead == 0)
  2665.                                     bytesRead = -1;
  2666.                                 // 0 is reserved for re-entry on already buffered data
  2667.                             }
  2668.                         }
  2669.                     }
  2670.                     catch (Exception exception) {
  2671.                         if (NclUtilities.IsFatal(exception))
  2672.                             throw;
  2673.                        
  2674.                         if (m_InnerException == null)
  2675.                             m_InnerException = exception;
  2676.                        
  2677.                         if (exception.GetType() == typeof(ObjectDisposedException))
  2678.                             errorStatus = WebExceptionStatus.RequestCanceled;
  2679.                        
  2680.                         // need to handle SSL errors too
  2681.                         else {
  2682.                             SocketException socketException = exception.InnerException as SocketException;
  2683.                             if (socketException != null) {
  2684.                                 if (socketException.ErrorCode == (int)SocketError.TimedOut)
  2685.                                     errorStatus = WebExceptionStatus.Timeout;
  2686.                                 else
  2687.                                     errorStatus = WebExceptionStatus.ReceiveFailure;
  2688.                             }
  2689.                         }
  2690.                        
  2691.                         GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SyncRead() Read() threw errorStatus:" + errorStatus.ToString() + " bytesRead:" + bytesRead.ToString());
  2692.                     }
  2693.                     catch {
  2694.                         if (m_InnerException == null)
  2695.                             m_InnerException = new Exception(SR.GetString(SR.net_nonClsCompliantException));
  2696.                        
  2697.                         // need to handle SSL errors too
  2698.                        
  2699.                         GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SyncRead() Read() threw errorStatus:" + errorStatus.ToString() + " bytesRead:" + bytesRead.ToString());
  2700.                     }
  2701.                    
  2702.                     if (pollSuccess)
  2703.                         requestDone = ReadComplete(bytesRead, errorStatus);
  2704.                    
  2705.                     bytesRead = -1;
  2706.                 }
  2707.                 while (!requestDone && (userRetrievedStream || requestContinueCount == request.RequestContinueCount));
  2708.             }
  2709.             finally {
  2710.                 t_SyncReadNesting--;
  2711.             }
  2712.            
  2713.             if (probeRead) {
  2714.                 if (pollSuccess) {
  2715.                     if (!request.Saw100Continue && !userRetrievedStream) {
  2716.                         //During polling, we got a response that wasn't a 100 continue.
  2717.                         request.SawInitialResponse = true;
  2718.                     }
  2719.                 }
  2720.                 else {
  2721.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::SyncRead() Poll has timed out, calling SetRequestContinue().");
  2722.                     request.SetRequestContinue();
  2723.                 }
  2724.             }
  2725.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::SyncRead()");
  2726.         }
  2727.        
  2728.        
  2729.         //
  2730.         // Performs read callback processing on connection
  2731.         // handles getting headers parsed and streams created
  2732.         //
  2733.         // bytesRead == 0 when we re-enter on buffered data without doing actual read
  2734.         // bytesRead == -1 when we got a connection close plus when errorStatus == sucess we got a graceful close.
  2735.         // Otheriwse bytesRead is read byted to add to m_BytesRead i.e. to previously buffered data
  2736.         //
  2737.         private bool ReadComplete(int bytesRead, WebExceptionStatus errorStatus)
  2738.         {
  2739.             bool requestDone = true;
  2740.             CoreResponseData continueResponseData = null;
  2741.             ConnectionReturnResult returnResult = null;
  2742.             HttpWebRequest currentRequest = null;
  2743.            
  2744.             try {
  2745.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() m_BytesRead:" + m_BytesRead.ToString() + " m_BytesScanned:" + m_BytesScanned + " (+= new bytesRead:" + bytesRead.ToString() + ")");
  2746.                
  2747.                 if (bytesRead < 0) {
  2748.                     // Means we might have gotten gracefull or hard connection close.
  2749.                    
  2750.                     // If this is the first thing we read for a request then it
  2751.                     // could be an idle connection closed by the server (isolated error)
  2752.                     if (m_ReadState == ReadState.Start && m_AtLeastOneResponseReceived) {
  2753.                         // Note that KeepAliveFailure will be checked against POST-type requests
  2754.                         // and it's fatal if the body was already started.
  2755.                         if (errorStatus == WebExceptionStatus.Success || errorStatus == WebExceptionStatus.ReceiveFailure)
  2756.                             errorStatus = WebExceptionStatus.KeepAliveFailure;
  2757.                     }
  2758.                     else if (errorStatus == WebExceptionStatus.Success) {
  2759.                         // we got unexpected FIN in the middle of the response, or on a fresh connection, that's fatal
  2760.                         errorStatus = WebExceptionStatus.ConnectionClosed;
  2761.                     }
  2762.                    
  2763.                     // Notify request's SubmitWriteStream that a socket error happened. This will cause future writes to
  2764.                     // throw an IOException.
  2765.                     HttpWebRequest curRequest = m_CurrentRequest;
  2766.                     if (curRequest != null) {
  2767.                         curRequest.ErrorStatusCodeNotify(this, false, true);
  2768.                     }
  2769.                    
  2770.                     HandleErrorWithReadDone(errorStatus, ref returnResult);
  2771.                     goto done;
  2772.                 }
  2773.                
  2774.                 // Otherwise, we've got data.
  2775.                 GlobalLog.Dump(m_ReadBuffer, m_BytesScanned, m_BytesRead - m_BytesScanned);
  2776.                 GlobalLog.Dump(m_ReadBuffer, m_BytesRead, bytesRead);
  2777.                
  2778.                
  2779.                 bytesRead += m_BytesRead;
  2780.                 if (bytesRead > m_ReadBuffer.Length)
  2781.                     throw new InternalException();
  2782.                 //in case we posted two receives at once
  2783.                 m_BytesRead = bytesRead;
  2784.                
  2785.                 // We have the parsing code seperated out in ParseResponseData
  2786.                 //
  2787.                 // If we don't have all the headers yet. Resubmit the receive,
  2788.                 // passing in the bytes read total as our index. When we get
  2789.                 // back here we'll end up reparsing from the beginning, which is
  2790.                 // OK. because this shouldn't be a performance case.
  2791.                
  2792.                 //if we're back here, we need to reset the scanned bytes to 0.
  2793.                
  2794.                 DataParseStatus parseStatus = ParseResponseData(ref returnResult, out requestDone, out continueResponseData);
  2795.                
  2796.                 // copy off m_CurrentRequest as we might start processing a next request before exiting this method
  2797.                 currentRequest = m_CurrentRequest;
  2798.                
  2799.                 if (parseStatus != DataParseStatus.NeedMoreData)
  2800.                     m_CurrentRequest = null;
  2801.                
  2802.                 if (parseStatus == DataParseStatus.Invalid || parseStatus == DataParseStatus.DataTooBig) {
  2803.                     // Tell the request's SubmitWriteStream that the connection will be closed. It should swallow any
  2804.                     // future writes so that the appropriate exception will be received in GetResponse().
  2805.                     if (currentRequest != null) {
  2806.                         currentRequest.ErrorStatusCodeNotify(this, false, false);
  2807.                     }
  2808.                    
  2809.                     //
  2810.                     // report error
  2811.                     //
  2812.                     if (parseStatus == DataParseStatus.Invalid) {
  2813.                         HandleErrorWithReadDone(WebExceptionStatus.ServerProtocolViolation, ref returnResult);
  2814.                     }
  2815.                     else {
  2816.                         HandleErrorWithReadDone(WebExceptionStatus.MessageLengthLimitExceeded, ref returnResult);
  2817.                     }
  2818.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() parseStatus:" + parseStatus + " returnResult:" + returnResult);
  2819.                     goto done;
  2820.                 }
  2821.                
  2822.                 //Done means the ConnectStream take care of this connection until ConnectStream.CallDone()
  2823.                 else if (parseStatus == DataParseStatus.Done) {
  2824.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() [The response stream is ready] parseStatus = DataParseStatus.Done");
  2825.                     goto done;
  2826.                 }
  2827.                
  2828.                 //
  2829.                 // we may reach the end of our buffer only when parsing headers.
  2830.                 // this can happen when the header section is bigger than our initial 4k guess
  2831.                 // which should be a good assumption in 99.9% of the cases. what we do here is:
  2832.                 // 1) if there's a single BIG header (bigger than the current size) we will need to
  2833.                 // grow the buffer before we move data over and read more data.
  2834.                 // 2) move unparsed data to the beginning of the buffer and read more data in the
  2835.                 // remaining part of the data.
  2836.                 //
  2837.                 if (parseStatus == DataParseStatus.NeedMoreData) {
  2838.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() OLD buffer. m_ReadBuffer.Length:" + m_ReadBuffer.Length.ToString() + " m_BytesRead:" + m_BytesRead.ToString() + " m_BytesScanned:" + m_BytesScanned.ToString());
  2839.                     int unparsedDataSize = m_BytesRead - m_BytesScanned;
  2840.                     if (unparsedDataSize != 0) {
  2841.                         if (m_BytesScanned == 0 && m_BytesRead == m_ReadBuffer.Length) {
  2842.                             //
  2843.                             // 1) we need to grow the buffer, move the unparsed data to the beginning of the buffer before reading more data.
  2844.                             // since the buffer size is 4k, should we just double
  2845.                             //
  2846.                                 /*+ ReadBufferSize*/                            byte[] newReadBuffer = new byte[m_ReadBuffer.Length * 2];
  2847.                             Buffer.BlockCopy(m_ReadBuffer, 0, newReadBuffer, 0, m_BytesRead);
  2848.                             m_ReadBuffer = newReadBuffer;
  2849.                         }
  2850.                         else {
  2851.                             //
  2852.                             // just move data around in the same buffer.
  2853.                             //
  2854.                             Buffer.BlockCopy(m_ReadBuffer, m_BytesScanned, m_ReadBuffer, 0, unparsedDataSize);
  2855.                         }
  2856.                     }
  2857.                     //
  2858.                     // update indexes and offsets in the new buffer
  2859.                     //
  2860.                     m_BytesRead = unparsedDataSize;
  2861.                     m_BytesScanned = 0;
  2862.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() NEW or shifted buffer. m_ReadBuffer.Length:" + m_ReadBuffer.Length.ToString() + " m_BytesRead:" + m_BytesRead.ToString() + " m_BytesScanned:" + m_BytesScanned.ToString());
  2863.                    
  2864.                     if (currentRequest != null) {
  2865.                         //
  2866.                         // This case means that we still parsing the headers, so need to post another read in the async case
  2867.                        
  2868.                         if (currentRequest.Async) {
  2869.                             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() Reposting Async Read. Buffer:" + ValidationHelper.HashString(m_ReadBuffer) + " BytesScanned:" + m_BytesScanned.ToString());
  2870.                            
  2871.                             if (Thread.CurrentThread.IsThreadPoolThread) {
  2872.                                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() Calling PostReceive().");
  2873.                                 PostReceive();
  2874.                             }
  2875.                             else {
  2876.                                 // Offload to the threadpool to protect against the case where one request's thread posts IO that another request
  2877.                                 // depends on, but the first thread dies in the mean time.
  2878.                                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() ThreadPool.UnsafeQueueUserWorkItem(m_PostReceiveDelegate, this)");
  2879.                                 ThreadPool.UnsafeQueueUserWorkItem(m_PostReceiveDelegate, this);
  2880.                             }
  2881.                         }
  2882.                     }
  2883.                 }
  2884.             }
  2885.             //
  2886.             // Any exception is processed by HandleError() and swallowed to avoid throwing on a thread pool
  2887.             // In the sync case the HandleError() will abort the request so the caller will pick up the result.
  2888.             //
  2889.             catch (Exception exception) {
  2890.                 if (NclUtilities.IsFatal(exception))
  2891.                     throw;
  2892.                
  2893.                 requestDone = true;
  2894.                
  2895.                 if (m_InnerException == null)
  2896.                     m_InnerException = exception;
  2897.                
  2898.                 // Notify request's SubmitWriteStream that a socket error happened. This will cause future writes to
  2899.                 // throw an IOException.
  2900.                 HttpWebRequest curRequest = m_CurrentRequest;
  2901.                 if (curRequest != null) {
  2902.                     curRequest.ErrorStatusCodeNotify(this, false, true);
  2903.                 }
  2904.                
  2905.                 HandleErrorWithReadDone(WebExceptionStatus.ReceiveFailure, ref returnResult);
  2906.             }
  2907.             done:
  2908.            
  2909.             try {
  2910.                 if ((continueResponseData != null || (returnResult != null && returnResult.IsNotEmpty)) && currentRequest != null) {
  2911.                     // if returnResult is not empty it must also contain some result for the currently active request
  2912.                     // Since it could be a POST request waiting on the body submit, signal the body here
  2913.                     currentRequest.SetRequestContinue(continueResponseData);
  2914.                 }
  2915.             }
  2916.             finally {
  2917.                 ConnectionReturnResult.SetResponses(returnResult);
  2918.             }
  2919.            
  2920.             return requestDone;
  2921.         }
  2922.        
  2923.        
  2924.         // This method is called by ConnectStream, only when resubmitting
  2925.         // We have sent the headers already in HttpWebRequest.EndSubmitRequest()
  2926.         // which calls ConnectStream.WriteHeaders() which calls to HttpWebRequest.EndWriteHeaders()
  2927.         // which calls ConnectStream.ResubmitWrite() which calls in here
  2928.         internal void Write(ScatterGatherBuffers writeBuffer)
  2929.         {
  2930.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::Write(ScatterGatherBuffers) networkstream#" + ValidationHelper.HashString(NetworkStream));
  2931.             //
  2932.             // parameter validation
  2933.             //
  2934.             GlobalLog.Assert(writeBuffer != null, "Connection#{0}::Write(ScatterGatherBuffers)|writeBuffer == null", ValidationHelper.HashString(this));
  2935.             //
  2936.             // set up array for MultipleWrite call
  2937.             // note that GetBuffers returns null if we never wrote to it.
  2938.             //
  2939.             BufferOffsetSize[] buffers = writeBuffer.GetBuffers();
  2940.             if (buffers != null) {
  2941.                 //
  2942.                 // This will block writing the buffers out.
  2943.                 //
  2944.                 MultipleWrite(buffers);
  2945.             }
  2946.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::Write(ScatterGatherBuffers) this:" + ValidationHelper.HashString(this) + " writeBuffer.Size:" + writeBuffer.Length.ToString());
  2947.         }
  2948.        
  2949.        
  2950. /*++
  2951.             FindChunkEntitySize - Find the total size of a chunked entity body.
  2952.             An internal utility function. This is called when we have chunk encoded
  2953.             data in our internal receive buffer and want to know how big the total
  2954.             entity body is. We'll parse through it, looking for the terminating
  2955.             0CRLFCRLF string. We return -1 if we can't find it.
  2956.             Input:
  2957.                     buffer              - Buffer to be checked
  2958.                     offset              - Offset into buffer to start checking.
  2959.                     size              - Number of bytes to check.
  2960.             Returns:
  2961.                     The total size of the chunked entity body, or -1
  2962.                     if it can't find determine it. We'll return 0 if there's a
  2963.                     syntax error ensizeered.
  2964.         --*/       
  2965.         private static int FindChunkEntitySize(byte[] buffer, int offset, int size)
  2966.         {
  2967.             BufferChunkBytes BufferStruct = new BufferChunkBytes();
  2968.            
  2969.             int EndOffset;
  2970.             int StartOffset;
  2971.             int BytesTaken;
  2972.             int ChunkLength;
  2973.             StartOffset = offset;
  2974.             EndOffset = offset + size;
  2975.             BufferStruct.Buffer = buffer;
  2976.            
  2977.             //
  2978.             // While we haven't reached the end, loop through the buffer. Get
  2979.             // the chunk length, and if we can do that and it's not zero figure
  2980.             // out how many bytes are taken up by extensions and CRLF. If we
  2981.             // have enough for that, add the chunk length to our offset and see
  2982.             // if we've reached the end. If the chunk length is 0 at any point
  2983.             // we might have all the chunks. Skip the CRLF and footers and next
  2984.             // CRLF, and if that all works return the index where we are.
  2985.             //
  2986.            
  2987.             while (offset < EndOffset) {
  2988.                 // Read the chunk size.
  2989.                
  2990.                 BufferStruct.Offset = offset;
  2991.                 BufferStruct.Count = size;
  2992.                
  2993.                 BytesTaken = ChunkParse.GetChunkSize(BufferStruct, out ChunkLength);
  2994.                
  2995.                 // See if we have enough data to read the chunk size.
  2996.                
  2997.                 if (BytesTaken == -1) {
  2998.                     // Didn't, so return -1.
  2999.                     return -1;
  3000.                 }
  3001.                
  3002.                 // Make sure we didn't have a syntax error in the parse.
  3003.                
  3004.                 if (BytesTaken == 0) {
  3005.                     return 0;
  3006.                 }
  3007.                
  3008.                 // Update our state for what we've taken.
  3009.                
  3010.                 offset += BytesTaken;
  3011.                 size -= BytesTaken;
  3012.                
  3013.                 // If the chunk length isn't 0, skip the extensions and CRLF.
  3014.                
  3015.                 if (ChunkLength != 0) {
  3016.                     // Not zero, skip extensions.
  3017.                    
  3018.                     BufferStruct.Offset = offset;
  3019.                     BufferStruct.Count = size;
  3020.                    
  3021.                     BytesTaken = ChunkParse.SkipPastCRLF(BufferStruct);
  3022.                    
  3023.                     // If we ran out of buffer doing it or had an error, return -1.
  3024.                    
  3025.                     if (BytesTaken <= 0) {
  3026.                         return BytesTaken;
  3027.                     }
  3028.                    
  3029.                     // Update our state for what we took.
  3030.                    
  3031.                     offset += BytesTaken;
  3032.                     size -= BytesTaken;
  3033.                    
  3034.                     // Now update our state for the chunk length and trailing CRLF.
  3035.                     offset += (ChunkLength + CRLFSize);
  3036.                     size -= (ChunkLength + CRLFSize);
  3037.                    
  3038.                    
  3039.                 }
  3040.                 else {
  3041.                     // The chunk length is 0. Skip the CRLF, then the footers.
  3042.                    
  3043.                     if (size < CRLFSize) {
  3044.                         // Not enough left for CRLF
  3045.                        
  3046.                         return -1;
  3047.                     }
  3048.                    
  3049.                     offset += CRLFSize;
  3050.                     size -= CRLFSize;
  3051.                    
  3052.                     // Skip the footers. We'll loop while we don't have a CRLF
  3053.                     // at the current offset.
  3054.                     while (size >= CRLFSize && (buffer[offset] != '\r' && buffer[offset + 1] != '\n')) {
  3055.                         BufferStruct.Offset = offset;
  3056.                         BufferStruct.Count = size;
  3057.                        
  3058.                         BytesTaken = ChunkParse.SkipPastCRLF(BufferStruct);
  3059.                        
  3060.                         // Make sure we had enough.
  3061.                        
  3062.                         if (BytesTaken <= 0) {
  3063.                             return BytesTaken;
  3064.                         }
  3065.                        
  3066.                         // Had enough, so update our sizes.
  3067.                         offset += BytesTaken;
  3068.                         size -= BytesTaken;
  3069.                     }
  3070.                    
  3071.                     // If we get here, either we found the last CRLF or we ran out
  3072.                     // of buffer. See which it is.
  3073.                    
  3074.                     if (size >= CRLFSize) {
  3075.                         // Found the last bit, return the size including the last CRLF
  3076.                         // after that.
  3077.                        
  3078.                         return (offset + CRLFSize) - StartOffset;
  3079.                     }
  3080.                     else {
  3081.                         // Ran out of buffer.
  3082.                         return -1;
  3083.                     }
  3084.                 }
  3085.                
  3086.             }
  3087.            
  3088.             return -1;
  3089.         }
  3090.        
  3091. /*++
  3092.             PostReceiveWrapper - Post a receive from a worker thread.
  3093.             This is our delegate, used for posting receives from a worker thread.
  3094.             We do this when we can't be sure that we're already on a worker thread,
  3095.             and we don't want to post from a client thread because if it goes away
  3096.             I/O gets cancelled.
  3097.             Input:
  3098.             state          - a null object
  3099.             Returns:
  3100.         --*/       
  3101.         private static void PostReceiveWrapper(object state)
  3102.         {
  3103.             Connection thisConnection = state as Connection;
  3104.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(thisConnection) + "::PostReceiveWrapper", "Cnt#" + ValidationHelper.HashString(thisConnection));
  3105.             GlobalLog.Assert(thisConnection != null, "Connection#{0}::PostReceiveWrapper()|thisConnection == null", ValidationHelper.HashString(thisConnection));
  3106.            
  3107.             thisConnection.PostReceive();
  3108.            
  3109.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(thisConnection) + "::PostReceiveWrapper");
  3110.         }
  3111.        
  3112.         private void PostReceive()
  3113.         {
  3114.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::PostReceive", "");
  3115.            
  3116.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::PostReceive() m_ReadBuffer:" + ValidationHelper.HashString(m_ReadBuffer) + " Length:" + m_ReadBuffer.Length.ToString());
  3117.            
  3118.             try {
  3119.                 GlobalLog.Assert(m_BytesScanned == 0, "PostReceive()|A receive should not be posted when m_BytesScanned != 0 (the data should be moved to offset 0).");
  3120.                
  3121.                 if (m_LastAsyncResult != null && !m_LastAsyncResult.IsCompleted)
  3122.                     throw new InternalException();
  3123.                 //This may cause duplicate requests if we let it through in retail
  3124.                
  3125.                 m_LastAsyncResult = UnsafeBeginRead(m_ReadBuffer, m_BytesRead, m_ReadBuffer.Length - m_BytesRead, m_ReadCallback, this);
  3126.                 if (m_LastAsyncResult.CompletedSynchronously) {
  3127.                     ReadCallback(m_LastAsyncResult);
  3128.                 }
  3129.             }
  3130.             catch (Exception exception) {
  3131.                 // Notify request's SubmitWriteStream that a socket error happened. This will cause future writes to
  3132.                 // throw an IOException.
  3133.                 HttpWebRequest curRequest = m_CurrentRequest;
  3134.                 if (curRequest != null) {
  3135.                     curRequest.ErrorStatusCodeNotify(this, false, true);
  3136.                 }
  3137.                
  3138.                 //ASYNCISSUE
  3139.                 ConnectionReturnResult returnResult = null;
  3140.                 HandleErrorWithReadDone(WebExceptionStatus.ReceiveFailure, ref returnResult);
  3141.                 ConnectionReturnResult.SetResponses(returnResult);
  3142.                 GlobalLog.LeaveException("Connection#" + ValidationHelper.HashString(this) + "::PostReceive", exception);
  3143.             }
  3144.            
  3145.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::PostReceive");
  3146.         }
  3147.        
  3148.        
  3149.        
  3150.         private static void TunnelThroughProxyWrapper(IAsyncResult result)
  3151.         {
  3152.             if (result.CompletedSynchronously) {
  3153.                 return;
  3154.             }
  3155.            
  3156.             bool success = false;
  3157.             WebExceptionStatus ws = WebExceptionStatus.ConnectFailure;
  3158.             HttpWebRequest req = (HttpWebRequest)((LazyAsyncResult)result).AsyncObject;
  3159.             Connection conn = (Connection)((TunnelStateObject)result.AsyncState).Connection;
  3160.             HttpWebRequest originalReq = (HttpWebRequest)((TunnelStateObject)result.AsyncState).OriginalRequest;
  3161.            
  3162.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(conn) + "::TunnelThroughProxyCallback");
  3163.            
  3164.             try {
  3165.                 req.EndGetResponse(result);
  3166.                 HttpWebResponse connectResponse = (HttpWebResponse)req.GetResponse();
  3167.                 ConnectStream connectStream = (ConnectStream)connectResponse.GetResponseStream();
  3168.                
  3169.                 // this stream will be used as the real stream for TlsStream
  3170.                 conn.NetworkStream = new NetworkStream(connectStream.Connection.NetworkStream, true);
  3171.                 // This will orphan the original connect stream now owned by tunnelStream
  3172.                 connectStream.Connection.NetworkStream.ConvertToNotSocketOwner();
  3173.                 success = true;
  3174.             }
  3175.            
  3176.             catch (Exception exception) {
  3177.                 if (conn.m_InnerException == null)
  3178.                     conn.m_InnerException = exception;
  3179.                
  3180.                 if (exception is WebException) {
  3181.                     ws = ((WebException)exception).Status;
  3182.                 }
  3183.                
  3184.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(conn) + "::TunnelThroughProxyCallback() exception occurred: " + exception);
  3185.             }
  3186.             if (!success) {
  3187.                 ConnectionReturnResult returnResult = null;
  3188.                 conn.HandleError(false, false, ws, ref returnResult);
  3189.                 ConnectionReturnResult.SetResponses(returnResult);
  3190.                 return;
  3191.             }
  3192.            
  3193.             conn.CompleteConnection(true, originalReq);
  3194.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(conn) + "::TunnelThroughProxyCallback");
  3195.         }
  3196.        
  3197.        
  3198.        
  3199.        
  3200.         private bool TunnelThroughProxy(Uri proxy, HttpWebRequest originalRequest, bool async)
  3201.         {
  3202.             GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::TunnelThroughProxy", "proxy=" + proxy + ", async=" + async + ", originalRequest #" + ValidationHelper.HashString(originalRequest));
  3203.            
  3204.             bool result = false;
  3205.             HttpWebRequest connectRequest = null;
  3206.             HttpWebResponse connectResponse = null;
  3207.            
  3208.             try {
  3209.                     // new Uri("https://" + originalRequest.Address.GetParts(UriComponents.HostAndPort, UriFormat.UriEscaped)),
  3210.                 connectRequest = new HttpWebRequest(proxy, originalRequest.Address, originalRequest);
  3211.                
  3212.                 connectRequest.Credentials = originalRequest.InternalProxy == null ? null : originalRequest.InternalProxy.Credentials;
  3213.                 connectRequest.InternalProxy = null;
  3214.                 connectRequest.PreAuthenticate = true;
  3215.                
  3216.                 if (async) {
  3217.                     TunnelStateObject o = new TunnelStateObject(originalRequest, this);
  3218.                     IAsyncResult asyncResult = connectRequest.BeginGetResponse(m_TunnelCallback, o);
  3219.                     if (!asyncResult.CompletedSynchronously) {
  3220.                         GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::TunnelThroughProxy completed asynchronously", true);
  3221.                         return true;
  3222.                     }
  3223.                     connectResponse = (HttpWebResponse)connectRequest.EndGetResponse(asyncResult);
  3224.                 }
  3225.                 else {
  3226.                     connectResponse = (HttpWebResponse)connectRequest.GetResponse();
  3227.                 }
  3228.                
  3229.                 ConnectStream connectStream = (ConnectStream)connectResponse.GetResponseStream();
  3230.                
  3231.                 // this stream will be used as the real stream for TlsStream
  3232.                 NetworkStream = new NetworkStream(connectStream.Connection.NetworkStream, true);
  3233.                 // This will orphan the original connect stream now owned by tunnelStream
  3234.                 connectStream.Connection.NetworkStream.ConvertToNotSocketOwner();
  3235.                 result = true;
  3236.             }
  3237.             catch (Exception exception) {
  3238.                 if (m_InnerException == null)
  3239.                     m_InnerException = exception;
  3240.                 GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::TunnelThroughProxy() exception occurred: " + exception);
  3241.             }
  3242.            
  3243.             GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::TunnelThroughProxy", result);
  3244.            
  3245.             return result;
  3246.         }
  3247.        
  3248.         //
  3249.         // CheckNonIdle - called after update of the WriteList/WaitList,
  3250.         // upon the departure of our Idle state our, BusyCount will
  3251.         // go to non-0, then we need to mark this transition
  3252.         //
  3253.        
  3254.         private void CheckNonIdle()
  3255.         {
  3256.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CheckNonIdle()");
  3257.             if (m_Idle && this.BusyCount != 0) {
  3258.                 m_Idle = false;
  3259.                 ServicePoint.IncrementConnection();
  3260.             }
  3261.         }
  3262.        
  3263.         //
  3264.         // CheckIdle - called after update of the WriteList/WaitList,
  3265.         // specifically called after we remove entries
  3266.         //
  3267.        
  3268.         private void CheckIdle()
  3269.         {
  3270.             // The timer thread is allowed to call this. (It doesn't call user code and doesn't block.)
  3271.             GlobalLog.ThreadContract(ThreadKinds.Unknown, ThreadKinds.SafeSources | ThreadKinds.Finalization | ThreadKinds.Timer, "Connection#" + ValidationHelper.HashString(this) + "::CheckIdle");
  3272.            
  3273.             GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CheckIdle() m_Idle = " + m_Idle + ", BusyCount = " + BusyCount);
  3274.             if (!m_Idle && this.BusyCount == 0) {
  3275.                 m_Idle = true;
  3276.                 ServicePoint.DecrementConnection();
  3277.                 if (ConnectionGroup != null) {
  3278.                     GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::CheckIdle() - calling ConnectionGoneIdle()");
  3279.                     ConnectionGroup.ConnectionGoneIdle();
  3280.                 }
  3281.                 // Remember the moment when this connection went idle.
  3282.                 m_IdleSinceUtc = DateTime.UtcNow;
  3283.             }
  3284.         }
  3285.        
  3286.         //
  3287.         // DebugDumpArrayListEntries - debug goop
  3288.         //
  3289.         [System.Diagnostics.Conditional("TRAVE")]
  3290.         private void DebugDumpArrayListEntries(ArrayList list, string listType)
  3291.         {
  3292.             for (int i = 0; i < list.Count; i++) {
  3293.                 GlobalLog.Print(listType + "[" + i.ToString() + "] Req: #" + ValidationHelper.HashString(list[i] as HttpWebRequest));
  3294.             }
  3295.         }
  3296.        
  3297.        
  3298.         //
  3299.         // Validation & debugging
  3300.         //
  3301.         [System.Diagnostics.Conditional("DEBUG")]
  3302.         internal void Debug(int requestHash)
  3303.         {
  3304.         }
  3305.        
  3306.     }
  3307. }

Developer Fusion