use std::io::{prelude::*, BufReader};
use std::net::{TcpListener, TcpStream};
use crate::{Error, ErrorKind};
#[derive(Debug, PartialEq, Eq)]
pub struct LocalServer {
pub uri: String,
pub address: String,
pub response: String,
}
impl Default for LocalServer {
fn default() -> Self {
Self {
uri: String::from("http://localhost:7878"),
address: String::from("localhost:7878"),
response: String::from("request processed!"),
}
}
}
impl LocalServer {
pub fn new() -> Self {
Self { ..Default::default() }
}
pub fn at<T: AsRef<str>> ( mut self, address: T, port: usize ) -> Self {
self.uri = format!( "http://{}:{}", address.as_ref(), port );
self.address = format!( "{}:{}", address.as_ref(), port );
self
}
pub fn with_response<T: AsRef<str>> ( mut self, response: T ) -> Self {
self.response = String::from( response.as_ref() );
self
}
fn get_accepted_response( &self ) -> String {
format!(
"HTTP/1.1 202 Accepted\r\n\
Content-Type: text/html; charset=utf-8\r\n\
Connection: Closed\r\n\
\r\n\
{}",
self.response,
)
}
fn get_requested_uri( &self, request_lines: &[String] ) -> String {
let first_line = request_lines[0].clone();
let first_line_parts: Vec<&str> = first_line.split(' ').collect();
format!( "{}{}", self.uri, first_line_parts[1] )
}
fn get_request_uri( &self, mut stream: TcpStream ) -> crate::Result<String> {
let buf_reader = BufReader::new(&mut stream);
let request_lines: Vec<String> = buf_reader
.lines()
.map( |result| result.unwrap() )
.take_while( |line| !line.is_empty() )
.collect();
let response = self.get_accepted_response();
stream.write_all( response.as_bytes() )?;
Ok( self.get_requested_uri(&request_lines) )
}
pub fn wait_for_request( &self ) -> crate::Result<String> {
let tcp_listener = TcpListener::bind(&self.address)?;
if let Some(stream) = tcp_listener.incoming().next() {
let stream = stream?;
return self.get_request_uri(stream)
}
#[cfg(not(tarpaulin_include))]
Err( Error::new(
ErrorKind::LocalServer,
"unable to listen for incoming requests",
) )
}
}
#[cfg(test)]
mod tests {
use std::thread;
use super::LocalServer;
use crate::utils::test::{LOCAL_SERVER_IN_USE, curl_local_server};
#[test]
fn new_test() {
let server = LocalServer::new();
assert_eq!( server, LocalServer::default() );
}
#[test]
fn at_test() {
let server = LocalServer::new().at("test-path", 2024);
assert_eq!( server.address, String::from("test-path:2024") );
assert_eq!( server.uri, String::from("http://test-path:2024") );
}
#[test]
fn with_response_test() {
let server = LocalServer::new().with_response("test response");
assert_eq!( server.response, String::from("test response") );
}
#[test]
fn wait_for_request_test() {
let _unused = LOCAL_SERVER_IN_USE.lock().unwrap();
let handle = thread::spawn( || {
let local_server = LocalServer::new();
let request = local_server.wait_for_request().unwrap();
assert_eq!( request, format!("{}/test-request-url", local_server.uri) )
} );
let response = curl_local_server("test-request-url").unwrap();
assert_eq!( response, LocalServer::new().response );
handle.join().unwrap();
}
}