nwep/logserver.rs
1#![allow(unsafe_op_in_unsafe_fn)]
2
3use crate::error::{Error, check};
4use crate::ffi;
5use crate::merkle::{MerkleEntry, MerkleLog};
6use crate::types::NodeId;
7
8/// `LogServerSettings` controls the behaviour of a [`LogServer`] instance.
9///
10/// All fields are optional. The default value (via [`Default`]) leaves `authorize`
11/// unset, which causes the server to reject every write with `ERR_PROTO_UNAUTHORIZED`.
12pub struct LogServerSettings {
13 /// Optional callback invoked for every write request before it is applied.
14 ///
15 /// Receives the requester's [`NodeId`] and the proposed [`MerkleEntry`]. Return
16 /// `Ok(())` to allow the write or `Err(e)` to reject it with the given error code.
17 /// When `None`, all writes are unconditionally rejected.
18 pub authorize: Option<Box<dyn Fn(&NodeId, &MerkleEntry) -> Result<(), Error> + Send>>,
19}
20
21impl Default for LogServerSettings {
22 fn default() -> Self {
23 LogServerSettings { authorize: None }
24 }
25}
26
27struct Callbacks {
28 authorize: Option<Box<dyn Fn(&NodeId, &MerkleEntry) -> Result<(), Error> + Send>>,
29}
30
31unsafe extern "C" fn authorize_cb(
32 user_data: *mut std::ffi::c_void,
33 nodeid: *const ffi::nwep_nodeid,
34 entry: *const ffi::nwep_merkle_entry,
35) -> std::ffi::c_int {
36 let cb = &*(user_data as *const Callbacks);
37 if let Some(f) = &cb.authorize {
38 let nid = NodeId((*nodeid).data);
39 let e = MerkleEntry::from_ffi(&*entry);
40 match f(&nid, &e) {
41 Ok(()) => 0,
42 Err(e) => e.code,
43 }
44 } else {
45 crate::error::ERR_PROTO_UNAUTHORIZED
46 }
47}
48
49/// `LogServer` wraps the NWEP C library's Merkle log server.
50///
51/// `LogServer` handles HTTP-like requests to the `/log` and `/log/*` endpoints,
52/// enabling peers to submit new Merkle log entries and read existing ones. The server
53/// holds a mutable reference to the underlying [`MerkleLog`] for the duration of its
54/// lifetime.
55///
56/// # Safety
57///
58/// The raw pointer `ptr` must remain valid for the lifetime of this value. `LogServer`
59/// is `Send` because access to the C object is mediated through `&mut self` methods.
60pub struct LogServer {
61 ptr: *mut ffi::nwep_log_server,
62 _callbacks: Box<Callbacks>,
63}
64
65unsafe impl Send for LogServer {}
66
67impl LogServer {
68 /// `new` creates a new log server backed by the given [`MerkleLog`].
69 ///
70 /// The `settings` argument configures the optional write-authorization callback.
71 /// Ownership of the callback closures is transferred into the returned `LogServer`
72 /// and kept alive for as long as the server exists.
73 ///
74 /// # Errors
75 ///
76 /// Returns `Err` if the underlying C call fails (e.g. allocation failure or an
77 /// invalid log pointer).
78 ///
79 /// # Examples
80 ///
81 /// ```no_run
82 /// # use nwep::{logserver::{LogServer, LogServerSettings}, merkle::{MerkleLog, LogStorage}, error::Error};
83 /// # struct MemLog(Vec<Vec<u8>>);
84 /// # impl LogStorage for MemLog {
85 /// # fn append(&mut self, _: u64, e: &[u8]) -> Result<(), Error> { self.0.push(e.to_vec()); Ok(()) }
86 /// # fn get(&self, i: u64, buf: &mut [u8]) -> Result<usize, Error> { let d = &self.0[i as usize]; let n = d.len().min(buf.len()); buf[..n].copy_from_slice(&d[..n]); Ok(n) }
87 /// # fn size(&self) -> u64 { self.0.len() as u64 }
88 /// # }
89 /// let mut log = MerkleLog::new(MemLog(vec![])).unwrap();
90 /// let settings = LogServerSettings {
91 /// authorize: Some(Box::new(|node_id, entry| {
92 /// // allow all writes from any peer
93 /// Ok(())
94 /// })),
95 /// };
96 /// let server = LogServer::new(&mut log, settings).unwrap();
97 /// ```
98 pub fn new(log: &mut MerkleLog, settings: LogServerSettings) -> Result<Self, Error> {
99 let has_authorize = settings.authorize.is_some();
100 let mut cb = Box::new(Callbacks {
101 authorize: settings.authorize,
102 });
103 let cb_ptr = cb.as_mut() as *mut _ as *mut std::ffi::c_void;
104
105 let ffi_settings = ffi::nwep_log_server_settings {
106 authorize: if has_authorize {
107 Some(authorize_cb)
108 } else {
109 None
110 },
111 user_data: cb_ptr,
112 };
113
114 let mut ptr: *mut ffi::nwep_log_server = std::ptr::null_mut();
115 check(unsafe { ffi::nwep_log_server_new(&mut ptr, log.as_ptr(), &ffi_settings) })?;
116 Ok(LogServer {
117 ptr,
118 _callbacks: cb,
119 })
120 }
121
122 /// `handle_request` dispatches a single incoming NWEP request to the log server.
123 ///
124 /// Both `stream` and `req` are raw C pointers supplied by the NWEP server
125 /// callback infrastructure. In practice callers do not invoke this method
126 /// directly — it is called automatically when a [`LogServer`] is registered with
127 /// `ServerBuilder::log_server()`.
128 ///
129 /// # Errors
130 ///
131 /// Returns `Err` if the C library reports a protocol or I/O error while handling
132 /// the request.
133 pub fn handle_request(
134 &mut self,
135 stream: *mut ffi::nwep_stream,
136 req: *const ffi::nwep_request,
137 ) -> Result<(), Error> {
138 check(unsafe { ffi::nwep_log_server_handle_request(self.ptr, stream, req) })
139 }
140
141 /// `as_ptr` returns the raw `nwep_log_server` pointer for use in FFI calls.
142 ///
143 /// The pointer is valid for the lifetime of this `LogServer`. Callers must not
144 /// free or outlive the returned pointer.
145 pub fn as_ptr(&mut self) -> *mut ffi::nwep_log_server {
146 self.ptr
147 }
148}
149
150impl Drop for LogServer {
151 fn drop(&mut self) {
152 if !self.ptr.is_null() {
153 unsafe { ffi::nwep_log_server_free(self.ptr) }
154 }
155 }
156}