• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Covertness / coap-rs / 28215463960

26 Jun 2026 03:35AM UTC coverage: 82.002% (-2.3%) from 84.29%
28215463960

Pull #137

github

web-flow
Merge fc68cd125 into 9972a3dd6
Pull Request #137: issue 136: more illegal parameter checks

91 of 136 new or added lines in 3 files covered. (66.91%)

13 existing lines in 3 files now uncovered.

1335 of 1628 relevant lines covered (82.0%)

3.69 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

74.39
/src/server.rs
1
#[cfg(feature = "router")]
2
use crate::router::{request::Request, Router};
3
use async_trait::async_trait;
4
use coap_lite::{
5
    BlockHandler, BlockHandlerConfig, CoapOption, CoapRequest, CoapResponse, ContentFormat,
6
    MessageClass, MessageType, Packet, RequestType, ResponseType,
7
};
8
use log::debug;
9
use std::{
10
    future::Future,
11
    net::{self, IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr, ToSocketAddrs},
12
    sync::Arc,
13
};
14
use tokio::{
15
    io,
16
    net::UdpSocket,
17
    select,
18
    sync::{
19
        mpsc::{self, UnboundedReceiver, UnboundedSender},
20
        Mutex,
21
    },
22
    task::JoinHandle,
23
};
24

25
use crate::observer::{encode_coap_uint, Observer};
26

27
#[derive(Debug)]
28
pub enum CoAPServerError {
29
    NetworkError,
30
    EventLoopError,
31
    AnotherHandlerIsRunning,
32
    EventSendError,
33
}
34

35
use tokio::io::Error;
36

37
#[async_trait]
38
pub trait Dispatcher: Send + Sync {
39
    async fn dispatch(&self, request: CoapRequest<SocketAddr>) -> Option<CoapResponse>;
40
}
41

42
#[async_trait]
43
/// This trait represents a generic way to respond to a listener. If you want to implement your own
44
/// listener, you have to implement this trait to be able to send responses back through the
45
/// correct transport
46
pub trait Responder: Sync + Send {
47
    async fn respond(&self, response: Vec<u8>);
48
    fn address(&self) -> SocketAddr;
49
}
50

51
/// channel to send new requests from a transport to the CoAP server
52
pub type TransportRequestSender = UnboundedSender<(Vec<u8>, Arc<dyn Responder>)>;
53

54
/// channel used by CoAP server to receive new requests
55
pub type TransportRequestReceiver = UnboundedReceiver<(Vec<u8>, Arc<dyn Responder>)>;
56

57
type UdpResponseReceiver = UnboundedReceiver<(Vec<u8>, SocketAddr)>;
58
type UdpResponseSender = UnboundedSender<(Vec<u8>, SocketAddr)>;
59

60
// listeners receive new connections
61
#[async_trait]
62
pub trait Listener: Send {
63
    async fn listen(
64
        self: Box<Self>,
65
        sender: TransportRequestSender,
66
    ) -> std::io::Result<JoinHandle<std::io::Result<()>>>;
67
}
68
/// listener for a UDP socket
69
pub struct UdpCoapListener {
70
    socket: UdpSocket,
71
    multicast_addresses: Vec<IpAddr>,
72
    response_receiver: UdpResponseReceiver,
73
    response_sender: UdpResponseSender,
74
}
75

76
#[async_trait]
77
/// A trait for handling incoming requests. Use this instead of a closure
78
/// if you want to modify some external state
79
pub trait RequestHandler: Send + Sync + 'static {
80
    async fn handle_request(
81
        &self,
82
        mut request: Box<CoapRequest<SocketAddr>>,
83
    ) -> Box<CoapRequest<SocketAddr>>;
84
}
85

86
#[async_trait]
87
impl<F, HandlerRet> RequestHandler for F
88
where
89
    F: Fn(Box<CoapRequest<SocketAddr>>) -> HandlerRet + Send + Sync + 'static,
90
    HandlerRet: Future<Output = Box<CoapRequest<SocketAddr>>> + Send,
91
{
92
    async fn handle_request(
47✔
93
        &self,
94
        request: Box<CoapRequest<SocketAddr>>,
95
    ) -> Box<CoapRequest<SocketAddr>> {
96
        self(request).await
28✔
97
    }
98
}
99

