// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Apply XOR mask to payload data
fn mask_payload(
data : FixedArray[Byte],
mask : BytesView,
offset~ : Int,
len~ : Int,
mask_offset? : Int = 0,
) -> Unit {
for i = 0; i < len; i = i + 1 {
data[offset + i] = data[offset + i] ^ mask[(mask_offset + i) % 4]
}
}
///|
const MAGIC = "258EAFA5-E914-47DA-95CA-C5AB0DC85B11"
///|
/// Generate WebSocket accept key from client key using SHA-1 and base64
#warnings("-unused_error_type")
fn generate_accept_key(client_key : String) -> String raise {
// WebSocket magic string as defined in RFC 6455
let combined = client_key + MAGIC
let combined_bytes = @utf8.encode(combined)
// Use the crypto library for proper SHA-1 hashing
let hash = @tls.sha1(combined_bytes)
// Use our base64 encoding function for now
@base64.encode(hash)
}
///|
fn verify_utf8(data : BytesView, remaining~ : Int) -> Int raise {
for i = 0, remaining = remaining; i < data.length(); {
match (remaining, data.unsafe_get(i)) {
(0, 0..<0x80) => continue i + 1, 0
(0, 0xc0..<0xe0) => continue i + 1, 1
(0, 0xe0..<0xf0) => continue i + 1, 2
(0, 0xf0..<0xf8) => continue i + 1, 3
(0, _) => {
ignore(@utf8.decode(data[i:i + 1]))
panic()
}
(remaining, 0x80..<0xc0) => continue i + 1, remaining - 1
(_, byte) => {
ignore(@utf8.decode([0xc0, byte]))
panic()
}
}
} nobreak {
remaining
}
}