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); } }
public static string MethodToString(ProtoMethod method) { var methodIndex = (int)method; var methodNames = _methodNames; if ((uint)methodIndex < (uint)methodNames.Length) { return(methodNames[methodIndex]); } return(null); }
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; }
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; }
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"); }
// 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); } }
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());
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);