100
/// A listener for UDP packets. This listener can also subscribe to multicast addresses
101
impl UdpCoapListener {
102
    pub fn new<A: ToSocketAddrs>(addr: A) -> Result<Self, Error> {
2✔
103
        let std_socket = net::UdpSocket::bind(addr)?;
2✔
104
        std_socket.set_nonblocking(true)?;
4✔
105
        let socket = UdpSocket::from_std(std_socket)?;
2✔
106
        Ok(Self::from_socket(socket))
4✔
107
    }
108

109
    pub fn from_socket(socket: tokio::net::UdpSocket) -> Self {
1✔
110
        let (tx, rx) = mpsc::unbounded_channel();
2✔
111
        Self {
112
            socket,
113
            multicast_addresses: Vec::new(),
1✔
114
            response_receiver: rx,
115
            response_sender: tx,
116
        }
117
    }
118

119
    /// join multicast - adds the multicast addresses to the unicast listener
120
    /// - IPv4 multicast address range is '224.0.0.0/4'
121
    /// - IPv6 AllCoAp multicast addresses are 'ff00::/8'
122
    ///
123
    /// Parameter segment is used with IPv6 to determine the first octet.
124
    /// - It's value can be between 0x0 and 0xf.
125
    /// - To join multiple segments, you have to call enable_discovery for each of the segments.
126
    ///
127
    /// Some Multicast address scope
128
    /// IPv6        IPv4 equivalent[16]            Scope                Purpose
129
    /// ffx1::/16    127.0.0.0/8                    Interface-local        Packets with this destination address may not be sent over any network link, but must remain within the current node; this is the multicast equivalent of the unicast loopback address.
130
    /// ffx2::/16    224.0.0.0/24                Link-local            Packets with this destination address may not be routed anywhere.
131
    /// ffx3::/16    239.255.0.0/16                IPv4 local scope
132
    /// ffx4::/16                                Admin-local            The smallest scope that must be administratively configured.
133
    /// ffx5::/16                                Site-local            Restricted to the local physical network.
134
    /// ffx8::/16    239.192.0.0/14                Organization-local    Restricted to networks used by the organization administering the local network. (For example, these addresses might be used over VPNs; when packets for this group are routed over the public internet (where these addresses are not valid), they would have to be encapsulated in some other protocol.)
135
    /// ffxe::/16    224.0.1.0-238.255.255.255    Global scope        Eligible to be routed over the public internet.
136
    ///
137
    /// Notable addresses:
138
    /// ff02::1        All nodes on the local network segment
139
    /// ff0x::c        Simple Service Discovery Protocol
140
    /// ff0x::fb    Multicast DNS
141
    /// ff0x::fb    Multicast CoAP
142
    /// ff0x::114    Used for experiments
143
    //    pub fn join_multicast(&mut self, addr: IpAddr) {
144
    //        self.udp_server.join_multicast(addr);
145
    //    }
146
    pub fn join_multicast(&mut self, addr: IpAddr) {
1✔
147
        assert!(addr.is_multicast());
1✔
148
        // determine wether IPv4 or IPv6 and
149
        // join the appropriate multicast address
150
        match self.socket.local_addr().unwrap() {
1✔
151
            SocketAddr::V4(val) => {
1✔
152
                match addr {
1✔
153
                    IpAddr::V4(ipv4) => {
1✔
154
                        let i = *val.ip();
1✔
155
                        self.socket.join_multicast_v4(ipv4, i).unwrap();
1✔
156
                        self.multicast_addresses.push(addr);
1✔
157
                    }
158
                    IpAddr::V6(_ipv6) => { /* handle IPv6 */ }
×
159
                }
160
            }
161
            SocketAddr::V6(_val) => {
×
162
                match addr {
×
163
                    IpAddr::V4(_ipv4) => { /* handle IPv4 */ }
×
164
                    IpAddr::V6(ipv6) => {
×
165
                        self.socket.join_multicast_v6(&ipv6, 0).unwrap();
×
166
                        self.multicast_addresses.push(addr);
×
167
                        //self.socket.set_only_v6(true)?;
168
                    }
169
                }
170
            }
171
        }
172
    }
173

174
    /// leave multicast - remove the multicast address from the listener
175
    pub fn leave_multicast(&mut self, addr: IpAddr) {
1✔
176
        assert!(addr.is_multicast());
1✔
177
        // determine wether IPv4 or IPv6 and
178
        // leave the appropriate multicast address
179
        match self.socket.local_addr().unwrap() {
1✔
180
            SocketAddr::V4(val) => {
1✔
181
                match addr {
1✔
182
                    IpAddr::V4(ipv4) => {
1✔
183
                        let i = *val.ip();
1✔
184
                        self.socket.leave_multicast_v4(ipv4, i).unwrap();
1✔
185
                        let index = self
1✔
186
                            .multicast_addresses
187
                            .iter()
188
                            .position(|&item| item == addr)
3✔
189
                            .unwrap();
190
                        self.multicast_addresses.remove(index);
1✔
191
                    }
192
                    IpAddr::V6(_ipv6) => { /* handle IPv6 */ }
×
193
                }
194
            }
195
            SocketAddr::V6(_val) => {
×
196
                match addr {
×
197
                    IpAddr::V4(_ipv4) => { /* handle IPv4 */ }
×
198
                    IpAddr::V6(ipv6) => {
×
199
                        self.socket.leave_multicast_v6(&ipv6, 0).unwrap();
×
200
                        let index = self
×
201
                            .multicast_addresses
202
                            .iter()
203
                            .position(|&item| item == addr)
×
204
                            .unwrap();
205
                        self.multicast_addresses.remove(index);
×
206
                    }
207
                }
208
            }
209
        }
210
    }
211
    /// enable AllCoAP multicasts - adds the AllCoap addresses to the listener
212
    /// - IPv4 AllCoAP multicast address is '224.0.1.187'
213
    /// - IPv6 AllCoAp multicast addresses are 'ff0?::fd'
214
    ///
215
    /// Parameter segment is used with IPv6 to determine the first octet.
216
    /// - It's value can be between 0x0 and 0xf.
217
    /// - To join multiple segments, you have to call enable_discovery for each of the segments.
218
    ///
219
    /// For further details see method join_multicast
220
    pub fn enable_all_coap(&mut self, segment: u8) {
1✔
221
        assert!(segment <= 0xf);
1✔
222
        let m = match self.socket.local_addr().unwrap() {
1✔
223
            SocketAddr::V4(_val) => IpAddr::V4(Ipv4Addr::new(224, 0, 1, 187)),
1✔
224
            SocketAddr::V6(_val) => IpAddr::V6(Ipv6Addr::new(
×
225
                0xff00 + segment as u16,
×
226
                0,
227
                0,
228
                0,
229
                0,
230
                0,
231
                0,
232
                0xfd,
233
            )),
234
        };
235
        self.join_multicast(m);
1✔
236
    }
237
}
238
#[async_trait]
239
impl Listener for UdpCoapListener {
240
    async fn listen(
5✔
241
        mut self: Box<Self>,
242
        sender: TransportRequestSender,
243
    ) -> std::io::Result<JoinHandle<std::io::Result<()>>> {
244
        return Ok(tokio::spawn(self.receive_loop(sender)));
3✔
245
    }
246
}
247

248
#[derive(Clone)]
249
struct UdpResponder {
250
    address: SocketAddr, // this is the address we are sending to
251
    tx: UdpResponseSender,
252
}
253

254
#[async_trait]
255
impl Responder for UdpResponder {
256
    async fn respond(&self, response: Vec<u8>) {
4✔
257
        let _ = self.tx.send((response, self.address));
2✔
258
    }
259
    fn address(&self) -> SocketAddr {
1✔
260
        self.address
1✔
261
    }
262
}
263

264
impl UdpCoapListener {
265
    pub async fn receive_loop(mut self, sender: TransportRequestSender) -> std::io::Result<()> {
4✔
266
        loop {
2✔
267
            let mut recv_vec = Vec::with_capacity(u16::MAX as usize);
2✔
268
            select! {
4✔
269
                message =self.socket.recv_buf_from(&mut recv_vec)=> {
270
                    match message {
271
                        Ok((_size, from)) => {
272
                            sender.send((recv_vec, Arc::new(UdpResponder{address: from, tx: self.response_sender.clone()}))).map_err( |_| std::io::Error::other("server channel error"))?;
273
                        }
274
                        Err(e) => {
275
                            return Err(e);
276
                        }
277
                    }
278
                },
279
                response = self.response_receiver.recv() => {
280
                    if let Some((bytes, to)) = response{
281
                        debug!("sending {:?} to {:?}", bytes, to);
282
                        self.socket.send_to(&bytes, to).await?;
283
                    }
284
                    else {
285
                        // in case nobody is listening to us, we can just terminate, though this
286
                        // should never happen for UDP
287
                        return Ok(());
288
                    }
289

290
                }
291
            }
292
        }
293
    }
294
}
295

296
#[derive(Debug)]
297
pub struct QueuedMessage {
298
    pub address: SocketAddr,
299
    pub message: Packet,
300
}
301

