qrport 0.1.0

transfering file to cell phone using QR code
Documentation
use std::cell::RefCell;
use std::collections::HashMap;
use std::net::{TcpStream, TcpListener, SocketAddr};
use std::io::{Read, Write};
use std::fs::File;
use std::thread;

#[derive(Debug)]
pub enum HttpError {
    NoBody,
    NoProtocol,
    InvalidProtocol,
    InvalidHeader,
    IOError(std::io::Error),
}

impl From<std::io::Error> for HttpError {
    fn from(err: std::io::Error) -> HttpError {
        HttpError::IOError(err)
    }
}

#[derive(Debug)]
pub struct Request {
    stream: RefCell<TcpStream>,
    pub method: String,
    pub path: String,
    pub headers: HashMap<String, String>,
    pub body: String,
}

impl TryFrom<TcpStream> for Request {
    type Error = HttpError;
    fn try_from(value: TcpStream) -> Result<Self, Self::Error> {
        parse(value)
    }
}

pub type HttpHandler = fn(req: Request);

impl Request {
    pub fn send_bytes(&self, bytes: &Vec<u8>) -> Result<(), HttpError> {
        let response = b"HTTP/1.1 200 OK\r
Content-Type: application/octet-stream\r
\r\n";
        self.stream.borrow_mut().write(response)?;
        self.stream.borrow_mut().write(&bytes)?;
        Ok(())
    }

    pub fn send_file(&self, path: &str) -> Result<(), HttpError> {

        let abs_path = if path.starts_with("~") {
            let home = std::env::var("HOME").unwrap();
            format!("{}{}", home, path.strip_prefix("~").unwrap())
        } else {
            path.to_string()
        };

        let mut file = File::open(&abs_path).unwrap();
        let mut content = Vec::new();
        file.read_to_end(&mut content).unwrap();
        self.send_bytes(&content)
    }

    pub fn not_found(&self, msg: &str) -> Result<(), HttpError> {
        let response = b"HTTP/1.1 404 Not Found\r
\r";
        self.stream.borrow_mut().write(response)?;
        self.stream.borrow_mut().write(msg.as_bytes())?;
        Ok(())
    }

    pub fn addr(&self) -> Result<SocketAddr, HttpError> {
        Ok(self.stream.borrow_mut().peer_addr()?)
    }
}

fn parse(mut stream: TcpStream) -> Result<Request, HttpError> {
    let mut buf = [0u8; 4096];
    let len = stream.read(&mut buf)?;
    let req = String::from_utf8_lossy(&buf[0..len]);

    let (raw_headers, body) = req.split_once("\r\n\r\n").ok_or(HttpError::NoBody)?;

    let mut splitted = raw_headers.split("\r\n");

    let mut protocol = splitted.next()
        .ok_or(HttpError::NoProtocol)?
        .split(' ');

    let method = protocol.next().ok_or(HttpError::InvalidProtocol)?;
    let path = protocol.next().ok_or(HttpError::InvalidProtocol)?;
    let _version = protocol.next().ok_or(HttpError::InvalidProtocol)?;

    let mut headers = HashMap::new();
    for header in splitted {
        let (key, value) = header.split_once(":").ok_or(HttpError::InvalidHeader)?;
        headers.insert(key.to_string(), value.to_string());
    }

    Ok(Request {
        stream: RefCell::new(stream),
        method: method.to_string(),
        path: path.to_string(),
        headers,
        body: body.to_string(),
    })
}

pub fn serve<T>(port: u16, handler: T) where T: Fn(&Request) + Send + Clone + 'static {
    let listener = TcpListener::bind(format!("0.0.0.0:{}", port)).unwrap();

    for stream in listener.incoming() {
        let handler = handler.clone();
        match stream {
            Ok(stream) => {
                thread::spawn(move || {
                    let request = Request::try_from(stream).unwrap();
                    handler(&request)
                });
            }
            Err(e) => {
                println!("Unable to connect: {}", e);
            }
        }
    }
}