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

ParseResponseData() private method

private ParseResponseData ( ConnectionReturnResult &returnResult, bool &requestDone, CoreResponseData &continueResponseData ) : DataParseStatus
returnResult ConnectionReturnResult
requestDone bool
continueResponseData CoreResponseData
return DataParseStatus
        private DataParseStatus ParseResponseData(ref ConnectionReturnResult returnResult, out bool requestDone, out CoreResponseData continueResponseData)
        {
            GlobalLog.Enter("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData()");

            DataParseStatus parseStatus = DataParseStatus.NeedMoreData;
            DataParseStatus parseSubStatus;

            // Indicates whether or not at least one whole request was processed in this loop.
            // (i.e. Whether ParseStreamData() was called.
            requestDone = false;
            continueResponseData = null;

            // loop in case of multiple sets of headers or streams,
            //  that may be generated due to a pipelined response

                // Invariants: at the start of this loop, m_BytesRead
                // is the number of bytes in the buffer, and m_BytesScanned
                // is how many bytes of the buffer we've consumed so far.
                // and the m_ReadState var will be updated at end of
                // each code path, call to this function to reflect,
                // the state, or error condition of the parsing of data
                //
                // We use the following variables in the code below:
                //
                //  m_ReadState - tracks the current state of our Parsing in a
                //      response. z.B.
                //      Start - initial start state and begining of response
                //      StatusLine - the first line sent in response, include status code
                //      Headers - \r\n delimiated Header parsing until we find entity body
                //      Data - Entity Body parsing, if we have all data, we create stream directly
                //
                //  m_ResponseData - An object used to gather Stream, Headers, and other
                //      tidbits so that a request/Response can receive this data when
                //      this code is finished processing
                //
                //  m_ReadBuffer - Of course the buffer of data we are parsing from
                //
                //  m_BytesScanned - The bytes scanned in this buffer so far,
                //      since its always assumed that parse to completion, this
                //      var becomes ended of known data at the end of this function,
                //      based off of 0
                //
                //  m_BytesRead - The total bytes read in buffer, should be const,
                //      till its updated at end of function.
                //

                //
                // Now attempt to parse the data,
                //   we first parse status line,
                //   then read headers,
                //   and finally transfer results to a new stream, and tell request
                //

                switch (m_ReadState) {

                    case ReadState.Start:


                        if (m_CurrentRequest == null)
                        {
                            lock (this)
                            {
                                if (m_WriteList.Count == 0 || ((m_CurrentRequest = m_WriteList[0] as HttpWebRequest) == null))
                                {
                                    m_ParseError.Section = WebParseErrorSection.Generic;
                                    m_ParseError.Code    = WebParseErrorCode.Generic;
                                    parseStatus = DataParseStatus.Invalid;
                                    break;
                                }
                            }
                        }
                        m_MaximumResponseHeadersLength = m_CurrentRequest.MaximumResponseHeadersLength * 1024;
                        m_ResponseData = new CoreResponseData();
                        m_ReadState = ReadState.StatusLine;
                        m_TotalResponseHeadersLength = 0;

                        InitializeParseStatusLine();
                        goto case ReadState.StatusLine;

                    case ReadState.StatusLine:
                        //
                        // Reads HTTP status response line
                        //
                        if (SettingsSectionInternal.Section.UseUnsafeHeaderParsing)
                        {
                            // This one uses an array to store the parsed values in.  Marshal between this legacy way.
                            int[] statusInts = new int[] { 0, m_StatusLineValues.MajorVersion, m_StatusLineValues.MinorVersion, m_StatusLineValues.StatusCode };
                            if (m_StatusLineValues.StatusDescription == null)
                                m_StatusLineValues.StatusDescription = "";

                            parseSubStatus = ParseStatusLine(
                                m_ReadBuffer, // buffer we're working with
                                m_BytesRead,  // total bytes read so far
                                ref m_BytesScanned, // index off of what we've scanned
                                ref statusInts,
                                ref m_StatusLineValues.StatusDescription,
                                ref m_StatusState,
                                ref m_ParseError);

                            m_StatusLineValues.MajorVersion = statusInts[1];
                            m_StatusLineValues.MinorVersion = statusInts[2];
                            m_StatusLineValues.StatusCode = statusInts[3];
                        }
                        else
                        {
                            parseSubStatus = ParseStatusLineStrict(
                                m_ReadBuffer, // buffer we're working with
                                m_BytesRead,  // total bytes read so far
                                ref m_BytesScanned, // index off of what we've scanned
                                ref m_StatusState,
                                m_StatusLineValues,
                                m_MaximumResponseHeadersLength,
                                ref m_TotalResponseHeadersLength,
                                ref m_ParseError);
                        }

                        if (parseSubStatus == DataParseStatus.Done)
                        {
                            if (Logging.On) 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));
                            SetStatusLineParsed();
                            m_ReadState = ReadState.Headers;
                            m_ResponseData.m_ResponseHeaders = new WebHeaderCollection(WebHeaderCollectionType.HttpWebResponse);
                            goto case ReadState.Headers;
                        }
                        else if (parseSubStatus != DataParseStatus.NeedMoreData)
                        {
                            //
                            // report error - either Invalid or DataTooBig
                            //
                            GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() ParseStatusLine() parseSubStatus:" + parseSubStatus.ToString());
                            parseStatus = parseSubStatus;
                            break;
                        }

                        break; // read more data

                    case ReadState.Headers:
                        //
                        // Parse additional lines of header-name: value pairs
                        //
                        if (m_BytesScanned >= m_BytesRead) {
                            //
                            // we already can tell we need more data
                            //
                            break;
                        }

                        if (SettingsSectionInternal.Section.UseUnsafeHeaderParsing)
                        {
                            parseSubStatus = m_ResponseData.m_ResponseHeaders.ParseHeaders(
                                m_ReadBuffer,
                                m_BytesRead,
                                ref m_BytesScanned,
                                ref m_TotalResponseHeadersLength,
                                m_MaximumResponseHeadersLength,
                                ref m_ParseError);
                        }
                        else
                        {
                            parseSubStatus = m_ResponseData.m_ResponseHeaders.ParseHeadersStrict(
                                m_ReadBuffer,
                                m_BytesRead,
                                ref m_BytesScanned,
                                ref m_TotalResponseHeadersLength,
                                m_MaximumResponseHeadersLength,
                                ref m_ParseError);
                        }

                        if (parseSubStatus == DataParseStatus.Invalid || parseSubStatus == DataParseStatus.DataTooBig)
                        {
                            //
                            // report error
                            //
                            GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() ParseHeaders() parseSubStatus:" + parseSubStatus.ToString());
                            parseStatus = parseSubStatus;
                            break;
                        }
                        else if (parseSubStatus == DataParseStatus.Done)
                        {
                            if (Logging.On) Logging.PrintInfo(Logging.Web, this, SR.GetString(SR.net_log_received_headers, m_ResponseData.m_ResponseHeaders.ToString(true)));

                            GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() DataParseStatus.Done StatusCode:" + (int)m_ResponseData.m_StatusCode + " m_BytesRead:" + m_BytesRead + " m_BytesScanned:" + m_BytesScanned);

                            //get the IIS server version
                            if(m_IISVersion == -1){
                                string server = m_ResponseData.m_ResponseHeaders.Server;
                                if (server != null && server.ToLower(CultureInfo.InvariantCulture).Contains("microsoft-iis")){
                                    int i = server.IndexOf("/");
                                    if(i++>0 && i <server.Length){
                                        m_IISVersion = server[i++] - '0';
                                        while(i < server.Length && Char.IsDigit(server[i])) {
                                            m_IISVersion = m_IISVersion*10 + server[i++] - '0';
                                        }
                                    }
                                }
                                //we got a response,so if we don't know the server by now and it wasn't a 100 continue,
                                //we can't assume we will ever know it.  IIS5 sends its server header w/ the continue

                                if(m_IISVersion == -1 && m_ResponseData.m_StatusCode != HttpStatusCode.Continue){
                                    m_IISVersion = 0;
                                }
                            }

                            if (m_ResponseData.m_StatusCode == HttpStatusCode.Continue || m_ResponseData.m_StatusCode == HttpStatusCode.BadRequest) {

                                GlobalLog.Assert(m_CurrentRequest != null, "Connection#{0}::ParseResponseData()|m_CurrentRequest == null", ValidationHelper.HashString(this));
                                GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() HttpWebRequest#" + ValidationHelper.HashString(m_CurrentRequest));

                                if (m_ResponseData.m_StatusCode == HttpStatusCode.BadRequest) {
                                    // If we have a 400 and we were sending a chunked request going through to a proxy with a chunked upload,
                                    // this proxy is a partially compliant so shut off fancy features (pipelining and chunked uploads)
                                    GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() got a 400 StatusDescription:" + m_ResponseData.m_StatusDescription);
                                    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) {
                                        GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() downgrading server to HTTP11PartiallyCompliant.");
                                        ServicePoint.HttpBehaviour = HttpBehaviour.HTTP11PartiallyCompliant;
                                    }
                                }
                                else {
                                    // If we have an HTTP continue, eat these headers and look
                                    //  for the 200 OK
                                    //
                                    // we got a 100 Continue. set this on the HttpWebRequest
                                    //
                                    m_CurrentRequest.Saw100Continue = true;
                                    if (!ServicePoint.Understands100Continue) {
                                        //
                                        // and start expecting it again if this behaviour was turned off
                                        //
                                        GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() HttpWebRequest#" + ValidationHelper.HashString(m_CurrentRequest) + " ServicePoint#" + ValidationHelper.HashString(ServicePoint) + " sent UNexpected 100 Continue");
                                        ServicePoint.Understands100Continue = true;
                                    }

                                    //
                                    // set Continue Ack on request.
                                    //
                                    GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() calling SetRequestContinue()");
                                    continueResponseData = m_ResponseData;

                                    //if we got a 100continue we swallow it and start looking for a final response
                                    goto case ReadState.Start;
                                }
                            }

                            m_ReadState = ReadState.Data;
                            goto case ReadState.Data;
                        }

                        // need more data,
                        break;

                    case ReadState.Data:

                        // (check status code for continue handling)
                        // 1. Figure out if its Chunked, content-length, or encoded
                        // 2. Takes extra data, place in stream(s)
                        // 3. Wake up blocked stream requests
                        //

                        // Got through one entire response
                        requestDone = true;

                        // parse and create a stream if needed
                        parseStatus = ParseStreamData(ref returnResult);

                        GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() result:" + parseStatus);
                        GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData()" + " WriteDone:" + m_WriteDone + " ReadDone:" + m_ReadDone + " WaitList:" + m_WaitList.Count + " WriteList:" + m_WriteList.Count);
                        break;
                }
            
            if (m_BytesScanned == m_BytesRead)
                ClearReaderState();

            GlobalLog.Print("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData() m_ReadState:" + m_ReadState);
            GlobalLog.Leave("Connection#" + ValidationHelper.HashString(this) + "::ParseResponseData()", parseStatus.ToString());
            return parseStatus;
        }