예제 #1
0
            private void ParseChunkedPrefix()
            {
                var scan     = _input.ConsumingStart();
                var consumed = scan;

                try
                {
                    var ch1 = scan.Take();
                    var ch2 = scan.Take();
                    if (ch1 == -1 || ch2 == -1)
                    {
                        return;
                    }

                    var chunkSize = CalculateChunkSize(ch1, 0);
                    ch1 = ch2;

                    do
                    {
                        if (ch1 == ';')
                        {
                            consumed = scan;

                            _inputLength = chunkSize;
                            _mode        = Mode.Extension;
                            return;
                        }

                        ch2 = scan.Take();
                        if (ch2 == -1)
                        {
                            return;
                        }

                        if (ch1 == '\r' && ch2 == '\n')
                        {
                            consumed     = scan;
                            _inputLength = chunkSize;

                            if (chunkSize > 0)
                            {
                                _mode = Mode.Data;
                            }
                            else
                            {
                                _mode = Mode.Trailer;
                            }

                            return;
                        }

                        chunkSize = CalculateChunkSize(ch1, chunkSize);
                        ch1       = ch2;
                    } while (ch1 != -1);
                }
                finally
                {
                    _input.ConsumingComplete(consumed, scan);
                }
            }
            private void ParseChunkedTrailer(SocketInput input)
            {
                var scan     = input.ConsumingStart();
                var consumed = scan;

                try
                {
                    var ch1 = scan.Take();
                    var ch2 = scan.Take();

                    if (ch1 == -1 || ch2 == -1)
                    {
                        return;
                    }
                    else if (ch1 == '\r' && ch2 == '\n')
                    {
                        consumed = scan;
                        _mode    = Mode.Complete;
                    }
                    else
                    {
                        _mode = Mode.TrailerHeaders;
                    }
                }
                finally
                {
                    input.ConsumingComplete(consumed, scan);
                }
            }
            private void ParseChunkedSuffix(SocketInput input)
            {
                var scan     = input.ConsumingStart();
                var consumed = scan;

                try
                {
                    var ch1 = scan.Take();
                    var ch2 = scan.Take();
                    if (ch1 == -1 || ch2 == -1)
                    {
                        return;
                    }
                    else if (ch1 == '\r' && ch2 == '\n')
                    {
                        consumed = scan;
                        _mode    = Mode.Prefix;
                    }
                    else
                    {
                        _context.RejectRequest("Bad chunk suffix");
                    }
                }
                finally
                {
                    input.ConsumingComplete(consumed, scan);
                }
            }
            private void ParseExtension(SocketInput input)
            {
                var scan     = input.ConsumingStart();
                var consumed = scan;

                try
                {
                    // Chunk-extensions not currently parsed
                    // Just drain the data
                    do
                    {
                        if (scan.Seek(ref _vectorCRs) == -1)
                        {
                            // End marker not found yet
                            consumed = scan;
                            return;
                        }
                        ;

                        var ch1 = scan.Take();
                        var ch2 = scan.Take();

                        if (ch2 == '\n')
                        {
                            consumed = scan;
                            if (_inputLength > 0)
                            {
                                _mode = Mode.Data;
                            }
                            else
                            {
                                _mode = Mode.Trailer;
                            }
                        }
                        else if (ch2 == -1)
                        {
                            return;
                        }
                    } while (_mode == Mode.Extension);
                }
                finally
                {
                    input.ConsumingComplete(consumed, scan);
                }
            }
        private static async Task <ArraySegment <byte> > PeekAsyncAwaited(this SocketInput input)
        {
            while (true)
            {
                await input;

                var fin = input.CheckFinOrThrow();

                var begin   = input.ConsumingStart();
                var segment = begin.PeekArraySegment();
                input.ConsumingComplete(begin, begin);

                if (segment.Count != 0 || fin)
                {
                    return(segment);
                }
            }
        }
        public static ValueTask <ArraySegment <byte> > PeekAsync(this SocketInput input)
        {
            while (input.IsCompleted)
            {
                var fin = input.CheckFinOrThrow();

                var begin   = input.ConsumingStart();
                var segment = begin.PeekArraySegment();
                input.ConsumingComplete(begin, begin);

                if (segment.Count != 0 || fin)
                {
                    return(new ValueTask <ArraySegment <byte> >(segment));
                }
            }

            return(new ValueTask <ArraySegment <byte> >(input.PeekAsyncAwaited()));
        }
        private static async Task <int> ReadAsyncAwaited(this SocketInput input, byte[] buffer, int offset, int count)
        {
            while (true)
            {
                await input;

                var fin = input.CheckFinOrThrow();

                var begin = input.ConsumingStart();
                int actual;
                var end = begin.CopyTo(buffer, offset, count, out actual);
                input.ConsumingComplete(end, end);

                if (actual != 0 || fin)
                {
                    return(actual);
                }
            }
        }
        public static ValueTask <int> ReadAsync(this SocketInput input, byte[] buffer, int offset, int count)
        {
            while (input.IsCompleted)
            {
                var fin = input.CheckFinOrThrow();

                var begin = input.ConsumingStart();
                int actual;
                var end = begin.CopyTo(buffer, offset, count, out actual);
                input.ConsumingComplete(end, end);

                if (actual != 0 || fin)
                {
                    return(new ValueTask <int>(actual));
                }
            }

            return(new ValueTask <int>(input.ReadAsyncAwaited(buffer, offset, count)));
        }
            private int ReadChunkedData(SocketInput input, byte[] buffer, int offset, int count)
            {
                var scan = input.ConsumingStart();
                int actual;

                try
                {
                    var limit = buffer == null ? _inputLength : Math.Min(count, _inputLength);
                    scan          = scan.CopyTo(buffer, offset, limit, out actual);
                    _inputLength -= actual;
                }
                finally
                {
                    input.ConsumingComplete(scan, scan);
                }

                if (_inputLength == 0)
                {
                    _mode = Mode.Suffix;
                }

                return(actual);
            }