302
struct ServerCoapState {
303
    observer: Observer,
304
    block_handler: BlockHandler<SocketAddr>,
305
    disable_observe: bool,
306
}
307

308
pub enum ShouldForwardToHandler {
309
    True,
310
    False,
311
}
312

313
impl ServerCoapState {
314
    fn set_error_response(
1✔
315
        request: &mut CoapRequest<SocketAddr>,
316
        status: ResponseType,
317
        payload: Option<Vec<u8>>,
318
    ) {
319
        if let Some(response) = request.response.as_mut() {
2✔
320
            response.message.header.code = MessageClass::Response(status);
1✔
321
            if let Some(payload) = payload {
1✔
NEW
322
                response.message.payload = payload;
×
323
            } else {
324
                response.message.payload.clear();
1✔
325
                response.message.clear_option(CoapOption::ContentFormat);
1✔
326
            }
327
        }
328
    }
329

NEW
330
    fn set_reset_response(request: &mut CoapRequest<SocketAddr>) {
×
NEW
331
        if let Some(response) = request.response.as_mut() {
×
NEW
332
            response.message = Packet::new();
×
NEW
333
            response.message.header.set_type(MessageType::Reset);
×
NEW
334
            response.message.header.code = MessageClass::Empty;
×
NEW
335
            response.message.header.message_id = request.message.header.message_id;
×
336
        }
337
    }
338

339
    fn is_reserved_class_code(code: u8) -> bool {
1✔
340
        let class = (code & 0xE0) >> 5;
1✔
341
        matches!(class, 1 | 6 | 7)
1✔
342
    }
343

344
    fn should_reject_confirmable_with_reset(request: &CoapRequest<SocketAddr>) -> bool {
1✔
345
        if request.message.header.get_type() != MessageType::Confirmable {
1✔
346
            return false;
1✔
347
        }
348

349
        match request.message.header.code {
1✔
350
            MessageClass::Empty => true,
1✔
351
            MessageClass::Reserved(code) => Self::is_reserved_class_code(code),
1✔
352
            _ => false,
1✔
353
        }
354
    }
355

356
    fn unknown_critical_options(request: &CoapRequest<SocketAddr>) -> Vec<u16> {
1✔
357
        request
358
            .message
359
            .options()
360
            .filter_map(|(number, _)| {
3✔
361
                if let CoapOption::Unknown(value) = CoapOption::from(*number) {
1✔
362
                    if value % 2 == 1 {
1✔
363
                        return Some(value);
1✔
364
                    }
365
                }
366
                None
1✔
367
            })
368
            .collect()
369
    }
370

371
    fn decode_coap_u16(value: &[u8]) -> Option<u16> {
1✔
372
        if value.len() > 2 {
1✔
NEW
373
            return None;
×
374
        }
375
        let mut out = 0u16;
1✔
376
        for byte in value {
3✔
377
            out = (out << 8) | (*byte as u16);
1✔
378
        }
379
        Some(out)
1✔
380
    }
381

382
    fn request_has_supported_accept(request: &CoapRequest<SocketAddr>) -> bool {
1✔
383
        let Some(values) = request.message.get_option(CoapOption::Accept) else {
1✔
384
            return true;
1✔
385
        };
386

387
        values.iter().any(|value| {
2✔
388
            let Some(format_id) = Self::decode_coap_u16(value) else {
1✔
NEW
389
                return false;
×
390
            };
391
            ContentFormat::try_from(format_id as usize).is_ok()
1✔
392
        })
393
    }
394

395
    fn request_accepts_response_content_format(request: &CoapRequest<SocketAddr>) -> bool {
1✔
396
        let Some(accept_values) = request.message.get_option(CoapOption::Accept) else {
1✔
397
            return true;
1✔
398
        };
399

NEW
400
        let Some(response) = request.response.as_ref() else {
×
NEW
401
            return true;
×
402
        };
403

NEW
404
        let Some(content_format) = response.message.get_content_format() else {
×
NEW
405
            return false;
×
406
        };
NEW
407
        let content_format = u16::try_from(usize::from(content_format)).ok();
×
408

NEW
409
        accept_values.iter().any(|value| {
×
NEW
410
            let Some(accept) = Self::decode_coap_u16(value) else {
×
NEW
411
                return false;
×
412
            };
NEW
413
            Some(accept) == content_format
×
414
        })
415
    }
416

417
    fn has_conditional_options(request: &CoapRequest<SocketAddr>) -> bool {
1✔
418
        request.message.get_option(CoapOption::IfMatch).is_some()
1✔
419
            || request.message.get_option(CoapOption::IfNoneMatch).is_some()
1✔
420
    }
421

422
    pub async fn intercept_request(
1✔
423
        &mut self,
424
        request: &mut CoapRequest<SocketAddr>,
425
        responder: Arc<dyn Responder>,
426
    ) -> ShouldForwardToHandler {
427
        if Self::should_reject_confirmable_with_reset(request) {
2✔
NEW
428
            Self::set_reset_response(request);
×
NEW
429
            return ShouldForwardToHandler::False;
×
430
        }
431

432
        let unknown_critical = Self::unknown_critical_options(request);
1✔
433
        if request.message.header.get_type() == MessageType::Confirmable && !unknown_critical.is_empty() {
3✔
NEW
434
            let diagnostic = format!("Unrecognized critical options: {:?}", unknown_critical)
×
435
                .into_bytes();
NEW
436
            Self::set_error_response(request, ResponseType::BadOption, Some(diagnostic));
×
NEW
437
            return ShouldForwardToHandler::False;
×
438
        }
439

440
        if !Self::request_has_supported_accept(request) {
2✔
NEW
441
            Self::set_error_response(request, ResponseType::NotAcceptable, None);
×
NEW
442
            return ShouldForwardToHandler::False;
×
443
        }
444

445
        // Fail-closed default for conditional requests because this layer has no resource-version store.
446
        if Self::has_conditional_options(request) {
2✔
NEW
447
            Self::set_error_response(request, ResponseType::PreconditionFailed, None);
×
NEW
448
            return ShouldForwardToHandler::False;
×
449
        }
450

451
        if *request.get_method() == RequestType::UnKnown {
2✔
452
            Self::set_error_response(request, ResponseType::MethodNotAllowed, None);
1✔
453
            return ShouldForwardToHandler::False;
1✔
454
        }
455

456
        match self.block_handler.intercept_request(request) {
3✔
457
            Ok(true) => return ShouldForwardToHandler::False,
1✔
458
            Err(_err) => return ShouldForwardToHandler::False,
×
459
            Ok(false) => {}
460
        };
461

462
        if self.disable_observe {
1✔
463
            return ShouldForwardToHandler::True;
1✔
464
        }
465

466
        let should_be_forwarded = self.observer.request_handler(request, responder).await;
2✔
467
        if should_be_forwarded {
2✔
468
            ShouldForwardToHandler::True
1✔
469
        } else {
470
            ShouldForwardToHandler::False
1✔
471
        }
472
    }
473

474
    pub async fn intercept_response(&mut self, request: &mut CoapRequest<SocketAddr>) {
4✔
475
        let resource_path = request.get_path();
1✔
476

477
        let is_block_fetch_for_observer = request.message.get_option(CoapOption::Block2).is_some()
3✔
478
            && request.message.get_option(CoapOption::Observe).is_none()
1✔
479
            && request.source.is_some()
1✔
480
            && self
2✔
481
                .observer
482
                .is_observing(&request.source.unwrap(), &resource_path);
2✔
483

484
        if is_block_fetch_for_observer {
1✔
485
            if let Some((payload, etag)) =
1✔
486
                self.observer.get_resource_payload_and_etag(&resource_path)
487
            {
488
                if let Some(ref mut response) = request.response {
2✔
489
                    response.message.payload = payload.to_vec();
2✔
490
                    response.message.clear_option(CoapOption::ETag);
1✔
491
                    response.message.add_option(CoapOption::ETag, etag);
1✔
492
                    // Prevent duplicate Size2 options, clear first.
493
                    response.message.clear_option(CoapOption::Size2);
1✔
494
                    response
1✔
495
                        .message
496
                        .add_option(CoapOption::Size2, encode_coap_uint(payload.len()));
1✔
497
                }
498
            }
499
        }
500

501
        if let Err(err) = self.block_handler.intercept_response(request) {
2✔
502
            let _ = request.apply_from_error(err);
×
503
        }
504

505
        if !Self::request_accepts_response_content_format(request) {
3✔
NEW
506
            Self::set_error_response(request, ResponseType::NotAcceptable, None);
×
507
        }
508
    }
509

510
    pub fn new() -> Self {
1✔
511
        Self {
512
            observer: Observer::new(),
1✔
513
            block_handler: BlockHandler::new(BlockHandlerConfig::default()),
2✔
514
            disable_observe: false,
515
        }
516
    }
517
    pub fn disable_observe_handling(&mut self, value: bool) {
1✔
518
        self.disable_observe = value
1✔
519
    }
520
}
521

