// Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Diagnostics.CodeAnalysis; using System.Web.Http; namespace System.Net.Http.Formatting.Parsers { /// /// The combines for parsing the HTTP Status Line /// and for parsing each header field. /// internal class HttpResponseHeaderParser { internal const int DefaultMaxStatusLineSize = 2 * 1024; internal const int DefaultMaxHeaderSize = 16 * 1024; // Same default size as IIS has for HTTP requests private HttpUnsortedResponse _httpResponse; private HttpResponseState _responseStatus = HttpResponseState.StatusLine; private HttpStatusLineParser _statusLineParser; private InternetMessageFormatHeaderParser _headerParser; /// /// Initializes a new instance of the class. /// /// The parsed HTTP response without any header sorting. public HttpResponseHeaderParser(HttpUnsortedResponse httpResponse) : this(httpResponse, DefaultMaxStatusLineSize, DefaultMaxHeaderSize) { } /// /// Initializes a new instance of the class. /// /// The parsed HTTP response without any header sorting. /// The max length of the HTTP status line. /// The max length of the HTTP header. public HttpResponseHeaderParser(HttpUnsortedResponse httpResponse, int maxResponseLineSize, int maxHeaderSize) { if (httpResponse == null) { throw Error.ArgumentNull("httpResponse"); } _httpResponse = httpResponse; // Create status line parser _statusLineParser = new HttpStatusLineParser(_httpResponse, maxResponseLineSize); // Create header parser _headerParser = new InternetMessageFormatHeaderParser(_httpResponse.HttpHeaders, maxHeaderSize); } private enum HttpResponseState { StatusLine = 0, // parsing status line ResponseHeaders // reading headers } /// /// Parse an HTTP response header and fill in the instance. /// /// Response buffer from where response is read /// Size of response buffer /// Offset into response buffer /// State of the parser. [SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Justification = "Exception is propagated.")] public ParserState ParseBuffer( byte[] buffer, int bytesReady, ref int bytesConsumed) { if (buffer == null) { throw Error.ArgumentNull("buffer"); } ParserState parseStatus = ParserState.NeedMoreData; ParserState subParseStatus = ParserState.NeedMoreData; switch (_responseStatus) { case HttpResponseState.StatusLine: try { subParseStatus = _statusLineParser.ParseBuffer(buffer, bytesReady, ref bytesConsumed); } catch (Exception) { subParseStatus = ParserState.Invalid; } if (subParseStatus == ParserState.Done) { _responseStatus = HttpResponseState.ResponseHeaders; subParseStatus = ParserState.NeedMoreData; goto case HttpResponseState.ResponseHeaders; } else if (subParseStatus != ParserState.NeedMoreData) { // Report error - either Invalid or DataTooBig parseStatus = subParseStatus; break; } break; // read more data case HttpResponseState.ResponseHeaders: if (bytesConsumed >= bytesReady) { // we already can tell we need more data break; } try { subParseStatus = _headerParser.ParseBuffer(buffer, bytesReady, ref bytesConsumed); } catch (Exception) { subParseStatus = ParserState.Invalid; } if (subParseStatus == ParserState.Done) { parseStatus = subParseStatus; } else if (subParseStatus != ParserState.NeedMoreData) { parseStatus = subParseStatus; break; } break; // need more data } return parseStatus; } } }