Skip to main content

ServerMechanism

Struct ServerMechanism 

Source
pub struct ServerMechanism { /* private fields */ }
Expand description

Entry point for building an HTTP route.

Pairs an HTTP method with a URL path and acts as the root of a fluent builder chain. Optionally attach shared state, a JSON body expectation, or URL query parameter deserialisation — then finalise with onconnect (async) or onconnect_sync (sync) to produce a SocketType ready to be mounted on a Server.

§Example


// Plain GET — no body, no state
let health = ServerMechanism::get("/health")
    .onconnect(|| async { reply!() });

// POST — JSON body deserialised into `CreateItem`
let create = ServerMechanism::post("/items")
    .json::<CreateItem>()
    .onconnect(|body| async move {
        let item = Item { id: 1, name: body.name };
        reply!(json => item, status => Status::Created)
    });

// GET — shared counter state injected on every request
let counter: Arc<Mutex<u64>> = Arc::new(Mutex::new(0));
let count_route = ServerMechanism::get("/count")
    .state(counter.clone())
    .onconnect(|state| async move {
        let n = *state.lock().unwrap();
        reply!(json => n)
    });

Implementations§

Source§

impl ServerMechanism

Source

pub fn get(path: impl Into<String>) -> Self

Creates a route matching HTTP GET requests at path.

Examples found in repository?
examples/socket_client_server.rs (line 70)
62async fn main() -> Result<(), Box<dyn std::error::Error>> {
63    // ── Build routes ──────────────────────────────────────────────────────────
64    let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
65
66    let mut server = Server::default();
67
68    // Plain GET — no body, no state.
69    server.mechanism(
70        ServerMechanism::get("/health")
71            .onconnect(|| async { reply!(json => Health { ok: true }) })
72    );
73
74    // POST with a JSON body — echoes back the created item.
75    server.mechanism(
76        ServerMechanism::post("/items")
77            .json::<NewItem>()
78            .onconnect(|body: NewItem| async move {
79                reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
80            })
81    );
82
83    // GET with query parameters — filters the stored items by prefix.
84    server.mechanism({
85        let store = store.clone();
86        ServerMechanism::get("/items/search")
87            .state(store)
88            .query::<Filter>()
89            .onconnect(|state: Arc<Mutex<Vec<Item>>>, f: Filter| async move {
90                let items = state.lock().unwrap();
91                let matches: Vec<Item> = items
92                    .iter()
93                    .filter(|i| i.name.starts_with(&f.prefix))
94                    .cloned()
95                    .collect();
96                reply!(json => SearchResult { matches })
97            })
98    });
99
100    // POST with shared state — stores the item and returns it.
101    server.mechanism({
102        let store = store.clone();
103        ServerMechanism::post("/items/store")
104            .state(store)
105            .json::<NewItem>()
106            .onconnect(|state: Arc<Mutex<Vec<Item>>>, body: NewItem| async move {
107                let mut s = state.lock().unwrap();
108                let id = s.len() as u32 + 1;
109                let item = Item { id, name: body.name };
110                s.push(item.clone());
111                reply!(json => item, status => Status::Created)
112            })
113    });
114
115    // POST with authenticated-encrypted body (ChaCha20-Poly1305 via SerializationKey).
116    // The body is decrypted before the handler is called; a wrong key returns 403.
117    server.mechanism(
118        ServerMechanism::post("/items/secure")
119            .encryption::<NewItem>(SerializationKey::Default)
120            .onconnect(|body: NewItem| async move {
121                let item = Item { id: 99, name: body.name };
122                // The response must also be sealed so the client can open it.
123                reply!(sealed => item, key => SerializationKey::Default)
124            })
125    );
126
127    // ── Serve with graceful shutdown ──────────────────────────────────────────
128    let (tx, rx) = tokio::sync::oneshot::channel::<()>();
129    let server_handle = tokio::spawn(async move {
130        server.serve_with_graceful_shutdown(
131            ([127, 0, 0, 1], PORT),
132            async { rx.await.ok(); },
133        ).await;
134    });
135
136    // Give the server time to bind before firing requests.
137    tokio::time::sleep(Duration::from_millis(200)).await;
138    println!("Server started on port {PORT}");
139
140    // ── Client requests ───────────────────────────────────────────────────────
141    let client = ClientBuilder::new(Target::Localhost(PORT))
142        .timeout(Duration::from_secs(5))
143        .build_async();
144
145    // GET /health
146    let health: Health = client.get("/health").send().await?;
147    assert!(health.ok);
148    println!("GET  /health                → ok={}", health.ok);
149
150    // POST /items (JSON body)
151    let created: Item = client
152        .post("/items")
153        .json(NewItem { name: "widget".into() })
154        .send()
155        .await?;
156    assert_eq!(created.name, "widget");
157    println!("POST /items                 → {:?}", created);
158
159    // POST /items/store (shared state — populates the store)
160    let stored: Item = client
161        .post("/items/store")
162        .json(NewItem { name: "gadget".into() })
163        .send()
164        .await?;
165    println!("POST /items/store           → {:?}", stored);
166
167    let stored2: Item = client
168        .post("/items/store")
169        .json(NewItem { name: "gizmo".into() })
170        .send()
171        .await?;
172    println!("POST /items/store           → {:?}", stored2);
173
174    // GET /items/search?prefix=ga (query params)
175    let result: SearchResult = client
176        .get("/items/search")
177        .query(Filter { prefix: "ga".into() })
178        .send()
179        .await?;
180    assert!(result.matches.iter().all(|i| i.name.starts_with("ga")));
181    println!("GET  /items/search?prefix=ga → {} match(es): {:?}", result.matches.len(), result.matches);
182
183    // POST /items/secure (authenticated-encrypted body)
184    let secure_item = client
185        .post("/items/secure")
186        .encryption(NewItem { name: "secret".into() }, SerializationKey::Default)
187        .send::<Item>()
188        .await?;
189    assert_eq!(secure_item.name, "secret");
190    println!("POST /items/secure          → {:?}", secure_item);
191
192    // ── Shutdown ──────────────────────────────────────────────────────────────
193    tx.send(()).ok();
194    server_handle.await?;
195    println!("\nAll requests successful ✓");
196    Ok(())
197}
Source

