diff --git a/.gitignore b/.gitignore index a9d37c560c6ab8d4afbf47eda643e8c42e857716..5057d9ef2736ef163008cf7b4baca67f1e866732 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,3 @@ target Cargo.lock +*.rs.bk diff --git a/README.md b/README.md index 8c46e80ce04fdd7d8ca23656f33f289f77e8c936..8c6d91a3dbeedb58403bf4e4e2f15d78d6ec1116 100644 --- a/README.md +++ b/README.md @@ -33,10 +33,9 @@ extern crate coap; extern crate coap; use std::io; -use coap::packet::*; -use coap::{CoAPServer, CoAPClient}; +use coap::{CoAPResponse, CoAPRequest}; -fn request_handler(req: Packet, response: Option<Packet>) -> Option<Packet> { +fn request_handler(req: CoAPRequest) -> Option<CoAPResponse> { println!("Receive request: {:?}", req); response } @@ -59,15 +58,14 @@ fn main() { ```rust extern crate coap; -use coap::packet::*; -use coap::CoAPClient; +use coap::{CoAPClient, CoAPResponse}; fn main() { let url = "coap://127.0.0.1:5683/Rust"; println!("Client request: {}", url); - let response: Packet = CoAPClient::request(url).unwrap(); - println!("Server reply: {}", String::from_utf8(response.payload).unwrap()); + let response: CoAPResponse = CoAPClient::request(url).unwrap(); + println!("Server reply: {}", String::from_utf8(response.message.payload).unwrap()); } ``` diff --git a/benches/client.rs b/benches/client.rs index 7fa45f2ab7f9408a06576a0c5adbc7fc5158d215..6ba830ba2cebde2709ce78daf55411aaa3ef5819 100644 --- a/benches/client.rs +++ b/benches/client.rs @@ -13,11 +13,11 @@ fn bench_client_request(b: &mut Bencher) { let request = "test"; let mut packet = Packet::new(); packet.header.set_version(1); - packet.header.set_type(PacketType::Confirmable); + packet.header.set_type(MessageType::Confirmable); packet.header.set_code("0.01"); packet.header.set_message_id(1); packet.set_token(vec!(0x51, 0x55, 0x77, 0xE8)); - packet.add_option(OptionType::UriPath, request.to_string().into_bytes()); + packet.add_option(CoAPOption::UriPath, request.to_string().into_bytes()); b.iter(|| { let client = CoAPClient::new(addr).unwrap(); diff --git a/examples/client.rs b/examples/client.rs index 865a56c37a4838d4bd8360b714caa5e16c3824cb..fd28755df58efad76898d5f85b07f6e11e79af83 100644 --- a/examples/client.rs +++ b/examples/client.rs @@ -1,34 +1,34 @@ extern crate coap; use std::io::ErrorKind; -use coap::packet::*; -use coap::CoAPClient; +use coap::{CoAPClient, CoAPRequest, IsMessage, MessageType, CoAPOption}; fn main() { - let addr = "127.0.0.1:5683"; - let request = "test"; + let addr = "127.0.0.1:5683"; + let endpoint = "test"; - let client = CoAPClient::new(addr).unwrap(); - let mut packet = Packet::new(); - packet.header.set_version(1); - packet.header.set_type(PacketType::Confirmable); - packet.header.set_code("0.01"); - packet.header.set_message_id(1); - packet.set_token(vec!(0x51, 0x55, 0x77, 0xE8)); - packet.add_option(OptionType::UriPath, request.to_string().into_bytes()); - client.send(&packet).unwrap(); - println!("Client request: coap://{}/{}", addr, request); + let client = CoAPClient::new(addr).unwrap(); + let mut request = CoAPRequest::new(); + request.set_version(1); + request.set_type(MessageType::Confirmable); + request.set_code("0.01"); + request.set_message_id(1); + request.set_token(vec![0x51, 0x55, 0x77, 0xE8]); + request.add_option(CoAPOption::UriPath, endpoint.to_string().into_bytes()); + client.send(&request).unwrap(); + println!("Client request: coap://{}/{}", addr, endpoint); - match client.receive() { - Ok(response) => { - println!("Server reply: {}", String::from_utf8(response.payload).unwrap()); - }, - Err(e) => { - match e.kind() { - ErrorKind::WouldBlock => println!("Request timeout"), // Unix - ErrorKind::TimedOut => println!("Request timeout"), // Windows - _ => println!("Request error: {:?}", e), - } - } - } + match client.receive() { + Ok(response) => { + println!("Server reply: {}", + String::from_utf8(response.message.payload).unwrap()); + } + Err(e) => { + match e.kind() { + ErrorKind::WouldBlock => println!("Request timeout"), // Unix + ErrorKind::TimedOut => println!("Request timeout"), // Windows + _ => println!("Request error: {:?}", e), + } + } + } } diff --git a/examples/client_and_server.rs b/examples/client_and_server.rs index 1d5fb6e0baf80e296e609ff41750810e8670ec93..0e474bdfca83e7e5fa7e4e832e7241752e496f82 100644 --- a/examples/client_and_server.rs +++ b/examples/client_and_server.rs @@ -1,27 +1,28 @@ extern crate coap; -use coap::packet::*; -use coap::{CoAPServer, CoAPClient}; +use coap::{CoAPServer, CoAPClient, CoAPRequest, CoAPResponse, CoAPOption}; +use coap::IsMessage; -fn request_handler(req: Packet, response: Option<Packet>) -> Option<Packet> { - let uri_path = req.get_option(OptionType::UriPath).unwrap(); +fn request_handler(request: CoAPRequest) -> Option<CoAPResponse> { + let uri_path = request.get_option(CoAPOption::UriPath).unwrap(); - return match response { - Some(mut packet) => { - packet.set_payload(uri_path.front().unwrap().clone()); - Some(packet) - }, - _ => None + return match request.response { + Some(mut response) => { + response.set_payload(uri_path.front().unwrap().clone()); + Some(response) + } + _ => None, }; } fn main() { - let mut server = CoAPServer::new("127.0.0.1:5683").unwrap(); - server.handle(request_handler).unwrap(); + let mut server = CoAPServer::new("127.0.0.1:5683").unwrap(); + server.handle(request_handler).unwrap(); - let url = "coap://127.0.0.1:5683/Rust"; - println!("Client request: {}", url); + let url = "coap://127.0.0.1:5683/Rust"; + println!("Client request: {}", url); - let response: Packet = CoAPClient::request(url).unwrap(); - println!("Server reply: {}", String::from_utf8(response.payload).unwrap()); + let response: CoAPResponse = CoAPClient::request(url).unwrap(); + println!("Server reply: {}", + String::from_utf8(response.message.payload).unwrap()); } diff --git a/examples/server.rs b/examples/server.rs index 887e4ae3b282a3d2dee641751d1304bc59c64054..28d13029087bba6fb4fc2dd631c2019cd219a1fc 100644 --- a/examples/server.rs +++ b/examples/server.rs @@ -1,29 +1,28 @@ extern crate coap; use std::io; -use coap::packet::*; -use coap::CoAPServer; +use coap::{CoAPServer, CoAPResponse, CoAPRequest, IsMessage}; -fn request_handler(_: Packet, response: Option<Packet>) -> Option<Packet> { - return match response { - Some(mut packet) => { - packet.set_payload(b"OK".to_vec()); - Some(packet) +fn request_handler(request: CoAPRequest) -> Option<CoAPResponse> { + return match request.response { + Some(mut message) => { + message.set_payload(b"OK".to_vec()); + Some(message) }, _ => None }; } fn main() { - let addr = "127.0.0.1:5683"; + let addr = "127.0.0.1:5683"; - let mut server = CoAPServer::new(addr).unwrap(); - server.handle(request_handler).unwrap(); + let mut server = CoAPServer::new(addr).unwrap(); + server.handle(request_handler).unwrap(); - println!("Server up on {}", addr); - println!("Press any key to stop..."); + println!("Server up on {}", addr); + println!("Press any key to stop..."); - io::stdin().read_line(&mut String::new()).unwrap(); + io::stdin().read_line(&mut String::new()).unwrap(); - println!("Server shutdown"); + println!("Server shutdown"); } diff --git a/src/client.rs b/src/client.rs index e5c5c89c14acb7d29a0c4a38a17de88f338351ba..dcac775fa48d93f49c7c68fafdfdce86b114a259 100644 --- a/src/client.rs +++ b/src/client.rs @@ -4,7 +4,11 @@ use std::time::Duration; use url::{UrlParser, SchemeType}; use num; use rand::{thread_rng, random, Rng}; -use packet::{Packet, PacketType, OptionType}; +use message::packet::{Packet, CoAPOption}; +use message::header::MessageType; +use message::response::CoAPResponse; +use message::request::CoAPRequest; +use message::IsMessage; const DEFAULT_RECEIVE_TIMEOUT: u64 = 5; // 5s @@ -46,19 +50,19 @@ impl CoAPClient { } /// Execute a request with the coap url and a specific timeout. Default timeout is 5s. - pub fn request_with_timeout(url: &str, timeout: Option<Duration>) -> Result<Packet> { + pub fn request_with_timeout(url: &str, timeout: Option<Duration>) -> Result<CoAPResponse> { let mut url_parser = UrlParser::new(); url_parser.scheme_type_mapper(Self::coap_scheme_type_mapper); match url_parser.parse(url) { Ok(url_params) => { - let mut packet = Packet::new(); - packet.header.set_version(1); - packet.header.set_type(PacketType::Confirmable); - packet.header.set_code("0.01"); + let mut packet = CoAPRequest::new(); + packet.set_version(1); + packet.set_type(MessageType::Confirmable); + packet.set_code("0.01"); let message_id = thread_rng().gen_range(0, num::pow(2u32, 16)) as u16; - packet.header.set_message_id(message_id); + packet.set_message_id(message_id); let mut token: Vec<u8> = vec![1, 1, 1, 1]; for x in token.iter_mut() { @@ -74,7 +78,7 @@ impl CoAPClient { if let Some(path) = url_params.path() { for p in path.iter() { - packet.add_option(OptionType::UriPath, p.clone().into_bytes().to_vec()); + packet.add_option(CoAPOption::UriPath, p.clone().into_bytes().to_vec()); } }; @@ -84,7 +88,7 @@ impl CoAPClient { try!(client.set_receive_timeout(timeout)); match client.receive() { Ok(receive_packet) => { - if receive_packet.header.get_message_id() == message_id && + if receive_packet.get_message_id() == message_id && *receive_packet.get_token() == token { return Ok(receive_packet); } else { @@ -99,13 +103,13 @@ impl CoAPClient { } /// Execute a request with the coap url. - pub fn request(url: &str) -> Result<Packet> { + pub fn request(url: &str) -> Result<CoAPResponse> { Self::request_with_timeout(url, Some(Duration::new(DEFAULT_RECEIVE_TIMEOUT, 0))) } /// Execute a request. - pub fn send(&self, packet: &Packet) -> Result<()> { - match packet.to_bytes() { + pub fn send(&self, request: &CoAPRequest) -> Result<()> { + match request.message.to_bytes() { Ok(bytes) => { let size = try!(self.socket.send_to(&bytes[..], self.peer_addr)); if size == bytes.len() { @@ -119,12 +123,12 @@ impl CoAPClient { } /// Receive a response. - pub fn receive(&self) -> Result<Packet> { + pub fn receive(&self) -> Result<CoAPResponse> { let mut buf = [0; 1500]; let (nread, _src) = try!(self.socket.recv_from(&mut buf)); match Packet::from_bytes(&buf[..nread]) { - Ok(packet) => Ok(packet), + Ok(packet) => Ok(CoAPResponse { message: packet }), Err(_) => Err(Error::new(ErrorKind::InvalidInput, "packet error")), } } @@ -148,7 +152,8 @@ mod test { use super::*; use std::time::Duration; use std::io::ErrorKind; - use packet::Packet; + use message::request::CoAPRequest; + use message::response::CoAPResponse; use server::CoAPServer; #[test] @@ -158,7 +163,7 @@ mod test { assert!(CoAPClient::request("127.0.0.1").is_err()); } - fn request_handler(_: Packet, _: Option<Packet>) -> Option<Packet> { + fn request_handler(_: CoAPRequest) -> Option<CoAPResponse> { None } diff --git a/src/lib.rs b/src/lib.rs index 5344b44803a051f8499b54c33680adbcee80c19c..1c1c7322e9a6b877ddf088a996463894c9632f41 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -27,11 +27,10 @@ //! extern crate coap; //! use std::io; -//! use coap::packet::*; -//! use coap::{CoAPServer, CoAPClient}; +//! use coap::{CoAPServer, CoAPClient, CoAPRequest, CoAPResponse}; -//! fn request_handler(req: Packet, resp: Option<Packet>) -> Option<Packet> { -//! println!("Receive request: {:?}", req); +//! fn request_handler(request: CoAPRequest) -> Option<CoAPResponse> { +//! println!("Receive request: {:?}", request); //! None //! } @@ -54,15 +53,15 @@ //! ```no_run //! extern crate coap; //! -//! use coap::packet::*; +//! use coap::message::response::CoAPResponse; //! use coap::CoAPClient; //! //! fn main() { //! let url = "coap://127.0.0.1:5683/Rust"; //! println!("Client request: {}", url); //! -//! let response: Packet = CoAPClient::request(url).unwrap(); -//! println!("Server reply: {}", String::from_utf8(response.payload).unwrap()); +//! let response: CoAPResponse = CoAPClient::request(url).unwrap(); +//! println!("Server reply: {}", String::from_utf8(response.message.payload).unwrap()); //! } //! ``` @@ -79,9 +78,14 @@ extern crate quickcheck; #[macro_use] extern crate log; -pub use server::CoAPServer; pub use client::CoAPClient; +pub use message::header::MessageType; +pub use message::IsMessage; +pub use message::packet::CoAPOption; +pub use message::request::CoAPRequest; +pub use message::response::CoAPResponse; +pub use server::CoAPServer; -pub mod packet; +pub mod message; pub mod client; pub mod server; diff --git a/src/message/header.rs b/src/message/header.rs new file mode 100644 index 0000000000000000000000000000000000000000..57ad3b2353875cd92e17167b3f57bdac2546efab --- /dev/null +++ b/src/message/header.rs @@ -0,0 +1,272 @@ +#[derive(Default, Debug, RustcEncodable, RustcDecodable)] +pub struct HeaderRaw { + ver_type_tkl: u8, + code: u8, + message_id: u16, +} + +#[derive(Debug)] +pub struct Header { + ver_type_tkl: u8, + pub code: MessageClass, + message_id: u16, +} + +#[derive(Debug, PartialEq)] +pub enum MessageClass { + Empty, + RequestType(Requests), + ResponseType(Responses), + Reserved, +} + +#[derive(Debug, PartialEq)] +pub enum Requests { + Get, + Post, + Put, + Delete, +} + +#[derive(Debug, PartialEq)] +pub enum Responses { + // 200 Codes + Created, + Deleted, + Valid, + Changed, + Content, + + // 400 Codes + BadRequest, + Unauthorized, + BadOption, + Forbidden, + NotFound, + MethodNotAllowed, + NotAcceptable, + PreconditionFailed, + RequestEntityTooLarge, + UnsupportedContentFormat, + + // 500 Codes + InternalServerError, + NotImplemented, + BadGateway, + ServiceUnavailable, + GatewayTimeout, + ProxyingNotSupported, +} + +#[derive(PartialEq, Eq, Debug)] +pub enum MessageType { + Confirmable, + NonConfirmable, + Acknowledgement, + Reset, + Invalid, +} + +impl Header { + pub fn new() -> Header { + return Header::from_raw(&HeaderRaw::default()); + } + + pub fn from_raw(raw: &HeaderRaw) -> Header { + return Header { + ver_type_tkl: raw.ver_type_tkl, + code: code_to_class(&raw.code), + message_id: raw.message_id, + }; + } + + pub fn to_raw(&self) -> HeaderRaw { + return HeaderRaw { + ver_type_tkl: self.ver_type_tkl, + code: class_to_code(&self.code), + message_id: self.message_id, + }; + } + + #[inline] + pub fn set_version(&mut self, v: u8) { + let type_tkl = 0x3F & self.ver_type_tkl; + self.ver_type_tkl = v << 6 | type_tkl; + } + + #[inline] + pub fn get_version(&self) -> u8 { + return self.ver_type_tkl >> 6; + } + + #[inline] + pub fn set_type(&mut self, t: MessageType) { + let tn = match t { + MessageType::Confirmable => 0, + MessageType::NonConfirmable => 1, + MessageType::Acknowledgement => 2, + MessageType::Reset => 3, + _ => unreachable!(), + }; + + let ver_tkl = 0xCF & self.ver_type_tkl; + self.ver_type_tkl = tn << 4 | ver_tkl; + } + + #[inline] + pub fn get_type(&self) -> MessageType { + let tn = (0x30 & self.ver_type_tkl) >> 4; + match tn { + 0 => MessageType::Confirmable, + 1 => MessageType::NonConfirmable, + 2 => MessageType::Acknowledgement, + 3 => MessageType::Reset, + _ => MessageType::Invalid, + } + } + + #[inline] + pub fn set_token_length(&mut self, tkl: u8) { + assert_eq!(0xF0 & tkl, 0); + + let ver_type = 0xF0 & self.ver_type_tkl; + self.ver_type_tkl = tkl | ver_type; + } + + #[inline] + pub fn get_token_length(&self) -> u8 { + return 0x0F & self.ver_type_tkl; + } + + pub fn set_code(&mut self, code: &str) { + let code_vec: Vec<&str> = code.split('.').collect(); + assert_eq!(code_vec.len(), 2); + + let class_code = code_vec[0].parse::<u8>().unwrap(); + let detail_code = code_vec[1].parse::<u8>().unwrap(); + assert_eq!(0xF8 & class_code, 0); + assert_eq!(0xE0 & detail_code, 0); + + self.code = code_to_class(&(class_code << 5 | detail_code)); + } + + pub fn get_code(&self) -> String { + class_to_str(&self.code) + } + + #[inline] + pub fn set_message_id(&mut self, message_id: u16) { + self.message_id = message_id; + } + + #[inline] + pub fn get_message_id(&self) -> u16 { + return self.message_id; + } +} + +pub fn class_to_code(class: &MessageClass) -> u8 { + return match *class { + MessageClass::Empty => 0x00, + + MessageClass::RequestType(Requests::Get) => 0x01, + MessageClass::RequestType(Requests::Post) => 0x02, + MessageClass::RequestType(Requests::Put) => 0x03, + MessageClass::RequestType(Requests::Delete) => 0x04, + + MessageClass::ResponseType(Responses::Created) => 0x41, + MessageClass::ResponseType(Responses::Deleted) => 0x42, + MessageClass::ResponseType(Responses::Valid) => 0x43, + MessageClass::ResponseType(Responses::Changed) => 0x44, + MessageClass::ResponseType(Responses::Content) => 0x45, + + MessageClass::ResponseType(Responses::BadRequest) => 0x80, + MessageClass::ResponseType(Responses::Unauthorized) => 0x81, + MessageClass::ResponseType(Responses::BadOption) => 0x82, + MessageClass::ResponseType(Responses::Forbidden) => 0x83, + MessageClass::ResponseType(Responses::NotFound) => 0x84, + MessageClass::ResponseType(Responses::MethodNotAllowed) => 0x85, + MessageClass::ResponseType(Responses::NotAcceptable) => 0x86, + MessageClass::ResponseType(Responses::PreconditionFailed) => 0x8C, + MessageClass::ResponseType(Responses::RequestEntityTooLarge) => 0x8D, + MessageClass::ResponseType(Responses::UnsupportedContentFormat) => 0x8F, + + MessageClass::ResponseType(Responses::InternalServerError) => 0x90, + MessageClass::ResponseType(Responses::NotImplemented) => 0x91, + MessageClass::ResponseType(Responses::BadGateway) => 0x92, + MessageClass::ResponseType(Responses::ServiceUnavailable) => 0x93, + MessageClass::ResponseType(Responses::GatewayTimeout) => 0x94, + MessageClass::ResponseType(Responses::ProxyingNotSupported) => 0x95, + + _ => 0xFF, + } as u8; +} + +pub fn code_to_class(code: &u8) -> MessageClass { + match *code { + 0x00 => MessageClass::Empty, + + 0x01 => MessageClass::RequestType(Requests::Get), + 0x02 => MessageClass::RequestType(Requests::Post), + 0x03 => MessageClass::RequestType(Requests::Put), + 0x04 => MessageClass::RequestType(Requests::Delete), + + 0x41 => MessageClass::ResponseType(Responses::Created), + 0x42 => MessageClass::ResponseType(Responses::Deleted), + 0x43 => MessageClass::ResponseType(Responses::Valid), + 0x44 => MessageClass::ResponseType(Responses::Changed), + 0x45 => MessageClass::ResponseType(Responses::Content), + + 0x80 => MessageClass::ResponseType(Responses::BadRequest), + 0x81 => MessageClass::ResponseType(Responses::Unauthorized), + 0x82 => MessageClass::ResponseType(Responses::BadOption), + 0x83 => MessageClass::ResponseType(Responses::Forbidden), + 0x84 => MessageClass::ResponseType(Responses::NotFound), + 0x85 => MessageClass::ResponseType(Responses::MethodNotAllowed), + 0x86 => MessageClass::ResponseType(Responses::NotAcceptable), + 0x8C => MessageClass::ResponseType(Responses::PreconditionFailed), + 0x8D => MessageClass::ResponseType(Responses::RequestEntityTooLarge), + 0x8F => MessageClass::ResponseType(Responses::UnsupportedContentFormat), + + 0x90 => MessageClass::ResponseType(Responses::InternalServerError), + 0x91 => MessageClass::ResponseType(Responses::NotImplemented), + 0x92 => MessageClass::ResponseType(Responses::BadGateway), + 0x93 => MessageClass::ResponseType(Responses::ServiceUnavailable), + 0x94 => MessageClass::ResponseType(Responses::GatewayTimeout), + 0x95 => MessageClass::ResponseType(Responses::ProxyingNotSupported), + + _ => MessageClass::Reserved, + } +} + +pub fn code_to_str(code: &u8) -> String { + let class_code = (0xE0 & code) >> 5; + let detail_code = 0x1F & code; + + return format!("{}.{:02}", class_code, detail_code); +} + +pub fn class_to_str(class: &MessageClass) -> String { + return code_to_str(&class_to_code(class)); +} + +#[cfg(test)] +mod test { + use message::header::*; + + #[test] + fn test_header_codes() { + for code in 0..255 { + let class = code_to_class(&code); + let code_str = code_to_str(&code); + let class_str = class_to_str(&class); + + // Reserved class could technically be many codes + // so only check valid items + if class != MessageClass::Reserved { + assert_eq!(class_to_code(&class), code); + assert_eq!(code_str, class_str); + } + } + } +} diff --git a/src/message/mod.rs b/src/message/mod.rs new file mode 100644 index 0000000000000000000000000000000000000000..6f1b93da80229362aa47de217b6f42a6aaca1ded --- /dev/null +++ b/src/message/mod.rs @@ -0,0 +1,60 @@ +pub mod header; +pub mod request; +pub mod response; +pub mod packet; + +use message::packet::Packet; +use message::header::Header; + +use std::collections::LinkedList; + +pub trait IsMessage { + fn get_message(&self) -> &Packet; + fn get_mut_message(&mut self) -> &mut Packet; + fn get_header(&self) -> &Header; + fn get_mut_header(&mut self) -> &mut Header; + + fn set_token(&mut self, token: Vec<u8>) { + self.get_mut_message().set_token(token); + } + fn get_token(&self) -> &Vec<u8> { + return self.get_message().get_token(); + } + fn set_option(&mut self, tp: packet::CoAPOption, value: LinkedList<Vec<u8>>) { + self.get_mut_message().set_option(tp, value); + } + fn set_payload(&mut self, payload: Vec<u8>) { + self.get_mut_message().set_payload(payload); + } + fn add_option(&mut self, tp: packet::CoAPOption, value: Vec<u8>) { + self.get_mut_message().add_option(tp, value); + } + fn get_option(&self, tp: packet::CoAPOption) -> Option<LinkedList<Vec<u8>>> { + return self.get_message().get_option(tp); + } + + fn get_message_id(&self) -> u16 { + return self.get_message().header.get_message_id(); + } + fn set_message_id(&mut self, message_id: u16) { + self.get_mut_message().header.set_message_id(message_id); + } + fn set_version(&mut self, v: u8) { + self.get_mut_message().header.set_version(v); + } + fn get_version(&self) -> u8 { + return self.get_message().header.get_version(); + } + fn set_type(&mut self, t: header::MessageType) { + self.get_mut_message().header.set_type(t); + } + fn get_type(&self) -> header::MessageType { + return self.get_message().header.get_type(); + } + fn get_code(&self) -> String { + return self.get_message().header.get_code(); + } + fn set_code(&mut self, code: &str) { + self.get_mut_message().header.set_code(code); + } +} diff --git a/src/packet.rs b/src/message/packet.rs similarity index 55% rename from src/packet.rs rename to src/message/packet.rs index 58d2a4a06bcd20783199992884b9649ea9c2e64c..a2e401012d53b8d38a8b5256ba250050bb439a2d 100644 --- a/src/packet.rs +++ b/src/message/packet.rs @@ -2,280 +2,16 @@ use bincode; use std::collections::BTreeMap; use std::collections::LinkedList; -macro_rules! u8_to_unsigned_be { - ($src:ident, $start:expr, $end:expr, $t:ty) => ({ - (0 .. $end - $start + 1).rev().fold(0, |acc, i| acc | $src[$start+i] as $t << i * 8) - }) -} - -#[derive(PartialEq, Eq, Debug)] -pub enum PacketType { - Confirmable, - NonConfirmable, - Acknowledgement, - Reset, - Invalid, -} - -#[derive(Default, Debug, RustcEncodable, RustcDecodable)] -pub struct PacketHeaderRaw { - ver_type_tkl: u8, - code: u8, - message_id: u16, -} - -#[derive(Debug)] -pub struct PacketHeader { - ver_type_tkl: u8, - pub code: PacketClass, - message_id: u16, -} - -#[derive(Debug, PartialEq)] -pub enum PacketClass { - Empty, - Request(Requests), - Response(Responses), - Reserved, -} - -#[derive(Debug, PartialEq)] -pub enum Requests { - Get, - Post, - Put, - Delete, -} - -#[derive(Debug, PartialEq)] -pub enum Responses { - // 200 Codes - Created, - Deleted, - Valid, - Changed, - Content, - - // 400 Codes - BadRequest, - Unauthorized, - BadOption, - Forbidden, - NotFound, - MethodNotAllowed, - NotAcceptable, - PreconditionFailed, - RequestEntityTooLarge, - UnsupportedContentFormat, - - // 500 Codes - InternalServerError, - NotImplemented, - BadGateway, - ServiceUnavailable, - GatewayTimeout, - ProxyingNotSupported, -} - -pub fn class_to_code(class: &PacketClass) -> u8 { - return match *class { - PacketClass::Empty => 0x00, - - PacketClass::Request(Requests::Get) => 0x01, - PacketClass::Request(Requests::Post) => 0x02, - PacketClass::Request(Requests::Put) => 0x03, - PacketClass::Request(Requests::Delete) => 0x04, - - PacketClass::Response(Responses::Created) => 0x41, - PacketClass::Response(Responses::Deleted) => 0x42, - PacketClass::Response(Responses::Valid) => 0x43, - PacketClass::Response(Responses::Changed) => 0x44, - PacketClass::Response(Responses::Content) => 0x45, - - PacketClass::Response(Responses::BadRequest) => 0x80, - PacketClass::Response(Responses::Unauthorized) => 0x81, - PacketClass::Response(Responses::BadOption) => 0x82, - PacketClass::Response(Responses::Forbidden) => 0x83, - PacketClass::Response(Responses::NotFound) => 0x84, - PacketClass::Response(Responses::MethodNotAllowed) => 0x85, - PacketClass::Response(Responses::NotAcceptable) => 0x86, - PacketClass::Response(Responses::PreconditionFailed) => 0x8C, - PacketClass::Response(Responses::RequestEntityTooLarge) => 0x8D, - PacketClass::Response(Responses::UnsupportedContentFormat) => 0x8F, - - PacketClass::Response(Responses::InternalServerError) => 0x90, - PacketClass::Response(Responses::NotImplemented) => 0x91, - PacketClass::Response(Responses::BadGateway) => 0x92, - PacketClass::Response(Responses::ServiceUnavailable) => 0x93, - PacketClass::Response(Responses::GatewayTimeout) => 0x94, - PacketClass::Response(Responses::ProxyingNotSupported) => 0x95, - - _ => 0xFF, - } as u8; -} - -pub fn code_to_class(code: &u8) -> PacketClass { - match *code { - 0x00 => PacketClass::Empty, - - 0x01 => PacketClass::Request(Requests::Get), - 0x02 => PacketClass::Request(Requests::Post), - 0x03 => PacketClass::Request(Requests::Put), - 0x04 => PacketClass::Request(Requests::Delete), - - 0x41 => PacketClass::Response(Responses::Created), - 0x42 => PacketClass::Response(Responses::Deleted), - 0x43 => PacketClass::Response(Responses::Valid), - 0x44 => PacketClass::Response(Responses::Changed), - 0x45 => PacketClass::Response(Responses::Content), - - 0x80 => PacketClass::Response(Responses::BadRequest), - 0x81 => PacketClass::Response(Responses::Unauthorized), - 0x82 => PacketClass::Response(Responses::BadOption), - 0x83 => PacketClass::Response(Responses::Forbidden), - 0x84 => PacketClass::Response(Responses::NotFound), - 0x85 => PacketClass::Response(Responses::MethodNotAllowed), - 0x86 => PacketClass::Response(Responses::NotAcceptable), - 0x8C => PacketClass::Response(Responses::PreconditionFailed), - 0x8D => PacketClass::Response(Responses::RequestEntityTooLarge), - 0x8F => PacketClass::Response(Responses::UnsupportedContentFormat), - - 0x90 => PacketClass::Response(Responses::InternalServerError), - 0x91 => PacketClass::Response(Responses::NotImplemented), - 0x92 => PacketClass::Response(Responses::BadGateway), - 0x93 => PacketClass::Response(Responses::ServiceUnavailable), - 0x94 => PacketClass::Response(Responses::GatewayTimeout), - 0x95 => PacketClass::Response(Responses::ProxyingNotSupported), - - _ => PacketClass::Reserved, - } -} - -pub fn code_to_str(code: &u8) -> String { - let class_code = (0xE0 & code) >> 5; - let detail_code = 0x1F & code; - - return format!("{}.{:02}", class_code, detail_code); -} - -pub fn class_to_str(class: &PacketClass) -> String { - return code_to_str(&class_to_code(class)); -} - -impl PacketHeader { - pub fn new() -> PacketHeader { - return PacketHeader::from_raw(&PacketHeaderRaw::default()); - } - - pub fn from_raw(raw: &PacketHeaderRaw) -> PacketHeader { - return PacketHeader { - ver_type_tkl: raw.ver_type_tkl, - code: code_to_class(&raw.code), - message_id: raw.message_id, - }; - } - - pub fn to_raw(&self) -> PacketHeaderRaw { - return PacketHeaderRaw { - ver_type_tkl: self.ver_type_tkl, - code: class_to_code(&self.code), - message_id: self.message_id, - }; - } - - #[inline] - pub fn set_version(&mut self, v: u8) { - let type_tkl = 0x3F & self.ver_type_tkl; - self.ver_type_tkl = v << 6 | type_tkl; - } - - #[inline] - pub fn get_version(&self) -> u8 { - return self.ver_type_tkl >> 6; - } - - #[inline] - pub fn set_type(&mut self, t: PacketType) { - let tn = match t { - PacketType::Confirmable => 0, - PacketType::NonConfirmable => 1, - PacketType::Acknowledgement => 2, - PacketType::Reset => 3, - _ => unreachable!(), - }; - - let ver_tkl = 0xCF & self.ver_type_tkl; - self.ver_type_tkl = tn << 4 | ver_tkl; - } - - #[inline] - pub fn get_type(&self) -> PacketType { - let tn = (0x30 & self.ver_type_tkl) >> 4; - match tn { - 0 => PacketType::Confirmable, - 1 => PacketType::NonConfirmable, - 2 => PacketType::Acknowledgement, - 3 => PacketType::Reset, - _ => PacketType::Invalid, - } - } - - #[inline] - fn set_token_length(&mut self, tkl: u8) { - assert_eq!(0xF0 & tkl, 0); - - let ver_type = 0xF0 & self.ver_type_tkl; - self.ver_type_tkl = tkl | ver_type; - } - - #[inline] - fn get_token_length(&self) -> u8 { - return 0x0F & self.ver_type_tkl; - } +use message::header; - pub fn set_code(&mut self, code: &str) { - let code_vec: Vec<&str> = code.split('.').collect(); - assert_eq!(code_vec.len(), 2); - - let class_code = code_vec[0].parse::<u8>().unwrap(); - let detail_code = code_vec[1].parse::<u8>().unwrap(); - assert_eq!(0xF8 & class_code, 0); - assert_eq!(0xE0 & detail_code, 0); - - self.code = code_to_class(&(class_code << 5 | detail_code)); - } - - pub fn get_code(&self) -> String { - class_to_str(&self.code) - } - - #[inline] - pub fn set_message_id(&mut self, message_id: u16) { - self.message_id = message_id; - } - - #[inline] - pub fn get_message_id(&self) -> u16 { - return self.message_id; - } -} - -#[derive(Debug)] -pub enum ParseError { - InvalidHeader, - InvalidTokenLength, - InvalidOptionDelta, - InvalidOptionLength, -} - -#[derive(Debug)] -pub enum PackageError { - InvalidHeader, - InvalidPacketLength, +macro_rules! u8_to_unsigned_be { + ($src:ident, $start:expr, $end:expr, $t:ty) => ({ + (0 .. $end - $start + 1).rev().fold(0, |acc, i| acc | $src[$start+i] as $t << i * 8) + }) } #[derive(PartialEq, Eq, Debug)] -pub enum OptionType { +pub enum CoAPOption { IfMatch, UriHost, ETag, @@ -296,9 +32,23 @@ pub enum OptionType { Size1, } +#[derive(Debug)] +pub enum PackageError { + InvalidHeader, + InvalidPacketLength, +} + +#[derive(Debug)] +pub enum ParseError { + InvalidHeader, + InvalidTokenLength, + InvalidOptionDelta, + InvalidOptionLength, +} + #[derive(Debug)] pub struct Packet { - pub header: PacketHeader, + pub header: header::Header, token: Vec<u8>, options: BTreeMap<usize, LinkedList<Vec<u8>>>, pub payload: Vec<u8>, @@ -307,7 +57,7 @@ pub struct Packet { impl Packet { pub fn new() -> Packet { Packet { - header: PacketHeader::new(), + header: header::Header::new(), token: Vec::new(), options: BTreeMap::new(), payload: Vec::new(), @@ -323,7 +73,7 @@ impl Packet { return &self.token; } - pub fn set_option(&mut self, tp: OptionType, value: LinkedList<Vec<u8>>) { + pub fn set_option(&mut self, tp: CoAPOption, value: LinkedList<Vec<u8>>) { let num = Self::get_option_number(tp); self.options.insert(num, value); } @@ -332,7 +82,7 @@ impl Packet { self.payload = payload; } - pub fn add_option(&mut self, tp: OptionType, value: Vec<u8>) { + pub fn add_option(&mut self, tp: CoAPOption, value: Vec<u8>) { let num = Self::get_option_number(tp); match self.options.get_mut(&num) { Some(list) => { @@ -347,7 +97,7 @@ impl Packet { self.options.insert(num, list); } - pub fn get_option(&self, tp: OptionType) -> Option<LinkedList<Vec<u8>>> { + pub fn get_option(&self, tp: CoAPOption) -> Option<LinkedList<Vec<u8>>> { let num = Self::get_option_number(tp); match self.options.get(&num) { Some(options) => Some(options.clone()), @@ -357,10 +107,10 @@ impl Packet { /// Decodes a byte slice and construct the equivalent Packet. pub fn from_bytes(buf: &[u8]) -> Result<Packet, ParseError> { - let header_result: bincode::DecodingResult<PacketHeaderRaw> = bincode::decode(buf); + let header_result: bincode::DecodingResult<header::HeaderRaw> = bincode::decode(buf); match header_result { Ok(raw_header) => { - let header = PacketHeader::from_raw(&raw_header); + let header = header::Header::from_raw(&raw_header); let token_length = header.get_token_length(); let options_start: usize = 4 + token_length as usize; @@ -535,7 +285,7 @@ impl Packet { } let mut buf_length = 4 + self.payload.len() + self.token.len(); - if self.header.code != PacketClass::Empty && self.payload.len() != 0 { + if self.header.code != header::MessageClass::Empty && self.payload.len() != 0 { buf_length += 1; } buf_length += options_bytes.len(); @@ -564,7 +314,7 @@ impl Packet { buf.set_len(buf_len + self.token.len() + options_bytes.len()); } - if self.header.code != PacketClass::Empty && self.payload.len() != 0 { + if self.header.code != header::MessageClass::Empty && self.payload.len() != 0 { buf.push(0xFF); buf.reserve(self.payload.len()); unsafe { @@ -582,71 +332,36 @@ impl Packet { } } - fn get_option_number(tp: OptionType) -> usize { + fn get_option_number(tp: CoAPOption) -> usize { match tp { - OptionType::IfMatch => 1, - OptionType::UriHost => 3, - OptionType::ETag => 4, - OptionType::IfNoneMatch => 5, - OptionType::Observe => 6, - OptionType::UriPort => 7, - OptionType::LocationPath => 8, - OptionType::UriPath => 11, - OptionType::ContentFormat => 12, - OptionType::MaxAge => 14, - OptionType::UriQuery => 15, - OptionType::Accept => 17, - OptionType::LocationQuery => 20, - OptionType::Block2 => 23, - OptionType::Block1 => 27, - OptionType::ProxyUri => 35, - OptionType::ProxyScheme => 39, - OptionType::Size1 => 60, + CoAPOption::IfMatch => 1, + CoAPOption::UriHost => 3, + CoAPOption::ETag => 4, + CoAPOption::IfNoneMatch => 5, + CoAPOption::Observe => 6, + CoAPOption::UriPort => 7, + CoAPOption::LocationPath => 8, + CoAPOption::UriPath => 11, + CoAPOption::ContentFormat => 12, + CoAPOption::MaxAge => 14, + CoAPOption::UriQuery => 15, + CoAPOption::Accept => 17, + CoAPOption::LocationQuery => 20, + CoAPOption::Block2 => 23, + CoAPOption::Block1 => 27, + CoAPOption::ProxyUri => 35, + CoAPOption::ProxyScheme => 39, + CoAPOption::Size1 => 60, } } } -/// Convert a request to a response -pub fn auto_response(request_packet: &Packet) -> Option<Packet> { - let mut packet = Packet::new(); - - packet.header.set_version(1); - let response_type = match request_packet.header.get_type() { - PacketType::Confirmable => PacketType::Acknowledgement, - PacketType::NonConfirmable => PacketType::NonConfirmable, - _ => return None, - }; - packet.header.set_type(response_type); - packet.header.code = PacketClass::Response(Responses::Content); - packet.header.set_message_id(request_packet.header.get_message_id()); - packet.set_token(request_packet.get_token().clone()); - - packet.payload = request_packet.payload.clone(); - - Some(packet) -} - #[cfg(test)] mod test { use super::*; + use super::super::header; use std::collections::LinkedList; - #[test] - fn test_header_codes() { - for code in 0..255 { - let class = code_to_class(&code); - let code_str = code_to_str(&code); - let class_str = class_to_str(&class); - - // Reserved class could technically be many codes - // so only check valid items - if class != PacketClass::Reserved { - assert_eq!(class_to_code(&class), code); - assert_eq!(code_str, class_str); - } - } - } - #[test] fn test_decode_packet_with_options() { let buf = [0x44, 0x01, 0x84, 0x9e, 0x51, 0x55, 0x77, 0xe8, 0xb2, 0x48, 0x69, 0x04, 0x54, @@ -655,14 +370,15 @@ mod test { assert!(packet.is_ok()); let packet = packet.unwrap(); assert_eq!(packet.header.get_version(), 1); - assert_eq!(packet.header.get_type(), PacketType::Confirmable); + assert_eq!(packet.header.get_type(), header::MessageType::Confirmable); assert_eq!(packet.header.get_token_length(), 4); - assert_eq!(packet.header.code, PacketClass::Request(Requests::Get)); + assert_eq!(packet.header.code, + header::MessageClass::RequestType(header::Requests::Get)); assert_eq!(packet.header.get_message_id(), 33950); assert_eq!(*packet.get_token(), vec![0x51, 0x55, 0x77, 0xE8]); assert_eq!(packet.options.len(), 2); - let uri_path = packet.get_option(OptionType::UriPath); + let uri_path = packet.get_option(CoAPOption::UriPath); assert!(uri_path.is_some()); let uri_path = uri_path.unwrap(); let mut expected_uri_path = LinkedList::new(); @@ -670,7 +386,7 @@ mod test { expected_uri_path.push_back("Test".as_bytes().to_vec()); assert_eq!(uri_path, expected_uri_path); - let uri_query = packet.get_option(OptionType::UriQuery); + let uri_query = packet.get_option(CoAPOption::UriQuery); assert!(uri_query.is_some()); let uri_query = uri_query.unwrap(); let mut expected_uri_query = LinkedList::new(); @@ -686,10 +402,11 @@ mod test { assert!(packet.is_ok()); let packet = packet.unwrap(); assert_eq!(packet.header.get_version(), 1); - assert_eq!(packet.header.get_type(), PacketType::Acknowledgement); + assert_eq!(packet.header.get_type(), + header::MessageType::Acknowledgement); assert_eq!(packet.header.get_token_length(), 4); assert_eq!(packet.header.code, - PacketClass::Response(Responses::Content)); + header::MessageClass::ResponseType(header::Responses::Content)); assert_eq!(packet.header.get_message_id(), 5117); assert_eq!(*packet.get_token(), vec![0xD0, 0xE2, 0x4D, 0xAC]); assert_eq!(packet.payload, "Hello".as_bytes().to_vec()); @@ -699,13 +416,13 @@ mod test { fn test_encode_packet_with_options() { let mut packet = Packet::new(); packet.header.set_version(1); - packet.header.set_type(PacketType::Confirmable); - packet.header.code = PacketClass::Request(Requests::Get); + packet.header.set_type(header::MessageType::Confirmable); + packet.header.code = header::MessageClass::RequestType(header::Requests::Get); packet.header.set_message_id(33950); packet.set_token(vec![0x51, 0x55, 0x77, 0xE8]); - packet.add_option(OptionType::UriPath, b"Hi".to_vec()); - packet.add_option(OptionType::UriPath, b"Test".to_vec()); - packet.add_option(OptionType::UriQuery, b"a=1".to_vec()); + packet.add_option(CoAPOption::UriPath, b"Hi".to_vec()); + packet.add_option(CoAPOption::UriPath, b"Test".to_vec()); + packet.add_option(CoAPOption::UriQuery, b"a=1".to_vec()); assert_eq!(packet.to_bytes().unwrap(), vec![0x44, 0x01, 0x84, 0x9e, 0x51, 0x55, 0x77, 0xe8, 0xb2, 0x48, 0x69, 0x04, 0x54, 0x65, 0x73, 0x74, 0x43, 0x61, 0x3d, 0x31]); @@ -715,8 +432,8 @@ mod test { fn test_encode_packet_with_payload() { let mut packet = Packet::new(); packet.header.set_version(1); - packet.header.set_type(PacketType::Acknowledgement); - packet.header.code = PacketClass::Response(Responses::Content); + packet.header.set_type(header::MessageType::Acknowledgement); + packet.header.code = header::MessageClass::ResponseType(header::Responses::Content); packet.header.set_message_id(5117); packet.set_token(vec![0xD0, 0xE2, 0x4D, 0xAC]); packet.payload = "Hello".as_bytes().to_vec(); diff --git a/src/message/request.rs b/src/message/request.rs new file mode 100644 index 0000000000000000000000000000000000000000..620bae9a5b04dbbf42dd45f6f158a6803b40a5dd --- /dev/null +++ b/src/message/request.rs @@ -0,0 +1,87 @@ +use message::IsMessage; +use message::response::CoAPResponse; +use message::packet::Packet; +use message::header::Header; +use std::net::SocketAddr; + +#[derive(Debug)] +pub struct CoAPRequest { + pub message: Packet, + pub response: Option<CoAPResponse>, + pub source: Option<SocketAddr>, +} + +impl CoAPRequest { + pub fn new() -> CoAPRequest { + CoAPRequest { + response: None, + message: Packet::new(), + source: None, + } + } + + pub fn from_packet(packet: Packet, source: &SocketAddr) -> CoAPRequest { + CoAPRequest { + response: CoAPResponse::new(&packet), + message: packet, + source: Some(source.clone()), + } + } +} + +impl IsMessage for CoAPRequest { + fn get_message(&self) -> &Packet { + &self.message + } + fn get_mut_message(&mut self) -> &mut Packet { + &mut self.message + } + fn get_header(&self) -> &Header { + &self.message.header + } + fn get_mut_header(&mut self) -> &mut Header { + &mut self.message.header + } +} + +#[cfg(test)] +mod test { + use super::*; + use message::packet::{Packet, CoAPOption}; + use message::header::MessageType; + use message::IsMessage; + use std::net::SocketAddr; + use std::str::FromStr; + + #[test] + fn test_request_create() { + + let mut packet = Packet::new(); + let mut request1 = CoAPRequest::new(); + + packet.set_token(vec![0x17, 0x38]); + request1.set_token(vec![0x17, 0x38]); + + packet.add_option(CoAPOption::UriPath, b"test-interface".to_vec()); + request1.add_option(CoAPOption::UriPath, b"test-interface".to_vec()); + + packet.header.set_message_id(42); + request1.set_message_id(42); + + packet.header.set_version(2); + request1.set_version(2); + + packet.header.set_type(MessageType::Confirmable); + request1.set_type(MessageType::Confirmable); + + packet.header.set_code("0.04"); + request1.set_code("0.04"); + + let request2 = CoAPRequest::from_packet(packet, + &SocketAddr::from_str("127.0.0.1:1234").unwrap()); + + assert_eq!(request1.message.to_bytes().unwrap(), + request2.message.to_bytes().unwrap()); + + } +} diff --git a/src/message/response.rs b/src/message/response.rs new file mode 100644 index 0000000000000000000000000000000000000000..e5351d6280843804ee946d9163b5770bf9bb3911 --- /dev/null +++ b/src/message/response.rs @@ -0,0 +1,72 @@ +use message::IsMessage; +use message::packet::Packet; +use message::header::{Header, MessageType, MessageClass, Responses}; + +#[derive(Debug)] +pub struct CoAPResponse { + pub message: Packet, +} + +impl CoAPResponse { + pub fn new(request: &Packet) -> Option<CoAPResponse> { + let mut packet = Packet::new(); + + packet.header.set_version(1); + let response_type = match request.header.get_type() { + MessageType::Confirmable => MessageType::Acknowledgement, + MessageType::NonConfirmable => MessageType::NonConfirmable, + _ => return None, + }; + packet.header.set_type(response_type); + packet.header.code = MessageClass::ResponseType(Responses::Content); + packet.header.set_message_id(request.header.get_message_id()); + packet.set_token(request.get_token().clone()); + + packet.payload = request.payload.clone(); + + Some(CoAPResponse { message: packet }) + } +} + +impl IsMessage for CoAPResponse { + fn get_message(&self) -> &Packet { + &self.message + } + fn get_mut_message(&mut self) -> &mut Packet { + &mut self.message + } + fn get_header(&self) -> &Header { + &self.message.header + } + fn get_mut_header(&mut self) -> &mut Header { + &mut self.message.header + } +} + +#[cfg(test)] +mod test { + use super::*; + use message::packet::Packet; + use message::IsMessage; + use message::header::MessageType; + + #[test] + fn test_new_response_valid() { + for mtyp in vec![MessageType::Confirmable, MessageType::NonConfirmable] { + let mut packet = Packet::new(); + packet.header.set_type(mtyp); + let opt_resp = CoAPResponse::new(&packet); + assert!(opt_resp.is_some()); + + let response = opt_resp.unwrap(); + assert_eq!(packet.payload, response.message.payload); + } + } + + #[test] + fn test_new_response_invalid() { + let mut packet = Packet::new(); + packet.header.set_type(MessageType::Acknowledgement); + assert!(CoAPResponse::new(&packet).is_none()); + } +} diff --git a/src/server.rs b/src/server.rs index 03c23c3941f0addf03c6dcc90c91d965d6f218f0..37b0392f0110edfc930dd9a1e76b0db72a1f18d3 100644 --- a/src/server.rs +++ b/src/server.rs @@ -5,12 +5,14 @@ use std::net::{ToSocketAddrs, SocketAddr}; use std::sync::mpsc; use mio::{EventLoop, PollOpt, EventSet, Handler, Sender, Token}; use mio::udp::UdpSocket; -use packet::{Packet, auto_response}; +use message::packet::Packet; +use message::request::CoAPRequest; +use message::response::CoAPResponse; use threadpool::ThreadPool; const DEFAULT_WORKER_NUM: usize = 4; -pub type TxQueue = mpsc::Sender<CoAPResponse>; -pub type RxQueue = mpsc::Receiver<CoAPResponse>; +type TxQueue = mpsc::Sender<QueuedResponse>; +type RxQueue = mpsc::Receiver<QueuedResponse>; #[derive(Debug)] pub enum CoAPServerError { @@ -20,21 +22,21 @@ pub enum CoAPServerError { } #[derive(Debug)] -pub struct CoAPResponse { +struct QueuedResponse { pub address: SocketAddr, - pub response: Packet, + pub response: CoAPResponse, } pub trait CoAPHandler: Sync + Send + Copy { - fn handle(&self, Packet, Option<Packet>) -> Option<Packet>; + fn handle(&self, CoAPRequest) -> Option<CoAPResponse>; } impl<F> CoAPHandler for F - where F: Fn(Packet, Option<Packet>) -> Option<Packet>, + where F: Fn(CoAPRequest) -> Option<CoAPResponse>, F: Sync + Send + Copy { - fn handle(&self, request: Packet, response: Option<Packet>) -> Option<Packet> { - return self(request, response); + fn handle(&self, request: CoAPRequest) -> Option<CoAPResponse> { + return self(request); } } @@ -77,17 +79,17 @@ impl<H: CoAPHandler + 'static> Handler for UdpHandler<H> { Ok(Some((nread, src))) => { debug!("Handling request from {}", src); let response_q = self.tx_sender.clone(); + self.thread_pool.execute(move || { match Packet::from_bytes(&buf[..nread]) { Ok(packet) => { - // Pre-generate a response - let auto_resp = auto_response(&packet); // Dispatch user handler, if there is a response packet // send the reply via the TX thread - match coap_handler.handle(packet, auto_resp) { + let rqst = CoAPRequest::from_packet(packet, &src); + match coap_handler.handle(rqst) { Some(response) => { debug!("Response: {:?}", response); - response_q.send(CoAPResponse { + response_q.send(QueuedResponse { address: src, response: response, }) @@ -224,7 +226,7 @@ fn transmit_handler(tx_recv: RxQueue, tx_only: UdpSocket) { loop { match tx_recv.recv() { Ok(q_res) => { - match q_res.response.to_bytes() { + match q_res.response.message.to_bytes() { Ok(bytes) => { let _ = tx_only.send_to(&bytes[..], &q_res.address); } @@ -252,18 +254,22 @@ impl Drop for CoAPServer { #[cfg(test)] mod test { - use super::*; - use packet::{Packet, PacketType, OptionType}; use client::CoAPClient; + use message::header; + use message::IsMessage; + use message::packet::CoAPOption; + use message::request::CoAPRequest; + use message::response::CoAPResponse; + use super::*; - fn request_handler(req: Packet, response: Option<Packet>) -> Option<Packet> { - let uri_path_list = req.get_option(OptionType::UriPath).unwrap(); + fn request_handler(req: CoAPRequest) -> Option<CoAPResponse> { + let uri_path_list = req.get_option(CoAPOption::UriPath).unwrap(); assert!(uri_path_list.len() == 1); - match response { - Some(mut packet) => { - packet.set_payload(uri_path_list.front().unwrap().clone()); - Some(packet) + match req.response { + Some(mut response) => { + response.set_payload(uri_path_list.front().unwrap().clone()); + Some(response) } _ => None, } @@ -275,17 +281,17 @@ mod test { server.handle(request_handler).unwrap(); let client = CoAPClient::new("127.0.0.1:5683").unwrap(); - let mut packet = Packet::new(); - packet.header.set_version(1); - packet.header.set_type(PacketType::Confirmable); - packet.header.set_code("0.01"); - packet.header.set_message_id(1); - packet.set_token(vec![0x51, 0x55, 0x77, 0xE8]); - packet.add_option(OptionType::UriPath, b"test-echo".to_vec()); - client.send(&packet).unwrap(); + let mut request = CoAPRequest::new(); + request.set_version(1); + request.set_type(header::MessageType::Confirmable); + request.set_code("0.01"); + request.set_message_id(1); + request.set_token(vec![0x51, 0x55, 0x77, 0xE8]); + request.add_option(CoAPOption::UriPath, b"test-echo".to_vec()); + client.send(&request).unwrap(); let recv_packet = client.receive().unwrap(); - assert_eq!(recv_packet.payload, b"test-echo".to_vec()); + assert_eq!(recv_packet.message.payload, b"test-echo".to_vec()); } #[test] @@ -294,15 +300,15 @@ mod test { server.handle(request_handler).unwrap(); let client = CoAPClient::new("127.0.0.1:5683").unwrap(); - let mut packet = Packet::new(); - packet.header.set_version(1); - packet.header.set_type(PacketType::Confirmable); - packet.header.set_code("0.01"); - packet.header.set_message_id(1); - packet.add_option(OptionType::UriPath, b"test-echo".to_vec()); + let mut packet = CoAPRequest::new(); + packet.set_version(1); + packet.set_type(header::MessageType::Confirmable); + packet.set_code("0.01"); + packet.set_message_id(1); + packet.add_option(CoAPOption::UriPath, b"test-echo".to_vec()); client.send(&packet).unwrap(); let recv_packet = client.receive().unwrap(); - assert_eq!(recv_packet.payload, b"test-echo".to_vec()); + assert_eq!(recv_packet.message.payload, b"test-echo".to_vec()); } }