System.Net.Connection.ReadComplete C# (CSharp) Method

ReadComplete() private method

private ReadComplete ( int bytesRead, WebExceptionStatus errorStatus ) : bool
bytesRead int
errorStatus WebExceptionStatus
return bool
        private bool ReadComplete(int bytesRead, WebExceptionStatus errorStatus)
        {
            bool requestDone = true;
            CoreResponseData continueResponseData = null;
            ConnectionReturnResult returnResult = null;
            HttpWebRequest currentRequest = null;

            try
            {
                GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() m_BytesRead:" + m_BytesRead.ToString() + " m_BytesScanned:" + m_BytesScanned + " (+= new bytesRead:" + bytesRead.ToString() + ")");

                if (bytesRead < 0)
                {
                    // Means we might have gotten gracefull or hard connection close.

                    // If this is the first thing we read for a request then it
                    // could be an idle connection closed by the server (isolated error)
                    if (m_ReadState == ReadState.Start && m_AtLeastOneResponseReceived)
                    {
                        // Note that KeepAliveFailure will be checked against POST-type requests
                        // and it's fatal if the body was already started.
                        if (errorStatus == WebExceptionStatus.Success || errorStatus == WebExceptionStatus.ReceiveFailure)
                            errorStatus = WebExceptionStatus.KeepAliveFailure;
                    }
                    else if (errorStatus == WebExceptionStatus.Success)
                    {
                        // we got unexpected FIN in the middle of the response, or on a fresh connection, that's fatal
                        errorStatus = WebExceptionStatus.ConnectionClosed;
                    }

                    // Notify request's SubmitWriteStream that a socket error happened.  This will cause future writes to
                    // throw an IOException.
                    HttpWebRequest curRequest = m_CurrentRequest;
                    if (curRequest != null)
                    {
                        curRequest.ErrorStatusCodeNotify(this, false, true);
                    }

                    HandleErrorWithReadDone(errorStatus, ref returnResult);
                    goto done;
                }

                // Otherwise, we've got data.
                GlobalLog.Dump(m_ReadBuffer, m_BytesScanned, m_BytesRead - m_BytesScanned);
                GlobalLog.Dump(m_ReadBuffer, m_BytesRead, bytesRead);


                bytesRead += m_BytesRead;
                if (bytesRead  > m_ReadBuffer.Length)
                    throw new InternalException();  //in case we posted two receives at once
                m_BytesRead = bytesRead;

                // We have the parsing code seperated out in ParseResponseData
                //
                // If we don't have all the headers yet. Resubmit the receive,
                // passing in the bytes read total as our index. When we get
                // back here we'll end up reparsing from the beginning, which is
                // OK. because this shouldn't be a performance case.

                //if we're back here, we need to reset the scanned bytes to 0.

                DataParseStatus parseStatus = ParseResponseData(ref returnResult, out requestDone, out continueResponseData);

                // copy off m_CurrentRequest as we might start processing a next request before exiting this method
                currentRequest = m_CurrentRequest;

                if (parseStatus != DataParseStatus.NeedMoreData)
                    m_CurrentRequest = null;

                if (parseStatus == DataParseStatus.Invalid || parseStatus == DataParseStatus.DataTooBig)
                {
                    // Tell the request's SubmitWriteStream that the connection will be closed.  It should swallow any
                    // future writes so that the appropriate exception will be received in GetResponse().
                    if (currentRequest != null)
                    {
                        currentRequest.ErrorStatusCodeNotify(this, false, false);
                    }

                    //
                    // report error
                    //
                    if (parseStatus == DataParseStatus.Invalid) {
                        HandleErrorWithReadDone(WebExceptionStatus.ServerProtocolViolation, ref returnResult);
                    }
                    else {
                        HandleErrorWithReadDone(WebExceptionStatus.MessageLengthLimitExceeded, ref returnResult);
                    }
                    GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() parseStatus:" + parseStatus + " returnResult:" + returnResult);
                    goto done;
                }

                //Done means the ConnectStream take care of this connection until ConnectStream.CallDone()
                else if (parseStatus == DataParseStatus.Done)
                {
                    GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() [The response stream is ready] parseStatus = DataParseStatus.Done");
                    goto done;
                }

                //
                // we may reach the end of our buffer only when parsing headers.
                // this can happen when the header section is bigger than our initial 4k guess
                // which should be a good assumption in 99.9% of the cases. what we do here is:
                // 1) if there's a single BIG header (bigger than the current size) we will need to
                //    grow the buffer before we move data over and read more data.
                // 2) move unparsed data to the beginning of the buffer and read more data in the
                //    remaining part of the data.
                //
                if (parseStatus == DataParseStatus.NeedMoreData)
                {
                    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());
                    int unparsedDataSize = m_BytesRead - m_BytesScanned;
                    if (unparsedDataSize != 0)
                    {
                        if (m_BytesScanned == 0 && m_BytesRead == m_ReadBuffer.Length)
                        {
                            //
                            // 1) we need to grow the buffer, move the unparsed data to the beginning of the buffer before reading more data.
                            // since the buffer size is 4k, should we just double
                            //
                            byte[] newReadBuffer = new byte[m_ReadBuffer.Length * 2 /*+ ReadBufferSize*/];
                            Buffer.BlockCopy(m_ReadBuffer, 0, newReadBuffer, 0, m_BytesRead);
                            m_ReadBuffer = newReadBuffer;
                        }
                        else
                        {
                            //
                            // just move data around in the same buffer.
                            //
                            Buffer.BlockCopy(m_ReadBuffer, m_BytesScanned, m_ReadBuffer, 0, unparsedDataSize);
                        }
                    }
                    //
                    // update indexes and offsets in the new buffer
                    //
                    m_BytesRead = unparsedDataSize;
                    m_BytesScanned = 0;
                    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());

                    if (currentRequest != null)
                    {
                        //
                        // This case means that we still parsing the headers, so need to post another read in the async case

                        if (currentRequest.Async)
                        {
                            GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() Reposting Async Read.  Buffer:" + ValidationHelper.HashString(m_ReadBuffer) + " BytesScanned:" + m_BytesScanned.ToString());

                            if (Thread.CurrentThread.IsThreadPoolThread)
                            {
                                GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() Calling PostReceive().");
                                PostReceive();
                            }
                            else
                            {
                                // Offload to the threadpool to protect against the case where one request's thread posts IO that another request
                                // depends on, but the first thread dies in the mean time.
                                GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ReadComplete() ThreadPool.UnsafeQueueUserWorkItem(m_PostReceiveDelegate, this)");
                                ThreadPool.UnsafeQueueUserWorkItem(m_PostReceiveDelegate, this);
                            }
                        }
                    }
                }
            }
            //
            // Any exception is processed by HandleError() and swallowed to avoid throwing on a thread pool
            // In the sync case the HandleError() will abort the request so the caller will pick up the result.
            //
            catch (Exception exception) {
                if (NclUtilities.IsFatal(exception)) throw;

                requestDone = true;

                if (m_InnerException == null)
                    m_InnerException = exception;

                // Notify request's SubmitWriteStream that a socket error happened.  This will cause future writes to
                // throw an IOException.
                HttpWebRequest curRequest = m_CurrentRequest;
                if (curRequest != null)
                {
                    curRequest.ErrorStatusCodeNotify(this, false, true);
                }

                HandleErrorWithReadDone(WebExceptionStatus.ReceiveFailure, ref returnResult);
            }

done:
            try {
                if((continueResponseData != null || (returnResult != null && returnResult.IsNotEmpty)) && currentRequest != null)
                {
                    // if returnResult is not empty it must also contain some result for the currently active request
                    // Since it could be a POST request waiting on the body submit, signal the body here
                    currentRequest.SetRequestContinue(continueResponseData);
                }
            }
            finally {
                ConnectionReturnResult.SetResponses(returnResult);
            }

            return requestDone;
        }