522
pub struct Server {
523
    listeners: Vec<Box<dyn Listener>>,
524
    coap_state: Arc<Mutex<ServerCoapState>>,
525
    new_packet_receiver: TransportRequestReceiver,
526
    new_packet_sender: TransportRequestSender,
527
}
528

529
impl Server {
530
    fn parse_reset_message_id_from_raw(raw: &[u8]) -> Option<u16> {
1✔
531
        if raw.len() < 4 {
1✔
NEW
532
            return None;
×
533
        }
534
        let version = (raw[0] & 0xC0) >> 6;
2✔
535
        let message_type = (raw[0] & 0x30) >> 4;
2✔
536
        if version != 1 || message_type != 0 {
2✔
NEW
537
            return None;
×
538
        }
539
        Some(u16::from_be_bytes([raw[2], raw[3]]))
1✔
540
    }
541

NEW
542
    fn build_reset(message_id: u16) -> Option<Vec<u8>> {
×
NEW
543
        let mut packet = Packet::new();
×
NEW
544
        packet.header.set_type(MessageType::Reset);
×
NEW
545
        packet.header.code = MessageClass::Empty;
×
NEW
546
        packet.header.message_id = message_id;
×
NEW
547
        packet.to_bytes().ok()
×
548
    }
549

550
    /// Creates a CoAP server listening on the given address.
551
    pub fn new_udp<A: ToSocketAddrs>(addr: A) -> Result<Self, io::Error> {
1✔
552
        let listener: Vec<Box<dyn Listener>> = vec![Box::new(UdpCoapListener::new(addr)?)];
2✔
553
        Ok(Self::from_listeners(listener))
2✔
554
    }
555

556
    pub fn from_listeners(listeners: Vec<Box<dyn Listener>>) -> Self {
1✔
557
        let (tx, rx) = mpsc::unbounded_channel();
2✔
558
        Server {
559
            listeners,
560
            coap_state: Arc::new(Mutex::new(ServerCoapState::new())),
2✔
561
            new_packet_receiver: rx,
562
            new_packet_sender: tx,
563
        }
564
    }
565

566
    async fn spawn_handles(
1✔
567
        listeners: Vec<Box<dyn Listener>>,
568
        sender: TransportRequestSender,
569
    ) -> std::io::Result<Vec<JoinHandle<std::io::Result<()>>>> {
570
        let mut handles = vec![];
1✔
571
        for listener in listeners.into_iter() {
4✔
572
            let handle = listener.listen(sender.clone()).await?;
3✔
573
            handles.push(handle);
1✔
574
        }
575
        Ok(handles)
1✔
576
    }
577

578
    /// run the server.
579
    pub async fn run<Handler: RequestHandler>(mut self, handler: Handler) -> Result<(), io::Error> {
64✔
580
        let _handles = Self::spawn_handles(self.listeners, self.new_packet_sender.clone()).await?;
26✔
581

582
        let handler_arc = Arc::new(handler);
26✔
583
        // receive an input, sync our cache / states, then call custom handler
584
        loop {
585
            let (bytes, respond) = self
62✔
586
                .new_packet_receiver
×
587
                .recv()
588
                .await
76✔
589
                .ok_or_else(|| std::io::Error::other("listen channel closed"))?;
12✔
590
            match Packet::from_bytes(&bytes) {
24✔
591
                Ok(packet) => {
12✔
592
                    let mut request = Box::new(CoapRequest::<SocketAddr>::from_packet(
36✔
593
                        packet,
12✔
594
                        respond.address(),
24✔
595
                    ));
596
                    let mut coap_state = self.coap_state.lock().await;
36✔
597
                    let should_forward = coap_state
36✔
598
                        .intercept_request(&mut request, respond.clone())
12✔
599
                        .await;
48✔
600

601
                    match should_forward {
12✔
602
                        ShouldForwardToHandler::True => {
603
                            let handler_clone = handler_arc.clone();
24✔
604
                            let coap_state_clone = self.coap_state.clone();
24✔
605
                            tokio::spawn(async move {
36✔
606
                                request = handler_clone.handle_request(request).await;
26✔
607
                                coap_state_clone
55✔
608
                                    .lock()
11✔
609
                                    .await
33✔
610
                                    .intercept_response(request.as_mut())
11✔
611
                                    .await;
22✔
612

613
                                Self::respond_to_request(request, respond).await;
11✔
614
                            });
615
                        }
616
                        ShouldForwardToHandler::False => {
617
                            Self::respond_to_request(request, respond).await;
12✔
618
                        }
619
                    }
620
                }
621
                Err(_) => {
NEW
622
                    if let Some(message_id) = Self::parse_reset_message_id_from_raw(&bytes) {
×
NEW
623
                        if let Some(reset) = Self::build_reset(message_id) {
×
NEW
624
                            respond.respond(reset).await;
×
625
                        }
626
                    }
627
                }
628
            }
629
        }
630
    }
631

632
    #[cfg(feature = "router")]
633
    pub async fn serve<S>(self, router: Router<S>) -> Result<(), io::Error>
4✔
634
    where
635
        S: Clone + Send + Sync + 'static,
636
    {
637
        let router = Arc::new(router);
4✔
638
        let handler = {
×
639
            move |req| {
4✔
640
                let r = router.clone();
4✔
641
                let req = Request::new(req);
2✔
642
                async move { r.handle(req).await.req }
6✔
643
            }
644
        };
645
        self.run(handler).await
6✔
646
    }
647

648
    async fn respond_to_request(req: Box<CoapRequest<SocketAddr>>, responder: Arc<dyn Responder>) {
4✔
649
        // if we have some reponse to send, send it
650
        if let Some(Ok(b)) = req.response.map(|resp| resp.message.to_bytes()) {
6✔
651
            responder.respond(b).await;
2✔
652
        }
653
    }
654
    #[deprecated(
655
        since = "0.21.0",
656
        note = "Use 'coap::Server::automatic_observe_handling' instead."
657
    )]
