Documentation
use poolio::{PanicSwitch, ThreadPool};

use std::io::prelude::*;
use std::net::TcpListener;
use std::net::TcpStream;
use std::thread;
use std::time::Duration;

const SIZE: usize = 4;

#[test]
fn test_http() {
    const ADDR: &str = "127.0.0.1:7878";

    thread::spawn(|| http_server(ADDR));

    let pool = threadpool::ThreadPool::new(SIZE);
    for _ in 0..10 {
        pool.execute(|| http_client(ADDR, &Method::Get));
        pool.execute(|| http_client(ADDR, &Method::Head));
        pool.execute(|| http_client(ADDR, &Method::Put));
    }
}

enum Method {
    Get,
    Head,
    Put,
}

fn http_client(addr: &str, method: &Method) {
    let mut stream = TcpStream::connect(addr).unwrap();

    let request = match method {
        Method::Get => request("GET", "/"),
        Method::Head => request("HEAD", "/"),
        Method::Put => request("PUT", "/"),
    };

    stream.write_all(request.as_bytes()).unwrap();
    stream.flush().unwrap();

    let mut buffer = [0; 1024];

    let _ = stream.read(&mut buffer).unwrap();

    let ok = b"HTTP/1.1 200 OK\r\n";
    let err = b"HTTP/1.1 404 NOT FOUND\r\n";

    match method {
        Method::Put | Method::Get => assert!(buffer.starts_with(ok)),
        Method::Head => assert!(buffer.starts_with(err)),
    };
}

fn request(method: &str, url: &str) -> String {
    let request = format!("{} {} HTTP/1.1", method, url);
    let headers = "";
    let body = "";
    format!("{}\r\n{}\r\n{}", request, headers, body)
}

fn http_server(addr: &str) {
    let pool = ThreadPool::new(SIZE, PanicSwitch::Respawn).unwrap();

    let listener = TcpListener::bind(addr).unwrap();

    for stream in listener.incoming() {
        let stream = stream.unwrap();

        pool.execute(|| {
            handle_connection(stream);
        });
    }
}

fn handle_connection(mut stream: TcpStream) {
    let mut buffer = [0; 1024];

    let _ = stream.read(&mut buffer).unwrap();

    let get = b"GET / HTTP/1.1\r\n";
    let put = b"PUT / HTTP/1.1\r\n";

    let response = if buffer.starts_with(get) {
        response("HTTP/1.1 200 OK")
    } else if buffer.starts_with(put) {
        thread::sleep(Duration::from_secs(1));
        response("HTTP/1.1 200 OK")
    } else {
        response("HTTP/1.1 404 NOT FOUND")
    };

    stream.write_all(response.as_bytes()).unwrap();
    stream.flush().unwrap();
}

fn response(status: &str) -> String {
    let headers = "";
    let body = "";
    format!("{}\r\n{}\r\n{}", status, headers, body)
}