use crate::ffi::errors::{ErrorHandle, Status};
use crate::ffi::{Buffer, Slice};
use crate::models::Role;
use crate::protocol::base::Transport;
use crate::websocket::{CloseCode, Frame, Opcode, WebSocketConnection};
pub struct WebSocket {
pub connection: WebSocketConnection<Box<dyn Transport>>,
pub handle: tokio::runtime::Handle,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_free(socket: *mut WebSocket) {
if !socket.is_null() {
drop(unsafe { Box::from_raw(socket) });
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_role(socket: *const WebSocket) -> u32 {
match unsafe { socket.as_ref() } {
Some(socket) => Role::build(socket.connection.role()),
None => Role::build(Role::UserAgent),
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_closing(socket: *const WebSocket) -> bool {
unsafe { socket.as_ref() }.is_some_and(|socket| socket.connection.closing())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_id(socket: *const WebSocket) -> Buffer {
match unsafe { socket.as_ref() } {
Some(socket) => Buffer::new(socket.connection.id().0.to_vec()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_send(socket: *mut WebSocket, fin: bool, opcode: u8, payload: *const u8, payload_len: usize, error: *mut *mut ErrorHandle) -> Status {
let Some(socket) = (unsafe { socket.as_mut() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let Some(opcode) = Opcode::from_code(opcode) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
let payload = unsafe { Slice::borrow(payload, payload_len) }.unwrap_or_default().to_vec();
let frame = Frame { fin, opcode, mask: None, payload: payload.into() };
match socket.handle.clone().block_on(socket.connection.send(frame)) {
Ok(()) => Status::Ok,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_receive(socket: *mut WebSocket, fin: *mut bool, opcode: *mut u8, out: *mut Buffer, error: *mut *mut ErrorHandle) -> Status {
let Some(socket) = (unsafe { socket.as_mut() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
match socket.handle.clone().block_on(socket.connection.receive()) {
Ok(frame) => {
if !fin.is_null() {
unsafe { *fin = frame.fin };
}
if !opcode.is_null() {
unsafe { *opcode = frame.opcode.code() };
}
unsafe { *out = Buffer::new(frame.payload.to_vec()) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_send_message(socket: *mut WebSocket, opcode: u8, payload: *const u8, payload_len: usize, error: *mut *mut ErrorHandle) -> Status {
unsafe { soyokaze_websocket_send(socket, true, opcode, payload, payload_len, error) }
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_receive_message(socket: *mut WebSocket, opcode: *mut u8, out: *mut Buffer, error: *mut *mut ErrorHandle) -> Status {
let Some(socket) = (unsafe { socket.as_mut() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
if out.is_null() {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
}
match socket.handle.clone().block_on(socket.connection.receive_message()) {
Ok((received, payload)) => {
if !opcode.is_null() {
unsafe { *opcode = received.code() };
}
unsafe { *out = Buffer::new(payload.to_vec()) };
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_close(socket: *mut WebSocket, code: u16, reason: *const u8, reason_len: usize) -> bool {
let Some(socket) = (unsafe { socket.as_mut() }) else {
return false;
};
let Some(code) = CloseCode::from_code(code) else {
return false;
};
let reason = unsafe { Slice::borrow_text(reason, reason_len) }.unwrap_or_default();
socket.handle.clone().block_on(socket.connection.close(code, reason));
true
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_guid() -> Slice {
Slice::text(crate::websocket::GUID)
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_version() -> Slice {
Slice::text(crate::websocket::VERSION)
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_protocol() -> Slice {
Slice::text(crate::websocket::PROTOCOL)
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_maximum_control_payload() -> usize {
crate::websocket::MAXIMUM_CONTROL_PAYLOAD
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_opcode_known(opcode: u8) -> bool {
Opcode::from_code(opcode).is_some()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_opcode_control(opcode: u8) -> bool {
Opcode::from_code(opcode).is_some_and(|opcode| opcode.control())
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_close_code_known(code: u16) -> bool {
CloseCode::from_code(code).is_some()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_close_code_permitted(code: u16) -> bool {
CloseCode::permitted(code)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_random(out: *mut u8, out_len: usize) -> bool {
if out.is_null() {
return false;
}
Frame::random(unsafe { std::slice::from_raw_parts_mut(out, out_len) }).is_ok()
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_masking_key() -> Buffer {
match Frame::masking_key() {
Ok(key) => Buffer::new(key.to_vec()),
Err(_) => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_apply_mask(mask: *const u8, payload: *mut u8, payload_len: usize) -> bool {
let Some(mask) = (unsafe { Slice::borrow(mask, 4) }) else {
return false;
};
let Ok(mask) = <[u8; 4]>::try_from(mask) else {
return false;
};
if payload.is_null() {
return false;
}
Frame::apply_mask(mask, unsafe { std::slice::from_raw_parts_mut(payload, payload_len) });
true
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct FrameHead {
pub fin: bool,
pub opcode: u8,
pub masked: bool,
pub mask: [u8; 4],
pub start: usize,
pub length: usize,
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_frame_head(data: *const u8, data_len: usize, out: *mut FrameHead, error: *mut *mut ErrorHandle) -> Status {
let Some(data) = (unsafe { Slice::borrow(data, data_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match crate::websocket::FrameHead::decode(data) {
Ok(Some(head)) => {
if !out.is_null() {
unsafe {
*out = FrameHead {
fin: head.fin,
opcode: head.opcode.code(),
masked: head.mask.is_some(),
mask: head.mask.unwrap_or([0; 4]),
start: head.start,
length: head.length,
}
};
}
Status::Ok
}
Ok(None) => Status::Closed,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_frame_encode(fin: bool, opcode: u8, mask: *const u8, payload: *const u8, payload_len: usize) -> Buffer {
let Some(opcode) = Opcode::from_code(opcode) else {
return Buffer::EMPTY;
};
let mask = match unsafe { Slice::borrow(mask, 4) } {
Some(mask) => match <[u8; 4]>::try_from(mask) {
Ok(mask) => Some(mask),
Err(_) => return Buffer::EMPTY,
},
None => None,
};
let payload = unsafe { Slice::borrow(payload, payload_len) }.unwrap_or_default();
let frame = Frame { fin, opcode, mask, payload: bytes::Bytes::copy_from_slice(payload) };
Buffer::new(frame.encode())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_frame_decode(data: *const u8, data_len: usize, out: *mut FrameHead, payload: *mut Buffer, read: *mut usize, error: *mut *mut ErrorHandle) -> Status {
let Some(data) = (unsafe { Slice::borrow(data, data_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match Frame::decode(data) {
Ok(Some((consumed, frame))) => {
if !out.is_null() {
unsafe {
*out = FrameHead {
fin: frame.fin,
opcode: frame.opcode.code(),
masked: frame.mask.is_some(),
mask: frame.mask.unwrap_or([0; 4]),
start: consumed - frame.payload.len(),
length: frame.payload.len(),
}
};
}
if !payload.is_null() {
unsafe { *payload = Buffer::new(frame.payload.to_vec()) };
}
if !read.is_null() {
unsafe { *read = consumed };
}
Status::Ok
}
Ok(None) => Status::Closed,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_accept_key(key: *const u8, key_len: usize) -> Buffer {
match unsafe { Slice::borrow_text(key, key_len) } {
Some(key) => Buffer::new(crate::websocket::Upgrade::accept_key(key).into_bytes()),
None => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_nonce() -> Buffer {
match crate::websocket::Upgrade::nonce() {
Ok(nonce) => Buffer::new(nonce.into_bytes()),
Err(_) => Buffer::EMPTY,
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_upgrade_request(host: *const u8, host_len: usize, target: *const u8, target_len: usize, key: *const u8, key_len: usize, version: i32) -> *mut crate::models::Message {
let (Some(host), Some(target), Some(key)) = (unsafe { Slice::borrow_text(host, host_len) }, unsafe { Slice::borrow_text(target, target_len) }, unsafe { Slice::borrow_text(key, key_len) }) else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(crate::websocket::Upgrade::request(host, target, key, crate::models::Version::of(version))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_upgrade_response(key: *const u8, key_len: usize, version: i32) -> *mut crate::models::Message {
let Some(key) = (unsafe { Slice::borrow_text(key, key_len) }) else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(crate::websocket::Upgrade::response(key, crate::models::Version::of(version))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_verify_upgrade_request(request: *const crate::models::Message, key: *mut Buffer, error: *mut *mut ErrorHandle) -> Status {
let Some(request) = (unsafe { request.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match crate::websocket::Upgrade::verify_request(request) {
Ok(accepted) => {
if !key.is_null() {
unsafe { *key = Buffer::new(accepted.into_bytes()) };
}
Status::Ok
}
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_verify_upgrade_response(response: *const crate::models::Message, key: *const u8, key_len: usize, error: *mut *mut ErrorHandle) -> Status {
let (Some(response), Some(key)) = (unsafe { response.as_ref() }, unsafe { Slice::borrow_text(key, key_len) }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match crate::websocket::Upgrade::verify_response(response, key) {
Ok(()) => Status::Ok,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_connect_request(authority: *const u8, authority_len: usize, target: *const u8, target_len: usize, version: i32) -> *mut crate::models::Message {
let (Some(authority), Some(target)) = (unsafe { Slice::borrow_text(authority, authority_len) }, unsafe { Slice::borrow_text(target, target_len) }) else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(crate::websocket::Connect::request(authority, target, crate::models::Version::of(version))))
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_connect_response(version: i32) -> *mut crate::models::Message {
Box::into_raw(Box::new(crate::websocket::Connect::response(crate::models::Version::of(version))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_verify_connect_request(request: *const crate::models::Message, error: *mut *mut ErrorHandle) -> Status {
let Some(request) = (unsafe { request.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match crate::websocket::Connect::verify_request(request) {
Ok(()) => Status::Ok,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_verify_connect_response(response: *const crate::models::Message, error: *mut *mut ErrorHandle) -> Status {
let Some(response) = (unsafe { response.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match crate::websocket::Connect::verify_response(response) {
Ok(()) => Status::Ok,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_requested(request: *const crate::models::Message) -> bool {
unsafe { request.as_ref() }.is_some_and(crate::websocket::Handshake::requested)
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_verify(request: *const crate::models::Message, error: *mut *mut ErrorHandle) -> Status {
let Some(request) = (unsafe { request.as_ref() }) else {
return unsafe { ErrorHandle::raise(error, Status::Invalid) };
};
match crate::websocket::Handshake::verify(request) {
Ok(()) => Status::Ok,
Err(failure) => unsafe { ErrorHandle::report(error, &failure) },
}
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_refusal(request: *const crate::models::Message, version: i32) -> *mut crate::models::Message {
let Some(request) = (unsafe { request.as_ref() }) else {
return std::ptr::null_mut();
};
Box::into_raw(Box::new(crate::websocket::Handshake::refusal(request, crate::models::Version::of(version))))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_token_present(headers: *const crate::models::Headers, name: *const u8, name_len: usize, token: *const u8, token_len: usize) -> bool {
let (Some(headers), Some(name), Some(token)) = (unsafe { headers.as_ref() }, unsafe { Slice::borrow_text(name, name_len) }, unsafe { Slice::borrow_text(token, token_len) }) else {
return false;
};
crate::websocket::Handshake::token_present(headers, name, token)
}
#[repr(C)]
#[derive(Clone, Copy)]
pub struct WebSocketLimits {
pub max_message_size: u64,
pub ws_max_fragments: u16,
pub ws_linger_timeout: f64,
pub read_timeout: f64,
pub write_timeout: f64,
pub read_chunk_size: u64,
pub idle_capacity: u64,
}
impl WebSocketLimits {
pub fn build(limits: &crate::websocket::WebSocketLimits) -> Self {
Self {
max_message_size: limits.max_message_size,
ws_max_fragments: limits.ws_max_fragments,
ws_linger_timeout: limits.ws_linger_timeout,
read_timeout: limits.read_timeout,
write_timeout: limits.write_timeout,
read_chunk_size: limits.read_chunk_size,
idle_capacity: limits.idle_capacity,
}
}
}
#[unsafe(no_mangle)]
pub extern "C" fn soyokaze_websocket_limits_default() -> WebSocketLimits {
WebSocketLimits::build(&crate::websocket::WebSocketLimits::default())
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_limits_of(limits: *const crate::ffi::models::Limits) -> WebSocketLimits {
WebSocketLimits::build(&crate::websocket::WebSocketLimits::from(unsafe { crate::ffi::models::Limits::or_default(limits) }))
}
#[unsafe(no_mangle)]
pub unsafe extern "C" fn soyokaze_websocket_limits(socket: *const WebSocket) -> WebSocketLimits {
match unsafe { socket.as_ref() } {
Some(socket) => WebSocketLimits::build(&socket.connection.limits()),
None => WebSocketLimits::build(&crate::websocket::WebSocketLimits::default()),
}
}