658
    /// disable auto-observe handling in server
659
    pub async fn disable_observe_handling(&mut self, value: bool) {
×
660
        self.automatic_observe_handling(value).await
×
661
    }
662
    /// Controls whether the server automatically handles observe options.
663
    /// Automatic handling is on by default.
664
    ///
665
    /// Set `bypass` to `true` when your handler needs full control over
666
    /// observe — the server will skip its built-in processing.
667
    pub async fn automatic_observe_handling(&mut self, bypass: bool) {
4✔
668
        let mut coap_state = self.coap_state.lock().await;
2✔
669
        coap_state.disable_observe_handling(bypass)
2✔
670
    }
671
}
672

673
#[cfg(test)]
674
pub mod test {
675
    use crate::request::RequestBuilder;
676

677
    use super::super::*;
678
    use super::*;
679
    use coap_lite::{
680
        block_handler::BlockValue, CoapOption, MessageClass, MessageType, RequestType,
681
        ResponseType,
682
    };
683
    use std::str;
684
    use std::time::Duration;
685

686
    pub fn spawn_server<
687
        F: Fn(Box<CoapRequest<SocketAddr>>) -> HandlerRet + Send + Sync + 'static,
688
        HandlerRet,
689
    >(
690
        ip: &'static str,
691
        request_handler: F,
692
    ) -> mpsc::UnboundedReceiver<u16>
693
    where
694
        HandlerRet: Future<Output = Box<CoapRequest<SocketAddr>>> + Send,
695
    {
696
        let (tx, rx) = mpsc::unbounded_channel();
697
        let _task = tokio::spawn(async move {
698
            let sock = UdpSocket::bind(ip).await.unwrap();
699
            let addr = sock.local_addr().unwrap();
700
            let listener = Box::new(UdpCoapListener::from_socket(sock));
701
            let server = Server::from_listeners(vec![listener]);
702
            tx.send(addr.port()).unwrap();
703
            server.run(request_handler).await.unwrap();
704
        });
705

706
        rx
707
    }
708

709
    async fn request_handler(
710
        mut req: Box<CoapRequest<SocketAddr>>,
711
    ) -> Box<CoapRequest<SocketAddr>> {
712
        let uri_path_list = req.message.get_option(CoapOption::UriPath).unwrap().clone();
713
        assert_eq!(uri_path_list.len(), 1);
714

715
        if let Some(ref mut response) = req.response {
716
            response.message.payload = uri_path_list.front().unwrap().clone();
717
        }
718
        req
719
    }
720

721
    #[test]
722
    fn test_unknown_critical_option_is_detected() {
723
        let mut req: CoapRequest<SocketAddr> = CoapRequest::new();
724
        req.message
725
            .add_option(CoapOption::Unknown(999), b"x".to_vec());
726

727
        let unknown = ServerCoapState::unknown_critical_options(&req);
728
        assert_eq!(unknown, vec![999]);
729
    }
730

731
    #[test]
732
    fn test_unsupported_accept_is_rejected() {
733
        let mut req: CoapRequest<SocketAddr> = CoapRequest::new();
734
        req.message
735
            .add_option(CoapOption::Accept, vec![0x27, 0x0F]); // 9999
736

737
        assert!(!ServerCoapState::request_has_supported_accept(&req));
738
    }
739

740
    #[test]
741
    fn test_confirmable_reserved_and_empty_are_rejected_with_reset() {
742
        let mut reserved_req: CoapRequest<SocketAddr> = CoapRequest::new();
743
        reserved_req.message.header.set_type(MessageType::Confirmable);
744
        reserved_req.message.header.code = MessageClass::Reserved(0x20); // class 1.xx
745

746
        assert!(ServerCoapState::should_reject_confirmable_with_reset(
747
            &reserved_req
748
        ));
749

750
        let mut empty_req: CoapRequest<SocketAddr> = CoapRequest::new();
751
        empty_req.message.header.set_type(MessageType::Confirmable);
752
        empty_req.message.header.code = MessageClass::Empty;
753

754
        assert!(ServerCoapState::should_reject_confirmable_with_reset(
755
            &empty_req
756
        ));
757
    }
758

759
    #[test]
760
    fn test_parse_reset_message_id_from_raw_confirmable() {
761
        let raw = [0x40, 0x01, 0x12, 0x34];
762
        let message_id = Server::parse_reset_message_id_from_raw(&raw);
763
        assert_eq!(message_id, Some(0x1234));
764
    }
765

766
    #[test]
767
    fn test_unknown_method_gets_method_not_allowed_response() {
768
        let mut req: CoapRequest<SocketAddr> = CoapRequest::new();
769
        req.response = coap_lite::CoapResponse::new(&req.message);
770
        req.message.header.code = MessageClass::Request(RequestType::UnKnown);
771

772
        ServerCoapState::set_error_response(&mut req, ResponseType::MethodNotAllowed, None);
773
        assert_eq!(
774
            req.response.unwrap().message.header.code,
775
            MessageClass::Response(ResponseType::MethodNotAllowed)
776
        );
777
    }
778

779
    pub fn spawn_server_with_all_coap<
780
        F: Fn(Box<CoapRequest<SocketAddr>>) -> HandlerRet + Send + Sync + 'static,
781
        HandlerRet,
782
    >(
783
        ip: &'static str,
784
        request_handler: F,
785
        segment: u8,
786
    ) -> mpsc::UnboundedReceiver<u16>
787
    where
788
        HandlerRet: Future<Output = Box<CoapRequest<SocketAddr>>> + Send,
789
    {
790
        let (tx, rx) = mpsc::unbounded_channel();
791

792
        std::thread::Builder::new()
793
            .name(String::from("v4-server"))
794
            .spawn(move || {
795
                tokio::runtime::Runtime::new()
796
                    .unwrap()
797
                    .block_on(async move {
798
                        // multicast needs a server on a real interface
799
                        let sock = UdpSocket::bind((ip, 0)).await.unwrap();
800
                        let addr = sock.local_addr().unwrap();
801
                        let mut listener = Box::new(UdpCoapListener::from_socket(sock));
802
                        listener.enable_all_coap(segment);
803
                        let server = Server::from_listeners(vec![listener]);
804
                        tx.send(addr.port()).unwrap();
805
                        server.run(request_handler).await.unwrap();
806
                    })
807
            })
808
            .unwrap();
809

810
        rx
811
    }
812

813
    pub fn spawn_server_disable_observe<
814
        F: Fn(Box<CoapRequest<SocketAddr>>) -> HandlerRet + Send + Sync + 'static,
815
        HandlerRet,
816
    >(
817
        ip: &'static str,
818
        request_handler: F,
819
    ) -> mpsc::UnboundedReceiver<u16>
820
    where
821
        HandlerRet: Future<Output = Box<CoapRequest<SocketAddr>>> + Send,
822
    {
823
        let (tx, rx) = mpsc::unbounded_channel();
824
        let _task = tokio::spawn(async move {
825
            let sock = UdpSocket::bind(ip).await.unwrap();
826
            let addr = sock.local_addr().unwrap();
827
            let listener = Box::new(UdpCoapListener::from_socket(sock));
828
            let mut server = Server::from_listeners(vec![listener]);
829
            // `bypass = true` sets the internal `disable_observe` flag,
830
            // so the server skips its built-in observe handling.
831
            server.automatic_observe_handling(true).await;
832
            tx.send(addr.port()).unwrap();
833
            server.run(request_handler).await.unwrap();
834
        });
835

836
        rx
837
    }
838

839
    #[tokio::test]
840
    async fn test_listener_instantiation() {
841
        let listener = UdpCoapListener::new("127.0.0.1:0").unwrap();
842
        assert!(
843
            listener.socket.local_addr().unwrap().ip() == IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
844
        );
845
        // assert!(listener.socket.blocking() == false);
846

847
        let explicit_socket = UdpSocket::bind("127.0.0.1:0").await.unwrap();
848
        let another_listener = UdpCoapListener::from_socket(explicit_socket);
849
        assert!(
850
            another_listener.socket.local_addr().unwrap().ip()
851
                == IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1))
852
        );
