// 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;
}
}
}