pub fn post(path: impl Into<String>) -> Self

Creates a route matching HTTP POST requests at path.

Examples found in repository?
examples/socket_client_server.rs (line 76)
62async fn main() -> Result<(), Box<dyn std::error::Error>> {
63    // ── Build routes ──────────────────────────────────────────────────────────
64    let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
65
66    let mut server = Server::default();
67
68    // Plain GET — no body, no state.
69    server.mechanism(
70        ServerMechanism::get("/health")
71            .onconnect(|| async { reply!(json => Health { ok: true }) })
72    );
73
74    // POST with a JSON body — echoes back the created item.
75    server.mechanism(
76        ServerMechanism::post("/items")
77            .json::<NewItem>()
78            .onconnect(|body: NewItem| async move {
79                reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
80            })
81    );
82
83    // GET with query parameters — filters the stored items by prefix.
84    server.mechanism({
85        let store = store.clone();
86        ServerMechanism::get("/items/search")
87            .state(store)
88            .query::<Filter>()
89            .onconnect(|state: Arc<Mutex<Vec<Item>>>, f: Filter| async move {
90                let items = state.lock().unwrap();
91                let matches: Vec<Item> = items
92                    .iter()
93                    .filter(|i| i.name.starts_with(&f.prefix))
94                    .cloned()
95                    .collect();
96                reply!(json => SearchResult { matches })
97            })
98    });
99
100    // POST with shared state — stores the item and returns it.
101    server.mechanism({
102        let store = store.clone();
103        ServerMechanism::post("/items/store")
104            .state(store)
105            .json::<NewItem>()
106            .onconnect(|state: Arc<Mutex<Vec<Item>>>, body: NewItem| async move {
107                let mut s = state.lock().unwrap();
108                let id = s.len() as u32 + 1;
109                let item = Item { id, name: body.name };
110                s.push(item.clone());
111                reply!(json => item, status => Status::Created)
112            })
113    });
114
115    // POST with authenticated-encrypted body (ChaCha20-Poly1305 via SerializationKey).
116    // The body is decrypted before the handler is called; a wrong key returns 403.
117    server.mechanism(
118        ServerMechanism::post("/items/secure")
119            .encryption::<NewItem>(SerializationKey::Default)
120            .onconnect(|body: NewItem| async move {
121                let item = Item { id: 99, name: body.name };
122                // The response must also be sealed so the client can open it.
123                reply!(sealed => item, key => SerializationKey::Default)
124            })
125    );
126
127    // ── Serve with graceful shutdown ──────────────────────────────────────────
128    let (tx, rx) = tokio::sync::oneshot::channel::<()>();
129    let server_handle = tokio::spawn(async move {
130        server.serve_with_graceful_shutdown(
131            ([127, 0, 0, 1], PORT),
132            async { rx.await.ok(); },
133        ).await;
134    });
135
136    // Give the server time to bind before firing requests.
137    tokio::time::sleep(Duration::from_millis(200)).await;
138    println!("Server started on port {PORT}");
139
140    // ── Client requests ───────────────────────────────────────────────────────
141    let client = ClientBuilder::new(Target::Localhost(PORT))
142        .timeout(Duration::from_secs(5))
143        .build_async();
144
145    // GET /health
146    let health: Health = client.get("/health").send().await?;
147    assert!(health.ok);
148    println!("GET  /health                → ok={}", health.ok);
149
150    // POST /items (JSON body)
151    let created: Item = client
152        .post("/items")
153        .json(NewItem { name: "widget".into() })
154        .send()
155        .await?;
156    assert_eq!(created.name, "widget");
157    println!("POST /items                 → {:?}", created);
158
159    // POST /items/store (shared state — populates the store)
160    let stored: Item = client
161        .post("/items/store")
162        .json(NewItem { name: "gadget".into() })
163        .send()
164        .await?;
165    println!("POST /items/store           → {:?}", stored);
166
167    let stored2: Item = client
168        .post("/items/store")
169        .json(NewItem { name: "gizmo".into() })
170        .send()
171        .await?;
172    println!("POST /items/store           → {:?}", stored2);
173
174    // GET /items/search?prefix=ga (query params)
175    let result: SearchResult = client
176        .get("/items/search")
177        .query(Filter { prefix: "ga".into() })
178        .send()
179        .await?;
180    assert!(result.matches.iter().all(|i| i.name.starts_with("ga")));
181    println!("GET  /items/search?prefix=ga → {} match(es): {:?}", result.matches.len(), result.matches);
182
183    // POST /items/secure (authenticated-encrypted body)
184    let secure_item = client
185        .post("/items/secure")
186        .encryption(NewItem { name: "secret".into() }, SerializationKey::Default)
187        .send::<Item>()
188        .await?;
189    assert_eq!(secure_item.name, "secret");
190    println!("POST /items/secure          → {:?}", secure_item);
191
192    // ── Shutdown ──────────────────────────────────────────────────────────────
193    tx.send(()).ok();
194    server_handle.await?;
195    println!("\nAll requests successful ✓");
196    Ok(())
197}
Source

