From b2e385e26897dc79b12357afcd5ffcc3d0b3ab83 Mon Sep 17 00:00:00 2001
From: Covertness <wuyingfengsui@gmail.com>
Date: Fri, 7 Aug 2015 22:29:38 +0800
Subject: [PATCH] add request API for client

---
 Cargo.toml                    |  7 ++--
 README.md                     | 21 +++---------
 examples/client.rs            | 12 +++++--
 examples/client_and_server.rs | 19 +++--------
 src/client.rs                 | 64 ++++++++++++++++++++++++++++++++++-
 src/lib.rs                    |  3 ++
 src/server.rs                 |  1 +
 7 files changed, 90 insertions(+), 37 deletions(-)

diff --git a/Cargo.toml b/Cargo.toml
index 97ee07c..30a5083 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -1,6 +1,6 @@
 [package]
 name = "coap"
-version = "0.1.1"
+version = "0.2.0"
 description = "A CoAP library"
 readme = "README.md"
 documentation = "http://covertness.github.io/coap-rs/coap/index.html"
@@ -13,4 +13,7 @@ keywords = ["CoAP"]
 bincode = "0.3.0"
 rustc-serialize = "0.3"
 mio = "0.3.7"
-threadpool = "0.1"
\ No newline at end of file
+threadpool = "0.1"
+url = "0.2.36"
+num = "0.1"
+rand = "0.3"
\ No newline at end of file
diff --git a/README.md b/README.md
index 28e0cd4..e7f3f11 100644
--- a/README.md
+++ b/README.md
@@ -17,7 +17,7 @@ First add this to your `Cargo.toml`:
 
 ```toml
 [dependencies]
-coap = "0.1.1"
+coap = "0.2.0"
 ```
 
 Then, add this to your crate root:
@@ -62,21 +62,10 @@ use coap::packet::*;
 use coap::CoAPClient;
 
 fn main() {
-	let addr = "127.0.0.1:5683";
-	let request = "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 response = client.receive().unwrap();
+	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());
 }
 ```
diff --git a/examples/client.rs b/examples/client.rs
index 6eacffc..eb4bfe0 100644
--- a/examples/client.rs
+++ b/examples/client.rs
@@ -6,7 +6,7 @@ use coap::CoAPClient;
 fn main() {
 	let addr = "127.0.0.1:5683";
 	let request = "test";
-		
+
 	let client = CoAPClient::new(addr).unwrap();
 	let mut packet = Packet::new();
 	packet.header.set_version(1);
@@ -18,6 +18,12 @@ fn main() {
 	client.send(&packet).unwrap();
 	println!("Client request: coap://{}/{}", addr, request);
 
-	let response = client.receive().unwrap();
-	println!("Server reply: {}", String::from_utf8(response.payload).unwrap());
+	match client.receive() {
+		Ok(response) => {
+			println!("Server reply: {}", String::from_utf8(response.payload).unwrap());
+		},
+		Err(e) => {
+			println!("Request error: {:?}", e);
+		}
+	}
 }
\ No newline at end of file
diff --git a/examples/client_and_server.rs b/examples/client_and_server.rs
index 2f69a92..cb4093b 100644
--- a/examples/client_and_server.rs
+++ b/examples/client_and_server.rs
@@ -19,23 +19,12 @@ fn request_handler(req: Packet, resp: CoAPClient) {
 }
 
 fn main() {
-	let addr = "127.0.0.1:5683";
-	let request = "test";
-
-	let mut server = CoAPServer::new(addr).unwrap();
+	let mut server = CoAPServer::new("127.0.0.1:5683").unwrap();
 	server.handle(request_handler).unwrap();
 		
-	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 url = "coap://127.0.0.1:5683/Rust";
+	println!("Client request: {}", url);
 
-	let response = client.receive().unwrap();
+	let response: Packet = CoAPClient::request(url).unwrap();
 	println!("Server reply: {}", String::from_utf8(response.payload).unwrap());
 }
\ No newline at end of file
diff --git a/src/client.rs b/src/client.rs
index 52fbbd5..152e08f 100644
--- a/src/client.rs
+++ b/src/client.rs
@@ -1,6 +1,9 @@
 use std::io::{Result, Error, ErrorKind};
 use std::net::{ToSocketAddrs, SocketAddr, UdpSocket};
-use packet::Packet;
+use url::{UrlParser, SchemeType};
+use num;
+use rand::{thread_rng, random, Rng};
+use packet::{Packet, PacketType, OptionType};
 
 pub struct CoAPClient {
     socket: UdpSocket,
@@ -33,6 +36,58 @@ impl CoAPClient {
 		})
 	}
 
+	/// Execute a request with the coap url.
+	pub fn request(url: &str) -> Result<Packet> {
+		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 message_id = thread_rng().gen_range(0, num::pow(2u32, 16)) as u16;
+				packet.header.set_message_id(message_id);
+
+				let mut token: Vec<u8> = vec!(1, 1, 1, 1);
+				for x in token.iter_mut() {
+				    *x = random()
+				}
+				packet.set_token(token.clone());
+
+				let domain = match url_params.domain() {
+					Some(d) => d,
+					None => return Err(Error::new(ErrorKind::InvalidInput, "domain error"))
+				};
+				let port = url_params.port_or_default().unwrap();
+
+				if let Some(path) = url_params.path() {
+					for p in path.iter() {
+						packet.add_option(OptionType::UriPath, p.clone().into_bytes().to_vec());
+					}
+				};
+
+				let client = try!(Self::new((domain, port)));
+				try!(client.send(&packet));
+
+				match client.receive() {
+				 	Ok(receive_packet) => {
+				 		if receive_packet.header.get_message_id() == message_id 
+				 			&& *receive_packet.get_token() == token {
+				 				return Ok(receive_packet)
+				 			} else {
+				 				return Err(Error::new(ErrorKind::Other, "receive invalid data"))
+				 			}
+				 	},
+				 	Err(e) => Err(e)
+				}
+			},
+			Err(_) => Err(Error::new(ErrorKind::InvalidInput, "url error"))
+		}
+	}
+
 	/// Execute a request.
 	pub fn send(&self, packet: &Packet) -> Result<()> {
 		match packet.to_bytes() {
@@ -58,4 +113,11 @@ impl CoAPClient {
 			Err(_) => Err(Error::new(ErrorKind::InvalidInput, "packet error"))
 		}
 	}
+
+	fn coap_scheme_type_mapper(scheme: &str) -> SchemeType {
+		match scheme {
+			"coap" => SchemeType::Relative(5683),
+			_ => SchemeType::NonRelative,
+		}
+	}
 }
\ No newline at end of file
diff --git a/src/lib.rs b/src/lib.rs
index f758ae7..eca1f6d 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -79,6 +79,9 @@ extern crate bincode;
 extern crate rustc_serialize;
 extern crate mio;
 extern crate threadpool;
+extern crate url;
+extern crate num;
+extern crate rand;
 
 pub use server::CoAPServer;
 pub use client::CoAPClient;
diff --git a/src/server.rs b/src/server.rs
index f32d3f2..ba291f8 100644
--- a/src/server.rs
+++ b/src/server.rs
@@ -123,6 +123,7 @@ impl CoAPServer {
 		}
 	}
 
+	/// Set the number of threads for handling requests
 	pub fn set_worker_num(&mut self, worker_num: usize) {
 		self.worker_num = worker_num;
 	}
-- 
GitLab