#![allow(
clippy::unwrap_used,
clippy::expect_used,
clippy::indexing_slicing,
clippy::panic,
dead_code
)]
use std::io::{Read, Write};
use std::net::{TcpListener, TcpStream};
use crate::ws::OpCode;
use crate::ws::frame::{MAX_HEADER_LEN, encode_header};
use crate::ws::handshake::compute_accept;
use crate::ws::mask::mask_inplace;
pub(crate) fn run_echo_server(listener: TcpListener, shutdown: std::sync::mpsc::Receiver<()>) {
let (mut stream, _) = listener.accept().expect("accept");
stream.set_nodelay(true).unwrap();
let _ = shutdown;
let mut buf = [0_u8; 4096];
let mut req = Vec::new();
loop {
let n = stream.read(&mut buf).unwrap();
assert!(n > 0, "client closed before sending request");
req.extend_from_slice(&buf[..n]);
if req.windows(4).any(|w| w == b"\r\n\r\n") {
break;
}
}
let req_str = std::str::from_utf8(&req).unwrap();
let key = req_str
.lines()
.find(|l| l.to_ascii_lowercase().starts_with("sec-websocket-key:"))
.and_then(|l| l.split(':').nth(1))
.expect("Sec-WebSocket-Key header")
.trim();
let accept = compute_accept(key);
let resp = format!(
"HTTP/1.1 101 Switching Protocols\r\nUpgrade: websocket\r\nConnection: Upgrade\r\nSec-WebSocket-Accept: {accept}\r\n\r\n"
);
stream.write_all(resp.as_bytes()).unwrap();
let (opcode, payload) = read_one_frame(&mut stream);
assert_eq!(opcode, OpCode::Text);
let mut frame = vec![0_u8; MAX_HEADER_LEN];
let hn = encode_header(&mut frame, true, OpCode::Text, None, payload.len() as u64);
frame.truncate(hn);
frame.extend_from_slice(&payload);
stream.write_all(&frame).unwrap();
let (close_opcode, _) = read_one_frame(&mut stream);
assert_eq!(close_opcode, OpCode::Close);
let mut close_frame = vec![0_u8; MAX_HEADER_LEN + 2];
let hn = encode_header(&mut close_frame, true, OpCode::Close, None, 2);
close_frame.truncate(hn);
close_frame.extend_from_slice(&1000_u16.to_be_bytes());
let _ = stream.write_all(&close_frame);
}
pub(crate) fn read_one_frame(stream: &mut TcpStream) -> (OpCode, Vec<u8>) {
let mut hdr = [0_u8; 2];
stream.read_exact(&mut hdr).unwrap();
let fin = (hdr[0] & 0x80) != 0;
assert!(fin, "fragmented frames not supported in test server");
let opcode = match hdr[0] & 0x0F {
0x1 => OpCode::Text,
0x2 => OpCode::Binary,
0x8 => OpCode::Close,
0x9 => OpCode::Ping,
0xA => OpCode::Pong,
other => panic!("unsupported opcode 0x{other:x}"),
};
let masked = (hdr[1] & 0x80) != 0;
let len_field = hdr[1] & 0x7F;
let len: usize = if len_field < 126 {
usize::from(len_field)
} else if len_field == 126 {
let mut b = [0_u8; 2];
stream.read_exact(&mut b).unwrap();
usize::from(u16::from_be_bytes(b))
} else {
let mut b = [0_u8; 8];
stream.read_exact(&mut b).unwrap();
usize::try_from(u64::from_be_bytes(b)).unwrap()
};
let mut mask = [0_u8; 4];
if masked {
stream.read_exact(&mut mask).unwrap();
}
let mut payload = vec![0_u8; len];
stream.read_exact(&mut payload).unwrap();
if masked {
mask_inplace(&mut payload, mask);
}
(opcode, payload)
}