示例#1
0
 public static unsafe bool GetKnownMethod(this Span <byte> span, out ProtoMethod method, out int length)
 {
     fixed(byte *data = span)
     {
         method = GetKnownMethod(data, span.Length, out length);
         return(method != ProtoMethod.Custom);
     }
 }
示例#2
0
        public static string MethodToString(ProtoMethod method)
        {
            var methodIndex = (int)method;
            var methodNames = _methodNames;

            if ((uint)methodIndex < (uint)methodNames.Length)
            {
                return(methodNames[methodIndex]);
            }
            return(null);
        }
示例#3
0
        private void OnAuthorityFormTarget(ProtoMethod method, Span <byte> target)
        {
            _requestTargetForm = ProtoRequestTarget.AuthorityForm;

            // This is not complete validation. It is just a quick scan for invalid characters
            // but doesn't check that the target fully matches the URI spec.
            if (ProtoCharacters.ContainsInvalidAuthorityChar(target))
            {
                ThrowRequestTargetRejected(target);
            }

            // The authority-form of request-target is only used for CONNECT
            // requests (https://tools.ietf.org/html/rfc7231#section-4.3.6).
            if (method != ProtoMethod.Connect)
            {
                BadProtoRequestException.Throw(RequestRejectionReason.ConnectMethodRequired);
            }

            // When making a CONNECT request to establish a tunnel through one or
            // more proxies, a client MUST send only the target URI's authority
            // component (excluding any userinfo and its "@" delimiter) as the
            // request-target.For example,
            //
            //  CONNECT www.example.com:80 HTTP/1.1
            //
            // Allowed characters in the 'host + port' section of authority.
            // See https://tools.ietf.org/html/rfc3986#section-3.2

            var previousValue = _parsedRawTarget;

            if (ServerOptions.DisableStringReuse ||
                previousValue == null || previousValue.Length != target.Length ||
                !StringUtilities.BytesOrdinalEqualsStringAndAscii(previousValue, target))
            {
                // The previous string does not match what the bytes would convert to,
                // so we will need to generate a new string.
                RawTarget = _parsedRawTarget = target.GetAsciiStringNonNullCharacters();
            }
            else
            {
                // Reuse previous value
                RawTarget = _parsedRawTarget;
            }

            Path        = string.Empty;
            QueryString = string.Empty;
            // Clear parsedData for path and queryString as we won't check it if we come via this path again,
            // an setting to null is fast as it doesn't need to use a GC write barrier.
            _parsedPath = _parsedQueryString = null;
        }
示例#4
0
        private void OnAsteriskFormTarget(ProtoMethod method)
        {
            _requestTargetForm = ProtoRequestTarget.AsteriskForm;

            // The asterisk-form of request-target is only used for a server-wide
            // OPTIONS request (https://tools.ietf.org/html/rfc7231#section-4.3.7).
            if (method != ProtoMethod.Options)
            {
                BadProtoRequestException.Throw(RequestRejectionReason.OptionsMethodRequired);
            }

            RawTarget   = Asterisk;
            Path        = string.Empty;
            QueryString = string.Empty;
            // Clear parsedData as we won't check it if we come via this path again,
            // an setting to null is fast as it doesn't need to use a GC write barrier.
            _parsedRawTarget = _parsedPath = _parsedQueryString = null;
        }
示例#5
0
        public void OnStartLine(ProtoMethod method, ProtoVersion version, Span <byte> target, Span <byte> path, Span <byte> query, Span <byte> customMethod, bool pathEncoded)
        {
            Debug.Assert(target.Length != 0, "Request target must be non-zero length");

            var ch = target[0];

            if (ch == ByteForwardSlash)
            {
                // origin-form.
                // The most common form of request-target.
                // https://tools.ietf.org/html/rfc7230#section-5.3.1
                OnOriginFormTarget(method, version, target, path, query, customMethod, pathEncoded);
            }
            else if (ch == ByteAsterisk && target.Length == 1)
            {
                OnAsteriskFormTarget(method);
            }
            else if (target.GetKnownProtoScheme(out var scheme))
            {
                OnAbsoluteFormTarget(target, query);
            }
            else
            {
                // Assume anything else is considered authority form.
                // FYI: this should be an edge case. This should only happen when
                // a client mistakenly thinks this server is a proxy server.
                OnAuthorityFormTarget(method, target);
            }

            Method = method;
            if (method == ProtoMethod.Custom)
            {
                _methodText = customMethod.GetAsciiStringNonNullCharacters();
            }

            _httpVersion = version;

            Debug.Assert(RawTarget != null, "RawTarget was not set");
            Debug.Assert(((IProtoRequestFeature)this).Method != null, "Method was not set");
            Debug.Assert(Path != null, "Path was not set");
            Debug.Assert(QueryString != null, "QueryString was not set");
            Debug.Assert(ProtoVersion != null, "ProtoVersion was not set");
        }
