use std::{
io,io::Read,io::Write,
net::{SocketAddr,TcpStream},
str
};
use super::os_windows;
#[derive(Debug)]
pub struct Client {
stream: TcpStream,
addr: SocketAddr,
request: Option<String>
}
fn read_all(stream: &mut TcpStream) -> Result<Vec<u8>,io::ErrorKind> {
let mut result = Vec::new();
loop {
const BUF_SIZE: usize = 4096;
let mut buf: [u8; BUF_SIZE] = [0u8; BUF_SIZE];
match stream.read(&mut buf) {
Ok(val) => if val > 0 {
result.append(&mut Vec::from(&buf[0..val]));
if val < BUF_SIZE {
return Ok(result);
}
} else {
return Ok(result);
},
Err(e) => match e.kind() {
::std::io::ErrorKind::WouldBlock => return Ok(result),
::std::io::ErrorKind::TimedOut => match os_windows() {
true => return Ok(result),
false => return Err(::std::io::ErrorKind::TimedOut)
},
kind => return Err(kind)
}
};
}
}
fn extract_request_url(buf: &[u8]) -> Option<String> {
let s = str::from_utf8(buf).unwrap();
for line in s.split("\r\n") {
if line.starts_with("GET ") {
let components = line.split(" ").collect::<Vec<&str>>();
if components.len() < 2 {
warn!("Invalid GET line: {}", line);
continue;
}
return Some(String::from(*components.get(1).unwrap()));
}
}
None
}
impl Client {
pub(crate) fn new(mut stream : TcpStream, addr : SocketAddr) -> Result<Client,::std::io::Error> {
let data = read_all(&mut stream)?;
let request = extract_request_url(&data);
Ok(Client {
stream: stream,
addr: addr,
request: match request {
Some(s) => s.into(),
None => None
}
})
}
pub fn addr(&self) -> SocketAddr {
self.addr
}
pub fn request(&self) -> &Option<String> {
&self.request
}
pub fn respond_ok(&mut self, data: &[u8]) -> io::Result<usize> {
self.respond_ok_chunked(data, data.len())
}
pub fn respond_ok_chunked(&mut self, data: impl Read, content_size: usize) -> io::Result<usize> {
self.respond_chunked("200 OK", data, content_size, &vec!())
}
pub fn respond(
&mut self,
status_code: &str,
data: &[u8],
headers: &Vec<String>) -> io::Result<usize>
{
self.respond_chunked(status_code, data, data.len(), headers)
}
pub fn respond_chunked(
&mut self,
status_code: &str,
mut data: impl Read,
content_size: usize,
headers: &Vec<String>) -> io::Result<usize>
{
let mut bytes_written =
self.stream.write(format!("HTTP/1.0 {}\r\nContent-Length: {}\r\n", status_code, content_size).as_bytes())?;
for h in headers {
bytes_written += self.stream.write(format!("{}\r\n", h).as_ref())?;
}
bytes_written += self.stream.write("\r\n".as_bytes())?;
let mut buffer = [0; Self::CHUNK_SIZE];
loop {
let bytes_read = data.read(&mut buffer)?;
if bytes_read == 0 { break; }
bytes_written += self.stream.write(&buffer[..bytes_read])?;
}
Ok(bytes_written)
}
const CHUNK_SIZE: usize = 4096;
}