853
    }
854

855
    #[tokio::test]
856
    async fn test_echo_server() {
857
        let server_port = spawn_server("127.0.0.1:0", request_handler)
858
            .recv()
859
            .await
860
            .unwrap();
861

862
        let client = UdpCoAPClient::new(format!("127.0.0.1:{}", server_port))
863
            .await
864
            .unwrap();
865
        let mut request = CoapRequest::new();
866
        request.message.header.set_version(1);
867
        request
868
            .message
869
            .header
870
            .set_type(coap_lite::MessageType::Confirmable);
871
        request.message.header.set_code("0.01");
872
        request.message.header.message_id = 1;
873
        request.message.set_token(vec![0x51, 0x55, 0x77, 0xE8]);
874
        request
875
            .message
876
            .add_option(CoapOption::UriPath, b"test-echo".to_vec());
877
        client.send_single_request(&request).await.unwrap();
878

879
        let recv_packet = client.send(request).await.unwrap();
880
        assert_eq!(recv_packet.message.payload, b"test-echo".to_vec());
881
    }
882

883
    #[tokio::test]
884
    async fn test_put_block() {
885
        let server_port = spawn_server("127.0.0.1:0", request_handler)
886
            .recv()
887
            .await
888
            .unwrap();
889
        let data = "hello this is a payload";
890
        let mut v = Vec::new();
891
        for _ in 0..1024 {
892
            v.extend_from_slice(data.as_bytes());
893
        }
894
        let payload_size = v.len();
895
        let server_string = format!("127.0.0.1:{}", server_port);
896
        let client = UdpCoAPClient::new(server_string.clone()).await.unwrap();
897

898
        let request = RequestBuilder::new("/large", RequestType::Put)
899
            .data(Some(v))
900
            .domain(server_string.clone())
901
            .build();
902

903
        let resp = client.send(request).await.unwrap();
904
        let block_opt = resp
905
            .message
906
            .get_first_option_as::<BlockValue>(CoapOption::Block1)
907
            .expect("expected block opt in response")
908
            .expect("could not decode block1 option");
909
        let expected_number = (payload_size as f32 / 1024.0).ceil() as u16 - 1;
910
        assert_eq!(
911
            block_opt.num, expected_number,
912
            "block not completely received!"
913
        );
914

915
        assert_eq!(resp.message.payload, b"large".to_vec());
916
    }
917

918
    #[tokio::test]
919
    #[ignore]
920
    async fn test_echo_server_v6() {
921
        let server_port = spawn_server("::1:0", request_handler).recv().await.unwrap();
922

923
        let client = UdpCoAPClient::new(format!("::1:{}", server_port))
924
            .await
925
            .unwrap();
926
        let mut request = CoapRequest::new();
927
        request.message.header.set_version(1);
928
        request
929
            .message
930
            .header
931
            .set_type(coap_lite::MessageType::Confirmable);
932
        request.message.header.set_code("0.01");
933
        request.message.header.message_id = 1;
934
        request.message.set_token(vec![0x51, 0x55, 0x77, 0xE8]);
935
        request
936
            .message
937
            .add_option(CoapOption::UriPath, b"test-echo".to_vec());
938

939
        let recv_packet = client.send(request).await.unwrap();
940
        assert_eq!(recv_packet.message.payload, b"test-echo".to_vec());
941
    }
942

943
    #[tokio::test]
