1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
extern crate hyper; extern crate hyper_native_tls; extern crate mime; extern crate serde; #[macro_use] extern crate serde_derive; extern crate serde_json; mod message; mod call; mod webhook; pub mod twiml; use std::io::Read; use self::hyper::header::{Authorization,Basic}; use hyper::net::HttpsConnector; use self::hyper::status::StatusCode; pub use self::hyper::method::Method::{Post,Get,Put}; use hyper_native_tls::NativeTlsClient; pub use message::{Message,OutboundMessage}; pub use call::{Call,OutboundCall}; use std::collections::HashMap; use std::io::Write; pub struct Client { account_id : String, auth_token : String, auth_header : Authorization<Basic>, } fn url_encode(params: &[(&str,&str)]) -> String { params.iter().map(|&t| { let (k,v) = t; format!("{}={}",k,v) }).fold("".to_string(), |mut acc, item| { acc.push_str(&item); acc.push_str("&"); acc }) } fn basic_auth_header(username: String, password: String) -> Authorization<Basic> { Authorization(Basic { username : username, password : Some(password) }) } #[derive(Debug)] pub enum TwilioError { NetworkError, HTTPError, ParsingError, AuthError, BadRequest, } pub trait FromMap { fn from_map(&HashMap<&str,&str>) -> Result<Box<Self>,TwilioError>; } impl Client { pub fn new(account_id: &str, auth_token: &str) -> Client { Client { account_id : account_id.to_string(), auth_token : auth_token.to_string(), auth_header : basic_auth_header(account_id.to_string(),auth_token.to_string()), } } fn send_request<T>(&self, method: hyper::method::Method, endpoint: &str, params: &[(&str,&str)]) -> Result<T, TwilioError> where T: serde::de::DeserializeOwned, { let url = format!("https://api.twilio.com/2010-04-01/Accounts/{}/{}.json",self.account_id,endpoint); let ssl = NativeTlsClient::new().unwrap(); let connector = HttpsConnector::new(ssl); let http_client = hyper::Client::with_connector(connector); let post_body: &str = &*url_encode(params); let mime: mime::Mime = "application/x-www-form-urlencoded".parse().unwrap(); let content_type_header = hyper::header::ContentType(mime); let req = http_client .request(method,&*url) .body(post_body) .header(self.auth_header.clone()) .header(content_type_header); let mut resp = match req.send() { Ok(res) => res, Err(_) => return Err(TwilioError::NetworkError), }; let mut body_str = "".to_string(); match resp.read_to_string(&mut body_str) { Ok(_) => (), Err(_) => return Err(TwilioError::NetworkError), }; match resp.status { StatusCode::Created | StatusCode::Ok => (), _ => { return Err(TwilioError::HTTPError) } }; let decoded: T = match serde_json::from_str(&body_str) { Ok(obj) => obj, Err(_) => return Err(TwilioError::ParsingError), }; Ok(decoded) } pub fn respond_to_webhook<T:FromMap,F>(&self, req: &mut hyper::server::Request, mut res: hyper::server::Response, mut logic: F) where F: FnMut(T) -> twiml::Twiml { let o: T = match self.parse_request::<T>(req){ Err(_) => { *res.status_mut() = hyper::BadRequest; let mut res = res.start().unwrap(); res.write_all("Error.".as_bytes()).unwrap(); res.end().unwrap(); return }, Ok(obj) => *obj, }; let t = logic(o); let body = t.as_twiml(); let mime: mime::Mime = "text/xml".parse().unwrap(); let content_type_header = hyper::header::ContentType(mime); res.headers_mut().set(content_type_header); res.headers_mut().set(hyper::header::ContentLength(body.len() as u64)); let mut res = res.start().unwrap(); res.write_all(body.as_bytes()).unwrap(); res.end().unwrap(); } }