pub fn put(path: impl Into<String>) -> Self

Creates a route matching HTTP PUT requests at path.

Source

pub fn delete(path: impl Into<String>) -> Self

Creates a route matching HTTP DELETE requests at path.

Source

pub fn patch(path: impl Into<String>) -> Self

Creates a route matching HTTP PATCH requests at path.

Source

pub fn head(path: impl Into<String>) -> Self

Creates a route matching HTTP HEAD requests at path.

Source

pub fn options(path: impl Into<String>) -> Self

Creates a route matching HTTP OPTIONS requests at path.

Source

pub fn state<S: Clone + Send + Sync + 'static>( self, state: S, ) -> StatefulSocketBuilder<S>

Attaches shared state S to this route, transitioning to StatefulSocketBuilder.

A fresh clone of S is injected into the handler on every request.

Examples found in repository?
examples/socket_client_server.rs (line 87)
62async fn main() -> Result<(), Box<dyn std::error::Error>> {
63    // ── Build routes ──────────────────────────────────────────────────────────
64    let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
65
66    let mut server = Server::default();
67
68    // Plain GET — no body, no state.
69    server.mechanism(
70        ServerMechanism::get("/health")
71            .onconnect(|| async { reply!(json => Health { ok: true }) })
72    );
73
74    // POST with a JSON body — echoes back the created item.
75    server.mechanism(
76        ServerMechanism::post("/items")
77            .json::<NewItem>()
78            .onconnect(|body: NewItem| async move {
79                reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
80            })
81    );
82
83    // GET with query parameters — filters the stored items by prefix.
84    server.mechanism({
85        let store = store.clone();
86        ServerMechanism::get("/items/search")
87            .state(store)
88            .query::<Filter>()
89            .onconnect(|state: Arc<Mutex<Vec<Item>>>, f: Filter| async move {
90                let items = state.lock().unwrap();
91                let matches: Vec<Item> = items
92                    .iter()
93                    .filter(|i| i.name.starts_with(&f.prefix))
94                    .cloned()
95                    .collect();
96                reply!(json => SearchResult { matches })
97            })
98    });
99
100    // POST with shared state — stores the item and returns it.
101    server.mechanism({
102        let store = store.clone();
103        ServerMechanism::post("/items/store")
104            .state(store)
105            .json::<NewItem>()
106            .onconnect(|state: Arc<Mutex<Vec<Item>>>, body: NewItem| async move {
107                let mut s = state.lock().unwrap();
108                let id = s.len() as u32 + 1;
109                let item = Item { id, name: body.name };
110                s.push(item.clone());
111                reply!(json => item, status => Status::Created)
112            })
113    });
114
115    // POST with authenticated-encrypted body (ChaCha20-Poly1305 via SerializationKey).
116    // The body is decrypted before the handler is called; a wrong key returns 403.
117    server.mechanism(
118        ServerMechanism::post("/items/secure")
119            .encryption::<NewItem>(SerializationKey::Default)
120            .onconnect(|body: NewItem| async move {
121                let item = Item { id: 99, name: body.name };
122                // The response must also be sealed so the client can open it.
123                reply!(sealed => item, key => SerializationKey::Default)
124            })
125    );
126
127    // ── Serve with graceful shutdown ──────────────────────────────────────────
128    let (tx, rx) = tokio::sync::oneshot::channel::<()>();
129    let server_handle = tokio::spawn(async move {
130        server.serve_with_graceful_shutdown(
131            ([127, 0, 0, 1], PORT),
132            async { rx.await.ok(); },
133        ).await;
134    });
135
136    // Give the server time to bind before firing requests.
137    tokio::time::sleep(Duration::from_millis(200)).await;
138    println!("Server started on port {PORT}");
139
140    // ── Client requests ───────────────────────────────────────────────────────
141    let client = ClientBuilder::new(Target::Localhost(PORT))
142        .timeout(Duration::from_secs(5))
143        .build_async();
144
145    // GET /health
146    let health: Health = client.get("/health").send().await?;
147    assert!(health.ok);
148    println!("GET  /health                → ok={}", health.ok);
149
150    // POST /items (JSON body)
151    let created: Item = client
152        .post("/items")
153        .json(NewItem { name: "widget".into() })
154        .send()
155        .await?;
156    assert_eq!(created.name, "widget");
157    println!("POST /items                 → {:?}", created);
158
159    // POST /items/store (shared state — populates the store)
160    let stored: Item = client
161        .post("/items/store")
162        .json(NewItem { name: "gadget".into() })
163        .send()
164        .await?;
165    println!("POST /items/store           → {:?}", stored);
166
167    let stored2: Item = client
168        .post("/items/store")
169        .json(NewItem { name: "gizmo".into() })
170        .send()
171        .await?;
172    println!("POST /items/store           → {:?}", stored2);
173
174    // GET /items/search?prefix=ga (query params)
175    let result: SearchResult = client
176        .get("/items/search")
177        .query(Filter { prefix: "ga".into() })
178        .send()
179        .await?;
180    assert!(result.matches.iter().all(|i| i.name.starts_with("ga")));
181    println!("GET  /items/search?prefix=ga → {} match(es): {:?}", result.matches.len(), result.matches);
182
183    // POST /items/secure (authenticated-encrypted body)
184    let secure_item = client
185        .post("/items/secure")
186        .encryption(NewItem { name: "secret".into() }, SerializationKey::Default)
187        .send::<Item>()
188        .await?;
189    assert_eq!(secure_item.name, "secret");
190    println!("POST /items/secure          → {:?}", secure_item);
191
192    // ── Shutdown ──────────────────────────────────────────────────────────────
193    tx.send(()).ok();
194    server_handle.await?;
195    println!("\nAll requests successful ✓");
196    Ok(())
197}
Source

