1use std::str::FromStr;
12
13use crate::{
14 error::{MessageConversionError, MessageTypeError, OptionValueError},
15 message::{construct_path_string, construct_query_string, CoapMessage, CoapMessageCommon, CoapOption},
16 protocol::{
17 CoapMatch, CoapMessageCode, CoapMessageType, CoapOptionType, CoapRequestCode, ContentFormat, ETag, HopLimit,
18 NoResponse, Observe,
19 },
20 session::CoapSessionCommon,
21 types::{CoapUri, CoapUriScheme},
22};
23
24#[derive(Debug, Clone, Eq, PartialEq)]
29pub struct CoapRequest {
30 pdu: CoapMessage,
31 uri: CoapUri,
32 accept: Option<ContentFormat>,
33 etag: Option<Vec<ETag>>,
34 if_match: Option<Vec<CoapMatch>>,
35 content_format: Option<ContentFormat>,
36 if_none_match: bool,
37 hop_limit: Option<HopLimit>,
38 no_response: Option<NoResponse>,
39 observe: Option<Observe>,
40}
41
42impl CoapRequest {
43 pub fn new(type_: CoapMessageType, code: CoapRequestCode, uri: CoapUri) -> Result<CoapRequest, MessageTypeError> {
49 match type_ {
50 CoapMessageType::Con | CoapMessageType::Non => {},
51 v => return Err(MessageTypeError::InvalidForMessageCode(v)),
52 }
53 Ok(CoapRequest {
54 pdu: CoapMessage::new(type_, code.into()),
55 uri,
56 accept: None,
57 etag: None,
58 if_match: None,
59 content_format: None,
60 if_none_match: false,
61 hop_limit: None,
62 no_response: None,
63 observe: None,
64 })
65 }
66
67 pub fn accept(&self) -> Option<ContentFormat> {
69 self.accept
70 }
71
72 pub fn set_accept(&mut self, accept: Option<ContentFormat>) {
79 self.accept = accept
80 }
81
82 pub fn etag(&self) -> Option<&Vec<ETag>> {
84 self.etag.as_ref()
85 }
86
87 pub fn set_etag(&mut self, etag: Option<Vec<ETag>>) {
97 self.etag = etag
98 }
99
100 pub fn if_match(&self) -> Option<&Vec<CoapMatch>> {
102 self.if_match.as_ref()
103 }
104
105 pub fn set_if_match(&mut self, if_match: Option<Vec<CoapMatch>>) {
113 self.if_match = if_match
114 }
115
116 pub fn content_format(&self) -> Option<ContentFormat> {
118 self.content_format
119 }
120
121 pub fn set_content_format(&mut self, content_format: Option<ContentFormat>) {
130 self.content_format = content_format;
131 }
132
133 pub fn if_none_match(&self) -> bool {
135 self.if_none_match
136 }
137
138 pub fn set_if_none_match(&mut self, if_none_match: bool) {
148 self.if_none_match = if_none_match
149 }
150
151 pub fn hop_limit(&self) -> Option<HopLimit> {
153 self.hop_limit
154 }
155
156 pub fn set_hop_limit(&mut self, hop_limit: Option<HopLimit>) {
164 self.hop_limit = hop_limit;
165 }
166
167 pub fn no_response(&self) -> Option<NoResponse> {
169 self.no_response
170 }
171
172 pub fn set_no_response(&mut self, no_response: Option<NoResponse>) {
180 self.no_response = no_response;
181 }
182
183 pub fn observe(&self) -> Option<Observe> {
185 self.observe
186 }
187
188 pub fn set_observe(&mut self, observe: Option<Observe>) {
196 self.observe = observe;
197 }
198
199 pub fn uri(&self) -> &CoapUri {
201 &self.uri
202 }
203
204 pub fn from_message<'a>(
208 mut pdu: CoapMessage,
209 session: &impl CoapSessionCommon<'a>,
210 ) -> Result<CoapRequest, MessageConversionError> {
211 let mut host = None;
212 let mut port = None;
213 let mut path = None;
214 let mut query = None;
215 let mut proxy_scheme = None;
216 let mut proxy_uri = None;
217 let mut content_format = None;
218 let mut etag = None;
219 let mut if_match = None;
220 let mut if_none_match = false;
221 let mut accept = None;
222 let mut hop_limit = None;
223 let mut no_response = None;
224 let mut observe = None;
225 let mut additional_opts = Vec::new();
226 for option in pdu.options_iter() {
227 match option {
228 CoapOption::IfMatch(value) => {
229 if if_match.is_none() {
230 if_match = Some(Vec::new());
231 }
232 if_match.as_mut().unwrap().push(value.clone());
233 },
234 CoapOption::IfNoneMatch => {
235 if if_none_match {
236 return Err(MessageConversionError::NonRepeatableOptionRepeated(
237 CoapOptionType::IfNoneMatch,
238 ));
239 }
240 if_none_match = true;
241 },
242 CoapOption::UriHost(value) => {
243 if host.is_some() {
244 return Err(MessageConversionError::NonRepeatableOptionRepeated(
245 CoapOptionType::UriHost,
246 ));
247 }
248 host = Some(value.clone().into_bytes());
249 },
250 CoapOption::UriPort(value) => {
251 if port.is_some() {
252 return Err(MessageConversionError::NonRepeatableOptionRepeated(
253 CoapOptionType::UriPort,
254 ));
255 }
256 port = Some(*value);
257 },
258 CoapOption::UriPath(value) => {
259 if path.is_none() {
260 path = Some(Vec::new());
261 }
262 path.as_mut().unwrap().push(value.clone());
263 },
264 CoapOption::UriQuery(value) => {
265 if query.is_none() {
266 query = Some(Vec::new());
267 }
268 query.as_mut().unwrap().push(value.clone());
269 },
270 CoapOption::LocationPath(_) => {
271 return Err(MessageConversionError::InvalidOptionForMessageType(
272 CoapOptionType::LocationPath,
273 ));
274 },
275 CoapOption::LocationQuery(_) => {
276 return Err(MessageConversionError::InvalidOptionForMessageType(
277 CoapOptionType::LocationQuery,
278 ));
279 },
280 CoapOption::ProxyUri(uri) => {
281 if proxy_uri.is_some() {
282 return Err(MessageConversionError::NonRepeatableOptionRepeated(
283 CoapOptionType::ProxyUri,
284 ));
285 }
286 proxy_uri = Some(uri.clone())
287 },
288 CoapOption::ProxyScheme(scheme) => {
289 if proxy_scheme.is_some() {
290 return Err(MessageConversionError::NonRepeatableOptionRepeated(
291 CoapOptionType::ProxyScheme,
292 ));
293 }
294 proxy_scheme = Some(CoapUriScheme::from_str(scheme)?)
295 },
296 CoapOption::ContentFormat(cformat) => {
297 if content_format.is_some() {
298 return Err(MessageConversionError::NonRepeatableOptionRepeated(
299 CoapOptionType::ContentFormat,
300 ));
301 }
302 content_format = Some(*cformat)
303 },
304 CoapOption::Accept(value) => {
305 if accept.is_some() {
306 return Err(MessageConversionError::NonRepeatableOptionRepeated(
307 CoapOptionType::Accept,
308 ));
309 }
310 accept = Some(*value);
311 },
312 CoapOption::Size1(_) => {},
314 CoapOption::Size2(_) => {
315 return Err(MessageConversionError::InvalidOptionForMessageType(
316 CoapOptionType::Size2,
317 ));
318 },
319 CoapOption::Block1(_) => {},
321 CoapOption::Block2(_) => {
322 return Err(MessageConversionError::InvalidOptionForMessageType(
323 CoapOptionType::Block2,
324 ));
325 },
326 CoapOption::QBlock1(_) => {},
328 CoapOption::QBlock2(_) => {},
329 CoapOption::HopLimit(value) => {
330 if hop_limit.is_some() {
331 return Err(MessageConversionError::NonRepeatableOptionRepeated(
332 CoapOptionType::HopLimit,
333 ));
334 }
335 hop_limit = Some(*value);
336 },
337 CoapOption::NoResponse(value) => {
338 if no_response.is_some() {
339 return Err(MessageConversionError::NonRepeatableOptionRepeated(
340 CoapOptionType::NoResponse,
341 ));
342 }
343 no_response = Some(*value);
344 },
345 CoapOption::ETag(value) => {
346 if etag.is_none() {
347 etag = Some(Vec::new());
348 }
349 etag.as_mut().unwrap().push(value.clone());
350 },
351 CoapOption::MaxAge(_value) => {
352 return Err(MessageConversionError::InvalidOptionForMessageType(
353 CoapOptionType::MaxAge,
354 ));
355 },
356 CoapOption::Observe(value) => {
357 if observe.is_some() {
358 return Err(MessageConversionError::NonRepeatableOptionRepeated(
359 CoapOptionType::MaxAge,
360 ));
361 }
362 observe = Some(*value);
363 },
364 CoapOption::Echo(_) => {},
366 CoapOption::RTag(_) => {},
369 CoapOption::Oscore(_v) => {},
372 CoapOption::Other(n, v) => {
374 additional_opts.push(CoapOption::Other(*n, v.clone()));
375 },
376 }
377 }
378 pdu.clear_options();
379 for opt in additional_opts {
380 pdu.add_option(opt);
381 }
382 if proxy_scheme.is_some() && proxy_uri.is_some() {
383 return Err(MessageConversionError::InvalidOptionCombination(
384 CoapOptionType::ProxyScheme,
385 CoapOptionType::ProxyUri,
386 ));
387 }
388 let uri = if let Some(v) = proxy_uri {
389 CoapUri::try_from_str_proxy(v.as_str())
390 } else {
391 let path_str = path.map(construct_path_string);
392 let query_str = query.map(construct_query_string);
393
394 match proxy_scheme {
395 Some(scheme) => CoapUri::new_proxy(
396 scheme,
397 host.as_deref().unwrap_or(&[]),
398 port.unwrap_or(0),
399 path_str.as_ref().map(|v| v.as_bytes()),
400 query_str.as_ref().map(|v| v.as_bytes()),
401 ),
402 None => CoapUri::new(
403 session.proto().into(),
404 host.as_deref().unwrap_or(&[]),
405 port.unwrap_or(0),
406 path_str.as_ref().map(|v| v.as_bytes()),
407 query_str.as_ref().map(|v| v.as_bytes()),
408 ),
409 }
410 }
411 .map_err(|e| MessageConversionError::InvalidOptionValue(None, OptionValueError::UriParsing(e)))?;
412
413 Ok(CoapRequest {
414 pdu,
415 uri,
416 accept,
417 etag,
418 if_match,
419 content_format,
420 if_none_match,
421 hop_limit,
422 no_response,
423 observe,
424 })
425 }
426
427 pub fn into_message(mut self) -> CoapMessage {
429 if self.uri.is_proxy() {
430 self.pdu.add_option(CoapOption::ProxyScheme(
431 self.uri.scheme().expect("Parsed CoAP URI must have scheme").to_string(),
432 ))
433 }
434 self.uri.into_options().into_iter().for_each(|v| self.pdu.add_option(v));
435 if let Some(accept) = self.accept {
436 self.pdu.add_option(CoapOption::Accept(accept))
437 }
438 if let Some(etags) = self.etag {
439 for etag in etags {
440 self.pdu.add_option(CoapOption::ETag(etag));
441 }
442 }
443 if let Some(if_match) = self.if_match {
444 for match_expr in if_match {
445 self.pdu.add_option(CoapOption::IfMatch(match_expr));
446 }
447 }
448 if let Some(content_format) = self.content_format {
449 self.pdu.add_option(CoapOption::ContentFormat(content_format));
450 }
451 if self.if_none_match {
452 self.pdu.add_option(CoapOption::IfNoneMatch);
453 }
454 if let Some(hop_limit) = self.hop_limit {
455 self.pdu.add_option(CoapOption::HopLimit(hop_limit));
456 }
457 if let Some(no_response) = self.no_response {
458 self.pdu.add_option(CoapOption::NoResponse(no_response));
459 }
460 if let Some(observe) = self.observe {
461 self.pdu.add_option(CoapOption::Observe(observe));
462 }
463 self.pdu
464 }
465}
466
467impl CoapMessageCommon for CoapRequest {
468 fn set_code<C: Into<CoapMessageCode>>(&mut self, code: C) {
473 match code.into() {
474 CoapMessageCode::Request(req) => self.pdu.set_code(CoapMessageCode::Request(req)),
475 CoapMessageCode::Response(_) | CoapMessageCode::Empty => {
476 panic!("attempted to set message code of request to value that is not a request code")
477 },
478 }
479 }
480
481 fn as_message(&self) -> &CoapMessage {
482 &self.pdu
483 }
484
485 fn as_message_mut(&mut self) -> &mut CoapMessage {
486 &mut self.pdu
487 }
488}