use std::borrow::{Cow};
use std::fmt::{Debug};
use std::net::{ToSocketAddrs};
use hyper::header::{Header, HeaderFormat};
use time::{Duration};
use error::{SSDPResult, MsgError};
use header::{HeaderRef, HeaderMut, MX};
use message::{self, MessageType};
use message::ssdp::{SSDPMessage};
use receiver::{SSDPReceiver, FromRawSSDP};
const NETWORK_TIMEOUT_OVERHEAD: u8 = 1;
const DEFAULT_UNICAST_TIMEOUT: u8 = 1 + NETWORK_TIMEOUT_OVERHEAD;
#[derive(Debug, Clone)]
pub struct SearchRequest {
message: SSDPMessage
}
impl SearchRequest {
pub fn new() -> SearchRequest {
SearchRequest{ message: SSDPMessage::new(MessageType::Search) }
}
pub fn unicast<A: ToSocketAddrs>(&mut self, dst_addr: A) -> SSDPResult<SSDPReceiver<SearchResponse>> {
let mut connectors = try!(message::all_local_connectors(None));
for connector in connectors.iter_mut() {
try!(self.message.send(connector, &dst_addr));
}
let mut raw_connectors = Vec::with_capacity(connectors.len());
raw_connectors.extend(connectors.into_iter().map(|conn| conn.deconstruct() ));
let opt_timeout = opt_unicast_timeout(self.get::<MX>());
Ok(try!(SSDPReceiver::new(raw_connectors, opt_timeout)))
}
pub fn multicast(&mut self) -> SSDPResult<SSDPReceiver<SearchResponse>> {
let mcast_addr = (message::UPNP_MULTICAST_ADDR, message::UPNP_MULTICAST_PORT);
let mcast_timeout = try!(multicast_timeout(self.get::<MX>()));
let mcast_ttl = Some(message::UPNP_MULTICAST_TTL);
let mut connectors = try!(message::all_local_connectors(mcast_ttl));
for conn in connectors.iter_mut() {
try!(self.message.send(conn, &mcast_addr));
}
let mut raw_connectors = Vec::with_capacity(connectors.len());
raw_connectors.extend(connectors.into_iter().map(|conn| conn.deconstruct() ));
Ok(try!(SSDPReceiver::new(raw_connectors, Some(mcast_timeout))))
}
}
fn multicast_timeout(mx: Option<&MX>) -> SSDPResult<Duration> {
match mx {
Some(&MX(n)) => Ok(Duration::seconds((n + NETWORK_TIMEOUT_OVERHEAD) as i64)),
None => try!(Err(MsgError::new("Multicast Searches Require An MX Header")))
}
}
fn opt_unicast_timeout(mx: Option<&MX>) -> Option<Duration> {
match mx {
Some(&MX(n)) => Some(Duration::seconds((n + NETWORK_TIMEOUT_OVERHEAD) as i64)),
None => Some(Duration::seconds(DEFAULT_UNICAST_TIMEOUT as i64))
}
}
impl FromRawSSDP for SearchRequest {
fn raw_ssdp(bytes: &[u8]) -> SSDPResult<SearchRequest> {
let message = try!(SSDPMessage::raw_ssdp(bytes));
if message.message_type() != MessageType::Search {
try!(Err(MsgError::new("SSDP Message Received Is Not A SearchRequest")))
} else {
Ok(SearchRequest{ message: message })
}
}
}
impl HeaderRef for SearchRequest {
fn get<H>(&self) -> Option<&H> where H: Header + HeaderFormat {
self.message.get::<H>()
}
fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
self.message.get_raw(name)
}
}
impl HeaderMut for SearchRequest {
fn set<H>(&mut self, value: H) where H: Header + HeaderFormat {
self.message.set(value)
}
fn set_raw<K>(&mut self, name: K, value: Vec<Vec<u8>>) where K: Into<Cow<'static, str>> + Debug {
self.message.set_raw(name, value)
}
}
#[derive(Debug, Clone)]
pub struct SearchResponse {
message: SSDPMessage
}
impl SearchResponse {
pub fn new() -> SearchResponse {
SearchResponse{ message: SSDPMessage::new(MessageType::Response) }
}
pub fn unicast<A: ToSocketAddrs>(&mut self, dst_addr: A) -> SSDPResult<()> {
let mut connectors = try!(message::all_local_connectors(None));
for conn in connectors.iter_mut() {
try!(self.message.send(conn, &dst_addr));
}
Ok(())
}
}
impl FromRawSSDP for SearchResponse {
fn raw_ssdp(bytes: &[u8]) -> SSDPResult<SearchResponse> {
let message = try!(SSDPMessage::raw_ssdp(bytes));
if message.message_type() != MessageType::Response {
try!(Err(MsgError::new("SSDP Message Received Is Not A SearchResponse")))
} else {
Ok(SearchResponse{ message: message })
}
}
}
impl HeaderRef for SearchResponse {
fn get<H>(&self) -> Option<&H> where H: Header + HeaderFormat {
self.message.get::<H>()
}
fn get_raw(&self, name: &str) -> Option<&[Vec<u8>]> {
self.message.get_raw(name)
}
}
impl HeaderMut for SearchResponse {
fn set<H>(&mut self, value: H) where H: Header + HeaderFormat {
self.message.set(value)
}
fn set_raw<K>(&mut self, name: K, value: Vec<Vec<u8>>) where K: Into<Cow<'static, str>> + Debug {
self.message.set_raw(name, value)
}
}
#[cfg(test)]
mod tests {
use header::{MX};
#[test]
fn positive_multicast_timeout() {
super::multicast_timeout(Some(&MX(5))).unwrap();
}
#[test]
fn positive_some_opt_multicast_timeout() {
super::opt_unicast_timeout(Some(&MX(5))).unwrap();
}
#[test]
fn positive_none_opt_multicast_timeout() {
super::opt_unicast_timeout(None).unwrap();
}
#[test]
#[should_panic]
fn negative_multicast_timeout() {
super::multicast_timeout(None).unwrap();
}
}