Skip to content
Snippets Groups Projects
Commit a021c24d authored by Joakim Lundberg's avatar Joakim Lundberg
Browse files

Test of Cargo build system

parent a4a05fca
No related branches found
No related tags found
No related merge requests found
[package]
name = "xoap"
version = "0.1.0"
description = "A CoAP library for bare-metal applications"
readme = "README.md"
documentation = ""
repository = ""
license = "MIT OR Apache-2.0"
authors = ["Joakim Lundberg <joakim@joakimlundberg.com>"]
keywords = ["CoAP", "xoap"]
[dependencies]
#alloc-cortex-m = {}
[dependencies.smoltcp]
version = "0.4"
default-features = false
features = ["alloc"]
[dependencies.nb]
git = "https://github.com/japaric/nb"
[dependencies.cast]
default-features = false
version = "0.2.2"
# xoap
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
A fast and stable [Constrained Application Protocol(CoAP)](https://tools.ietf.org/html/rfc7252) library implemented in Rust.
built from
[Documentation](http://covertness.github.io/coap-rs/coap/index.html)
## Installation
First add this to your `Cargo.toml`:
```toml
[dependencies]
xoap = "0.5"
```
Then, add this to your crate root:
```rust
extern crate xoap;
```
## Example
### Server:
```rust
extern crate xoap;
use std::io;
use xoap::{CoAPServer, CoAPResponse, CoAPRequest};
fn request_handler(req: CoAPRequest) -> Option<CoAPResponse> {
println!("Receive request: {:?}", req);
// Return the auto-generated response
req.response
}
fn main() {
let addr = "127.0.0.1:5683";
let mut server = CoAPServer::new(addr).unwrap();
server.handle(request_handler).unwrap();
println!("Server up on {}", addr);
println!("Press any key to stop...");
io::stdin().read_line(&mut String::new()).unwrap();
println!("Server shutdown");
}
```
### Client:
```rust
extern crate coap;
use coap::{CoAPClient, CoAPResponse};
fn main() {
let url = "coap://127.0.0.1:5683/Rust";
println!("Client request: {}", url);
let response: CoAPResponse = CoAPClient::request(url).unwrap();
println!("Server reply: {}", String::from_utf8(response.message.payload).unwrap());
}
```
## Benchmark
```bash
$ cargo run --example server
$ cargo bench
```
[dependencies.core]
stage = 0
[dependencies.alloc]
stage = 0
[dependencies.compiler_builtins]
stage = 1
//! Implementation of the [CoAP Protocol][spec].
//!
//! This library provides both a client interface (`CoAPClient`)
//! and a server interface (`CoAPServer`).
//!
//! [spec]: https://tools.ietf.org/html/rfc7252
//!
#![deny(missing_docs)]
#![deny(warnings)]
#[allow(deprecated)]
#![feature(collections)]
#![no_std]
#![allow(unused_mut)]
//#[macro_use]
extern crate smoltcp;
extern crate nb;
extern crate cast;
extern crate alloc_cortex_m;
#[macro_use]
extern crate collections;
#[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));
}
//use bincode;
use alloc::BTreeMap;
use alloc::LinkedList;
//use num::FromPrimitive;
use message::header;
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 CoAPOption {
IfMatch,
UriHost,
ETag,
IfNoneMatch,
Observe,
UriPort,
LocationPath,
UriPath,
ContentFormat,
MaxAge,
UriQuery,
Accept,
LocationQuery,
Block2,
Block1,
ProxyUri,
ProxyScheme,
Size1,
}
enum_from_primitive! {
#[derive(PartialEq, Eq, Debug)]
pub enum ContentFormat {
TextPlain = 0,
ApplicationLinkFormat = 40,
ApplicationXML = 41,
ApplicationOctetStream = 42,
ApplicationEXI = 47,
ApplicationJSON = 50,
}
}
#[derive(Debug)]
pub enum PackageError {
InvalidHeader,
InvalidPacketLength,
}
#[derive(Debug)]
pub enum ParseError {
InvalidHeader,
InvalidTokenLength,
InvalidOptionDelta,
InvalidOptionLength,
}
#[derive(Debug)]
pub struct Packet {
pub header: header::Header,
token: Vec<u8>,
options: BTreeMap<usize, LinkedList<Vec<u8>>>,
pub payload: Vec<u8>,
}
impl Packet {
pub fn new() -> Packet {
Packet {
header: header::Header::new(),
token: Vec::new(),
options: BTreeMap::new(),
payload: Vec::new(),
}
}
pub fn set_token(&mut self, token: Vec<u8>) {
self.header.set_token_length(token.len() as u8);
self.token = token;
}
pub fn get_token(&self) -> &Vec<u8> {
return &self.token;
}
pub fn set_option(&mut self, tp: CoAPOption, value: LinkedList<Vec<u8>>) {
let num = Self::get_option_number(tp);
self.options.insert(num, value);
}
pub fn set_content_format(&mut self, cf: ContentFormat) {
let content_format = cf as u16;
let msb = (content_format >> 8) as u8;
let lsb = (content_format & 0xFF) as u8;
let content_format: Vec<u8> = vec![msb, lsb];
self.add_option(CoAPOption::ContentFormat, content_format);
}
pub fn set_payload(&mut self, payload: Vec<u8>) {
self.payload = payload;
}
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) => {
list.push_back(value);
return;
}
None => (),
};
let mut list = LinkedList::new();
list.push_back(value);
self.options.insert(num, list);
}
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()),
None => None,
}
}
pub fn get_content_format(&self) -> Option<ContentFormat> {
if let Some(list) = self.get_option(CoAPOption::ContentFormat) {
if let Some(vector) = list.front() {
let msb = vector[0] as u16;
let lsb = vector[1] as u16;
let number = (msb << 8) + lsb;
return ContentFormat::from_u16(number);
}
}
None
}
/// Decodes a byte slice and construct the equivalent Packet.
pub fn from_bytes(buf: &[u8]) -> Result<Packet, ParseError> {
let header_result: bincode::DecodingResult<header::HeaderRaw> = bincode::decode(buf);
match header_result {
Ok(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;
if token_length > 8 {
return Err(ParseError::InvalidTokenLength);
}
if options_start > buf.len() {
return Err(ParseError::InvalidTokenLength);
}
let token = buf[4..options_start].to_vec();
let mut idx = options_start;
let mut options_number = 0;
let mut options: BTreeMap<usize, LinkedList<Vec<u8>>> = BTreeMap::new();
while idx < buf.len() {
let byte = buf[idx];
if byte == 255 || idx > buf.len() {
break;
}
let mut delta = (byte >> 4) as usize;
let mut length = (byte & 0xF) as usize;
idx += 1;
// Check for special delta characters
match delta {
13 => {
if idx >= buf.len() {
return Err(ParseError::InvalidOptionLength);
}
delta = buf[idx] as usize + 13;
idx += 1;
}
14 => {
if idx + 1 >= buf.len() {
return Err(ParseError::InvalidOptionLength);
}
delta = (u16::from_be(u8_to_unsigned_be!(buf, idx, idx + 1, u16)) + 269)
as usize;
idx += 2;
}
15 => {
return Err(ParseError::InvalidOptionDelta);
}
_ => {}
};
// Check for special length characters
match length {
13 => {
if idx >= buf.len() {
return Err(ParseError::InvalidOptionLength);
}
length = buf[idx] as usize + 13;
idx += 1;
}
14 => {
if idx + 1 >= buf.len() {
return Err(ParseError::InvalidOptionLength);
}
length = (u16::from_be(u8_to_unsigned_be!(buf, idx, idx + 1, u16))
+ 269) as usize;
idx += 2;
}
15 => {
return Err(ParseError::InvalidOptionLength);
}
_ => {}
};
options_number += delta;
let end = idx + length;
if end > buf.len() {
return Err(ParseError::InvalidOptionLength);
}
let options_value = buf[idx..end].to_vec();
if options.contains_key(&options_number) {
let mut options_list = options.get_mut(&options_number).unwrap();
options_list.push_back(options_value);
} else {
let mut list = LinkedList::new();
list.push_back(options_value);
options.insert(options_number, list);
}
idx += length;
}
let mut payload = Vec::new();
if idx < buf.len() {
payload = buf[(idx + 1)..buf.len()].to_vec();
}
Ok(Packet {
header: header,
token: token,
options: options,
payload: payload,
})
}
Err(_) => Err(ParseError::InvalidHeader),
}
}
/// Returns a vector of bytes representing the Packet.
pub fn to_bytes(&self) -> Result<Vec<u8>, PackageError> {
let mut options_delta_length = 0;
let mut options_bytes: Vec<u8> = Vec::new();
for (number, value_list) in self.options.iter() {
for value in value_list.iter() {
let mut header: Vec<u8> = Vec::with_capacity(1 + 2 + 2);
let delta = number - options_delta_length;
let mut byte: u8 = 0;
if delta <= 12 {
byte |= (delta << 4) as u8;
} else if delta < 269 {
byte |= 13 << 4;
} else {
byte |= 14 << 4;
}
if value.len() <= 12 {
byte |= value.len() as u8;
} else if value.len() < 269 {
byte |= 13;
} else {
byte |= 14;
}
header.push(byte);
if delta > 12 && delta < 269 {
header.push((delta - 13) as u8);
} else if delta >= 269 {
let fix = (delta - 269) as u16;
header.push((fix >> 8) as u8);
header.push((fix & 0xFF) as u8);
}
if value.len() > 12 && value.len() < 269 {
header.push((value.len() - 13) as u8);
} else if value.len() >= 269 {
let fix = (value.len() - 269) as u16;
header.push((fix >> 8) as u8);
header.push((fix & 0xFF) as u8);
}
options_delta_length += delta;
options_bytes.reserve(header.len() + value.len());
unsafe {
use std::ptr;
let buf_len = options_bytes.len();
ptr::copy(
header.as_ptr(),
options_bytes.as_mut_ptr().offset(buf_len as isize),
header.len(),
);
ptr::copy(
value.as_ptr(),
options_bytes
.as_mut_ptr()
.offset((buf_len + header.len()) as isize),
value.len(),
);
options_bytes.set_len(buf_len + header.len() + value.len());
}
}
}
let mut buf_length = 4 + self.payload.len() + self.token.len();
if self.header.code != header::MessageClass::Empty && self.payload.len() != 0 {
buf_length += 1;
}
buf_length += options_bytes.len();
if buf_length > 1280 {
return Err(PackageError::InvalidPacketLength);
}
let mut buf: Vec<u8> = Vec::with_capacity(buf_length);
let header_result: bincode::EncodingResult<()> = bincode::encode_into(
&self.header.to_raw(),
&mut buf,
bincode::SizeLimit::Infinite,
);
match header_result {
Ok(_) => {
buf.reserve(self.token.len() + options_bytes.len());
unsafe {
use std::ptr;
let buf_len = buf.len();
ptr::copy(
self.token.as_ptr(),
buf.as_mut_ptr().offset(buf_len as isize),
self.token.len(),
);
ptr::copy(
options_bytes.as_ptr(),
buf.as_mut_ptr()
.offset((buf_len + self.token.len()) as isize),
options_bytes.len(),
);
buf.set_len(buf_len + self.token.len() + options_bytes.len());
}
if self.header.code != header::MessageClass::Empty && self.payload.len() != 0 {
buf.push(0xFF);
buf.reserve(self.payload.len());
unsafe {
use std::ptr;
let buf_len = buf.len();
ptr::copy(
self.payload.as_ptr(),
buf.as_mut_ptr().offset(buf.len() as isize),
self.payload.len(),
);
buf.set_len(buf_len + self.payload.len());
}
}
Ok(buf)
}
Err(_) => Err(PackageError::InvalidHeader),
}
}
fn get_option_number(tp: CoAPOption) -> usize {
match tp {
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,
}
}
}
#[cfg(test)]
mod test {
use super::*;
use super::super::header;
use std::collections::LinkedList;
#[test]
fn test_decode_packet_with_options() {
let buf = [
0x44,
0x01,
0x84,
0x9e,
0x51,
0x55,
0x77,
0xe8,
0xb2,
0x48,
0x69,
0x04,
0x54,
0x65,
0x73,
0x74,
0x43,
0x61,
0x3d,
0x31,
];
let packet = Packet::from_bytes(&buf);
assert!(packet.is_ok());
let packet = packet.unwrap();
assert_eq!(packet.header.get_version(), 1);
assert_eq!(packet.header.get_type(), header::MessageType::Confirmable);
assert_eq!(packet.header.get_token_length(), 4);
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(CoAPOption::UriPath);
assert!(uri_path.is_some());
let uri_path = uri_path.unwrap();
let mut expected_uri_path = LinkedList::new();
expected_uri_path.push_back("Hi".as_bytes().to_vec());
expected_uri_path.push_back("Test".as_bytes().to_vec());
assert_eq!(uri_path, expected_uri_path);
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();
expected_uri_query.push_back("a=1".as_bytes().to_vec());
assert_eq!(uri_query, expected_uri_query);
}
#[test]
fn test_decode_packet_with_payload() {
let buf = [
0x64,
0x45,
0x13,
0xFD,
0xD0,
0xE2,
0x4D,
0xAC,
0xFF,
0x48,
0x65,
0x6C,
0x6C,
0x6F,
];
let packet = Packet::from_bytes(&buf);
assert!(packet.is_ok());
let packet = packet.unwrap();
assert_eq!(packet.header.get_version(), 1);
assert_eq!(
packet.header.get_type(),
header::MessageType::Acknowledgement
);
assert_eq!(packet.header.get_token_length(), 4);
assert_eq!(
packet.header.code,
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());
}
#[test]
fn test_encode_packet_with_options() {
let mut packet = Packet::new();
packet.header.set_version(1);
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(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,
]
);
}
#[test]
fn test_encode_packet_with_payload() {
let mut packet = Packet::new();
packet.header.set_version(1);
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();
assert_eq!(
packet.to_bytes().unwrap(),
vec![
0x64,
0x45,
0x13,
0xFD,
0xD0,
0xE2,
0x4D,
0xAC,
0xFF,
0x48,
0x65,
0x6C,
0x6C,
0x6F,
]
);
}
#[test]
fn test_encode_decode_content_format() {
let mut packet = Packet::new();
packet.set_content_format(ContentFormat::ApplicationJSON);
assert_eq!(
ContentFormat::ApplicationJSON,
packet.get_content_format().unwrap()
)
}
#[test]
fn test_decode_empty_content_format() {
let packet = Packet::new();
assert!(packet.get_content_format().is_none());
}
#[test]
fn test_malicious_packet() {
use rand;
use quickcheck::{QuickCheck, StdGen, TestResult};
fn run(x: Vec<u8>) -> TestResult {
match Packet::from_bytes(&x[..]) {
Ok(packet) => TestResult::from_bool(
packet.get_token().len() == packet.header.get_token_length() as usize,
),
Err(_) => TestResult::passed(),
}
}
QuickCheck::new()
.tests(10000)
.gen(StdGen::new(rand::thread_rng(), 1500))
.quickcheck(run as fn(Vec<u8>) -> TestResult)
}
}
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());
}
}
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment