From d354d3ce4ea3c94b22c6efaf802df7e698618794 Mon Sep 17 00:00:00 2001 From: Miha Zupan Date: Mon, 2 Jan 2023 12:09:46 +0100 Subject: [PATCH] Use IndexOfAnyValues in WebSocketValidate (#79974) --- .../Net/WebSockets/WebSocketValidate.cs | 47 ++++++------------- 1 file changed, 15 insertions(+), 32 deletions(-) diff --git a/src/libraries/Common/src/System/Net/WebSockets/WebSocketValidate.cs b/src/libraries/Common/src/System/Net/WebSockets/WebSocketValidate.cs index d0f3c6aedee73..95218a63269b0 100644 --- a/src/libraries/Common/src/System/Net/WebSockets/WebSocketValidate.cs +++ b/src/libraries/Common/src/System/Net/WebSockets/WebSocketValidate.cs @@ -1,8 +1,8 @@ // Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. +using System.Buffers; using System.Diagnostics; -using System.Globalization; using System.Text; namespace System.Net.WebSockets @@ -27,7 +27,10 @@ internal static partial class WebSocketValidate private const int CloseStatusCodeFailedTLSHandshake = 1015; private const int InvalidCloseStatusCodesFrom = 0; private const int InvalidCloseStatusCodesTo = 999; - private const string Separators = "()<>@,;:\\\"/[]?={} "; + + // [0x21, 0x7E] except separators "()<>@,;:\\\"/[]?={} ". + private static readonly IndexOfAnyValues s_validSubprotocolChars = + IndexOfAnyValues.Create("!#$%&'*+-.0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ^_`abcdefghijklmnopqrstuvwxyz|~"); internal static void ThrowIfInvalidState(WebSocketState currentState, bool isDisposed, WebSocketState[] validStates) { @@ -60,32 +63,16 @@ internal static void ValidateSubprotocol(string subProtocol) throw new ArgumentException(SR.net_WebSockets_InvalidEmptySubProtocol, nameof(subProtocol)); } - string? invalidChar = null; - int i = 0; - while (i < subProtocol.Length) + int indexOfInvalidChar = subProtocol.AsSpan().IndexOfAnyExcept(s_validSubprotocolChars); + if (indexOfInvalidChar >= 0) { - char ch = subProtocol[i]; - if (ch < 0x21 || ch > 0x7e) - { - invalidChar = $"[{(int)ch}]"; - break; - } + char invalidChar = subProtocol[indexOfInvalidChar]; - if (!char.IsLetterOrDigit(ch) && -#pragma warning disable CA2249 // Consider using 'string.Contains' instead of 'string.IndexOf'. This file is built into a project that doesn't have string.Contains(char). - Separators.IndexOf(ch) >= 0) -#pragma warning restore CA2249 - { - invalidChar = ch.ToString(); - break; - } + string invalidCharDescription = char.IsBetween(invalidChar, (char)0x21, (char)0x7E) + ? invalidChar.ToString() // ASCII separator + : $"[{(int)invalidChar}]"; - i++; - } - - if (invalidChar != null) - { - throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCharInProtocolString, subProtocol, invalidChar), nameof(subProtocol)); + throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCharInProtocolString, subProtocol, invalidCharDescription), nameof(subProtocol)); } } @@ -112,13 +99,9 @@ internal static void ValidateCloseStatus(WebSocketCloseStatus closeStatus, strin nameof(closeStatus)); } - int length = 0; - if (!string.IsNullOrEmpty(statusDescription)) - { - length = Encoding.UTF8.GetByteCount(statusDescription); - } - - if (length > MaxControlFramePayloadLength) + if (!string.IsNullOrEmpty(statusDescription) && + Encoding.UTF8.GetMaxByteCount(statusDescription.Length) > MaxControlFramePayloadLength && + Encoding.UTF8.GetByteCount(statusDescription) > MaxControlFramePayloadLength) { throw new ArgumentException(SR.Format(SR.net_WebSockets_InvalidCloseStatusDescription, statusDescription,