예제 #10
0
        protected RequestLineStatus TakeStartLine(SocketInput input)
        {
            var scan     = input.ConsumingStart();
            var consumed = scan;

            try
            {
                // We may hit this when the client has stopped sending data but
                // the connection hasn't closed yet, and therefore Frame.Stop()
                // hasn't been called yet.
                if (scan.Peek() == -1)
                {
                    return(RequestLineStatus.Empty);
                }

                _requestProcessingStatus = RequestProcessingStatus.RequestStarted;

                string method;
                var    begin = scan;
                if (!begin.GetKnownMethod(out method))
                {
                    if (scan.Seek(ref _vectorSpaces) == -1)
                    {
                        return(RequestLineStatus.MethodIncomplete);
                    }

                    method = begin.GetAsciiString(scan);

                    if (method == null)
                    {
                        RejectRequest("Missing method.");
                    }

                    // Note: We're not in the fast path any more (GetKnownMethod should have handled any HTTP Method we're aware of)
                    // So we can be a tiny bit slower and more careful here.
                    for (int i = 0; i < method.Length; i++)
                    {
                        if (!IsValidTokenChar(method[i]))
                        {
                            RejectRequest("Invalid method.");
                        }
                    }
                }
                else
                {
                    scan.Skip(method.Length);
                }

                scan.Take();
                begin = scan;
                var needDecode = false;
                var chFound    = scan.Seek(ref _vectorSpaces, ref _vectorQuestionMarks, ref _vectorPercentages);
                if (chFound == -1)
                {
                    return(RequestLineStatus.TargetIncomplete);
                }
                else if (chFound == '%')
                {
                    needDecode = true;
                    chFound    = scan.Seek(ref _vectorSpaces, ref _vectorQuestionMarks);
                    if (chFound == -1)
                    {
                        return(RequestLineStatus.TargetIncomplete);
                    }
                }

                var pathBegin = begin;
                var pathEnd   = scan;

                var queryString = "";
                if (chFound == '?')
                {
                    begin = scan;
                    if (scan.Seek(ref _vectorSpaces) == -1)
                    {
                        return(RequestLineStatus.TargetIncomplete);
                    }
                    queryString = begin.GetAsciiString(scan);
                }

                var queryEnd = scan;

                if (pathBegin.Peek() == ' ')
                {
                    RejectRequest("Missing request target.");
                }

                scan.Take();
                begin = scan;
                if (scan.Seek(ref _vectorCRs) == -1)
                {
                    return(RequestLineStatus.VersionIncomplete);
                }

                string httpVersion;
                if (!begin.GetKnownVersion(out httpVersion))
                {
                    // A slower fallback is necessary since the iterator's PeekLong() method
                    // used in GetKnownVersion() only examines two memory blocks at most.
                    // Although unlikely, it is possible that the 8 bytes forming the version
                    // could be spread out on more than two blocks, if the connection
                    // happens to be unusually slow.
                    httpVersion = begin.GetAsciiString(scan);

                    if (httpVersion == null)
                    {
                        RejectRequest("Missing HTTP version.");
                    }
                    else if (httpVersion != "HTTP/1.0" && httpVersion != "HTTP/1.1")
                    {
                        RejectRequest("Unrecognized HTTP version.");
                    }
                }

                scan.Take();
                var next = scan.Take();
                if (next == -1)
                {
                    return(RequestLineStatus.Incomplete);
                }
                else if (next != '\n')
                {
                    RejectRequest("Missing LF in request line.");
                }

                // URIs are always encoded/escaped to ASCII https://tools.ietf.org/html/rfc3986#page-11
                // Multibyte Internationalized Resource Identifiers (IRIs) are first converted to utf8;
                // then encoded/escaped to ASCII  https://www.ietf.org/rfc/rfc3987.txt "Mapping of IRIs to URIs"
                string requestUrlPath;
                string rawTarget;
                if (needDecode)
                {
                    // Read raw target before mutating memory.
                    rawTarget = pathBegin.GetAsciiString(queryEnd);

                    // URI was encoded, unescape and then parse as utf8
                    pathEnd        = UrlPathDecoder.Unescape(pathBegin, pathEnd);
                    requestUrlPath = pathBegin.GetUtf8String(pathEnd);
                }
                else
                {
                    // URI wasn't encoded, parse as ASCII
                    requestUrlPath = pathBegin.GetAsciiString(pathEnd);

                    if (queryString.Length == 0)
                    {
                        // No need to allocate an extra string if the path didn't need
                        // decoding and there's no query string following it.
                        rawTarget = requestUrlPath;
                    }
                    else
                    {
                        rawTarget = pathBegin.GetAsciiString(queryEnd);
                    }
                }

                var normalizedTarget = PathNormalizer.RemoveDotSegments(requestUrlPath);

                consumed    = scan;
                Method      = method;
                QueryString = queryString;
                RawTarget   = rawTarget;
                HttpVersion = httpVersion;

                bool caseMatches;
                if (RequestUrlStartsWithPathBase(normalizedTarget, out caseMatches))
                {
                    PathBase = caseMatches ? _pathBase : normalizedTarget.Substring(0, _pathBase.Length);
                    Path     = normalizedTarget.Substring(_pathBase.Length);
                }
                else if (rawTarget[0] == '/') // check rawTarget since normalizedTarget can be "" or "/" after dot segment removal
                {
                    Path = normalizedTarget;
                }
                else
                {
                    Path        = string.Empty;
                    PathBase    = string.Empty;
                    QueryString = string.Empty;
                }

                return(RequestLineStatus.Done);
            }
            finally
            {
                input.ConsumingComplete(consumed, scan);
            }
        }
