use base64::{Engine, engine::general_purpose::STANDARD};
use rama_http_types::HeaderValue;
use crate::{HeaderDecode, HeaderEncode, TypedHeader};
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct SecWebSocketKey(pub(super) HeaderValue);
impl SecWebSocketKey {
#[must_use]
pub fn random() -> Self {
let r: [u8; 16] = rand::random();
r.into()
}
}
impl TypedHeader for SecWebSocketKey {
fn name() -> &'static ::rama_http_types::header::HeaderName {
&::rama_http_types::header::SEC_WEBSOCKET_KEY
}
}
impl HeaderDecode for SecWebSocketKey {
fn decode<'i, I>(values: &mut I) -> Result<Self, crate::Error>
where
I: Iterator<Item = &'i ::rama_http_types::header::HeaderValue>,
{
let value = crate::util::TryFromValues::try_from_values(values).map(SecWebSocketKey)?;
let mut k = [0u8; 16];
if STANDARD.decode_slice(value.0.as_bytes(), &mut k[..]).ok() != Some(16) {
Err(crate::Error::invalid())
} else {
Ok(value)
}
}
}
impl HeaderEncode for SecWebSocketKey {
fn encode<E: Extend<::rama_http_types::HeaderValue>>(&self, values: &mut E) {
values.extend(::std::iter::once((&self.0).into()));
}
}
impl From<[u8; 16]> for SecWebSocketKey {
fn from(bytes: [u8; 16]) -> Self {
#[expect(
clippy::expect_used,
reason = " ASSUMPTION standard base64 encoding of 16 bytes is always valid http header"
)]
let mut value = HeaderValue::try_from(STANDARD.encode(bytes))
.expect(" ASSUMPTION standard base64 encoding of 16 bytes is always valid http header");
value.set_sensitive(true);
Self(value)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::common::test_decode;
#[test]
fn from_bytes() {
let bytes: [u8; 16] = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16];
_ = SecWebSocketKey::from(bytes);
}
#[test]
fn test_invalid_websocket_key_empty() {
assert!(test_decode::<SecWebSocketKey>(&[""]).is_none());
}
#[test]
fn test_invalid_websocket_key_too_long() {
assert!(test_decode::<SecWebSocketKey>(&["dGhlIHNhbXBsZSBub25jZQ==AAAAAAAAAA"]).is_none());
}
#[test]
fn test_invalid_websocket_key_base64_symbol() {
assert!(test_decode::<SecWebSocketKey>(&["dGhlIHNhbXBsZSBub25jZQ!!"]).is_none());
}
#[test]
fn test_invalid_websocket_key_decoded_length() {
assert!(test_decode::<SecWebSocketKey>(&["AAAAAAAAAAAAAAAAAAAAAAAA"]).is_none());
}
#[test]
fn test_valid_websocket_key() {
_ = test_decode::<SecWebSocketKey>(&["dGhlIHNhbXBsZSBub25jZQ=="]).unwrap();
}
}