1use alloc::{
2 string::{String, ToString},
3 vec::Vec,
4};
5use core::convert::TryFrom;
6
7use crate::{
8 error::{HandlingError, IncompatibleOptionValueFormat, InvalidObserve},
9 header::{MessageClass, RequestType as Method},
10 option_value::OptionValueString,
11 packet::{CoapOption, ObserveOption, Packet},
12 response::CoapResponse,
13 ContentFormat,
14};
15
16#[derive(Clone, Debug, PartialEq)]
18pub struct CoapRequest<Endpoint> {
19 pub message: Packet,
20 pub response: Option<CoapResponse>,
21 pub source: Option<Endpoint>,
22}
23
24impl<Endpoint> CoapRequest<Endpoint> {
25 pub fn new() -> CoapRequest<Endpoint> {
27 Default::default()
28 }
29
30 pub fn from_packet(
32 packet: Packet,
33 source: Endpoint,
34 ) -> CoapRequest<Endpoint> {
35 CoapRequest {
36 response: CoapResponse::new(&packet),
37 message: packet,
38 source: Some(source),
39 }
40 }
41
42 pub fn apply_from_error(&mut self, error: HandlingError) -> bool {
45 if let Some(reply) = &mut self.response {
46 if let Some(code) = error.code {
47 let message = &mut reply.message;
48 message.header.code = MessageClass::Response(code);
49 message.set_content_format(ContentFormat::TextPlain);
50 message.payload = error.message.into_bytes();
51 return true;
52 }
53 }
54 false
55 }
56
57 pub fn set_method(&mut self, method: Method) {
59 self.message.header.code = MessageClass::Request(method);
60 }
61
62 pub fn get_method(&self) -> &Method {
64 match self.message.header.code {
65 MessageClass::Request(Method::Get) => &Method::Get,
66 MessageClass::Request(Method::Post) => &Method::Post,
67 MessageClass::Request(Method::Put) => &Method::Put,
68 MessageClass::Request(Method::Delete) => &Method::Delete,
69 MessageClass::Request(Method::Fetch) => &Method::Fetch,
70 MessageClass::Request(Method::Patch) => &Method::Patch,
71 MessageClass::Request(Method::IPatch) => &Method::IPatch,
72 _ => &Method::UnKnown,
73 }
74 }
75
76 pub fn set_path(&mut self, path: &str) {
78 self.message.clear_option(CoapOption::UriPath);
79
80 let segs = path.split('/');
81 for (i, s) in segs.enumerate() {
82 if i == 0 && s.is_empty() {
83 continue;
84 }
85
86 self.message
87 .add_option(CoapOption::UriPath, s.as_bytes().to_vec());
88 }
89 }
90
91 pub fn get_path(&self) -> String {
93 match self.message.get_option(CoapOption::UriPath) {
94 Some(options) => {
95 let mut vec = Vec::new();
96 for option in options.iter() {
97 if let Ok(seg) = core::str::from_utf8(option) {
98 vec.push(seg);
99 }
100 }
101 vec.join("/")
102 }
103 _ => "".to_string(),
104 }
105 }
106
107 pub fn get_path_as_vec(
110 &self,
111 ) -> Result<Vec<String>, IncompatibleOptionValueFormat> {
112 self.message
113 .get_options_as::<OptionValueString>(CoapOption::UriPath)
114 .map_or_else(
115 || Ok(vec![]),
116 |paths| {
117 paths
118 .into_iter()
119 .map(|segment_result| {
120 segment_result.map(|segment| segment.0)
121 })
122 .collect::<Result<Vec<_>, _>>()
123 },
124 )
125 }
126
127 pub fn get_observe_flag(
130 &self,
131 ) -> Option<Result<ObserveOption, InvalidObserve>> {
132 self.message.get_observe_value().map(|observe| {
133 observe
134 .map(|value| usize::try_from(value).unwrap())
135 .map_or(Err(InvalidObserve), |value| {
136 ObserveOption::try_from(value)
137 })
138 })
139 }
140
141 pub fn set_observe_flag(&mut self, flag: ObserveOption) {
143 let value = u32::try_from(usize::from(flag)).unwrap();
144 self.message.set_observe_value(value);
145 }
146}
147
148impl<Endpoint> Default for CoapRequest<Endpoint> {
149 fn default() -> Self {
150 CoapRequest {
151 response: None,
152 message: Packet::new(),
153 source: None,
154 }
155 }
156}
157
158#[cfg(test)]
159mod test {
160 use super::*;
161 use crate::header::MessageType;
162
163 struct Endpoint(String);
164
165 #[test]
166 fn test_request_create() {
167 let mut packet = Packet::new();
168 let mut request1: CoapRequest<Endpoint> = CoapRequest::new();
169
170 packet.set_token(vec![0x17, 0x38]);
171 request1.message.set_token(vec![0x17, 0x38]);
172
173 packet.add_option(CoapOption::UriPath, b"test-interface".to_vec());
174 request1
175 .message
176 .add_option(CoapOption::UriPath, b"test-interface".to_vec());
177
178 packet.header.message_id = 42;
179 request1.message.header.message_id = 42;
180
181 packet.header.set_version(2);
182 request1.message.header.set_version(2);
183
184 packet.header.set_type(MessageType::Confirmable);
185 request1.message.header.set_type(MessageType::Confirmable);
186
187 packet.header.set_code("0.04");
188 request1.message.header.set_code("0.04");
189
190 let endpoint = Endpoint(String::from("127.0.0.1:1234"));
191 let request2 = CoapRequest::from_packet(packet, endpoint);
192
193 assert_eq!(
194 request1.message.to_bytes().unwrap(),
195 request2.message.to_bytes().unwrap()
196 );
197 }
198
199 #[test]
200 fn test_method() {
201 let mut request: CoapRequest<Endpoint> = CoapRequest::new();
202
203 request.message.header.set_code("0.01");
204 assert_eq!(&Method::Get, request.get_method());
205
206 request.message.header.set_code("0.02");
207 assert_eq!(&Method::Post, request.get_method());
208
209 request.message.header.set_code("0.03");
210 assert_eq!(&Method::Put, request.get_method());
211
212 request.message.header.set_code("0.04");
213 assert_eq!(&Method::Delete, request.get_method());
214
215 request.message.header.set_code("0.06");
216 assert_eq!(&Method::Patch, request.get_method());
217
218 request.set_method(Method::Get);
219 assert_eq!("0.01", request.message.header.get_code());
220
221 request.set_method(Method::Post);
222 assert_eq!("0.02", request.message.header.get_code());
223
224 request.set_method(Method::Put);
225 assert_eq!("0.03", request.message.header.get_code());
226
227 request.set_method(Method::Delete);
228 assert_eq!("0.04", request.message.header.get_code());
229
230 request.set_method(Method::IPatch);
231 assert_eq!("0.07", request.message.header.get_code());
232 }
233
234 #[test]
235 fn test_path() {
236 let mut request: CoapRequest<Endpoint> = CoapRequest::new();
237
238 let path = "test-interface";
239 request
240 .message
241 .add_option(CoapOption::UriPath, path.as_bytes().to_vec());
242 assert_eq!(path, request.get_path());
243
244 let path2 = "test-interface2";
245 request.set_path(path2);
246 assert_eq!(
247 path2.as_bytes().to_vec(),
248 *request
249 .message
250 .get_option(CoapOption::UriPath)
251 .unwrap()
252 .front()
253 .unwrap()
254 );
255
256 request.set_path("/test-interface2");
257 assert_eq!(
258 path2.as_bytes().to_vec(),
259 *request
260 .message
261 .get_option(CoapOption::UriPath)
262 .unwrap()
263 .front()
264 .unwrap()
265 );
266
267 let path3 = "test-interface2/";
268 request.set_path(path3);
269 assert_eq!(path3, request.get_path());
270 }
271
272 #[test]
273 fn test_path_as_vec() {
274 let mut request: CoapRequest<Endpoint> = CoapRequest::new();
275
276 let path = "test-interface";
277 request
278 .message
279 .add_option(CoapOption::UriPath, path.as_bytes().to_vec());
280 assert_eq!(Ok(vec![path.to_string()]), request.get_path_as_vec());
281
282 request.set_path("/test-interface/second/third");
283 assert_eq!(
284 Ok(["test-interface", "second", "third"]
285 .map(|x| x.to_string())
286 .to_vec()),
287 request.get_path_as_vec()
288 );
289
290 let bogus_path: Vec<u8> = vec![0xfe, 0xfe, 0xff, 0xff];
291 request.message.clear_option(CoapOption::UriPath);
292 request.message.add_option(CoapOption::UriPath, bogus_path);
293 request
294 .get_path_as_vec()
295 .expect_err("must be a utf-8 decoding error");
296 }
297
298 #[test]
299 fn test_unknown_observe_flag() {
300 let mut request: CoapRequest<Endpoint> = CoapRequest::new();
301
302 request.message.set_observe_value(32);
303 let expected = Some(Err(InvalidObserve));
304 let actual = request.get_observe_flag();
305 assert_eq!(actual, expected);
306 }
307
308 #[test]
309 fn test_garbage_in_observe_field() {
310 let mut request: CoapRequest<Endpoint> = CoapRequest::new();
311
312 request
313 .message
314 .add_option(CoapOption::Observe, b"bunch of nonsense".to_vec());
315 let expected = Some(Err(InvalidObserve));
316 let actual = request.get_observe_flag();
317 assert_eq!(actual, expected);
318 }
319}