pub fn json<T: DeserializeOwned + Send>(self) -> JsonSocketBuilder<T>

Declares that this route expects a JSON-encoded request body, transitioning to JsonSocketBuilder.

Examples found in repository?
examples/socket_client_server.rs (line 77)
62async fn main() -> Result<(), Box<dyn std::error::Error>> {
63    // ── Build routes ──────────────────────────────────────────────────────────
64    let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
65
66    let mut server = Server::default();
67
68    // Plain GET — no body, no state.
69    server.mechanism(
70        ServerMechanism::get("/health")
71            .onconnect(|| async { reply!(json => Health { ok: true }) })
72    );
73
74    // POST with a JSON body — echoes back the created item.
75    server.mechanism(
76        ServerMechanism::post("/items")
77            .json::<NewItem>()
78            .onconnect(|body: NewItem| async move {
79                reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
80            })
81    );
82
83    // GET with query parameters — filters the stored items by prefix.
84    server.mechanism({
85        let store = store.clone();
86        ServerMechanism::get("/items/search")
87            .state(store)
88            .query::<Filter>()
89            .onconnect(|state: Arc<Mutex<Vec<Item>>>, f: Filter| async move {
90                let items = state.lock().unwrap();
91                let matches: Vec<Item> = items
92                    .iter()
93                    .filter(|i| i.name.starts_with(&f.prefix))
94                    .cloned()
95                    .collect();
96                reply!(json => SearchResult { matches })
97            })
98    });
99
100    // POST with shared state — stores the item and returns it.
101    server.mechanism({
102        let store = store.clone();
103        ServerMechanism::post("/items/store")
104            .state(store)
105            .json::<NewItem>()
106            .onconnect(|state: Arc<Mutex<Vec<Item>>>, body: NewItem| async move {
107                let mut s = state.lock().unwrap();
108                let id = s.len() as u32 + 1;
109                let item = Item { id, name: body.name };
110                s.push(item.clone());
111                reply!(json => item, status => Status::Created)
112            })
113    });
114
115    // POST with authenticated-encrypted body (ChaCha20-Poly1305 via SerializationKey).
116    // The body is decrypted before the handler is called; a wrong key returns 403.
117    server.mechanism(
118        ServerMechanism::post("/items/secure")
119            .encryption::<NewItem>(SerializationKey::Default)
120            .onconnect(|body: NewItem| async move {
121                let item = Item { id: 99, name: body.name };
122                // The response must also be sealed so the client can open it.
123                reply!(sealed => item, key => SerializationKey::Default)
124            })
125    );
126
127    // ── Serve with graceful shutdown ──────────────────────────────────────────
128    let (tx, rx) = tokio::sync::oneshot::channel::<()>();
129    let server_handle = tokio::spawn(async move {
130        server.serve_with_graceful_shutdown(
131            ([127, 0, 0, 1], PORT),
132            async { rx.await.ok(); },
133        ).await;
134    });
135
136    // Give the server time to bind before firing requests.
137    tokio::time::sleep(Duration::from_millis(200)).await;
138    println!("Server started on port {PORT}");
139
140    // ── Client requests ───────────────────────────────────────────────────────
141    let client = ClientBuilder::new(Target::Localhost(PORT))
142        .timeout(Duration::from_secs(5))
143        .build_async();
144
145    // GET /health
146    let health: Health = client.get("/health").send().await?;
147    assert!(health.ok);
148    println!("GET  /health                → ok={}", health.ok);
149
150    // POST /items (JSON body)
151    let created: Item = client
152        .post("/items")
153        .json(NewItem { name: "widget".into() })
154        .send()
155        .await?;
156    assert_eq!(created.name, "widget");
157    println!("POST /items                 → {:?}", created);
158
159    // POST /items/store (shared state — populates the store)
160    let stored: Item = client
161        .post("/items/store")
162        .json(NewItem { name: "gadget".into() })
163        .send()
164        .await?;
165    println!("POST /items/store           → {:?}", stored);
166
167    let stored2: Item = client
168        .post("/items/store")
169        .json(NewItem { name: "gizmo".into() })
170        .send()
171        .await?;
172    println!("POST /items/store           → {:?}", stored2);
173
174    // GET /items/search?prefix=ga (query params)
175    let result: SearchResult = client
176        .get("/items/search")
177        .query(Filter { prefix: "ga".into() })
178        .send()
179        .await?;
180    assert!(result.matches.iter().all(|i| i.name.starts_with("ga")));
181    println!("GET  /items/search?prefix=ga → {} match(es): {:?}", result.matches.len(), result.matches);
182
183    // POST /items/secure (authenticated-encrypted body)
184    let secure_item = client
185        .post("/items/secure")
186        .encryption(NewItem { name: "secret".into() }, SerializationKey::Default)
187        .send::<Item>()
188        .await?;
189    assert_eq!(secure_item.name, "secret");
190    println!("POST /items/secure          → {:?}", secure_item);
191
192    // ── Shutdown ──────────────────────────────────────────────────────────────
193    tx.send(()).ok();
194    server_handle.await?;
195    println!("\nAll requests successful ✓");
196    Ok(())
197}
Source