예제 #11
0
        public bool TakeMessageHeaders(SocketInput input, FrameRequestHeaders requestHeaders)
        {
            var scan     = input.ConsumingStart();
            var consumed = scan;

            try
            {
                while (!scan.IsEnd)
                {
                    var ch = scan.Peek();
                    if (ch == -1)
                    {
                        return(false);
                    }
                    else if (ch == '\r')
                    {
                        // Check for final CRLF.
                        scan.Take();
                        ch = scan.Take();

                        if (ch == -1)
                        {
                            return(false);
                        }
                        else if (ch == '\n')
                        {
                            consumed = scan;
                            return(true);
                        }

                        // Headers don't end in CRLF line.
                        RejectRequest("Headers corrupted, invalid header sequence.");
                    }
                    else if (ch == ' ' || ch == '\t')
                    {
                        RejectRequest("Header line must not start with whitespace.");
                    }

                    var beginName = scan;
                    if (scan.Seek(ref _vectorColons, ref _vectorCRs) == -1)
                    {
                        return(false);
                    }
                    var endName = scan;

                    ch = scan.Take();
                    if (ch != ':')
                    {
                        RejectRequest("No ':' character found in header line.");
                    }

                    var validateName = beginName;
                    if (validateName.Seek(ref _vectorSpaces, ref _vectorTabs, ref _vectorColons) != ':')
                    {
                        RejectRequest("Whitespace is not allowed in header name.");
                    }

                    var beginValue = scan;
                    ch = scan.Peek();

                    if (ch == -1)
                    {
                        return(false);
                    }

                    // Skip header value leading whitespace.
                    while (ch == ' ' || ch == '\t')
                    {
                        scan.Take();
                        beginValue = scan;

                        ch = scan.Peek();
                        if (ch == -1)
                        {
                            return(false);
                        }
                    }

                    scan = beginValue;
                    if (scan.Seek(ref _vectorCRs) == -1)
                    {
                        // no "\r" in sight, burn used bytes and go back to await more data
                        return(false);
                    }

                    scan.Take();      // we know this is '\r'
                    ch = scan.Take(); // expecting '\n'

                    if (ch == -1)
                    {
                        return(false);
                    }
                    else if (ch != '\n')
                    {
                        RejectRequest("Header line must end in CRLF; only CR found.");
                    }

                    var next = scan.Peek();
                    if (next == -1)
                    {
                        return(false);
                    }
                    else if (next == ' ' || next == '\t')
                    {
                        // From https://tools.ietf.org/html/rfc7230#section-3.2.4:
                        //
                        // Historically, HTTP header field values could be extended over
                        // multiple lines by preceding each extra line with at least one space
                        // or horizontal tab (obs-fold).  This specification deprecates such
                        // line folding except within the message/http media type
                        // (Section 8.3.1).  A sender MUST NOT generate a message that includes
                        // line folding (i.e., that has any field-value that contains a match to
                        // the obs-fold rule) unless the message is intended for packaging
                        // within the message/http media type.
                        //
                        // A server that receives an obs-fold in a request message that is not
                        // within a message/http container MUST either reject the message by
                        // sending a 400 (Bad Request), preferably with a representation
                        // explaining that obsolete line folding is unacceptable, or replace
                        // each received obs-fold with one or more SP octets prior to
                        // interpreting the field value or forwarding the message downstream.
                        RejectRequest("Header value line folding not supported.");
                    }

                    // Trim trailing whitespace from header value by repeatedly advancing to next
                    // whitespace or CR.
                    //
                    // - If CR is found, this is the end of the header value.
                    // - If whitespace is found, this is the _tentative_ end of the header value.
                    //   If non-whitespace is found after it and it's not CR, seek again to the next
                    //   whitespace or CR for a new (possibly tentative) end of value.
                    var ws       = beginValue;
                    var endValue = scan;
                    do
                    {
                        ws.Seek(ref _vectorSpaces, ref _vectorTabs, ref _vectorCRs);
                        endValue = ws;

                        ch = ws.Take();
                        while (ch == ' ' || ch == '\t')
                        {
                            ch = ws.Take();
                        }
                    } while (ch != '\r');

                    var name  = beginName.GetArraySegment(endName);
                    var value = beginValue.GetAsciiString(endValue);

                    consumed = scan;
                    requestHeaders.Append(name.Array, name.Offset, name.Count, value);
                }

                return(false);
            }
            finally
            {
                input.ConsumingComplete(consumed, scan);
            }
        }