use crate::address::Address;
use crate::error::{Error, Result};
use crate::identity::{Identity, NodeId};
use crate::message::Headers;
use crate::raw::RawSocket;
use crate::wire::Status;
use core::ffi::{c_int, c_void, CStr};
use core::ptr;
use nwep_sys as sys;
use std::ffi::CString;
pub struct Request {
msg: *const sys::nwep_message,
server: *mut sys::nwep_server,
conn_id: u64,
stream_id: u64,
}
impl Request {
pub fn conn_id(&self) -> u64 {
self.conn_id
}
pub fn stream_id(&self) -> u64 {
self.stream_id
}
pub fn peer_node_id(&self) -> Result<NodeId> {
let mut out = sys::nwep_node_id {
bytes: [0; sys::NWEP_NODEID_SIZE],
};
Error::check(unsafe {
sys::nwep_server_get_peer_nodeid(self.server, self.conn_id, &mut out)
})?;
Ok(NodeId::from_bytes(out.bytes))
}
pub fn compression(&self) -> Compression {
match unsafe { sys::nwep_server_conn_compression(self.server, self.conn_id) } {
0 => Compression::None,
1 => Compression::Zstd,
_ => Compression::Unknown,
}
}
pub(crate) fn raw_msg(&self) -> *const sys::nwep_message {
self.msg
}
pub fn path(&self) -> Option<&str> {
self.header(":path")
}
pub fn header(&self, name: &str) -> Option<&str> {
let cname = std::ffi::CString::new(name).ok()?;
let p = unsafe { sys::nwep_message_get_header(self.msg, cname.as_ptr()) };
if p.is_null() {
return None;
}
unsafe { CStr::from_ptr(p) }.to_str().ok()
}
pub fn body(&self) -> &[u8] {
let mut len = 0usize;
let p = unsafe { sys::nwep_message_get_body(self.msg, &mut len) };
if p.is_null() || len == 0 {
&[]
} else {
unsafe { core::slice::from_raw_parts(p, len) }
}
}
pub fn headers(&self) -> Headers<'_> {
Headers::new(self.msg)
}
pub fn is_fresh(&self, etag: &str) -> bool {
let Ok(c) = CString::new(etag) else {
return false;
};
unsafe { sys::nwep_request_is_fresh(self.msg, c.as_ptr()) != 0 }
}
pub fn range(&self, total_len: u64, etag: Option<&str>) -> Result<RangeOutcome> {
let etag_c = match etag {
Some(e) => Some(CString::new(e).map_err(|_| Error::ProtoInvalidHeader)?),
None => None,
};
let etag_ptr = etag_c.as_ref().map_or(ptr::null(), |c| c.as_ptr());
let mut out = [sys::nwep_range { start: 0, end: 0 }; MAX_RANGES];
let mut count = 0usize;
let rc = unsafe {
sys::nwep_request_range(
self.msg,
total_len,
etag_ptr,
out.as_mut_ptr(),
MAX_RANGES,
&mut count,
)
};
match rc {
sys::NWEP_RANGE_OK => Ok(RangeOutcome::Ranges(
out[..count]
.iter()
.map(|r| ByteRange {
start: r.start,
end: r.end,
})
.collect(),
)),
sys::NWEP_RANGE_UNSATISFIABLE => Ok(RangeOutcome::Unsatisfiable),
sys::NWEP_RANGE_NONE => Ok(RangeOutcome::Full),
other => Err(Error::from_code(other)),
}
}
}
const MAX_RANGES: usize = 16;
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub struct ByteRange {
pub start: u64,
pub end: u64,
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RangeOutcome {
Full,
Unsatisfiable,
Ranges(Vec<ByteRange>),
}
pub struct Reply(());
pub struct Responder {
buf: *mut sys::nwep_buf,
rc: *mut c_int,
server: *mut sys::nwep_server,
conn_id: u64,
stream_id: u64,
}
impl Responder {
pub fn ok(self, body: &[u8]) -> Reply {
let (ptr, len) = slice_parts(body);
unsafe { *self.rc = sys::nwep_response_ok(self.buf, ptr, len) };
Reply(())
}
pub fn status(self, status: Status, body: &[u8]) -> Reply {
let token = unsafe { sys::nwep_status_str(status.code()) };
let (ptr, len) = slice_parts(body);
unsafe { *self.rc = sys::nwep_response_status(self.buf, token, ptr, len) };
Reply(())
}
pub fn not_found(self) -> Reply {
self.status(Status::NotFound, b"")
}
pub fn created(self, body: &[u8]) -> Reply {
self.status(Status::Created, body)
}
pub fn accepted(self, body: &[u8]) -> Reply {
self.status(Status::Accepted, body)
}
pub fn no_content(self) -> Reply {
self.status(Status::NoContent, b"")
}
pub fn moved(self, location: &str) -> Reply {
self.header("location", location).status(Status::Moved, b"")
}
pub fn bad_request(self, body: &[u8]) -> Reply {
self.status(Status::BadRequest, body)
}
pub fn unauthorized(self, body: &[u8]) -> Reply {
self.status(Status::Unauthorized, body)
}
pub fn forbidden(self, body: &[u8]) -> Reply {
self.status(Status::Forbidden, body)
}
pub fn not_allowed(self) -> Reply {
self.status(Status::NotAllowed, b"")
}
pub fn conflict(self, body: &[u8]) -> Reply {
self.status(Status::Conflict, body)
}
pub fn gone(self) -> Reply {
self.status(Status::Gone, b"")
}
pub fn too_large(self) -> Reply {
self.status(Status::TooLarge, b"")
}
pub fn precondition_failed(self) -> Reply {
self.status(Status::PreconditionFailed, b"")
}
pub fn rate_limited(self, retry_after: &str) -> Reply {
self.header("retry-after", retry_after)
.status(Status::RateLimited, b"")
}
pub fn error(self, body: &[u8]) -> Reply {
self.status(Status::Error, body)
}
pub fn unavailable(self) -> Reply {
self.status(Status::Unavailable, b"")
}
pub fn timeout(self) -> Reply {
self.status(Status::Timeout, b"")
}
pub fn not_implemented(self) -> Reply {
self.status(Status::NotImplemented, b"")
}
pub fn not_modified(self, etag: &str) -> Reply {
let token = CString::new(etag).unwrap_or_default();
unsafe { *self.rc = sys::nwep_response_not_modified(self.buf, token.as_ptr()) };
Reply(())
}
pub fn partial(self, body: &[u8], ranges: &[ByteRange], content_type: &str) -> Reply {
let raw: Vec<sys::nwep_range> = ranges
.iter()
.map(|r| sys::nwep_range {
start: r.start,
end: r.end,
})
.collect();
let ctype = CString::new(content_type).unwrap_or_default();
let (ptr, len) = slice_parts(body);
unsafe {
*self.rc = sys::nwep_response_partial(
self.buf,
ptr,
len,
raw.as_ptr(),
raw.len(),
ctype.as_ptr(),
)
};
Reply(())
}
pub fn range_not_satisfiable(self, total_len: u64) -> Reply {
unsafe { *self.rc = sys::nwep_response_range_not_satisfiable(self.buf, total_len) };
Reply(())
}
pub fn header(self, name: &str, value: &str) -> Self {
if let (Ok(n), Ok(v)) = (CString::new(name), CString::new(value)) {
unsafe { sys::nwep_response_header(self.buf, n.as_ptr(), v.as_ptr()) };
}
self
}
pub fn stream(self, path: &str, status: Status, headers: &[(&str, &str)]) -> Reply {
let cpath = CString::new(path).unwrap_or_default();
let token = unsafe { sys::nwep_status_str(status.code()) };
let cstrings: Vec<(CString, CString)> = headers
.iter()
.filter_map(|(n, v)| Some((CString::new(*n).ok()?, CString::new(*v).ok()?)))
.collect();
let mut array: Vec<sys::nwep_header> = cstrings
.iter()
.map(|(n, v)| sys::nwep_header {
name: n.as_ptr(),
value: v.as_ptr(),
})
.collect();
array.push(sys::nwep_header {
name: ptr::null(),
value: ptr::null(),
});
unsafe {
*self.rc = sys::nwep_server_begin_stream(
self.server,
self.conn_id,
self.stream_id,
cpath.as_ptr(),
token,
array.as_ptr(),
)
};
Reply(())
}
pub fn blit(self, frame: &[u8]) -> Reply {
let (ptr, len) = slice_parts(frame);
unsafe { *self.rc = sys::nwep_response_blit(self.buf, ptr, len) };
Reply(())
}
pub fn capturing(self) -> CapturingResponder {
CapturingResponder(self)
}
pub fn relay(self, origin: &crate::Response) -> Reply {
unsafe { *self.rc = sys::nwep_response_relay(self.buf, origin.as_raw()) };
Reply(())
}
pub fn defer(self) -> Reply {
unsafe { *self.rc = NWEP_DEFER };
Reply(())
}
pub(crate) fn raw_buf(&self) -> *mut sys::nwep_buf {
self.buf
}
pub(crate) fn finish(self, code: c_int) -> Reply {
unsafe { *self.rc = code };
Reply(())
}
}
pub struct CapturingResponder(Responder);
impl CapturingResponder {
pub fn ok(self, body: &[u8]) -> (Reply, Vec<u8>) {
let (ptr, len) = slice_parts(body);
let buf = self.0.buf;
let rc = self.0.rc;
let build_rc = unsafe { sys::nwep_response_ok(buf, ptr, len) };
Self::finish_capture(buf, rc, build_rc)
}
pub fn status(self, status: Status, body: &[u8]) -> (Reply, Vec<u8>) {
let token = unsafe { sys::nwep_status_str(status.code()) };
let (ptr, len) = slice_parts(body);
let buf = self.0.buf;
let rc = self.0.rc;
let build_rc = unsafe { sys::nwep_response_status(buf, token, ptr, len) };
Self::finish_capture(buf, rc, build_rc)
}
fn finish_capture(
buf: *mut sys::nwep_buf,
rc: *mut c_int,
build_rc: c_int,
) -> (Reply, Vec<u8>) {
if build_rc != 0 {
unsafe { *rc = build_rc };
return (Reply(()), Vec::new());
}
let mut len = 0usize;
let frame = if unsafe { sys::nwep_response_capture(buf, ptr::null_mut(), 0, &mut len) } == 0
{
let mut f = vec![0u8; len];
if unsafe { sys::nwep_response_capture(buf, f.as_mut_ptr(), f.len(), &mut len) } == 0 {
f.truncate(len);
f
} else {
Vec::new()
}
} else {
Vec::new()
};
unsafe { *rc = 0 };
(Reply(()), frame)
}
}
fn slice_parts(b: &[u8]) -> (*const u8, usize) {
if b.is_empty() {
(ptr::null(), 0)
} else {
(b.as_ptr(), b.len())
}
}
const NWEP_DEFER: c_int = sys::NWEP_DEFER;
type Handler = dyn FnMut(&Request, Responder) -> Reply + Send;
struct HandlerBox {
f: Box<Handler>,
}
unsafe extern "C" fn trampoline(
server: *mut sys::nwep_server,
conn_id: u64,
stream_id: u64,
request: *const sys::nwep_message,
resp_buf: *mut sys::nwep_buf,
userdata: *mut c_void,
) -> c_int {
let handler = unsafe { &mut *(userdata as *mut HandlerBox) };
let mut rc: c_int = 0;
let req = Request {
msg: request,
server,
conn_id,
stream_id,
};
let res = Responder {
buf: resp_buf,
rc: &mut rc,
server,
conn_id,
stream_id,
};
let outcome = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
(handler.f)(&req, res);
}));
match outcome {
Ok(()) => rc,
Err(_) => Error::Internal.code(),
}
}
#[derive(Default)]
pub struct ServerBuilder {
identity: Option<Identity>,
bind: Option<Address>,
handler: Option<Box<Handler>>,
reuse_port: bool,
max_parked: Option<usize>,
pub(crate) dht_bootstraps: Vec<crate::Bootstrap>,
pub(crate) dht_announce: Option<Address>,
pub(crate) dht_initial_seq: u64,
}
impl ServerBuilder {
pub fn identity(mut self, identity: Identity) -> Self {
self.identity = Some(identity);
self
}
pub fn bind(mut self, addr: impl Into<Address>) -> Self {
self.bind = Some(addr.into());
self
}
pub fn reuse_port(mut self, on: bool) -> Self {
self.reuse_port = on;
self
}
pub fn max_parked(mut self, max: usize) -> Self {
self.max_parked = Some(max);
self
}
pub fn dht(mut self, contacts: impl IntoIterator<Item = crate::Bootstrap>) -> Self {
self.dht_bootstraps.extend(contacts);
self
}
pub fn announce_as(mut self, addr: impl Into<Address>) -> Self {
self.dht_announce = Some(addr.into());
self
}
pub fn dht_initial_seq(mut self, seq: u64) -> Self {
self.dht_initial_seq = seq;
self
}
#[cfg(feature = "runtime")]
pub(crate) fn take_managed_dht(
&mut self,
) -> Option<(Vec<crate::Bootstrap>, Option<Address>, u64)> {
if self.dht_bootstraps.is_empty() {
return None;
}
Some((
core::mem::take(&mut self.dht_bootstraps),
self.dht_announce.take(),
self.dht_initial_seq,
))
}
pub fn on_request(
mut self,
handler: impl FnMut(&Request, Responder) -> Reply + Send + 'static,
) -> Self {
self.handler = Some(Box::new(handler));
self
}
pub fn build_from_fd(mut self, fd: RawSocket, shard_id: Option<u16>) -> Result<Server> {
let identity = self.identity.take().ok_or(Error::ConfigMissing)?;
let fd = crate::raw::to_c(fd);
let mut raw: *mut sys::nwep_server = ptr::null_mut();
identity.with_keypair(|kp| {
Error::check(unsafe {
match shard_id {
Some(s) => sys::nwep_server_listen_fd_sharded(&mut raw, kp, fd, s),
None => sys::nwep_server_listen_fd(&mut raw, kp, fd),
}
})
})?;
self.finish_build(raw)
}
pub fn build(mut self) -> Result<Server> {
let identity = self.identity.take().ok_or(Error::ConfigMissing)?;
let bind = self.bind.take().ok_or(Error::ConfigMissing)?;
let mut raw: *mut sys::nwep_server = ptr::null_mut();
identity.with_keypair(|kp| {
Error::check(unsafe {
if self.reuse_port {
sys::nwep_server_listen_reuseport(&mut raw, kp, bind.as_raw())
} else {
sys::nwep_server_listen(&mut raw, kp, bind.as_raw())
}
})
})?;
self.finish_build(raw)
}
fn finish_build(self, raw: *mut sys::nwep_server) -> Result<Server> {
if let Some(max) = self.max_parked {
unsafe { sys::nwep_server_set_max_parked(raw, max) };
}
let mut handler_ptr: *mut c_void = ptr::null_mut();
if let Some(f) = self.handler {
let boxed = Box::into_raw(Box::new(HandlerBox { f }));
let rc = unsafe {
sys::nwep_server_set_handler(raw, Some(trampoline), boxed as *mut c_void)
};
if let Err(e) = Error::check(rc) {
unsafe {
drop(Box::from_raw(boxed));
sys::nwep_server_close(raw);
}
return Err(e);
}
handler_ptr = boxed as *mut c_void;
}
Ok(Server {
raw,
handler: handler_ptr,
})
}
}
pub struct Server {
raw: *mut sys::nwep_server,
handler: *mut c_void,
}
impl Server {
pub fn builder() -> ServerBuilder {
ServerBuilder::default()
}
pub fn tick(&self, now_ms: i64) -> Result<()> {
Error::check(unsafe { sys::nwep_server_tick(self.raw, now_ms) })
}
pub fn fd(&self) -> RawSocket {
crate::raw::from_c(unsafe { sys::nwep_server_fd(self.raw) })
}
pub fn next_timeout(&self, now_ms: i64) -> Option<u32> {
let ms = unsafe { sys::nwep_server_next_timeout_ms(self.raw, now_ms) };
if ms < 0 {
None
} else {
Some(ms as u32)
}
}
pub fn local_port(&self) -> u16 {
unsafe { sys::nwep_server_local_port(self.raw) }
}
pub fn node_id(&self) -> Result<NodeId> {
let mut out = sys::nwep_node_id {
bytes: [0; sys::NWEP_NODEID_SIZE],
};
Error::check(unsafe { sys::nwep_server_local_nodeid(self.raw, &mut out) })?;
Ok(NodeId::from_bytes(out.bytes))
}
pub fn stream_send(&self, conn_id: u64, stream_id: u64, body: &[u8]) -> Result<usize> {
let (ptr, len) = if body.is_empty() {
(ptr::null(), 0)
} else {
(body.as_ptr(), body.len())
};
let rc = unsafe { sys::nwep_server_stream_send(self.raw, conn_id, stream_id, ptr, len) };
if rc < 0 {
return Err(Error::from_code(rc));
}
Ok(rc as usize)
}
pub fn stream_end(&self, conn_id: u64, stream_id: u64) -> Result<()> {
Error::check(unsafe { sys::nwep_server_stream_end(self.raw, conn_id, stream_id) })
}
pub fn peer_node_id(&self, conn_id: u64) -> Result<NodeId> {
let mut out = sys::nwep_node_id {
bytes: [0; sys::NWEP_NODEID_SIZE],
};
Error::check(unsafe { sys::nwep_server_get_peer_nodeid(self.raw, conn_id, &mut out) })?;
Ok(NodeId::from_bytes(out.bytes))
}
pub fn metrics(&self) -> Metrics {
let mut m = sys::nwep_server_metrics {
connections_active: 0,
connections_accepted: 0,
connections_refused: 0,
connections_closed: 0,
bytes_received: 0,
bytes_sent: 0,
datagrams_received: 0,
datagrams_sent: 0,
requests_dispatched: 0,
requests_shed: 0,
parked_active: 0,
load: 0,
};
unsafe { sys::nwep_server_metrics_get(self.raw, &mut m) };
Metrics {
connections_active: m.connections_active,
connections_accepted: m.connections_accepted,
connections_refused: m.connections_refused,
connections_closed: m.connections_closed,
bytes_received: m.bytes_received,
bytes_sent: m.bytes_sent,
datagrams_received: m.datagrams_received,
datagrams_sent: m.datagrams_sent,
requests_dispatched: m.requests_dispatched,
requests_shed: m.requests_shed,
parked_active: m.parked_active,
load: m.load,
}
}
pub fn load(&self) -> i32 {
unsafe { sys::nwep_server_load(self.raw) }
}
pub fn set_overloaded(&self, on: bool) {
unsafe { sys::nwep_server_set_overloaded(self.raw, on as c_int) };
}
pub fn conn_compression(&self, conn_id: u64) -> Compression {
match unsafe { sys::nwep_server_conn_compression(self.raw, conn_id) } {
0 => Compression::None,
1 => Compression::Zstd,
_ => Compression::Unknown,
}
}
pub fn last_handshake_error(&self) -> Option<Error> {
let rc = unsafe { sys::nwep_server_last_handshake_error(self.raw) };
if rc < 0 {
Some(Error::from_code(rc))
} else {
None
}
}
pub fn drain(&self) -> Result<()> {
Error::check(unsafe { sys::nwep_server_drain(self.raw) })
}
pub fn is_drained(&self) -> bool {
unsafe { sys::nwep_server_is_drained(self.raw) == 1 }
}
pub fn respond(&self, conn_id: u64, stream_id: u64) -> DeferredResponder<'_> {
DeferredResponder {
server: self,
conn_id,
stream_id,
headers: Vec::new(),
}
}
pub fn notify(&self, conn_id: u64, event: &str, body: &[u8]) -> Result<()> {
let cevent = CString::new(event).map_err(|_| Error::ProtoInvalidHeader)?;
let (ptr, len) = slice_parts(body);
Error::check(unsafe {
sys::nwep_server_notify(
self.raw,
conn_id,
cevent.as_ptr(),
core::ptr::null(),
ptr,
len,
)
})
}
pub fn as_ptr(&self) -> *mut sys::nwep_server {
self.raw
}
}
pub struct DeferredResponder<'s> {
server: &'s Server,
conn_id: u64,
stream_id: u64,
headers: Vec<(String, String)>,
}
impl DeferredResponder<'_> {
pub fn header(mut self, name: &str, value: &str) -> Self {
self.headers.push((name.to_owned(), value.to_owned()));
self
}
pub fn send(self, status: Status, body: &[u8]) -> Result<()> {
for (name, value) in &self.headers {
let n = CString::new(name.as_str()).map_err(|_| Error::ProtoInvalidHeader)?;
let v = CString::new(value.as_str()).map_err(|_| Error::ProtoInvalidHeader)?;
Error::check(unsafe {
sys::nwep_server_respond_header(
self.server.raw,
self.conn_id,
self.stream_id,
n.as_ptr(),
v.as_ptr(),
)
})?;
}
let token = unsafe { sys::nwep_status_str(status.code()) };
let (ptr, len) = slice_parts(body);
Error::check(unsafe {
sys::nwep_server_respond(
self.server.raw,
self.conn_id,
self.stream_id,
token,
ptr,
len,
)
})
}
pub fn relay(self, origin: &crate::Response) -> Result<()> {
Error::check(unsafe {
sys::nwep_server_relay(
self.server.raw,
self.conn_id,
self.stream_id,
origin.as_raw(),
)
})
}
pub fn blit(self, frame: &[u8]) -> Result<()> {
let (ptr, len) = slice_parts(frame);
Error::check(unsafe {
sys::nwep_server_respond_blit(self.server.raw, self.conn_id, self.stream_id, ptr, len)
})
}
}
#[derive(Clone, Copy, Debug, Default)]
pub struct Metrics {
pub connections_active: u64,
pub connections_accepted: u64,
pub connections_refused: u64,
pub connections_closed: u64,
pub bytes_received: u64,
pub bytes_sent: u64,
pub datagrams_received: u64,
pub datagrams_sent: u64,
pub requests_dispatched: u64,
pub requests_shed: u64,
pub parked_active: u64,
pub load: i32,
}
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum Compression {
None,
Zstd,
Unknown,
}
impl Drop for Server {
fn drop(&mut self) {
unsafe { sys::nwep_server_close(self.raw) };
if !self.handler.is_null() {
drop(unsafe { Box::from_raw(self.handler as *mut HandlerBox) });
}
}
}
pub fn reuse_port_supported() -> bool {
unsafe { sys::nwep_reuse_port_supported() != 0 }
}
pub fn cid_shard_id(cid: &[u8]) -> Option<u16> {
let rc = unsafe { sys::nwep_cid_shard_id(cid.as_ptr(), cid.len()) };
if rc < 0 {
None
} else {
Some(rc as u16)
}
}