1use std::collections::HashMap;
2use std::net::{SocketAddr, UdpSocket};
3use std::str;
4use std::time::{Duration, Instant};
5
6use attohttpc::{Method, RequestBuilder};
7use log::debug;
8
9use crate::common::options::{DEFAULT_TIMEOUT, RESPONSE_TIMEOUT};
10use crate::common::{messages, parsing, SearchOptions};
11use crate::errors::SearchError;
12use crate::gateway::Gateway;
13
14pub fn search_gateway(options: SearchOptions) -> Result<Gateway, SearchError> {
31 let start = Instant::now();
32 let max_time = options.timeout.unwrap_or(DEFAULT_TIMEOUT);
33
34 let socket = UdpSocket::bind(options.bind_addr)?;
35
36 let read_timeout = options.single_search_timeout.unwrap_or(RESPONSE_TIMEOUT);
37 socket.set_read_timeout(Some(read_timeout))?;
38
39 socket.send_to(messages::SEARCH_REQUEST.as_bytes(), options.broadcast_address)?;
40
41 while start.elapsed() < max_time {
42 let mut buf = [0u8; 1500];
43
44 socket.set_read_timeout(Some(max_time - start.elapsed()))?;
46 let (read, _) = socket.recv_from(&mut buf)?;
47 let text = str::from_utf8(&buf[..read])?;
48
49 let (addr, root_url) = parsing::parse_search_result(text)?;
50
51 let (control_schema_url, control_url) = match get_control_urls(&addr, &root_url, max_time - start.elapsed()) {
52 Ok(o) => o,
53 Err(e) => {
54 debug!(
55 "Error has occurred while getting control urls. error: {}, addr: {}, root_url: {}",
56 e, addr, root_url
57 );
58 continue;
59 }
60 };
61
62 let control_schema = match get_schemas(&addr, &control_schema_url, max_time - start.elapsed()) {
63 Ok(o) => o,
64 Err(e) => {
65 debug!(
66 "Error has occurred while getting schemas. error: {}, addr: {}, control_schema_url: {}",
67 e, addr, control_schema_url
68 );
69 continue;
70 }
71 };
72
73 return Ok(Gateway {
74 addr,
75 root_url,
76 control_url,
77 control_schema_url,
78 control_schema,
79 });
80 }
81
82 Err(SearchError::NoResponseWithinTimeout)
83}
84
85fn get_control_urls(addr: &SocketAddr, root_url: &str, timeout: Duration) -> Result<(String, String), SearchError> {
86 let url = format!("http://{}:{}{}", addr.ip(), addr.port(), root_url);
87 match RequestBuilder::try_new(Method::GET, url) {
88 Ok(request_builder) => {
89 let response = request_builder.timeout(timeout).send()?;
90 parsing::parse_control_urls(&response.bytes()?[..])
91 }
92 Err(error) => Err(SearchError::HttpError(error)),
93 }
94}
95
96fn get_schemas(
97 addr: &SocketAddr,
98 control_schema_url: &str,
99 timeout: Duration,
100) -> Result<HashMap<String, Vec<String>>, SearchError> {
101 let url = format!("http://{}:{}{}", addr.ip(), addr.port(), control_schema_url);
102 match RequestBuilder::try_new(Method::GET, url) {
103 Ok(request_builder) => {
104 let response = request_builder.timeout(timeout).send()?;
105 parsing::parse_schemas(&response.bytes()?[..])
106 }
107 Err(error) => Err(SearchError::HttpError(error)),
108 }
109}