libcoap_rs/message/
mod.rs

1// SPDX-License-Identifier: BSD-2-Clause
2/*
3 * Copyright © The libcoap-rs Contributors, all rights reserved.
4 * This file is part of the libcoap-rs project, see the README file for
5 * general information on this project and the NOTICE.md and LICENSE files
6 * for information regarding copyright ownership and terms of use.
7 *
8 * message/mod.rs - Types related to CoAP messages.
9 */
10
11//! Types related to message handling, parsing and creation.
12//!
13//! The base unit that is transmitted between a CoAP client and a CoAP server is called a CoAP
14//! message (in libcoap also referred to as a "pdu"). This module contains both the basic
15//! representation for messages in libcoap-rs ([CoapMessage]) as well as builders that simplify the
16//! process of creating requests and responses and setting the appropriate options ([CoapRequest]
17//! and [CoapResponse]).
18
19use std::{ffi::c_void, fmt::Write, mem::MaybeUninit, slice::Iter};
20
21use libcoap_sys::{
22    coap_add_data, coap_add_data_large_request, coap_add_optlist_pdu, coap_add_token, coap_delete_optlist,
23    coap_delete_pdu, coap_get_data, coap_insert_optlist, coap_new_optlist, coap_opt_length, coap_opt_t, coap_opt_value,
24    coap_option_iterator_init, coap_option_next, coap_option_num_t, coap_optlist_t, coap_pdu_get_code,
25    coap_pdu_get_mid, coap_pdu_get_token, coap_pdu_get_type, coap_pdu_init, coap_pdu_set_code, coap_pdu_set_type,
26    coap_pdu_t, coap_session_t,
27};
28use num_traits::FromPrimitive;
29pub use request::CoapRequest;
30pub use response::CoapResponse;
31
32use crate::{
33    context::ensure_coap_started,
34    error::{MessageConversionError, OptionValueError},
35    protocol::{
36        Block, CoapMatch, CoapMessageCode, CoapMessageType, CoapOptionNum, CoapOptionType, ContentFormat, ETag, Echo,
37        HopLimit, MaxAge, NoResponse, Observe, Oscore, ProxyScheme, ProxyUri, RequestTag, Size, UriHost, UriPath,
38        UriPort, UriQuery,
39    },
40    session::CoapSessionCommon,
41    types::{
42        decode_var_len_u16, decode_var_len_u32, decode_var_len_u8, encode_var_len_u16, encode_var_len_u32,
43        encode_var_len_u8, CoapMessageId,
44    },
45};
46
47pub mod request;
48pub mod response;
49
50/// Representation of a CoAP option including its value.
51///
52/// For an enum describing the possible option types (and their associated option numbers), see
53/// [CoapOptionType], for the data type representing option numbers, see [CoapOptionNum]
54#[derive(Debug, Hash, Eq, PartialEq, Clone)]
55pub enum CoapOption {
56    IfMatch(CoapMatch),
57    IfNoneMatch,
58    UriHost(UriHost),
59    UriPort(UriPort),
60    UriPath(UriPath),
61    UriQuery(UriQuery),
62    LocationPath(UriPath),
63    LocationQuery(UriQuery),
64    ProxyUri(ProxyUri),
65    ProxyScheme(ProxyScheme),
66    ContentFormat(ContentFormat),
67    Accept(ContentFormat),
68    Size1(Size),
69    Size2(Size),
70    Block1(Block),
71    Block2(Block),
72    HopLimit(HopLimit),
73    NoResponse(NoResponse),
74    ETag(ETag),
75    MaxAge(MaxAge),
76    Observe(Observe),
77    Oscore(Oscore),
78    Echo(Echo),
79    RTag(RequestTag),
80    QBlock1(Block),
81    QBlock2(Block),
82    Other(CoapOptionNum, Box<[u8]>),
83}
84
85impl CoapOption {
86    /// Create a CoAP option from its raw representation in the C library.
87    ///
88    /// # Safety
89    /// `opt` must be a valid pointer to a well-formed coap_opt_t value as returned by
90    /// [coap_option_next()].
91    pub(crate) unsafe fn from_raw_opt(
92        number: coap_option_num_t,
93        opt: *const coap_opt_t,
94    ) -> Result<CoapOption, OptionValueError> {
95        let value = Vec::from(std::slice::from_raw_parts(
96            coap_opt_value(opt),
97            coap_opt_length(opt) as usize,
98        ));
99        Self::from_type_value(number, value)
100    }
101
102    /// Returns the option number associated with this option.
103    pub fn number(&self) -> CoapOptionNum {
104        match self {
105            CoapOption::IfMatch(_) => CoapOptionType::IfMatch as u16,
106            CoapOption::IfNoneMatch => CoapOptionType::IfNoneMatch as u16,
107            CoapOption::UriHost(_) => CoapOptionType::UriHost as u16,
108            CoapOption::UriPort(_) => CoapOptionType::UriPort as u16,
109            CoapOption::UriPath(_) => CoapOptionType::UriPath as u16,
110            CoapOption::UriQuery(_) => CoapOptionType::UriQuery as u16,
111            CoapOption::LocationPath(_) => CoapOptionType::LocationPath as u16,
112            CoapOption::LocationQuery(_) => CoapOptionType::LocationQuery as u16,
113            CoapOption::ProxyUri(_) => CoapOptionType::ProxyUri as u16,
114            CoapOption::ProxyScheme(_) => CoapOptionType::ProxyScheme as u16,
115            CoapOption::ContentFormat(_) => CoapOptionType::ContentFormat as u16,
116            CoapOption::Accept(_) => CoapOptionType::Accept as u16,
117            CoapOption::Size1(_) => CoapOptionType::Size1 as u16,
118            CoapOption::Size2(_) => CoapOptionType::Size2 as u16,
119            CoapOption::Block1(_) => CoapOptionType::Block1 as u16,
120            CoapOption::Block2(_) => CoapOptionType::Block2 as u16,
121            CoapOption::HopLimit(_) => CoapOptionType::HopLimit as u16,
122            CoapOption::NoResponse(_) => CoapOptionType::NoResponse as u16,
123            CoapOption::ETag(_) => CoapOptionType::ETag as u16,
124            CoapOption::MaxAge(_) => CoapOptionType::MaxAge as u16,
125            CoapOption::Observe(_) => CoapOptionType::Observe as u16,
126            CoapOption::Oscore(_) => CoapOptionType::Oscore as u16,
127            CoapOption::Echo(_) => CoapOptionType::Echo as u16,
128            CoapOption::RTag(_) => CoapOptionType::RTag as u16,
129            CoapOption::QBlock1(_) => CoapOptionType::QBlock1 as u16,
130            CoapOption::QBlock2(_) => CoapOptionType::QBlock2 as u16,
131            CoapOption::Other(num, _) => *num,
132        }
133    }
134
135    /// Converts the option into a `Box<[u8]>` containing the value bytes.
136    pub fn into_value_bytes(self) -> Result<Box<[u8]>, OptionValueError> {
137        let num = self.number();
138        let bytes = match self {
139            CoapOption::IfMatch(val) => match val {
140                CoapMatch::ETag(tag) => tag,
141                CoapMatch::Empty => Box::new([]),
142            },
143            CoapOption::IfNoneMatch => Box::new([]),
144            CoapOption::UriHost(value) => value.into_boxed_str().into_boxed_bytes(),
145            CoapOption::UriPort(value) => encode_var_len_u16(value),
146            CoapOption::UriPath(value) => value.into_boxed_str().into_boxed_bytes(),
147            CoapOption::UriQuery(value) => value.into_boxed_str().into_boxed_bytes(),
148            CoapOption::LocationPath(value) => value.into_boxed_str().into_boxed_bytes(),
149            CoapOption::LocationQuery(value) => value.into_boxed_str().into_boxed_bytes(),
150            CoapOption::ProxyUri(value) => value.into_boxed_str().into_boxed_bytes(),
151            CoapOption::ProxyScheme(value) => value.into_boxed_str().into_boxed_bytes(),
152            CoapOption::ContentFormat(value) => encode_var_len_u16(value),
153            CoapOption::Accept(value) => encode_var_len_u16(value),
154            CoapOption::Size1(value) => encode_var_len_u32(value),
155            CoapOption::Size2(value) => encode_var_len_u32(value),
156            CoapOption::Block1(value) => encode_var_len_u32(value),
157            CoapOption::Block2(value) => encode_var_len_u32(value),
158            CoapOption::HopLimit(value) => encode_var_len_u16(value),
159            CoapOption::NoResponse(value) => encode_var_len_u8(value),
160            CoapOption::ETag(value) => value,
161            CoapOption::MaxAge(value) => encode_var_len_u32(value),
162            CoapOption::Observe(value) => encode_var_len_u32(value),
163            CoapOption::Oscore(value) => value,
164            CoapOption::Echo(value) => value,
165            CoapOption::RTag(value) => value,
166            CoapOption::QBlock1(value) => encode_var_len_u32(value),
167            CoapOption::QBlock2(value) => encode_var_len_u32(value),
168            CoapOption::Other(_num, data) => data,
169        };
170        if let Some(opt_type) = <CoapOptionType as FromPrimitive>::from_u16(num) {
171            if bytes.len() < opt_type.min_len() {
172                return Err(OptionValueError::TooShort);
173            } else if bytes.len() > opt_type.max_len() {
174                return Err(OptionValueError::TooLong);
175            }
176        }
177        Ok(bytes)
178    }
179
180    /// Converts this option into a raw coap_optlist_t instance, suitable for addition to a raw
181    /// coap_pdu_t.
182    pub(crate) fn into_optlist_entry(self) -> Result<*mut coap_optlist_t, OptionValueError> {
183        let num = self.number();
184        let value = self.into_value_bytes()?;
185        Ok(unsafe { coap_new_optlist(num, value.len(), value.as_ptr()) })
186    }
187
188    /// Attempts to convert a raw coap_optlist_t instance into a [CoapOption].
189    ///
190    /// # Errors
191    ///
192    /// Returns an [OptionValueError] if the provided `optlist_entry` has an invalid value.
193    ///
194    /// # Safety
195    ///
196    /// optlist_entry must be a valid coap_optlist_t instance whose `number`, `data` and `length`
197    /// fields describe a CoAP option number and the option value respectively.
198    pub(crate) unsafe fn from_optlist_entry(optlist_entry: &coap_optlist_t) -> Result<Self, OptionValueError> {
199        let value = Vec::from(std::slice::from_raw_parts(optlist_entry.data, optlist_entry.length));
200        Self::from_type_value(optlist_entry.number, value)
201    }
202
203    /// Convert coap_option_num_t and option value into a parsed [CoapOption] if possible.
204    fn from_type_value(type_: coap_option_num_t, value: Vec<u8>) -> Result<Self, OptionValueError> {
205        match CoapOptionType::try_from(type_) {
206            Ok(opt_type) => {
207                if opt_type.min_len() > value.len() {
208                    return Err(OptionValueError::TooShort);
209                } else if opt_type.max_len() < value.len() {
210                    return Err(OptionValueError::TooLong);
211                }
212                match opt_type {
213                    CoapOptionType::IfMatch => Ok(CoapOption::IfMatch(if value.is_empty() {
214                        CoapMatch::Empty
215                    } else {
216                        CoapMatch::ETag(value.into_boxed_slice())
217                    })),
218                    CoapOptionType::UriHost => Ok(CoapOption::UriHost(String::from_utf8(value)?)),
219                    CoapOptionType::ETag => Ok(CoapOption::ETag(value.into_boxed_slice())),
220                    CoapOptionType::IfNoneMatch => Ok(CoapOption::IfNoneMatch),
221                    CoapOptionType::UriPort => Ok(CoapOption::UriPort(decode_var_len_u16(value.as_slice()))),
222                    CoapOptionType::LocationPath => Ok(CoapOption::LocationPath(String::from_utf8(value)?)),
223                    CoapOptionType::UriPath => Ok(CoapOption::UriPath(String::from_utf8(value)?)),
224                    CoapOptionType::ContentFormat => {
225                        Ok(CoapOption::ContentFormat(decode_var_len_u16(value.as_slice())))
226                    },
227                    CoapOptionType::MaxAge => Ok(CoapOption::MaxAge(decode_var_len_u32(value.as_slice()))),
228                    CoapOptionType::UriQuery => Ok(CoapOption::UriQuery(String::from_utf8(value)?)),
229                    CoapOptionType::Accept => Ok(CoapOption::Accept(decode_var_len_u16(value.as_slice()))),
230                    CoapOptionType::LocationQuery => Ok(CoapOption::LocationQuery(String::from_utf8(value)?)),
231                    CoapOptionType::ProxyUri => Ok(CoapOption::ProxyUri(String::from_utf8(value)?)),
232                    CoapOptionType::ProxyScheme => Ok(CoapOption::ProxyScheme(String::from_utf8(value)?)),
233                    CoapOptionType::Size1 => Ok(CoapOption::Size1(decode_var_len_u32(value.as_slice()))),
234                    CoapOptionType::Size2 => Ok(CoapOption::Size2(decode_var_len_u32(value.as_slice()))),
235                    CoapOptionType::Block1 => Ok(CoapOption::Block1(decode_var_len_u32(value.as_slice()))),
236                    CoapOptionType::Block2 => Ok(CoapOption::Block2(decode_var_len_u32(value.as_slice()))),
237                    CoapOptionType::HopLimit => Ok(CoapOption::HopLimit(decode_var_len_u16(value.as_slice()))),
238                    CoapOptionType::NoResponse => {
239                        Ok(CoapOption::NoResponse(decode_var_len_u8(value.as_slice()) as NoResponse))
240                    },
241                    CoapOptionType::Observe => Ok(CoapOption::Observe(decode_var_len_u32(value.as_slice()))),
242                    CoapOptionType::Oscore => Ok(CoapOption::Oscore(value.into_boxed_slice())),
243                    CoapOptionType::Echo => Ok(CoapOption::Echo(value.into_boxed_slice())),
244                    CoapOptionType::RTag => Ok(CoapOption::RTag(value.into_boxed_slice())),
245                    CoapOptionType::QBlock1 => Ok(CoapOption::QBlock1(decode_var_len_u32(value.as_slice()))),
246                    CoapOptionType::QBlock2 => Ok(CoapOption::QBlock2(decode_var_len_u32(value.as_slice()))),
247                }
248            },
249            _ => Ok(CoapOption::Other(type_, value.into_boxed_slice())),
250        }
251    }
252}
253
254/// Constructs a path string from a [Vec] of strings containing the separate path components.
255pub(crate) fn construct_path_string(path_components: Vec<String>) -> String {
256    path_components.into_iter().fold(String::new(), |mut a: String, v| {
257        // Writing to a String _shouldn't_ cause an error.
258        // If it does, something is terribly wrong and we should panic.
259        write!(&mut a, "/{}", v).expect("unable to create path string");
260        a
261    })
262}
263
264/// Constructs a query string from a [Vec] of strings containing the separate query components.
265pub(crate) fn construct_query_string(query_components: Vec<String>) -> String {
266    let mut iter = query_components.iter();
267    let mut out_str = String::from("?");
268    if let Some(q) = iter.next() {
269        out_str = out_str + q
270    }
271    for q in iter {
272        out_str += format!("&{}", q).as_ref();
273    }
274    out_str
275}
276
277/// Interface for CoAP messages common between requests, responses and other messages.
278pub trait CoapMessageCommon {
279    /// Add the supplied CoAP option to this message.
280    fn add_option(&mut self, option: CoapOption) {
281        self.as_message_mut().options.push(option);
282    }
283
284    /// Clear the list of options that were added to this message using [add_option()](CoapMessageCommon::add_option()).
285    fn clear_options(&mut self) {
286        self.as_message_mut().options.clear();
287    }
288
289    /// Returns an iterator over the options contained in this message.
290    fn options_iter(&self) -> Iter<CoapOption> {
291        self.as_message().options.iter()
292    }
293
294    /// Returns the CoAP message type (confirmable, non-confirmable, acknowledgement, rst) of this message.
295    fn type_(&self) -> CoapMessageType {
296        self.as_message().type_
297    }
298
299    /// Sets the CoAP message type (confirmable, non-confirmable, acknowledgement, rst) of this message.
300    fn set_type_(&mut self, type_: CoapMessageType) {
301        self.as_message_mut().type_ = type_;
302    }
303
304    /// Returns the message code of this message.
305    /// To determine whether the message is a request or response, use [CoapMessageCode::try_from()]
306    /// and match for the enum variants.
307    fn code(&self) -> CoapMessageCode {
308        self.as_message().code
309    }
310
311    /// Sets the message code of this message.
312    fn set_code<C: Into<CoapMessageCode>>(&mut self, code: C) {
313        self.as_message_mut().code = code.into();
314    }
315
316    /// Returns the CoAP message ID for this message.
317    fn mid(&self) -> Option<CoapMessageId> {
318        self.as_message().mid
319    }
320
321    /// Sets the CoAP message ID for this message.
322    fn set_mid(&mut self, mid: Option<CoapMessageId>) {
323        self.as_message_mut().mid = mid;
324    }
325
326    /// Returns a reference to the data/body of this message.
327    fn data(&self) -> Option<&[u8]> {
328        self.as_message().data.as_ref().map(|v| v.as_ref())
329    }
330
331    /// Sets the data/body of this message.
332    fn set_data<D: Into<Box<[u8]>>>(&mut self, data: Option<D>) {
333        self.as_message_mut().data = data.map(Into::into);
334    }
335
336    /// Returns the message token.
337    fn token(&self) -> Option<&[u8]> {
338        self.as_message().token.as_ref().map(|v| v.as_ref())
339    }
340
341    /// Sets the message token.
342    ///
343    /// Note that [CoapSessionCommon::send_request()] will automatically set the token to a random
344    /// value if you don't.
345    fn set_token<D: Into<Box<[u8]>>>(&mut self, token: Option<D>) {
346        self.as_message_mut().token = token.map(Into::into);
347    }
348
349    /// Returns a reference to this message.
350    fn as_message(&self) -> &CoapMessage;
351    /// Returns a mutable reference to this message.
352    fn as_message_mut(&mut self) -> &mut CoapMessage;
353}
354
355/// Representation of a CoAP message.
356#[derive(Debug, Clone, PartialEq, Eq, Hash)]
357pub struct CoapMessage {
358    /// CoAP message type (CON, NON, ACK, RST).
359    type_: CoapMessageType,
360    /// CoAP message code (e.g. 2.00 Content or 4.04 Not Found)
361    code: CoapMessageCode,
362    /// ID of this message – unique to each message sent over this session.
363    mid: Option<CoapMessageId>,
364    /// List of CoAP options associated with this message.
365    options: Vec<CoapOption>,
366    /// CoAP message token – used for request-response-matching.
367    token: Option<Box<[u8]>>,
368    /// Message body of this message.
369    data: Option<Box<[u8]>>,
370}
371
372impl CoapMessage {
373    /// Creates a new CoAP message with the given type and code.
374    pub fn new(type_: CoapMessageType, code: CoapMessageCode) -> CoapMessage {
375        ensure_coap_started();
376        CoapMessage {
377            type_,
378            code,
379            mid: None,
380            options: Vec::new(),
381            token: None,
382            data: None,
383        }
384    }
385
386    /// Parses the given raw coap_pdu_t into a CoapMessage.
387    ///
388    /// # Safety
389    /// raw_pdu must point to a valid instance of coap_pdu_t.
390    pub unsafe fn from_raw_pdu(raw_pdu: *const coap_pdu_t) -> Result<CoapMessage, MessageConversionError> {
391        ensure_coap_started();
392        let mut option_iter = MaybeUninit::zeroed();
393        coap_option_iterator_init(raw_pdu, option_iter.as_mut_ptr(), std::ptr::null());
394        let mut option_iter = option_iter.assume_init();
395        let mut options = Vec::new();
396        while let Some(read_option) = coap_option_next(&mut option_iter).as_ref() {
397            options.push(CoapOption::from_raw_opt(option_iter.number, read_option).map_err(|e| {
398                MessageConversionError::InvalidOptionValue(CoapOptionType::try_from(option_iter.number).ok(), e)
399            })?);
400        }
401        let mut len: usize = 0;
402        let mut data = std::ptr::null();
403        coap_get_data(raw_pdu, &mut len, &mut data);
404        let data = match len {
405            0 => None,
406            len => Some(Vec::from(std::slice::from_raw_parts(data, len)).into_boxed_slice()),
407        };
408        let raw_token = coap_pdu_get_token(raw_pdu);
409        let token = Vec::from(std::slice::from_raw_parts(raw_token.s, raw_token.length));
410        Ok(CoapMessage {
411            type_: coap_pdu_get_type(raw_pdu).into(),
412            code: coap_pdu_get_code(raw_pdu).try_into().unwrap(),
413            mid: Some(coap_pdu_get_mid(raw_pdu)),
414            options,
415            token: Some(token.into_boxed_slice()),
416            data,
417        })
418    }
419
420    /// Converts this message into a raw PDU suitable for sending using the raw [coap_send()](libcoap_sys::coap_send())
421    /// function.
422    ///
423    /// The caller is responsible for freeing the returned PDU, either by calling [coap_send()](libcoap_sys::coap_send()) or
424    /// [coap_delete_pdu()].
425    pub fn into_raw_pdu<'a, S: CoapSessionCommon<'a> + ?Sized>(
426        mut self,
427        session: &S,
428    ) -> Result<*mut coap_pdu_t, MessageConversionError> {
429        let message = self.as_message_mut();
430
431        // SAFETY: all values are valid, cannot cause UB.
432        let pdu = unsafe {
433            coap_pdu_init(
434                message.type_.to_raw_pdu_type(),
435                message.code.to_raw_pdu_code(),
436                message.mid.ok_or(MessageConversionError::MissingMessageId)?,
437                session.max_pdu_size(),
438            )
439        };
440        if pdu.is_null() {
441            return Err(MessageConversionError::Unknown);
442        }
443        // SAFETY: We just checked that pdu is a valid pointer.
444        unsafe {
445            let result = self.apply_to_raw_pdu(pdu, session);
446            if result.is_err() {
447                coap_delete_pdu(pdu);
448            }
449            result
450        }
451    }
452
453    /// Applies this message to the given raw PDU.
454    ///
455    /// Note that existing parameters of the PDU are not explicitly removed. However, because this
456    /// function calls [coap_add_token()](libcoap_sys::coap_add_token()), any pre-added options and
457    /// data will probably be destroyed.
458    ///
459    /// If you don't have a raw pdu instance, use [CoapMessage::into_raw_pdu()] instead of this
460    /// function.
461    ///
462    /// # Panics
463    /// Panics if the provided `raw_pdu` is a null pointer.
464    ///
465    /// # Safety
466    /// raw_pdu must point to a valid mutable instance of coap_pdu_t.
467    pub(crate) unsafe fn apply_to_raw_pdu<'a, S: CoapSessionCommon<'a> + ?Sized>(
468        mut self,
469        raw_pdu: *mut coap_pdu_t,
470        session: &S,
471    ) -> Result<*mut coap_pdu_t, MessageConversionError> {
472        assert!(!raw_pdu.is_null(), "attempted to apply CoapMessage to null pointer");
473        coap_pdu_set_type(raw_pdu, self.type_.to_raw_pdu_type());
474        coap_pdu_set_code(raw_pdu, self.code.to_raw_pdu_code());
475        let message = self.as_message_mut();
476        let token: &[u8] = message.token.as_ref().ok_or(MessageConversionError::MissingToken)?;
477        if coap_add_token(raw_pdu, token.len(), token.as_ptr()) == 0 {
478            return Err(MessageConversionError::Unknown);
479        }
480        let mut optlist = None;
481        let option_iter = std::mem::take(&mut message.options).into_iter();
482        for option in option_iter {
483            let optnum = option.number();
484            let entry = option
485                .into_optlist_entry()
486                .map_err(|e| MessageConversionError::InvalidOptionValue(CoapOptionType::try_from(optnum).ok(), e))?;
487            if entry.is_null() {
488                if let Some(optlist) = optlist {
489                    coap_delete_optlist(optlist);
490                    return Err(MessageConversionError::Unknown);
491                }
492            }
493            match optlist {
494                None => {
495                    optlist = Some(entry);
496                },
497                Some(mut optlist) => {
498                    coap_insert_optlist(&mut optlist, entry);
499                },
500            }
501        }
502        if let Some(mut optlist) = optlist {
503            let optlist_add_success = coap_add_optlist_pdu(raw_pdu, &mut optlist);
504            coap_delete_optlist(optlist);
505            if optlist_add_success == 0 {
506                return Err(MessageConversionError::Unknown);
507            }
508        }
509        if let Some(data) = message.data.take() {
510            match message.code {
511                CoapMessageCode::Empty => return Err(MessageConversionError::DataInEmptyMessage),
512                CoapMessageCode::Request(_) => {
513                    let len = data.len();
514                    let box_ptr = Box::into_raw(data);
515                    coap_add_data_large_request(
516                        session.raw_session_mut(),
517                        raw_pdu,
518                        len,
519                        box_ptr as *mut u8,
520                        Some(large_data_cleanup_handler),
521                        box_ptr as *mut c_void,
522                    );
523                },
524                CoapMessageCode::Response(_) => {
525                    // TODO blockwise transfer here as well.
526                    // (for some reason libcoap needs the request PDU here?)
527                    let data: &[u8] = data.as_ref();
528                    if coap_add_data(raw_pdu, data.len(), data.as_ptr()) == 0 {
529                        return Err(MessageConversionError::Unknown);
530                    }
531                },
532            }
533        }
534        Ok(raw_pdu)
535    }
536}
537
538impl CoapMessageCommon for CoapMessage {
539    fn as_message(&self) -> &CoapMessage {
540        self
541    }
542
543    fn as_message_mut(&mut self) -> &mut CoapMessage {
544        self
545    }
546}
547
548impl From<CoapRequest> for CoapMessage {
549    fn from(val: CoapRequest) -> Self {
550        val.into_message()
551    }
552}
553
554impl From<CoapResponse> for CoapMessage {
555    fn from(val: CoapResponse) -> Self {
556        val.into_message()
557    }
558}
559
560/// Handler provided to libcoap to cleanup large message bodies.
561unsafe extern "C" fn large_data_cleanup_handler(_session: *mut coap_session_t, app_ptr: *mut c_void) {
562    std::mem::drop(Box::from_raw(app_ptr as *mut u8));
563}