#[derive(PartialEq, Eq, Debug)]
pub struct Message {
pub version: u8,
pub mtype: Mtype,
pub code: Code,
pub mid: u16,
pub token: Vec<u8>,
pub options: Vec<option::Option>,
pub payload: Vec<u8>,
}
#[derive(PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Mtype {
Confirmable = 0,
NonConfirmable = 1,
Acknowledgement = 2,
Reset = 3,
}
impl Into<u8> for Mtype {
fn into(self) -> u8 {
self as u8
}
}
impl PartialEq<u8> for Mtype {
fn eq(&self, other: &u8) -> bool {
*self as u8 == *other
}
fn ne(&self, other: &u8) -> bool {
*self as u8 != *other
}
}
macro_rules! code {
($c:expr, $d:expr) => ((($c & 0x07) << 5) | ($d & 0x1F))
}
#[derive(PartialEq, Eq, Debug)]
#[repr(u8)]
pub enum Code {
Empty = code!(0,00),
Get = code!(0,01),
Post = code!(0,02),
Put = code!(0,03),
Delete = code!(0,04),
Created = code!(2,01),
Deleted = code!(2,02),
Valid = code!(2,03),
Changed = code!(2,04),
Content = code!(2,05),
BadRequest = code!(4,00),
Unauthorized = code!(4,01),
BadOption = code!(4,02),
Forbidden = code!(4,03),
NotFound = code!(4,04),
MethodNotAllowed = code!(4,05),
NotAcceptable = code!(4,06),
PreconditionFailed = code!(4,12),
RequestEntityTooLarge = code!(4,13),
UnsupportedContentFormat = code!(4,15),
InternalServerError = code!(5,00),
NotImplemented = code!(5,01),
BadGateway = code!(5,02),
ServiceUnavailable = code!(5,03),
GatewayTimeout = code!(5,04),
ProxyingNotSupported = code!(5,05),
}
pub mod option {
#[derive(PartialEq, Eq, Debug)]
pub struct Option {
pub number: Number,
pub value: Vec<u8>
}
#[derive(PartialEq, Eq, Debug)]
#[repr(u16)]
pub enum Number {
ReservedOrUnknown = 0,
IfMatch = 1,
UriHost = 3,
ETag = 4,
IfNoneMatch = 5,
Observe = 6,
UriPort = 7,
LocationPath = 8,
UriPath = 11,
ContentFormat = 12,
MaxAge = 14,
UriQuery = 15,
Accept = 17,
LocationQuery = 20,
ProxyUri = 35,
ProxyScheme = 39,
Size1 = 60,
NoResponse = 284,
}
pub fn new(number: Number, value: Vec<u8>) -> Option {
Option{
number: number,
value: value
}
}
pub fn from_u16(numeric: u16, value: Vec<u8>) -> Option {
use std::mem;
new(unsafe { mem::transmute(numeric)}, value)
}
}
impl Message {
pub fn from_bin(pkt: &[u8]) -> Result<Message, &str> {
use std::mem;
let mut i: usize;
let version = pkt[0] >> 6;
let mtype = unsafe{ mem::transmute((pkt[0] >> 4) & 0x03) };
let token_length = pkt[0] & 0x0F;
let code = unsafe{ mem::transmute(pkt[1]) };
let mid = ((pkt[2] as u16) << 8) | pkt[3] as u16;
let mut token = vec![];
for j in 0..token_length {
token.push(pkt[4+j as usize]);
}
i = 4 + token_length as usize;
let mut options: Vec<option::Option> = vec![];
let mut option_number_offset = 0u16;
while i < pkt.len() {
if pkt[i] == 0xFF {
i+=1;
break;
}
let delta = match pkt[i] >> 4 {
d @ 0 ... 12 => d as u16,
13 => {i+=1; pkt[i] as u16 + 13},
14 => {i+=2; (((pkt[i-1] as u16) << 8) | pkt[i] as u16) + 269},
15 => panic!("message format error"),
_ => unreachable!(),
};
let length = match pkt[i] & 0x0F {
d @ 0 ... 12 => d as u16,
13 => {i+=1; pkt[i] as u16 + 13},
14 => {i+=2; ((pkt[i-1] as u16) << 8) | pkt[i] as u16 + 269},
15 => panic!("message format error"),
_ => unreachable!(),
};
i += 1;
let option_number = option_number_offset + delta;
option_number_offset = option_number;
if length >= 65000 {
return Err("message format error");
}
if pkt.len() >= i+(length as usize) {
options.push(option::from_u16(option_number,pkt[i..i+(length as usize)].to_vec()));
} else {
return Err("message format error");
}
i += length as usize;
}
let payload = if i < pkt.len() {
pkt[i..].to_vec()
} else {
vec![]
};
Ok(Message{
version: version,
mtype: mtype,
code: code,
mid: mid,
token: token,
options: options,
payload: payload,
})
}
}
#[test]
fn test_msg_parse_empty() {
let ref_bin: [u8;4] = [64,0,0,0];
let msg = Message::from_bin(&ref_bin).unwrap();
assert!(msg.version == 1);
assert!(msg.mtype == Mtype::Confirmable);
assert!(msg.code == Code::Empty);
assert!(msg.mid == 0);
assert!(msg.token == vec![]);
assert!(msg.options == vec![]);
assert!(msg.payload == vec![]);
}
#[test]
fn test_msg_parse_empty_con_with_token() {
let ref_bin: [u8;6] = [66,0,0,0,37,42];
let msg = Message::from_bin(&ref_bin).unwrap();
assert!(msg.version == 1);
assert!(msg.mtype == Mtype::Confirmable);
assert!(msg.code == Code::Empty);
assert!(msg.mid == 0);
assert!(msg.token == vec![37, 42]);
assert!(msg.options == vec![]);
assert!(msg.payload == vec![]);
}
#[test]
fn test_msg_parse_get_con() {
let ref_bin: [u8;8] = [0x41,0x01,0x00,0x37,0x99,0xFF,0x01,0x02];
let msg = Message::from_bin(&ref_bin).unwrap();
assert!(msg.version == 1);
assert!(msg.mtype == Mtype::Confirmable);
assert!(msg.code == Code::Get);
assert!(msg.mid == 0x37);
assert!(msg.token == vec![0x99]);
assert!(msg.options == vec![]);
assert!(msg.payload == vec![0x01, 0x02]);
}
#[test]
fn test_msg_parse_get_con_with_opts() {
let ref_bin: [u8;57] = [0x40,0x02,0x00,0x37,0xb2,0x31,0x61,0x04,0x74,0x65,
0x6d,0x70,0x4d,0x1b,0x61,0x33,0x32,0x63,0x38,0x35,
0x62,0x61,0x39,0x64,0x64,0x61,0x34,0x35,0x38,0x32,
0x33,0x62,0x65,0x34,0x31,0x36,0x32,0x34,0x36,0x63,
0x66,0x38,0x62,0x34,0x33,0x33,0x62,0x61,0x61,0x30,
0x36,0x38,0x64,0x37,0xFF,0x39,0x39];
let msg = Message::from_bin(&ref_bin).unwrap();
assert!(msg.version == 1);
assert!(msg.mtype == Mtype::Confirmable);
assert!(msg.code == Code::Post);
assert!(msg.mid == 0x0037);
assert!(msg.token == vec![]);
assert!(msg.options == vec![
option::Option{
number: option::Number::UriPath,
value: vec![0x31,0x61]
},
option::Option{
number: option::Number::UriPath,
value: vec![0x74,0x65,0x6d,0x70]
},
option::Option{
number: option::Number::UriQuery,
value: vec![0x61,0x33,0x32,0x63,0x38,0x35,0x62,0x61,0x39,0x64,
0x64,0x61,0x34,0x35,0x38,0x32,0x33,0x62,0x65,0x34,
0x31,0x36,0x32,0x34,0x36,0x63,0x66,0x38,0x62,0x34,
0x33,0x33,0x62,0x61,0x61,0x30,0x36,0x38,0x64,0x37]
},
]);
assert!(msg.payload == vec![0x39, 0x39]);
}