private static HttpRequestMessage CreateHttpRequestMessage(
            string uriScheme,
            HttpUnsortedRequest httpRequest,
            Stream contentStream,
            int rewind
            )
        {
            Contract.Assert(uriScheme != null, "URI scheme must be non null");
            Contract.Assert(httpRequest != null, "httpRequest must be non null");
            Contract.Assert(contentStream != null, "contentStream must be non null");

            HttpRequestMessage httpRequestMessage = new HttpRequestMessage();

            // Set method, requestURI, and version
            httpRequestMessage.Method     = httpRequest.Method;
            httpRequestMessage.RequestUri = CreateRequestUri(uriScheme, httpRequest);
            httpRequestMessage.Version    = httpRequest.Version;

            // Set the header fields and content if any
            httpRequestMessage.Content = CreateHeaderFields(
                httpRequest.HttpHeaders,
                httpRequestMessage.Headers,
                contentStream,
                rewind
                );

            return(httpRequestMessage);
        }
        /// <summary>
        /// Creates the request URI by combining scheme (provided) with parsed values of
        /// host and path.
        /// </summary>
        /// <param name="uriScheme">The URI scheme to use for the request URI.</param>
        /// <param name="httpRequest">The unsorted HTTP request.</param>
        /// <returns>A fully qualified request URI.</returns>
        private static Uri CreateRequestUri(string uriScheme, HttpUnsortedRequest httpRequest)
        {
            Contract.Assert(httpRequest != null, "httpRequest cannot be null.");
            Contract.Assert(uriScheme != null, "uriScheme cannot be null");

            IEnumerable <string> hostValues;

            if (httpRequest.HttpHeaders.TryGetValues(FormattingUtilities.HttpHostHeader, out hostValues))
            {
                int hostCount = hostValues.Count();
                if (hostCount != 1)
                {
                    throw Error.InvalidOperation(Properties.Resources.HttpMessageParserInvalidHostCount, FormattingUtilities.HttpHostHeader, hostCount);
                }
            }
            else
            {
                throw Error.InvalidOperation(Properties.Resources.HttpMessageParserInvalidHostCount, FormattingUtilities.HttpHostHeader, 0);
            }

            // We don't use UriBuilder as hostValues.ElementAt(0) contains 'host:port' and UriBuilder needs these split out into separate host and port.
            string requestUri = String.Format(CultureInfo.InvariantCulture, "{0}://{1}{2}", uriScheme, hostValues.ElementAt(0), httpRequest.RequestUri);

            return(new Uri(requestUri));
        }
        private static async Task <HttpRequestMessage> ReadAsHttpRequestMessageAsyncCore(this HttpContent content,
                                                                                         string uriScheme, int bufferSize, int maxHeaderSize, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();
            Stream stream = await content.ReadAsStreamAsync();

            HttpUnsortedRequest     httpRequest = new HttpUnsortedRequest();
            HttpRequestHeaderParser parser      = new HttpRequestHeaderParser(httpRequest,
                                                                              HttpRequestHeaderParser.DefaultMaxRequestLineSize, maxHeaderSize);
            ParserState parseStatus;

            byte[] buffer         = new byte[bufferSize];
            int    bytesRead      = 0;
            int    headerConsumed = 0;

            while (true)
            {
                try
                {
                    bytesRead = await stream.ReadAsync(buffer, 0, buffer.Length, cancellationToken);
                }
                catch (Exception e)
                {
                    throw new IOException(Properties.Resources.HttpMessageErrorReading, e);
                }

                try
                {
                    parseStatus = parser.ParseBuffer(buffer, bytesRead, ref headerConsumed);
                }
                catch (Exception)
                {
                    parseStatus = ParserState.Invalid;
                }

                if (parseStatus == ParserState.Done)
                {
                    return(CreateHttpRequestMessage(uriScheme, httpRequest, stream, bytesRead - headerConsumed));
                }
                else if (parseStatus != ParserState.NeedMoreData)
                {
                    throw Error.InvalidOperation(Properties.Resources.HttpMessageParserError, headerConsumed, buffer);
                }
                else if (bytesRead == 0)
                {
                    throw new IOException(Properties.Resources.ReadAsHttpMessageUnexpectedTermination);
                }
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="HttpRequestHeaderParser"/> class.
        /// </summary>
        /// <param name="httpRequest">The parsed HTTP request without any header sorting.</param>
        /// <param name="maxRequestLineSize">The max length of the HTTP request line.</param>
        /// <param name="maxHeaderSize">The max length of the HTTP header.</param>
        public HttpRequestHeaderParser(HttpUnsortedRequest httpRequest, int maxRequestLineSize, int maxHeaderSize)
        {
            if (httpRequest == null)
            {
                throw Error.ArgumentNull("httpRequest");
            }

            this._httpRequest = httpRequest;

            // Create request line parser
            this._requestLineParser = new HttpRequestLineParser(this._httpRequest, maxRequestLineSize);

            // Create header parser
            this._headerParser = new InternetMessageFormatHeaderParser(this._httpRequest.HttpHeaders, maxHeaderSize);
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="HttpRequestLineParser"/> class.
        /// </summary>
        /// <param name="httpRequest"><see cref="HttpUnsortedRequest"/> instance where the request line properties will be set as they are parsed.</param>
        /// <param name="maxRequestLineSize">Maximum length of HTTP header.</param>
        public HttpRequestLineParser(HttpUnsortedRequest httpRequest, int maxRequestLineSize)
        {
            // The minimum length which would be an empty header terminated by CRLF
            if (maxRequestLineSize < MinRequestLineSize)
            {
                throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxRequestLineSize", maxRequestLineSize, MinRequestLineSize);
            }

            if (httpRequest == null)
            {
                throw Error.ArgumentNull("httpRequest");
            }

            this._httpRequest = httpRequest;
            this._maximumHeaderLength = maxRequestLineSize;
        }
        private static HttpRequestMessage CreateHttpRequestMessage(HttpUnsortedRequest httpRequest, Stream contentStream, int rewind)
        {
            Contract.Assert(httpRequest != null, "httpRequest must be non null");
            Contract.Assert(contentStream != null, "contentStream must be non null");

            HttpRequestMessage httpRequestMessage = new HttpRequestMessage();

            // Set method, requestURI, and version
            httpRequestMessage.Method = httpRequest.Method;
            httpRequestMessage.RequestUri = CreateRequestUri(httpRequest);
            httpRequestMessage.Version = httpRequest.Version;

            // Set the header fields and content if any
            httpRequestMessage.Content = CreateHeaderFields(httpRequest.HttpHeaders, httpRequestMessage.Headers, contentStream, rewind);

            return httpRequestMessage;
        }
        public static async Task<HttpRequestMessage> ReadAsBatchHttpRequestMessageAsync(this HttpContent content)
        {
            Stream stream = await content.ReadAsStreamAsync();
            HttpUnsortedRequest httpRequest = new HttpUnsortedRequest();
            HttpRequestHeaderParser parser = new HttpRequestHeaderParser(httpRequest, HttpRequestHeaderParser.DefaultMaxRequestLineSize, HttpRequestHeaderParser.DefaultMaxHeaderSize);
            ParserState parseStatus;

            byte[] buffer = new byte[DefaultBufferSize];
            int bytesRead = 0;
            int headerConsumed = 0;

            while (true)
            {
                try
                {
                    bytesRead = stream.Read(buffer, 0, buffer.Length);
                }
                catch (Exception e)
                {
                    throw new IOException(Properties.Resources.HttpMessageErrorReading, e);
                }

                try
                {
                    parseStatus = parser.ParseBuffer(buffer, bytesRead, ref headerConsumed);
                }
                catch (Exception)
                {
                    parseStatus = ParserState.Invalid;
                }

                if (parseStatus == ParserState.Done)
                {
                    return CreateHttpRequestMessage(httpRequest, stream, bytesRead - headerConsumed);
                }
                else if (parseStatus != ParserState.NeedMoreData)
                {
                    throw Error.InvalidOperation(Properties.Resources.HttpMessageParserError, headerConsumed, buffer);
                }
                else if (bytesRead == 0)
                {
                    throw new IOException(Properties.Resources.ReadAsHttpMessageUnexpectedTermination);
                }
            }
        }
        public static Task <HttpRequestMessage> ReadAsHttpRequestMessageAsync(this HttpContent content, string uriScheme, int bufferSize, int maxHeaderSize)
        {
            if (content == null)
            {
                throw Error.ArgumentNull("content");
            }

            if (uriScheme == null)
            {
                throw Error.ArgumentNull("uriScheme");
            }

            if (!Uri.CheckSchemeName(uriScheme))
            {
                throw Error.Argument("uriScheme", Properties.Resources.HttpMessageParserInvalidUriScheme, uriScheme, typeof(Uri).Name);
            }

            if (bufferSize < MinBufferSize)
            {
                throw Error.ArgumentMustBeGreaterThanOrEqualTo("bufferSize", bufferSize, MinBufferSize);
            }

            if (maxHeaderSize < InternetMessageFormatHeaderParser.MinHeaderSize)
            {
                throw Error.ArgumentMustBeGreaterThanOrEqualTo("maxHeaderSize", maxHeaderSize, InternetMessageFormatHeaderParser.MinHeaderSize);
            }

            HttpMessageContent.ValidateHttpMessageContent(content, true, true);

            return(content.ReadAsStreamAsync().Then(stream =>
            {
                HttpUnsortedRequest httpRequest = new HttpUnsortedRequest();
                HttpRequestHeaderParser parser = new HttpRequestHeaderParser(httpRequest, HttpRequestHeaderParser.DefaultMaxRequestLineSize, maxHeaderSize);
                ParserState parseStatus;

                byte[] buffer = new byte[bufferSize];
                int bytesRead = 0;
                int headerConsumed = 0;

                while (true)
                {
                    try
                    {
                        bytesRead = stream.Read(buffer, 0, buffer.Length);
                    }
                    catch (Exception e)
                    {
                        throw new IOException(Properties.Resources.HttpMessageErrorReading, e);
                    }

                    try
                    {
                        parseStatus = parser.ParseBuffer(buffer, bytesRead, ref headerConsumed);
                    }
                    catch (Exception)
                    {
                        parseStatus = ParserState.Invalid;
                    }

                    if (parseStatus == ParserState.Done)
                    {
                        return CreateHttpRequestMessage(uriScheme, httpRequest, stream, bytesRead - headerConsumed);
                    }
                    else if (parseStatus != ParserState.NeedMoreData)
                    {
                        throw Error.InvalidOperation(Properties.Resources.HttpMessageParserError, headerConsumed, buffer);
                    }
                }
            }));
        }
        /// <summary>
        /// Creates the request URI by combining scheme (provided) with parsed values of
        /// host and path.
        /// </summary>
        /// <param name="uriScheme">The URI scheme to use for the request URI.</param>
        /// <param name="httpRequest">The unsorted HTTP request.</param>
        /// <returns>A fully qualified request URI.</returns>
        private static Uri CreateRequestUri(string uriScheme, HttpUnsortedRequest httpRequest)
        {
            Contract.Assert(httpRequest != null, "httpRequest cannot be null.");
            Contract.Assert(uriScheme != null, "uriScheme cannot be null");

            IEnumerable<string> hostValues;
            if (httpRequest.HttpHeaders.TryGetValues(FormattingUtilities.HttpHostHeader, out hostValues))
            {
                int hostCount = hostValues.Count();
                if (hostCount != 1)
                {
                    throw Error.InvalidOperation(Properties.Resources.HttpMessageParserInvalidHostCount, FormattingUtilities.HttpHostHeader, hostCount);
                }
            }
            else
            {
                throw Error.InvalidOperation(Properties.Resources.HttpMessageParserInvalidHostCount, FormattingUtilities.HttpHostHeader, 0);
            }

            // We don't use UriBuilder as hostValues.ElementAt(0) contains 'host:port' and UriBuilder needs these split out into separate host and port.
            string requestUri = String.Format(CultureInfo.InvariantCulture, "{0}://{1}{2}", uriScheme, hostValues.ElementAt(0), httpRequest.RequestUri);
            return new Uri(requestUri);
        }
 /// <summary>
 /// Initializes a new instance of the <see cref="HttpRequestHeaderParser"/> class.
 /// </summary>
 /// <param name="httpRequest">The parsed HTTP request without any header sorting.</param>
 public HttpRequestHeaderParser(HttpUnsortedRequest httpRequest)
     : this(httpRequest, DefaultMaxRequestLineSize, DefaultMaxHeaderSize)
 {
 }
 public void Constructor_InitializesHeaders()
 {
     HttpUnsortedRequest request = new HttpUnsortedRequest();
     Assert.IsType<HttpUnsortedHeaders>(request.HttpHeaders);
 }
Example #12
0
        public void Constructor_InitializesHeaders()
        {
            HttpUnsortedRequest request = new HttpUnsortedRequest();

            Assert.IsType <HttpUnsortedHeaders>(request.HttpHeaders);
        }
        public static Task<HttpRequestMessage> ReadAsHttpRequestMessageAsync(this HttpContent content, string uriScheme, int bufferSize)
        {
            if (content == null)
            {
                throw new ArgumentNullException("content");
            }

            if (uriScheme == null)
            {
                throw new ArgumentNullException("uriScheme");
            }

            if (!Uri.CheckSchemeName(uriScheme))
            {
                throw new ArgumentException(RS.Format(Properties.Resources.HttpMessageParserInvalidUriScheme, uriScheme, typeof(Uri).Name), "uriScheme");
            }

            if (bufferSize < MinBufferSize)
            {
                throw new ArgumentOutOfRangeException("bufferSize", bufferSize, RS.Format(Properties.Resources.ArgumentMustBeGreaterThanOrEqualTo, MinBufferSize));
            }

            HttpMessageContent.ValidateHttpMessageContent(content, true, true);

            return content.ReadAsStreamAsync().Then(stream =>
            {
                HttpUnsortedRequest httpRequest = new HttpUnsortedRequest();
                HttpRequestHeaderParser parser = new HttpRequestHeaderParser(httpRequest);
                ParserState parseStatus;

                byte[] buffer = new byte[bufferSize];
                int bytesRead = 0;
                int headerConsumed = 0;

                while (true)
                {
                    try
                    {
                        bytesRead = stream.Read(buffer, 0, buffer.Length);
                    }
                    catch (Exception e)
                    {
                        throw new IOException(Properties.Resources.HttpMessageErrorReading, e);
                    }

                    try
                    {
                        parseStatus = parser.ParseBuffer(buffer, bytesRead, ref headerConsumed);
                    }
                    catch (Exception)
                    {
                        parseStatus = ParserState.Invalid;
                    }

                    if (parseStatus == ParserState.Done)
                    {
                        return CreateHttpRequestMessage(uriScheme, httpRequest, stream, bytesRead - headerConsumed);
                    }
                    else if (parseStatus != ParserState.NeedMoreData)
                    {
                        throw new IOException(RS.Format(Properties.Resources.HttpMessageParserError, headerConsumed, buffer));
                    }
                }
            });
        }
        private static Uri CreateRequestUri(HttpUnsortedRequest httpRequest)
        {
            Contract.Assert(httpRequest != null, "httpRequest cannot be null.");

            // We don't use UriBuilder as hostValues.ElementAt(0) contains 'host:port' and UriBuilder needs these split out into separate host and port.
            string requestUri = String.Format(CultureInfo.InvariantCulture, "http://localhost{0}", httpRequest.RequestUri);
            return new Uri(requestUri);
        }
        private static ParserState ParseRequestLine(
            byte[] buffer,
            int bytesReady,
            ref int bytesConsumed,
            ref HttpRequestLineState requestLineState,
            int maximumHeaderLength,
            ref int totalBytesConsumed,
            StringBuilder currentToken,
            HttpUnsortedRequest httpRequest)
        {
            Contract.Assert((bytesReady - bytesConsumed) >= 0, "ParseRequestLine()|(bytesReady - bytesConsumed) < 0");
            Contract.Assert(maximumHeaderLength <= 0 || totalBytesConsumed <= maximumHeaderLength, "ParseRequestLine()|Headers already read exceeds limit.");

            // Remember where we started.
            int initialBytesParsed = bytesConsumed;
            int segmentStart;

            // Set up parsing status with what will happen if we exceed the buffer.
            ParserState parseStatus = ParserState.DataTooBig;
            int effectiveMax = maximumHeaderLength <= 0 ? Int32.MaxValue : (maximumHeaderLength - totalBytesConsumed + bytesConsumed);
            if (bytesReady < effectiveMax)
            {
                parseStatus = ParserState.NeedMoreData;
                effectiveMax = bytesReady;
            }

            Contract.Assert(bytesConsumed < effectiveMax, "We have already consumed more than the max header length.");

            switch (requestLineState)
            {
                case HttpRequestLineState.RequestMethod:
                    segmentStart = bytesConsumed;
                    while (buffer[bytesConsumed] != ' ')
                    {
                        if (buffer[bytesConsumed] < 0x21 || buffer[bytesConsumed] > 0x7a)
                        {
                            parseStatus = ParserState.Invalid;
                            goto quit;
                        }

                        if (++bytesConsumed == effectiveMax)
                        {
                            string method = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                            currentToken.Append(method);
                            goto quit;
                        }
                    }

                    if (bytesConsumed > segmentStart)
                    {
                        string method = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                        currentToken.Append(method);
                    }

                    // Copy value out
                    httpRequest.Method = new HttpMethod(currentToken.ToString());
                    currentToken.Clear();

                    // Move past the SP
                    requestLineState = HttpRequestLineState.RequestUri;
                    if (++bytesConsumed == effectiveMax)
                    {
                        goto quit;
                    }

                    goto case HttpRequestLineState.RequestUri;

                case HttpRequestLineState.RequestUri:
                    segmentStart = bytesConsumed;
                    while (buffer[bytesConsumed] != ' ')
                    {
                        if (buffer[bytesConsumed] == '\r')
                        {
                            parseStatus = ParserState.Invalid;
                            goto quit;
                        }

                        if (++bytesConsumed == effectiveMax)
                        {
                            string addr = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                            currentToken.Append(addr);
                            goto quit;
                        }
                    }

                    if (bytesConsumed > segmentStart)
                    {
                        string addr = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                        currentToken.Append(addr);
                    }

                    // URI validation happens when we create the URI later.
                    if (currentToken.Length == 0)
                    {
                        throw new FormatException(Resources.HttpMessageParserEmptyUri);
                    }

                    // Copy value out
                    httpRequest.RequestUri = currentToken.ToString();
                    currentToken.Clear();

                    // Move past the SP
                    requestLineState = HttpRequestLineState.BeforeVersionNumbers;
                    if (++bytesConsumed == effectiveMax)
                    {
                        goto quit;
                    }

                    goto case HttpRequestLineState.BeforeVersionNumbers;

                case HttpRequestLineState.BeforeVersionNumbers:
                    segmentStart = bytesConsumed;
                    while (buffer[bytesConsumed] != '/')
                    {
                        if (buffer[bytesConsumed] < 0x21 || buffer[bytesConsumed] > 0x7a)
                        {
                            parseStatus = ParserState.Invalid;
                            goto quit;
                        }

                        if (++bytesConsumed == effectiveMax)
                        {
                            string token = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                            currentToken.Append(token);
                            goto quit;
                        }
                    }

                    if (bytesConsumed > segmentStart)
                    {
                        string token = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                        currentToken.Append(token);
                    }

                    // Validate value
                    string version = currentToken.ToString();
                    if (String.CompareOrdinal(FormattingUtilities.HttpVersionToken, version) != 0)
                    {
                        throw new FormatException(Error.Format(Resources.HttpInvalidVersion, version, FormattingUtilities.HttpVersionToken));
                    }

                    currentToken.Clear();

                    // Move past the '/'
                    requestLineState = HttpRequestLineState.MajorVersionNumber;
                    if (++bytesConsumed == effectiveMax)
                    {
                        goto quit;
                    }

                    goto case HttpRequestLineState.MajorVersionNumber;

                case HttpRequestLineState.MajorVersionNumber:
                    segmentStart = bytesConsumed;
                    while (buffer[bytesConsumed] != '.')
                    {
                        if (buffer[bytesConsumed] < '0' || buffer[bytesConsumed] > '9')
                        {
                            parseStatus = ParserState.Invalid;
                            goto quit;
                        }

                        if (++bytesConsumed == effectiveMax)
                        {
                            string major = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                            currentToken.Append(major);
                            goto quit;
                        }
                    }

                    if (bytesConsumed > segmentStart)
                    {
                        string major = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                        currentToken.Append(major);
                    }

                    // Move past the "."
                    currentToken.Append('.');
                    requestLineState = HttpRequestLineState.MinorVersionNumber;
                    if (++bytesConsumed == effectiveMax)
                    {
                        goto quit;
                    }

                    goto case HttpRequestLineState.MinorVersionNumber;

                case HttpRequestLineState.MinorVersionNumber:
                    segmentStart = bytesConsumed;
                    while (buffer[bytesConsumed] != '\r')
                    {
                        if (buffer[bytesConsumed] < '0' || buffer[bytesConsumed] > '9')
                        {
                            parseStatus = ParserState.Invalid;
                            goto quit;
                        }

                        if (++bytesConsumed == effectiveMax)
                        {
                            string minor = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                            currentToken.Append(minor);
                            goto quit;
                        }
                    }

                    if (bytesConsumed > segmentStart)
                    {
                        string minor = Encoding.UTF8.GetString(buffer, segmentStart, bytesConsumed - segmentStart);
                        currentToken.Append(minor);
                    }

                    // Copy out value
                    httpRequest.Version = Version.Parse(currentToken.ToString());
                    currentToken.Clear();

                    // Move past the CR
                    requestLineState = HttpRequestLineState.AfterCarriageReturn;
                    if (++bytesConsumed == effectiveMax)
                    {
                        goto quit;
                    }

                    goto case HttpRequestLineState.AfterCarriageReturn;

                case HttpRequestLineState.AfterCarriageReturn:
                    if (buffer[bytesConsumed] != '\n')
                    {
                        parseStatus = ParserState.Invalid;
                        goto quit;
                    }

                    parseStatus = ParserState.Done;
                    bytesConsumed++;
                    break;
            }

        quit:
            totalBytesConsumed += bytesConsumed - initialBytesParsed;
            return parseStatus;
        }