pub fn query<T: DeserializeOwned + Send>(self) -> QuerySocketBuilder<T>

Declares that this route extracts its input from URL query parameters, transitioning to QuerySocketBuilder.

Source

pub fn encryption<T>(self, key: SerializationKey) -> EncryptedBodyBuilder<T>

Declares that this route expects an authenticated-encrypted request body (ChaCha20-Poly1305), transitioning to EncryptedBodyBuilder.

Examples found in repository?
examples/socket_client_server.rs (line 119)
62async fn main() -> Result<(), Box<dyn std::error::Error>> {
63    // ── Build routes ──────────────────────────────────────────────────────────
64    let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
65
66    let mut server = Server::default();
67
68    // Plain GET — no body, no state.
69    server.mechanism(
70        ServerMechanism::get("/health")
71            .onconnect(|| async { reply!(json => Health { ok: true }) })
72    );
73
74    // POST with a JSON body — echoes back the created item.
75    server.mechanism(
76        ServerMechanism::post("/items")
77            .json::<NewItem>()
78            .onconnect(|body: NewItem| async move {
79                reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
80            })
81    );
82
83    // GET with query parameters — filters the stored items by prefix.
84    server.mechanism({
85        let store = store.clone();
86        ServerMechanism::get("/items/search")
87            .state(store)
88            .query::<Filter>()
89            .onconnect(|state: Arc<Mutex<Vec<Item>>>, f: Filter| async move {
90                let items = state.lock().unwrap();
91                let matches: Vec<Item> = items
92                    .iter()
93                    .filter(|i| i.name.starts_with(&f.prefix))
94                    .cloned()
95                    .collect();
96                reply!(json => SearchResult { matches })
97            })
98    });
99
100    // POST with shared state — stores the item and returns it.
101    server.mechanism({
102        let store = store.clone();
103        ServerMechanism::post("/items/store")
104            .state(store)
105            .json::<NewItem>()
106            .onconnect(|state: Arc<Mutex<Vec<Item>>>, body: NewItem| async move {
107                let mut s = state.lock().unwrap();
108                let id = s.len() as u32 + 1;
109                let item = Item { id, name: body.name };
110                s.push(item.clone());
111                reply!(json => item, status => Status::Created)
112            })
113    });
114
115    // POST with authenticated-encrypted body (ChaCha20-Poly1305 via SerializationKey).
116    // The body is decrypted before the handler is called; a wrong key returns 403.
117    server.mechanism(
118        ServerMechanism::post("/items/secure")
119            .encryption::<NewItem>(SerializationKey::Default)
120            .onconnect(|body: NewItem| async move {
121                let item = Item { id: 99, name: body.name };
122                // The response must also be sealed so the client can open it.
123                reply!(sealed => item, key => SerializationKey::Default)
124            })
125    );
126
127    // ── Serve with graceful shutdown ──────────────────────────────────────────
128    let (tx, rx) = tokio::sync::oneshot::channel::<()>();
129    let server_handle = tokio::spawn(async move {
130        server.serve_with_graceful_shutdown(
131            ([127, 0, 0, 1], PORT),
132            async { rx.await.ok(); },
133        ).await;
134    });
135
136    // Give the server time to bind before firing requests.
137    tokio::time::sleep(Duration::from_millis(200)).await;
138    println!("Server started on port {PORT}");
139
140    // ── Client requests ───────────────────────────────────────────────────────
141    let client = ClientBuilder::new(Target::Localhost(PORT))
142        .timeout(Duration::from_secs(5))
143        .build_async();
144
145    // GET /health
146    let health: Health = client.get("/health").send().await?;
147    assert!(health.ok);
148    println!("GET  /health                → ok={}", health.ok);
149
150    // POST /items (JSON body)
151    let created: Item = client
152        .post("/items")
153        .json(NewItem { name: "widget".into() })
154        .send()
155        .await?;
156    assert_eq!(created.name, "widget");
157    println!("POST /items                 → {:?}", created);
158
159    // POST /items/store (shared state — populates the store)
160    let stored: Item = client
161        .post("/items/store")
162        .json(NewItem { name: "gadget".into() })
163        .send()
164        .await?;
165    println!("POST /items/store           → {:?}", stored);
166
167    let stored2: Item = client
168        .post("/items/store")
169        .json(NewItem { name: "gizmo".into() })
170        .send()
171        .await?;
172    println!("POST /items/store           → {:?}", stored2);
173
174    // GET /items/search?prefix=ga (query params)
175    let result: SearchResult = client
176        .get("/items/search")
177        .query(Filter { prefix: "ga".into() })
178        .send()
179        .await?;
180    assert!(result.matches.iter().all(|i| i.name.starts_with("ga")));
181    println!("GET  /items/search?prefix=ga → {} match(es): {:?}", result.matches.len(), result.matches);
182
183    // POST /items/secure (authenticated-encrypted body)
184    let secure_item = client
185        .post("/items/secure")
186        .encryption(NewItem { name: "secret".into() }, SerializationKey::Default)
187        .send::<Item>()
188        .await?;
189    assert_eq!(secure_item.name, "secret");
190    println!("POST /items/secure          → {:?}", secure_item);
191
192    // ── Shutdown ──────────────────────────────────────────────────────────────
193    tx.send(()).ok();
194    server_handle.await?;
195    println!("\nAll requests successful ✓");
196    Ok(())
197}
Source