944
    async fn test_echo_server_no_token() {
945
        let server_port = spawn_server("127.0.0.1:0", request_handler)
946
            .recv()
947
            .await
948
            .unwrap();
949

950
        let client = UdpCoAPClient::new(format!("127.0.0.1:{}", server_port))
951
            .await
952
            .unwrap();
953
        let mut packet = CoapRequest::new();
954
        packet.message.header.set_version(1);
955
        packet
956
            .message
957
            .header
958
            .set_type(coap_lite::MessageType::Confirmable);
959
        packet.message.header.set_code("0.01");
960
        packet.message.header.message_id = 1;
961
        packet
962
            .message
963
            .add_option(CoapOption::UriPath, b"test-echo".to_vec());
964
        let recv_packet = client.send(packet).await.unwrap();
965
        assert_eq!(recv_packet.message.payload, b"test-echo".to_vec());
966
    }
967

968
    #[tokio::test]
969
    #[ignore]
970
    async fn test_echo_server_no_token_v6() {
971
        let server_port = spawn_server("::1:0", request_handler).recv().await.unwrap();
972

973
        let client = UdpCoAPClient::new(format!("::1:{}", server_port))
974
            .await
975
            .unwrap();
976
        let mut packet = CoapRequest::new();
977
        packet.message.header.set_version(1);
978
        packet
979
            .message
980
            .header
981
            .set_type(coap_lite::MessageType::Confirmable);
982
        packet.message.header.set_code("0.01");
983
        packet.message.header.message_id = 1;
984
        packet
985
            .message
986
            .add_option(CoapOption::UriPath, b"test-echo".to_vec());
987

988
        let recv_packet = client.send(packet).await.unwrap();
989
        assert_eq!(recv_packet.message.payload, b"test-echo".to_vec());
990
    }
991

992
    #[tokio::test]
993
    async fn test_update_resource() {
994
        let path = "/test";
995
        let payload1 = b"data1".to_vec();
996
        let payload2 = b"data2".to_vec();
997
        let (tx, mut rx) = mpsc::unbounded_channel();
998
        let (tx2, mut rx2) = mpsc::unbounded_channel();
999
        let mut step = 1;
1000

1001
        let server_port = spawn_server("127.0.0.1:0", request_handler)
1002
            .recv()
1003
            .await
1004
            .unwrap();
1005

1006
        let client = UdpCoAPClient::new(format!("127.0.0.1:{}", server_port))
1007
            .await
1008
            .unwrap();
1009

1010
        tx.send(step).unwrap();
1011
        let mut request = CoapRequest::new();
1012
        request.set_method(RequestType::Put);
1013
        request.set_path(path);
1014
        request.message.payload = payload1.clone();
1015
        client.send(request.clone()).await.unwrap();
1016

1017
        let mut receive_step = 1;
1018
        let payload1_clone = payload1.clone();
1019
        let payload2_clone = payload2.clone();
1020
        client
1021
            .observe(path, move |result| {
1022
                let msg = result.unwrap();
1023
                if let Ok(n) = rx.try_recv() {
1024
                    receive_step = n;
1025
                }
1026

1027
                match receive_step {
1028
                    1 => assert_eq!(msg.payload, payload1_clone),
1029
                    2 => {
1030
                        assert_eq!(msg.payload, payload2_clone);
1031
                        tx2.send(()).unwrap();
1032
                    }
1033
                    _ => panic!("unexpected step"),
1034
                }
1035
            })
1036
            .await
1037
            .unwrap();
1038

1039
        step = 2;
1040
        tx.send(step).unwrap();
1041
        request.message.payload = payload2.clone();
1042
        let client2 = UdpCoAPClient::new(format!("127.0.0.1:{}", server_port))
1043
            .await
1044
            .unwrap();
1045
        let _ = client2.send(request).await.unwrap();
1046
        assert_eq!(
1047
            tokio::time::timeout(Duration::new(5, 0), rx2.recv())
1048
                .await
1049
                .unwrap(),
1050
            Some(())
1051
        );
1052
    }
1053

1054
    #[tokio::test]
1055
    async fn test_observe_transparent_transmission() {
1056
        let path = "/test";
1057
        let (tx, mut rx) = mpsc::unbounded_channel();
1058

1059
        let server_port = spawn_server_disable_observe("127.0.0.1:0", request_handler)
1060
            .recv()
1061
            .await
1062
            .unwrap();
1063

1064
        let client = UdpCoAPClient::new(format!("127.0.0.1:{}", server_port))
1065
            .await
1066
            .unwrap();
1067

1068
        client
1069
            .observe(path, move |result| {
1070
                let msg = result.unwrap();
1071
                assert_eq!(msg.payload, b"test".to_vec());
1072
                tx.send(()).unwrap();
1073
            })
1074
            .await
1075
            .unwrap();
1076

1077
        assert_eq!(
1078
            tokio::time::timeout(Duration::new(5, 0), rx.recv())
1079
                .await
1080
                .unwrap(),
1081
            Some(())
1082
        );
1083
    }
1084

1085
    #[tokio::test]
1086
    async fn multicast_server_all_coap() {
1087
        // segment not relevant with IPv4
1088
        let segment = 0x0;
1089
        let server_port = spawn_server_with_all_coap("0.0.0.0", request_handler, segment)
1090
            .recv()
1091
            .await
1092
            .unwrap();
1093

1094
        let client = UdpCoAPClient::new(format!("127.0.0.1:{}", server_port))
1095
            .await
1096
            .unwrap();
1097
        let mut request = CoapRequest::new();
1098
        request.message.header.set_version(1);
1099
        request
1100
            .message
1101
            .header
1102
            .set_type(coap_lite::MessageType::Confirmable);
1103
        request.message.header.set_code("0.01");
1104
        request.message.header.message_id = 1;
1105
        request.message.set_token(vec![0x51, 0x55, 0x77, 0xE8]);
1106
        request
1107
            .message
1108
            .add_option(CoapOption::UriPath, b"test-echo".to_vec());
1109
        let recv_packet = client.send(request).await.unwrap();
1110

1111
        assert_eq!(recv_packet.message.payload, b"test-echo".to_vec());
1112

1113
        let client = UdpCoAPClient::new(format!("224.0.1.187:{}", server_port))
1114
            .await
1115
            .unwrap();
1116
        let mut request = RequestBuilder::new("test-echo", RequestType::Get)
1117
            .data(Some(vec![0x51, 0x55, 0x77, 0xE8]))
1118
            .confirmable(true)
1119
            .build();
1120

1121
        let mut receiver = client.create_receiver_for(&request).await;
1122
        client.send_all_coap(&mut request, segment).await.unwrap();
1123
        let recv_packet = receiver.receive().await.unwrap();
1124
        assert_eq!(recv_packet.message.payload, b"test-echo".to_vec());
1125
    }
