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

  1. // ------------------------------------------------------------------------------
  2. // <copyright file="CommandStream.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. //
  16. namespace System.Net
  17. {
  18.    
  19.     using System.Collections;
  20.     using System.IO;
  21.     using System.Security.Cryptography.X509Certificates;
  22.     using System.Net.Sockets;
  23.     using System.Security.Permissions;
  24.     using System.Text;
  25.     using System.Threading;
  26.    
  27.     /// <devdoc>
  28.     /// <para>
  29.     /// Impliments basic sending and receiving of network commands.
  30.     /// Handles generic parsing of server responses and provides
  31.     /// a Pipeline sequencing mechnism for sending the commands to the
  32.     /// server.
  33.     /// </para>
  34.     /// </devdoc>
  35.     internal class CommandStream : PooledStream
  36.     {
  37.        
  38.         private static readonly AsyncCallback m_WriteCallbackDelegate = new AsyncCallback(WriteCallback);
  39.         private static readonly AsyncCallback m_ReadCallbackDelegate = new AsyncCallback(ReadCallback);
  40.        
  41.         private bool m_RecoverableFailure;
  42.        
  43.         //
  44.         // Active variables used for the command state machine
  45.         //
  46.        
  47.         protected WebRequest m_Request;
  48.         protected bool m_Async;
  49.         private bool m_Aborted;
  50.        
  51.         protected PipelineEntry[] m_Commands;
  52.         protected int m_Index;
  53.         private bool m_DoRead;
  54.         private bool m_DoSend;
  55.         private ResponseDescription m_CurrentResponseDescription;
  56.         protected string m_AbortReason;
  57.        
  58.         const int _WaitingForPipeline = 1;
  59.         const int _CompletedPipeline = 2;
  60.        
  61.        
  62.         /// <devdoc>
  63.         /// <para>
  64.         /// Setups and Creates a NetworkStream connection to the server
  65.         /// perform any initalization if needed
  66.         /// </para>
  67.         /// </devdoc>
  68.         internal CommandStream(ConnectionPool connectionPool, TimeSpan lifetime, bool checkLifetime) : base(connectionPool, lifetime, checkLifetime)
  69.         {
  70.         }
  71.        
  72.        
  73.         internal virtual void Abort(Exception e)
  74.         {
  75.             GlobalLog.Print("CommandStream" + ValidationHelper.HashString(this) + "::Abort() - closing control Stream");
  76.            
  77.             lock (this) {
  78.                 if (m_Aborted)
  79.                     return;
  80.                 m_Aborted = true;
  81.                 CanBePooled = false;
  82.             }
  83.            
  84.             try {
  85.                 base.Close(0);
  86.             }
  87.             finally {
  88.                 if (e != null) {
  89.                     InvokeRequestCallback(e);
  90.                 }
  91.                 else {
  92.                     InvokeRequestCallback(null);
  93.                 }
  94.             }
  95.         }
  96.        
  97.         /// <summary>
  98.         /// <para>Used to reset the connection</para>
  99.         /// </summary>
  100.         protected override void Dispose(bool disposing)
  101.         {
  102.             GlobalLog.Print("CommandStream" + ValidationHelper.HashString(this) + "::Close()");
  103.             InvokeRequestCallback(null);
  104.            
  105.             // Do not call base.Dispose(bool), which would close the web request.
  106.             // This stream effectively should be a wrapper around a web
  107.             // request that does not own the web request.
  108.         }
  109.        
  110.         /// <summary>
  111.         /// <para>A WebRequest can use a different Connection after an Exception is set, or a null is passed
  112.         /// to mark completion. We shouldn't continue calling the Request.RequestCallback after that point</para>
  113.         /// </summary>
  114.         protected void InvokeRequestCallback(object obj)
  115.         {
  116.             WebRequest webRequest = m_Request;
  117.             if (webRequest != null) {
  118.                 webRequest.RequestCallback(obj);
  119.             }
  120.         }
  121.        
  122.         /// <summary>
  123.         /// <para>Indicates that we caught an error that should allow us to resubmit a request</para>
  124.         /// </summary>
  125.         internal bool RecoverableFailure {
  126.             get { return m_RecoverableFailure; }
  127.         }
  128.        
  129.         /// <summary>
  130.         /// <para>We only offer recovery, if we're at the start of the first command</para>
  131.         /// </summary>
  132.         protected void MarkAsRecoverableFailure()
  133.         {
  134.             if (m_Index <= 1) {
  135.                 m_RecoverableFailure = true;
  136.             }
  137.         }
  138.        
  139.         /// <devdoc>
  140.         /// <para>
  141.         /// Setups and Creates a NetworkStream connection to the server
  142.         /// perform any initalization if needed
  143.         /// </para>
  144.         /// </devdoc>
  145.        
  146.         internal Stream SubmitRequest(WebRequest request, bool async, bool readInitalResponseOnConnect)
  147.         {
  148.             ClearState();
  149.             UpdateLifetime();
  150.             PipelineEntry[] commands = BuildCommandsList(request);
  151.             InitCommandPipeline(request, commands, async);
  152.             if (readInitalResponseOnConnect && JustConnected) {
  153.                 m_DoSend = false;
  154.                 m_Index = -1;
  155.             }
  156.             return ContinueCommandPipeline();
  157.         }
  158.        
  159.         protected virtual void ClearState()
  160.         {
  161.             InitCommandPipeline(null, null, false);
  162.         }
  163.        
  164.         protected virtual PipelineEntry[] BuildCommandsList(WebRequest request)
  165.         {
  166.             return null;
  167.         }
  168.        
  169.         protected Exception GenerateException(WebExceptionStatus status, Exception innerException)
  170.         {
  171.                 /* no response */            return new WebException(NetRes.GetWebStatusString("net_connclosed", status), innerException, status, null);
  172.         }
  173.        
  174.        
  175.         protected Exception GenerateException(FtpStatusCode code, string statusDescription, Exception innerException)
  176.         {
  177.            
  178.             return new WebException(SR.GetString(SR.net_servererror, NetRes.GetWebStatusCodeString(code, statusDescription)), innerException, WebExceptionStatus.ProtocolError, null);
  179.         }
  180.        
  181.        
  182.         protected void InitCommandPipeline(WebRequest request, PipelineEntry[] commands, bool async)
  183.         {
  184.             m_Commands = commands;
  185.             m_Index = 0;
  186.             m_Request = request;
  187.             m_Aborted = false;
  188.             m_DoRead = true;
  189.             m_DoSend = true;
  190.             m_CurrentResponseDescription = null;
  191.             m_Async = async;
  192.             m_RecoverableFailure = false;
  193.             m_AbortReason = string.Empty;
  194.         }
  195.        
  196.         internal void CheckContinuePipeline()
  197.         {
  198.             if (m_Async)
  199.                 return;
  200.             try {
  201.                 ContinueCommandPipeline();
  202.             }
  203.             catch (Exception e) {
  204.                 Abort(e);
  205.             }
  206.             catch {
  207.                 Abort(new Exception(SR.GetString(SR.net_nonClsCompliantException)));
  208.             }
  209.         }
  210.        
  211.         /// Pipelined command resoluton, how this works:
  212.         /// a list of commands that need to be sent to the FTP server are spliced together into a array,
  213.         /// each command such STOR, PORT, etc, is sent to the server, then the response is parsed into a string,
  214.         /// with the response, the delegate is called, which returns an instruction (either continue, stop, or read additional
  215.         /// responses from server).
  216.         ///
  217.         /// When done calling Close() to Notify ConnectionGroup that we are free
  218.         protected Stream ContinueCommandPipeline()
  219.         {
  220.             // In async case, The BeginWrite can actually result in a
  221.             // series of synchronous completions that eventually close
  222.             // the connection. So we need to save the members that
  223.             // we need to access, since they may not be valid after
  224.             // BeginWrite returns
  225.             bool async = m_Async;
  226.             while (m_Index < m_Commands.Length) {
  227.                 if (m_DoSend) {
  228.                     if (m_Index < 0)
  229.                         throw new InternalException();
  230.                    
  231.                     byte[] sendBuffer = Encoding.GetBytes(m_Commands[m_Index].Command);
  232.                     if (Logging.On) {
  233.                         string sendCommand = m_Commands[m_Index].Command.Substring(0, m_Commands[m_Index].Command.Length - 2);
  234.                         if (m_Commands[m_Index].HasFlag(PipelineEntryFlags.DontLogParameter)) {
  235.                             int index = sendCommand.IndexOf(' ');
  236.                             if (index != -1)
  237.                                 sendCommand = sendCommand.Substring(0, index) + " ********";
  238.                         }
  239.                         Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_sending_command, sendCommand));
  240.                     }
  241.                     try {
  242.                         if (async) {
  243.                             BeginWrite(sendBuffer, 0, sendBuffer.Length, m_WriteCallbackDelegate, this);
  244.                         }
  245.                         else {
  246.                             Write(sendBuffer, 0, sendBuffer.Length);
  247.                         }
  248.                     }
  249.                     catch (IOException) {
  250.                         MarkAsRecoverableFailure();
  251.                         throw;
  252.                     }
  253.                     catch {
  254.                         throw;
  255.                     }
  256.                    
  257.                     if (async) {
  258.                         return null;
  259.                     }
  260.                 }
  261.                
  262.                 Stream stream = null;
  263.                 bool isReturn = PostSendCommandProcessing(ref stream);
  264.                 if (isReturn) {
  265.                     return stream;
  266.                 }
  267.             }
  268.            
  269.             lock (this) {
  270.                 Close();
  271.             }
  272.            
  273.             return null;
  274.         }
  275.         //
  276.         private bool PostSendCommandProcessing(ref Stream stream)
  277.         {
  278.             /*
  279.             ** I don;t see how this code can be still relevant, remove it of no problems observed **
  280.             //
  281.             // This is a general race condition in Sync mode, if the server returns an error
  282.             // after we open the data connection, we will be off reading the data connection,
  283.             // and not the control connection. The best we can do is try to poll, and in the
  284.             // the worst case, we will timeout on establishing the data connection.
  285.             //
  286.             if (!m_DoRead && !m_Async) {
  287.                 m_DoRead = Poll(100 * 1000, SelectMode.SelectRead);  // Poll is in Microseconds.
  288.             }
  289. */           
  290. if (m_DoRead) {
  291.                 // In async case, The next call can actually result in a
  292.                 // series of synchronous completions that eventually close
  293.                 // the connection. So we need to save the members that
  294.                 // we need to access, since they may not be valid after the
  295.                 // next call returns
  296.                 bool async = m_Async;
  297.                 int index = m_Index;
  298.                 PipelineEntry[] commands = m_Commands;
  299.                
  300.                 try {
  301.                     ResponseDescription response = ReceiveCommandResponse();
  302.                     if (async) {
  303.                         return true;
  304.                     }
  305.                     m_CurrentResponseDescription = response;
  306.                 }
  307.                 catch {
  308.                     // If we get an exception on the QUIT command (which is
  309.                     // always the last command), ignore the final exception
  310.                     // and continue with the pipeline regardlss of sync/async
  311.                     if (index < 0 || index >= commands.Length || commands[index].Command != "QUIT\r\n")
  312.                         throw;
  313.                 }
  314.             }
  315.             return PostReadCommandProcessing(ref stream);
  316.         }
  317.         //
  318.         private bool PostReadCommandProcessing(ref Stream stream)
  319.         {
  320.             if (m_Index >= m_Commands.Length)
  321.                 return false;
  322.            
  323.             // Set up front to prevent a race condition on result == PipelineInstruction.Pause
  324.             m_DoSend = false;
  325.             m_DoRead = false;
  326.            
  327.             PipelineInstruction result;
  328.             PipelineEntry entry;
  329.             if (m_Index == -1)
  330.                 entry = null;
  331.             else
  332.                 entry = m_Commands[m_Index];
  333.            
  334.             // Final QUIT command may get exceptions since the connectin
  335.             // may be already closed by the server. So there is no response
  336.             // to process, just advance the pipeline to continue
  337.             if (m_CurrentResponseDescription == null && entry.Command == "QUIT\r\n")
  338.                 result = PipelineInstruction.Advance;
  339.             else
  340.                 result = PipelineCallback(entry, m_CurrentResponseDescription, false, ref stream);
  341.            
  342.             if (result == PipelineInstruction.Abort) {
  343.                 Exception exception;
  344.                 if (m_AbortReason != string.Empty)
  345.                     exception = new WebException(m_AbortReason);
  346.                 else
  347.                     exception = GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
  348.                 Abort(exception);
  349.                 throw exception;
  350.             }
  351.             else if (result == PipelineInstruction.Advance) {
  352.                 m_CurrentResponseDescription = null;
  353.                 m_DoSend = true;
  354.                 m_DoRead = true;
  355.                 m_Index++;
  356.                
  357.             }
  358.             else if (result == PipelineInstruction.Pause) {
  359.                 //
  360.                 // PipelineCallback did an async operation and will have to re-enter again
  361.                 // Hold on for now
  362.                 //
  363.                 return true;
  364.             }
  365.             else if (result == PipelineInstruction.GiveStream) {
  366.                 //
  367.                 // We will have another response coming, don't send
  368.                 //
  369.                 m_CurrentResponseDescription = null;
  370.                 m_DoRead = true;
  371.                 if (m_Async) {
  372.                     // If they block in the requestcallback we should still continue the pipeline
  373.                     ContinueCommandPipeline();
  374.                     InvokeRequestCallback(stream);
  375.                 }
  376.                 return true;
  377.             }
  378.             else if (result == PipelineInstruction.Reread) {
  379.                 // Another response is expected after this one
  380.                 m_CurrentResponseDescription = null;
  381.                 m_DoRead = true;
  382.             }
  383.             return false;
  384.         }
  385.        
  386.         internal enum PipelineInstruction
  387.         {
  388.             Abort,
  389.             // aborts the pipeline
  390.             Advance,
  391.             // advances to the next pipelined command
  392.             Pause,
  393.             // Let async callback to continue the pipeline
  394.             Reread,
  395.             // rereads from the command socket
  396.             GiveStream
  397.             // returns with open data stream, let stream close to continue
  398.         }
  399.        
  400.         [Flags()]
  401.         internal enum PipelineEntryFlags
  402.         {
  403.             UserCommand = 1,
  404.             GiveDataStream = 2,
  405.             CreateDataConnection = 4,
  406.             DontLogParameter = 8
  407.         }
  408.        
  409.         internal class PipelineEntry
  410.         {
  411.             internal PipelineEntry(string command)
  412.             {
  413.                 Command = command;
  414.             }
  415.             internal PipelineEntry(string command, PipelineEntryFlags flags)
  416.             {
  417.                 Command = command;
  418.                 Flags = flags;
  419.             }
  420.             internal bool HasFlag(PipelineEntryFlags flags)
  421.             {
  422.                 return (Flags & flags) != 0;
  423.             }
  424.             internal string Command;
  425.             internal PipelineEntryFlags Flags;
  426.         }
  427.        
  428.         protected virtual PipelineInstruction PipelineCallback(PipelineEntry entry, ResponseDescription response, bool timeout, ref Stream stream)
  429.         {
  430.             return PipelineInstruction.Abort;
  431.         }
  432.        
  433.         //
  434.         // I/O callback methods
  435.         //
  436.        
  437.         /// <summary>
  438.         /// <para>Provides a wrapper for the async operations, so that the code can be shared with sync</para>
  439.         /// </summary>
  440.         private static void ReadCallback(IAsyncResult asyncResult)
  441.         {
  442.             ReceiveState state = (ReceiveState)asyncResult.AsyncState;
  443.             try {
  444.                 Stream stream = (Stream)state.Connection;
  445.                 int bytesRead = 0;
  446.                 try {
  447.                     bytesRead = stream.EndRead(asyncResult);
  448.                     if (bytesRead == 0)
  449.                         state.Connection.CloseSocket();
  450.                 }
  451.                 catch (IOException) {
  452.                     state.Connection.MarkAsRecoverableFailure();
  453.                     throw;
  454.                 }
  455.                 catch {
  456.                     throw;
  457.                 }
  458.                
  459.                 state.Connection.ReceiveCommandResponseCallback(state, bytesRead);
  460.             }
  461.             catch (Exception e) {
  462.                 state.Connection.Abort(e);
  463.             }
  464.         }
  465.        
  466.        
  467.         /// <summary>
  468.         /// <para>Provides a wrapper for the async write operations</para>
  469.         /// </summary>
  470.         private static void WriteCallback(IAsyncResult asyncResult)
  471.         {
  472.             CommandStream connection = (CommandStream)asyncResult.AsyncState;
  473.             try {
  474.                 try {
  475.                     connection.EndWrite(asyncResult);
  476.                 }
  477.                 catch (IOException) {
  478.                     connection.MarkAsRecoverableFailure();
  479.                     throw;
  480.                 }
  481.                 catch {
  482.                     throw;
  483.                 }
  484.                 Stream stream = null;
  485.                 if (connection.PostSendCommandProcessing(ref stream))
  486.                     return;
  487.                 connection.ContinueCommandPipeline();
  488.             }
  489.             catch (Exception e) {
  490.                 connection.Abort(e);
  491.             }
  492.         }
  493.        
  494.         //
  495.         // Read parsing methods and privates
  496.         //
  497.        
  498.         private byte[] m_Buffer = new byte[256];
  499.         private int m_BufferOffset;
  500.         private int m_BufferCount;
  501.         private Encoding m_Encoding = Encoding.UTF8;
  502.        
  503.        
  504.         protected Encoding Encoding {
  505.             get { return m_Encoding; }
  506.             set { m_Encoding = value; }
  507.         }
  508.        
  509.         /// <summary>
  510.         /// Sets Buffer = buffer, BufferOffset = offset, and BufferCount = count.
  511.         /// CAUTION! The buffer is not copied. The buffer reference is used.
  512.         /// </summary>
  513.         protected void SetBuffer(byte[] buffer, int offset, int count)
  514.         {
  515.             if (buffer == null)
  516.                 throw new ArgumentNullException("buffer");
  517.             if (offset < 0)
  518.                 throw new ArgumentException(SR.GetString(SR.net_value_cannot_be_negative), "offset");
  519.             if (count < 0)
  520.                 throw new ArgumentException(SR.GetString(SR.net_value_cannot_be_negative), "count");
  521.             if (offset + count > buffer.Length)
  522.                 throw new ArgumentException(SR.GetString(SR.net_offset_plus_count));
  523.             m_Buffer = buffer;
  524.             m_BufferOffset = offset;
  525.             m_BufferCount = count;
  526.         }
  527.        
  528.        
  529.         /// <summary>
  530.         /// This function is called a derived class to determine whether a response is valid, and when it is complete.
  531.         /// </summary>
  532.         protected virtual bool CheckValid(ResponseDescription response, ref int validThrough, ref int completeLength)
  533.         {
  534.             return false;
  535.         }
  536.        
  537.         /// <summary>
  538.         /// Kicks off an asynchronous or sync request to receive a response from the server.
  539.         /// Uses the Encoding <code>encoding</code> to transform the bytes received into a string to be
  540.         /// returned in the GeneralResponseDescription's StatusDescription field.
  541.         /// </summary>
  542.         private ResponseDescription ReceiveCommandResponse()
  543.         {
  544.             // These are the things that will be needed to maintain state
  545.             ReceiveState state = new ReceiveState(this);
  546.            
  547.             // If there are any bytes left over in the buffer, then attempt to form a string out of the bytes remaining, decoding it
  548.             // into a string using the implementor's encoding.
  549.             if (m_BufferCount > 0) {
  550.                 state.StringFromBuffer = Encoding.GetString(m_Buffer, m_BufferOffset, m_BufferCount);
  551.             }
  552.             try {
  553.                 // If a string of nonzero length was decoded from the buffered bytes, then we
  554.                 // will use this string as our first string to append to the response StatusBuffer, and we will
  555.                 // forego a Connection.Receive here.
  556.                
  557.                 // Note that we get the number of bytes in the byte encoding of the decoded string, in order to determine
  558.                 // how many bytes were used in the string. The number of bytes left in the buffer is decremented by this
  559.                 // amount (this will usually mean buffer count goes to zero) and increment the buffer offset to indicate
  560.                 // that there are a few buffer bytes left over.
  561.                 if (state.StringFromBuffer.Length > 0) {
  562.                     int bytesUsed = Encoding.GetByteCount(state.StringFromBuffer);
  563.                     m_BufferCount -= bytesUsed;
  564.                     m_BufferOffset += bytesUsed;
  565.                     ReceiveCommandResponseCallback(state, -1);
  566.                 }
  567.                 else {
  568.                     int bytesRead;
  569.                     int readOffset = 0;
  570.                     int readLength = state.Buffer.Length;
  571.                    
  572.                     // we didn't get anything out of the buffer -
  573.                     // this means that either the only contents of the buffer were less than a complete character in the encoding,
  574.                     // or that there was nothing in the buffer at all.
  575.                     if (m_BufferCount > 0) {
  576.                         // if there was anything in the buffer, then the buffer contents represent a partial character in the encoding used.
  577.                         // This means that in order to get anything productive done, we need to perform a Receive on the connection in order
  578.                         // to finish at least one character to send to CheckValid.
  579.                         // So copy the remaining contents of the buffer into the beginning of the receive buffer (state.Buffer).
  580.                         // Then try to fill the rest of the buffer with a receive.
  581.                        
  582.                         Buffer.BlockCopy(m_Buffer, m_BufferOffset, state.Buffer, 0, m_BufferCount);
  583.                         state.BytesInBuffer = m_BufferCount;
  584.                        
  585.                         readOffset = m_BufferCount;
  586.                         readLength = state.Buffer.Length - readOffset;
  587.                        
  588.                         m_BufferCount = 0;
  589.                         // buffer is flushed.
  590.                     }
  591.                    
  592.                    
  593.                     try {
  594.                         if (m_Async) {
  595.                             BeginRead(state.Buffer, readOffset, readLength, m_ReadCallbackDelegate, state);
  596.                             return null;
  597.                         }
  598.                         else {
  599.                             bytesRead = Read(state.Buffer, readOffset, readLength);
  600.                             if (bytesRead == 0)
  601.                                 CloseSocket();
  602.                             ReceiveCommandResponseCallback(state, bytesRead);
  603.                         }
  604.                     }
  605.                     catch (IOException) {
  606.                         MarkAsRecoverableFailure();
  607.                         throw;
  608.                     }
  609.                     catch {
  610.                         throw;
  611.                     }
  612.                 }
  613.             }
  614.             catch (Exception e) {
  615.                 if (e is WebException)
  616.                     throw;
  617.                 throw GenerateException(WebExceptionStatus.ReceiveFailure, e);
  618.             }
  619.             return state.Resp;
  620.         }
  621.        
  622.        
  623.         /// <summary>
  624.         /// ReceiveCommandResponseCallback is the main "while loop" of the ReceiveCommandResponse function family.
  625.         /// In general, what is does is perform an EndReceive() to complete the previous retrieval of bytes from the
  626.         /// server (unless it is using a buffered response) It then processes what is received by using the
  627.         /// implementing class's CheckValid() function, as described above. If the response is complete, it returns the single complete
  628.         /// response in the GeneralResponseDescription created in BeginReceiveComamndResponse, and buffers the rest as described above.
  629.         ///
  630.         /// If the resposne is not complete, it issues another Connection.BeginReceive, with callback ReceiveCommandResponse2,
  631.         /// so the action will continue at the next invocation of ReceiveCommandResponse2.
  632.         /// </summary>
  633.         /// <param name="asyncResult"></param>
  634.         ///
  635.         private void ReceiveCommandResponseCallback(ReceiveState state, int bytesRead)
  636.         {
  637.             // completeLength will be set to a nonnegative number by CheckValid if the response is complete:
  638.             // it will set completeLength to the length of a complete response.
  639.             int completeLength = -1;
  640.            
  641.             while (true) {
  642.                 int validThrough = state.ValidThrough;
  643.                 // passed to checkvalid
  644.                
  645.                 // If we got a string from decoding the contents of the Buffered response, then we didn't call BeginReceive, so don't
  646.                 // call end receive...
  647.                 if (state.StringFromBuffer.Length > 0) {
  648.                     // Append the string we got from the buffer, and flush it out.
  649.                     state.Resp.StatusBuffer.Append(state.StringFromBuffer);
  650.                     state.StringFromBuffer = "";
  651.                    
  652.                     // invoke checkvalid.
  653.                     if (!CheckValid(state.Resp, ref validThrough, ref completeLength)) {
  654.                         throw GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
  655.                     }
  656.                    
  657.                     // if the response is NOT complete, then we'll have to do a Receive. However, there may be
  658.                     // bytes left over in the buffer that were not decoded into the string.
  659.                     // Copy these bytes into the receive buffer so that they will not be lost in the next Receive().
  660.                     if (completeLength < 0) {
  661.                         Buffer.BlockCopy(m_Buffer, m_BufferOffset, state.Buffer, 0, m_BufferCount);
  662.                         state.BytesInBuffer = m_BufferCount;
  663.                         m_BufferCount = 0;
  664.                     }
  665.                     // else if the response is complete, then we can leave the buffer, possibly with leftover bytes, as is, and all is accurate.
  666.                    
  667.                     // Update the "last length of the statusBuffer" info.
  668.                     state.LastResponseLength += state.StringFromBuffer.Length;
  669.                 }
  670.                 // we did a Connection.BeginReceive. Note that in this case, all bytes received are in the receive buffer (because bytes from
  671.                 // the buffer were transferred there if necessary
  672.                 else {
  673.                     // this indicates the connection was closed.
  674.                     if (bytesRead <= 0) {
  675.                         throw GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
  676.                     }
  677.                    
  678.                     // There may have been bytes in the receive buffer before, so increase the count of bytes by the number read from (receive).
  679.                     state.BytesInBuffer += bytesRead;
  680.                    
  681.                     // decode the bytes in the receive buffer into a string, append it to the statusbuffer, and invoke checkvalid.
  682.                     string szResponse = Encoding.GetString(state.Buffer, 0, state.BytesInBuffer);
  683.                     state.Resp.StatusBuffer.Append(szResponse);
  684.                     if (!CheckValid(state.Resp, ref validThrough, ref completeLength)) {
  685.                         throw GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
  686.                     }
  687.                    
  688.                     // If the response is complete, then determine how many bytes are left over...these bytes need to be set into Buffer.
  689.                     // To do this we see how many characters from the last appended string are used up in the complete response.
  690.                     // This number is completeLength - state.LastResponseLength. Note at every execution of ReceiveCommandresponse2,
  691.                     // the invariant is that the bytes in the receive buffer are exactly those bytes beyond the end of the last status buffer
  692.                     // string, so the encoding in bytes of the status buffer from (state.LastResponseLength) to (completeLength) are those
  693.                     // bytes used from the last string appended, and the rest are to be buffered.
  694.                     if (completeLength >= 0) {
  695.                         if (completeLength < state.LastResponseLength) {
  696.                             throw GenerateException(WebExceptionStatus.ServerProtocolViolation, null);
  697.                             // CompleteLength Too Low
  698.                         }
  699.                        
  700.                         int bytesFromThisReceive = Encoding.GetByteCount(szResponse.Substring(0, completeLength - state.LastResponseLength));
  701.                         // bytes to buffer
  702.                         if (state.BytesInBuffer > bytesFromThisReceive) {
  703.                             SetBuffer(state.Buffer, bytesFromThisReceive, state.BytesInBuffer - bytesFromThisReceive);
  704.                         }
  705.                     }
  706.                     // not complete. Any bytes that were not in the string that was appended (szResponse)
  707.                     // are moved to the beginning of the receive buffer so they can be considered in the next Receive().
  708.                     // this prevents loss of any bytes.
  709.                     else {
  710.                         state.LastResponseLength = state.Resp.StatusBuffer.Length;
  711.                         // update the last length of the statusbuffer.
  712.                         int bytesUsed = Encoding.GetByteCount(szResponse);
  713.                         state.BytesInBuffer -= bytesUsed;
  714.                        
  715.                         Buffer.BlockCopy(state.Buffer, bytesUsed, state.Buffer, 0, state.BytesInBuffer);
  716.                     }
  717.                 }
  718.                
  719.                 // Now, in general, if the response is not complete, update the "valid through" length for the efficiency of checkValid.
  720.                 // and perform the next receive.
  721.                 // Note that there may be bytes in the beginning of the receive buffer (if there were partial characters left over after the
  722.                 // last encoding), so start the receive at state.BytesInBuffer.
  723.                 if (completeLength < 0) {
  724.                     state.ValidThrough = validThrough;
  725.                     try {
  726.                         if (m_Async) {
  727.                             BeginRead(state.Buffer, state.BytesInBuffer, state.Buffer.Length - state.BytesInBuffer, m_ReadCallbackDelegate, state);
  728.                             return;
  729.                         }
  730.                         else {
  731.                             bytesRead = Read(state.Buffer, state.BytesInBuffer, state.Buffer.Length - state.BytesInBuffer);
  732.                             if (bytesRead == 0)
  733.                                 CloseSocket();
  734.                             continue;
  735.                         }
  736.                     }
  737.                     catch (IOException) {
  738.                         MarkAsRecoverableFailure();
  739.                         throw;
  740.                     }
  741.                     catch {
  742.                         throw;
  743.                     }
  744.                 }
  745.                 // the response is completed
  746.                 break;
  747.             }
  748.            
  749.            
  750.             // Otherwise, we have a complete response.
  751.             string responseString = state.Resp.StatusBuffer.ToString();
  752.             state.Resp.StatusDescription = responseString.Substring(0, completeLength);
  753.             // set the StatusDescription to the complete part of the response. Note that the Buffer has already been taken care of above.
  754.            
  755.             if (Logging.On)
  756.                 Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_received_response, responseString.Substring(0, completeLength - 2)));
  757.            
  758.             if (m_Async) {
  759.                 // Tell who is listening what was received.
  760.                 if (state.Resp != null) {
  761.                     m_CurrentResponseDescription = state.Resp;
  762.                 }
  763.                 Stream stream = null;
  764.                 if (PostReadCommandProcessing(ref stream))
  765.                     return;
  766.                 ContinueCommandPipeline();
  767.             }
  768.         }
  769.        
  770.     }
  771.     // class CommandStream
  772.    
  773.     /// <summary>
  774.     /// Contains the parsed status line from the server
  775.     /// </summary>
  776.     internal class ResponseDescription
  777.     {
  778.         internal const int NoStatus = -1;
  779.         internal bool Multiline = false;
  780.        
  781.         internal int Status = NoStatus;
  782.         internal string StatusDescription;
  783.         internal StringBuilder StatusBuffer = new StringBuilder();
  784.        
  785.         internal string StatusCodeString;
  786.        
  787.         internal bool PositiveIntermediate {
  788.             get { return (Status >= 100 && Status <= 199); }
  789.         }
  790.         internal bool PositiveCompletion {
  791.             get { return (Status >= 200 && Status <= 299); }
  792.         }
  793.         //internal bool PositiveAuthRelated { get { return (Status >= 300 && Status <= 399); } }
  794.         internal bool TransientFailure {
  795.             get { return (Status >= 400 && Status <= 499); }
  796.         }
  797.         internal bool PermanentFailure {
  798.             get { return (Status >= 500 && Status <= 599); }
  799.         }
  800.         internal bool InvalidStatusCode {
  801.             get { return (Status < 100 || Status > 599); }
  802.         }
  803.     }
  804.    
  805.    
  806.     /// <summary>
  807.     /// State information that is used during ReceiveCommandResponse()'s async operations
  808.     /// </summary>
  809.     internal class ReceiveState
  810.     {
  811.         private const int bufferSize = 1024;
  812.        
  813.         internal Decoder Decoder;
  814.         internal ResponseDescription Resp;
  815.         internal int ValidThrough;
  816.         internal byte[] Buffer;
  817.         internal int BytesInBuffer;
  818.         internal string StringFromBuffer;
  819.         internal CommandStream Connection;
  820.        
  821.         internal int LastResponseLength;
  822.        
  823.         internal ReceiveState(CommandStream connection)
  824.         {
  825.             Connection = connection;
  826.             Resp = new ResponseDescription();
  827.             Buffer = new byte[bufferSize];
  828.             //1024
  829.             LastResponseLength = 0;
  830.             StringFromBuffer = String.Empty;
  831.             ValidThrough = 0;
  832.         }
  833.     }
  834.    
  835.    
  836.    
  837. }
  838. // namespace System.Net

Developer Fusion