<commit_before>﻿using Channels.Text.Primitives;
using System;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Channels.WebSockets
{
    internal static class WebSocketProtocol
    {
        public static string Name => "RFC6455";
        static readonly byte[]
            StandardPrefixBytes = Encoding.ASCII.GetBytes("HTTP/1.1 101 Switching Protocols\r\n"
                            + "Upgrade: websocket\r\n"
                            + "Connection: Upgrade\r\n"
                            + "Sec-WebSocket-Accept: "),
            StandardPostfixBytes = Encoding.ASCII.GetBytes("\r\n\r\n");
        internal static Task CompleteHandshakeAsync(ref HttpRequest request, WebSocketConnection socket)
        {
            var key = request.Headers.GetRaw("Sec-WebSocket-Key");

            var connection = socket.Connection;

            const int ResponseTokenLength = 28;

            var buffer = connection.Output.Alloc(StandardPrefixBytes.Length +
                ResponseTokenLength + StandardPostfixBytes.Length);
            string hashBase64 = ComputeReply(key);
            if (hashBase64.Length != ResponseTokenLength) throw new InvalidOperationException("Unexpected response key length");
            WebSocketServer.WriteStatus($"Response token: {hashBase64}");

            buffer.Write(StandardPrefixBytes);
            WritableBufferExtensions.WriteAsciiString(ref buffer, hashBase64);
            buffer.Write(StandardPostfixBytes);

            return buffer.FlushAsync();
        }
        static readonly byte[] WebSocketKeySuffixBytes = Encoding.ASCII.GetBytes("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");

        static bool IsBase64(byte value)
        {
            return (value >= (byte)'0' && value <= (byte)'9')
                || (value >= (byte)'a' && value <= (byte)'z')
                || (value >= (byte)'A' && value <= (byte)'Z')
                || (value == (byte)'/')
                || (value == (byte)'+')
                || (value == (byte)'=');
        }
        internal static string ComputeReply(ReadableBuffer key)
        {
            //To prove that the handshake was received, the server has to take two
            //pieces of information and combine them to form a response.  The first
            //piece of information comes from the |Sec-WebSocket-Key| header field
            //in the client handshake:

            //     Sec-WebSocket-Key: <KEY>

            //For this header field, the server has to take the value (as present
            //in the header field, e.g., the base64-encoded [RFC4648] version minus
            //any leading and trailing whitespace) and concatenate this with the
            //Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-
            //95CA-C5AB0DC85B11" in string form, which is unlikely to be used by
            //network endpoints that do not understand the WebSocket Protocol.  A
            //SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of
            //[RFC4648]), of this concatenation is then returned in the server's
            //handshake.

            const int ExpectedKeyLength = 24;
            if (key.Length != ExpectedKeyLength) throw new ArgumentException("Invalid key length", nameof(key));

            byte[] arr = new byte[ExpectedKeyLength + WebSocketKeySuffixBytes.Length];
            key.CopyTo(arr);
            Buffer.BlockCopy( // append the magic number from RFC6455
                WebSocketKeySuffixBytes, 0,
                arr, ExpectedKeyLength,
                WebSocketKeySuffixBytes.Length);

            // compute the hash
            using (var sha = SHA1.Create())
            {
                var hash = sha.ComputeHash(arr, 0,
                    ExpectedKeyLength + WebSocketKeySuffixBytes.Length);
                return Convert.ToBase64String(hash);
            }
        }



        internal static void WriteFrameHeader(ref WritableBuffer output, WebSocketsFrame.FrameFlags flags, WebSocketsFrame.OpCodes opCode, int payloadLength, int mask)
        {
            output.Ensure(MaxHeaderLength);

            int index = 0;
            var span = output.Memory;
            
            span[index++] = (byte)(((int)flags & 240) | ((int)opCode & 15));
            if (payloadLength > ushort.MaxValue)
            { // write as a 64-bit length
                span[index++] = (byte)((mask != 0 ? 128 : 0) | 127);
                span.Slice(index).Write(payloadLength);
                index += 8;
            }
            else if (payloadLength > 125)
            { // write as a 16-bit length
                span[index++] = (byte)((mask != 0 ? 128 : 0) | 126);
                span.Slice(index).Write((ushort)payloadLength >> 8);
                index += 2;
            }
            else
            { // write in the header
                span[index++] = (byte)((mask != 0 ? 128 : 0) | payloadLength);
            }
            if (mask != 0)
            {
                span.Slice(index).Write(mask);
                index += 4;
            }<commit_msg>Use Base64.Encode to do the hash encode directly rather than to a string<commit_after>﻿using Channels.Text.Primitives;
using System;
using System.Binary;
using System.Runtime.CompilerServices;
using System.Security.Cryptography;
using System.Text;
using System.Threading.Tasks;

namespace Channels.WebSockets
{
    internal static class WebSocketProtocol
    {
        public static string Name => "RFC6455";
        static readonly byte[]
            StandardPrefixBytes = Encoding.ASCII.GetBytes("HTTP/1.1 101 Switching Protocols\r\n"
                            + "Upgrade: websocket\r\n"
                            + "Connection: Upgrade\r\n"
                            + "Sec-WebSocket-Accept: "),
            StandardPostfixBytes = Encoding.ASCII.GetBytes("\r\n\r\n");
        internal unsafe static Task CompleteHandshakeAsync(ref HttpRequest request, WebSocketConnection socket)
        {
            var key = request.Headers.GetRaw("Sec-WebSocket-Key");

            var connection = socket.Connection;

            const int ResponseTokenLength = 28;

            var buffer = connection.Output.Alloc(StandardPrefixBytes.Length +
                ResponseTokenLength + StandardPostfixBytes.Length);

            var hashBase64Buffer = stackalloc byte[ResponseTokenLength];
            var hashBase64Span = new Span<byte>(hashBase64Buffer, ResponseTokenLength);
            ComputeReply(key, hashBase64Span);
            WebSocketServer.WriteStatus($"Response token: {hashBase64Span}");

            buffer.Write(StandardPrefixBytes);
            buffer.Write(hashBase64Span);
            buffer.Write(StandardPostfixBytes);

            return buffer.FlushAsync();
        }
        static readonly byte[] WebSocketKeySuffixBytes = Encoding.ASCII.GetBytes("258EAFA5-E914-47DA-95CA-C5AB0DC85B11");

        static bool IsBase64(byte value)
        {
            return (value >= (byte)'0' && value <= (byte)'9')
                || (value >= (byte)'a' && value <= (byte)'z')
                || (value >= (byte)'A' && value <= (byte)'Z')
                || (value == (byte)'/')
                || (value == (byte)'+')
                || (value == (byte)'=');
        }
        internal static void ComputeReply(ReadableBuffer key, Span<byte> destination)
        {
            //To prove that the handshake was received, the server has to take two
            //pieces of information and combine them to form a response.  The first
            //piece of information comes from the |Sec-WebSocket-Key| header field
            //in the client handshake:

            //     Sec-WebSocket-Key: <KEY>

            //For this header field, the server has to take the value (as present
            //in the header field, e.g., the base64-encoded [RFC4648] version minus
            //any leading and trailing whitespace) and concatenate this with the
            //Globally Unique Identifier (GUID, [RFC4122]) "258EAFA5-E914-47DA-
            //95CA-C5AB0DC85B11" in string form, which is unlikely to be used by
            //network endpoints that do not understand the WebSocket Protocol.  A
            //SHA-1 hash (160 bits) [FIPS.180-3], base64-encoded (see Section 4 of
            //[RFC4648]), of this concatenation is then returned in the server's
            //handshake.

            const int ExpectedKeyLength = 24;
            if (key.Length != ExpectedKeyLength) throw new ArgumentException("Invalid key length", nameof(key));

            byte[] arr = new byte[ExpectedKeyLength + WebSocketKeySuffixBytes.Length];
            key.CopyTo(arr);
            Buffer.BlockCopy( // append the magic number from RFC6455
                WebSocketKeySuffixBytes, 0,
                arr, ExpectedKeyLength,
                WebSocketKeySuffixBytes.Length);

            // compute the hash
            using (var sha = SHA1.Create())
            {
                var hash = sha.ComputeHash(arr, 0,
                    ExpectedKeyLength + WebSocketKeySuffixBytes.Length);

                Base64.Encode(hash, destination);
            }
        }



        internal static void WriteFrameHeader(ref WritableBuffer output, WebSocketsFrame.FrameFlags flags, WebSocketsFrame.OpCodes opCode, int payloadLength, int mask)
        {
            output.Ensure(MaxHeaderLength);

            int index = 0;
            var span = output.Memory;
            
            span[index++] = (byte)(((int)flags & 240) | ((int)opCode & 15));
            if (payloadLength > ushort.MaxValue)
            { // write as a 64-bit length
                span[index++] = (byte)((mask != 0 ? 128 : 0) | 127);
                span.Slice(index).Write(payloadLength);
                index += 8;
            }
            else if (payloadLength > 125)
            { // write as a 16-bit length
                span[index++] = (byte)((mask != 0 ? 128 : 0) | 126);
                span.Slice(index).Write((ushort)payloadLength);
                index += 2;
            }
            else
            { // write in the header
                span[index++] = (byte)((mask != 0 ? 128 : 0) | payloadLength);
            }
            if (mask != 0)
            {
                span.Slice(index).Write(mask);
                index += 4;
            }
{'fraction_non_alphanumeric': 0.10486204900636698, 'fraction_numerical': 0.02595022187922053, 'mean_word_length': 1.8622308117062396, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 21, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '405177', 'n_tokens_mistral': 2927, 'n_tokens_neox': 2671, 'n_words': 737}