1126

1127
    //This test right now does not work on windows
1128
    #[cfg(unix)]
1129
    #[tokio::test]
1130
    #[ignore]
1131
    async fn multicast_server_all_coap_v6() {
1132
        // use segment 0x04 which should be the smallest administered scope
1133

1134
        let segment = 0x04;
1135
        let server_port = spawn_server_with_all_coap("::0", request_handler, segment)
1136
            .recv()
1137
            .await
1138
            .unwrap();
1139

1140
        let client = UdpCoAPClient::new(format!("::1:{}", server_port))
1141
            .await
1142
            .unwrap();
1143
        let mut request = CoapRequest::new();
1144
        request.message.header.set_version(1);
1145
        request
1146
            .message
1147
            .header
1148
            .set_type(coap_lite::MessageType::Confirmable);
1149
        request.message.header.set_code("0.01");
1150
        request.message.header.message_id = 1;
1151
        request.message.set_token(vec![0x51, 0x55, 0x77, 0xE8]);
1152
        request
1153
            .message
1154
            .add_option(CoapOption::UriPath, b"test-echo".to_vec());
1155
        client.send_single_request(&request).await.unwrap();
1156

1157
        let recv_packet = client.send(request).await.unwrap();
1158
        assert_eq!(recv_packet.message.payload, b"test-echo".to_vec());
1159

1160
        // use 0xff02 to keep it within this network
1161
        let client = UdpCoAPClient::new(format!("ff0{}::fd:{}", segment, server_port))
1162
            .await
1163
            .unwrap();
1164
        let mut request = CoapRequest::new();
1165
        request.message.header.set_version(1);
1166
        request
1167
            .message
1168
            .header
1169
            .set_type(coap_lite::MessageType::NonConfirmable);
1170
        request.message.header.set_code("0.01");
1171
        request.message.header.message_id = 2;
1172
        request.message.set_token(vec![0x51, 0x55, 0x77, 0xE8]);
1173
        request
1174
            .message
1175
            .add_option(CoapOption::UriPath, b"test-echo".to_vec());
1176
        let mut receiver = client.create_receiver_for(&request).await;
1177
        client.send_all_coap(&mut request, segment).await.unwrap();
1178
        let recv_packet = receiver.receive().await.unwrap();
1179
        assert_eq!(recv_packet.message.payload, b"test-echo".to_vec());
1180
    }
1181

1182
    #[test]
1183
    fn multicast_join_leave() {
1184
        std::thread::Builder::new()
1185
            .name(String::from("v4-server"))
1186
            .spawn(move || {
1187
                tokio::runtime::Runtime::new()
1188
                    .unwrap()
1189
                    .block_on(async move {
1190
                        // multicast needs a server on a real interface
1191
                        let sock = UdpSocket::bind(("0.0.0.0", 0)).await.unwrap();
1192
                        let mut listener = Box::new(UdpCoapListener::from_socket(sock));
1193
                        listener.join_multicast(IpAddr::V4(Ipv4Addr::new(224, 0, 1, 1)));
1194
                        listener.join_multicast(IpAddr::V4(Ipv4Addr::new(224, 1, 1, 1)));
1195
                        listener.leave_multicast(IpAddr::V4(Ipv4Addr::new(224, 0, 1, 1)));
1196
                        listener.leave_multicast(IpAddr::V4(Ipv4Addr::new(224, 1, 1, 1)));
1197
                        let server = Server::from_listeners(vec![listener]);
1198
                        server.run(request_handler).await.unwrap();
1199
                    })
1200
            })
1201
            .unwrap();
1202

1203
        std::thread::sleep(std::time::Duration::from_secs(1));
1204
    }
1205
    #[test]
1206
    #[ignore]
1207
    fn multicast_join_leave_v6() {
1208
        std::thread::Builder::new()
1209
            .name(String::from("v6-server"))
1210
            .spawn(move || {
1211
                tokio::runtime::Runtime::new()
1212
                    .unwrap()
1213
                    .block_on(async move {
1214
                        // multicast needs a server on a real interface
1215
                        let sock = UdpSocket::bind(("0.0.0.0", 0)).await.unwrap();
1216
                        let mut listener = Box::new(UdpCoapListener::from_socket(sock));
1217
                        listener.join_multicast(IpAddr::V6(Ipv6Addr::new(
1218
                            0xff02, 0, 0, 0, 0, 0, 1, 0x1,
1219
                        )));
1220
                        listener.join_multicast(IpAddr::V6(Ipv6Addr::new(
1221
                            0xff02, 0, 0, 0, 0, 1, 0, 0x2,
1222
                        )));
1223
                        listener.leave_multicast(IpAddr::V6(Ipv6Addr::new(
1224
                            0xff02, 0, 0, 0, 0, 0, 1, 0x1,
1225
                        )));
1226
                        listener.join_multicast(IpAddr::V6(Ipv6Addr::new(
1227
                            0xff02, 0, 0, 0, 0, 1, 0, 0x2,
1228
                        )));
1229
                        let server = Server::from_listeners(vec![listener]);
1230
                        server.run(request_handler).await.unwrap();
1231
                    })
1232
            })
1233
            .unwrap();
1234

1235
        std::thread::sleep(std::time::Duration::from_secs(1));
1236
    }
1237

1238
    fn get_expected_response() -> Vec<u8> {
1239
        let mut resp = vec![];
1240
        for c in b'a'..=b'z' {
1241
            resp.resize(resp.len() + 1024, c);
1242
        }
1243
        resp
1244
    }
1245
    async fn block2_responder(
1246
        mut req: Box<CoapRequest<SocketAddr>>,
1247
    ) -> Box<CoapRequest<SocketAddr>> {
1248
        // vec should contain 'a' 1024 times, then 'b' 1024, up to ascii 'z'
1249

1250
        if let Some(ref mut response) = req.response {
1251
            response.message.payload = get_expected_response();
1252
        }
1253
        req
1254
    }
1255
    #[tokio::test]
1256
    async fn test_block2_server_response() {
1257
        let server_port = spawn_server("127.0.0.1:0", block2_responder)
1258
            .recv()
1259
            .await
1260
            .unwrap();
1261

1262
        let client = UdpCoAPClient::new(format!("127.0.0.1:{}", server_port))
1263
            .await
1264
            .unwrap();
1265
        let resp = client
1266
            .send(RequestBuilder::new("/", RequestType::Get).build())
1267
            .await
1268
            .unwrap();
1269
        assert_eq!(
1270
            resp.message.payload,
1271
            get_expected_response(),
1272
            "responses do not match"
1273
        );
1274
    }
1275
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc