We need you! We're working hard on the next version of Developer Fusion - Let us know what you think we should be up to!

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(th