示例#6
0
        // Compare with Proto2Stream.TryValidatePseudoHeaders
        private void OnOriginFormTarget(ProtoMethod method, ProtoVersion version, Span <byte> target, Span <byte> path, Span <byte> query, Span <byte> customMethod, bool pathEncoded)
        {
            Debug.Assert(target[0] == ByteForwardSlash, "Should only be called when path starts with /");

            _requestTargetForm = ProtoRequestTarget.OriginForm;

            if (target.Length == 1)
            {
                // If target.Length == 1 it can only be a forward slash (e.g. home page)
                // and we know RawTarget and Path are the same and QueryString is Empty
                RawTarget   = ForwardSlash;
                Path        = ForwardSlash;
                QueryString = string.Empty;
                // Clear parsedData as we won't check it if we come via this path again,
                // an setting to null is fast as it doesn't need to use a GC write barrier.
                _parsedRawTarget = _parsedPath = _parsedQueryString = null;
                return;
            }

            // 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"

            try
            {
                var disableStringReuse = ServerOptions.DisableStringReuse;
                // Read raw target before mutating memory.
                var previousValue = _parsedRawTarget;
                if (disableStringReuse ||
                    previousValue == null || previousValue.Length != target.Length ||
                    !StringUtilities.BytesOrdinalEqualsStringAndAscii(previousValue, target))
                {
                    // The previous string does not match what the bytes would convert to,
                    // so we will need to generate a new string.
                    RawTarget = _parsedRawTarget = target.GetAsciiStringNonNullCharacters();

                    previousValue = _parsedQueryString;
                    if (disableStringReuse ||
                        previousValue == null || previousValue.Length != query.Length ||
                        !StringUtilities.BytesOrdinalEqualsStringAndAscii(previousValue, query))
                    {
                        // The previous string does not match what the bytes would convert to,
                        // so we will need to generate a new string.
                        QueryString = _parsedQueryString = query.GetAsciiStringNonNullCharacters();
                    }
                    else
                    {
                        // Same as previous
                        QueryString = _parsedQueryString;
                    }

                    if (path.Length == 1)
                    {
                        // If path.Length == 1 it can only be a forward slash (e.g. home page)
                        Path = _parsedPath = ForwardSlash;
                    }
                    else
                    {
                        Path = _parsedPath = PathNormalizer.DecodePath(path, pathEncoded, RawTarget, query.Length);
                    }
                }
                else
                {
                    // As RawTarget is the same we can reuse the previous parsed values.
                    RawTarget   = _parsedRawTarget;
                    Path        = _parsedPath;
                    QueryString = _parsedQueryString;
                }
            }
            catch (InvalidOperationException)
            {
                ThrowRequestTargetRejected(target);
            }
        }
示例#7
0
 private static void SetKnownMethod(ulong mask, ulong knownMethodUlong, ProtoMethod knownMethod, int length)
 {
     _knownMethods[GetKnownMethodIndex(knownMethodUlong)] = new Tuple <ulong, ulong, ProtoMethod, int>(mask, knownMethodUlong, knownMethod, length);
 }
 internal static void Throw(RequestRejectionReason reason, ProtoMethod method)
 => throw GetException(reason, method.ToString().ToUpperInvariant());
示例#9
0
 public void OnStartLine(ProtoMethod method, ProtoVersion version, Span <byte> target, Span <byte> path, Span <byte> query, Span <byte> customMethod, bool pathEncoded)
 => Connection.OnStartLine(method, version, target, path, query, customMethod, pathEncoded);