pub fn encrypted_query<T>( self, key: SerializationKey, ) -> EncryptedQueryBuilder<T>

Declares that this route expects authenticated-encrypted URL query parameters (ChaCha20-Poly1305), transitioning to EncryptedQueryBuilder.

Source

pub fn onconnect<F, Fut, Re>(self, handler: F) -> SocketType
where F: Fn() -> Fut + Clone + Send + Sync + 'static, Fut: Future<Output = Result<Re, Rejection>> + Send, Re: Reply + Send,

Finalises this route with an async handler that receives no arguments.

Returns a SocketType ready to be passed to Server::mechanism.

§Example

let route = ServerMechanism::get("/ping")
    .onconnect(|| async {
        reply!(json => Pong { ok: true })
    });
Examples found in repository?
examples/socket_client_server.rs (line 71)
62async fn main() -> Result<(), Box<dyn std::error::Error>> {
63    // ── Build routes ──────────────────────────────────────────────────────────
64    let store: Arc<Mutex<Vec<Item>>> = Arc::new(Mutex::new(vec![]));
65
66    let mut server = Server::default();
67
68    // Plain GET — no body, no state.
69    server.mechanism(
70        ServerMechanism::get("/health")
71            .onconnect(|| async { reply!(json => Health { ok: true }) })
72    );
73
74    // POST with a JSON body — echoes back the created item.
75    server.mechanism(
76        ServerMechanism::post("/items")
77            .json::<NewItem>()
78            .onconnect(|body: NewItem| async move {
79                reply!(json => Item { id: 1, name: body.name }, status => Status::Created)
80            })
81    );
82
83    // GET with query parameters — filters the stored items by prefix.
84    server.mechanism({
85        let store = store.clone();
86        ServerMechanism::get("/items/search")
87            .state(store)
88            .query::<Filter>()
89            .onconnect(|state: Arc<Mutex<Vec<Item>>>, f: Filter| async move {
90                let items = state.lock().unwrap();
91                let matches: Vec<Item> = items
92                    .iter()
93                    .filter(|i| i.name.starts_with(&f.prefix))
94                    .cloned()
95                    .collect();
96                reply!(json => SearchResult { matches })
97            })
98    });
99
100    // POST with shared state — stores the item and returns it.
101    server.mechanism({
102        let store = store.clone();
103        ServerMechanism::post("/items/store")
104            .state(store)
105            .json::<NewItem>()
106            .onconnect(|state: Arc<Mutex<Vec<Item>>>, body: NewItem| async move {
107                let mut s = state.lock().unwrap();
108                let id = s.len() as u32 + 1;
109                let item = Item { id, name: body.name };
110                s.push(item.clone());
111                reply!(json => item, status => Status::Created)
112            })
113    });
114
115    // POST with authenticated-encrypted body (ChaCha20-Poly1305 via SerializationKey).
116    // The body is decrypted before the handler is called; a wrong key returns 403.
117    server.mechanism(
118        ServerMechanism::post("/items/secure")
119            .encryption::<NewItem>(SerializationKey::Default)
120            .onconnect(|body: NewItem| async move {
121                let item = Item { id: 99, name: body.name };
122                // The response must also be sealed so the client can open it.
123                reply!(sealed => item, key => SerializationKey::Default)
124            })
125    );
126
127    // ── Serve with graceful shutdown ──────────────────────────────────────────
128    let (tx, rx) = tokio::sync::oneshot::channel::<()>();
129    let server_handle = tokio::spawn(async move {
130        server.serve_with_graceful_shutdown(
131            ([127, 0, 0, 1], PORT),
132            async { rx.await.ok(); },
133        ).await;
134    });
135
136    // Give the server time to bind before firing requests.
137    tokio::time::sleep(Duration::from_millis(200)).await;
138    println!("Server started on port {PORT}");
139
140    // ── Client requests ───────────────────────────────────────────────────────
141    let client = ClientBuilder::new(Target::Localhost(PORT))
142        .timeout(Duration::from_secs(5))
143        .build_async();
144
145    // GET /health
146    let health: Health = client.get("/health").send().await?;
147    assert!(health.ok);
148    println!("GET  /health                → ok={}", health.ok);
149
150    // POST /items (JSON body)
151    let created: Item = client
152        .post("/items")
153        .json(NewItem { name: "widget".into() })
154        .send()
155        .await?;
156    assert_eq!(created.name, "widget");
157    println!("POST /items                 → {:?}", created);
158
159    // POST /items/store (shared state — populates the store)
160    let stored: Item = client
161        .post("/items/store")
162        .json(NewItem { name: "gadget".into() })
163        .send()
164        .await?;
165    println!("POST /items/store           → {:?}", stored);
166
167    let stored2: Item = client
168        .post("/items/store")
169        .json(NewItem { name: "gizmo".into() })
170        .send()
171        .await?;
172    println!("POST /items/store           → {:?}", stored2);
173
174    // GET /items/search?prefix=ga (query params)
175    let result: SearchResult = client
176        .get("/items/search")
177        .query(Filter { prefix: "ga".into() })
178        .send()
179        .await?;
180    assert!(result.matches.iter().all(|i| i.name.starts_with("ga")));
181    println!("GET  /items/search?prefix=ga → {} match(es): {:?}", result.matches.len(), result.matches);
182
183    // POST /items/secure (authenticated-encrypted body)
184    let secure_item = client
185        .post("/items/secure")
186        .encryption(NewItem { name: "secret".into() }, SerializationKey::Default)
187        .send::<Item>()
188        .await?;
189    assert_eq!(secure_item.name, "secret");
190    println!("POST /items/secure          → {:?}", secure_item);
191
192    // ── Shutdown ──────────────────────────────────────────────────────────────
193    tx.send(()).ok();
194    server_handle.await?;
195    println!("\nAll requests successful ✓");
196    Ok(())
197}
Source

pub unsafe fn onconnect_sync<F, Re>(self, handler: F) -> SocketType
where F: Fn() -> Result<Re, Rejection> + Clone + Send + Sync + 'static, Re: Reply + Send + 'static,

Finalises this route with a synchronous handler that receives no arguments.

§Safety

Every incoming request spawns an independent task on Tokio’s blocking thread pool. The pool caps the number of live OS threads (default 512), but the queue of waiting tasks is unbounded — under a traffic surge, tasks accumulate without limit, consuming unbounded memory and causing severe latency spikes or OOM crashes. Additionally, any panic inside the handler is silently converted into a 500 response, masking runtime errors. Callers must ensure the handler completes quickly and that adequate backpressure or rate limiting is applied externally.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V

Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more