gnostr-cat 0.0.40

Command-line client for web sockets, like netcat/curl/socat for ws://.
Documentation
use std::fs::File;
use std::rc::Rc;

use futures::future::Future;

use self::hyper::http::h1::Incoming;
use self::hyper::method::Method;
use self::hyper::uri::RequestUri;
use self::hyper::uri::RequestUri::AbsolutePath;
use super::hyper;
use crate::my_copy::{copy, CopyOptions};
use crate::options::StaticFile;
use crate::trivial_peer::get_literal_peer_now;
use crate::Peer;

const BAD_REQUEST :&[u8] = b"HTTP/1.1 400 Bad Request\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nOnly WebSocket connections are welcome here\n";

const NOT_FOUND: &[u8] = b"HTTP/1.1 404 Not Found\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nURI does not match any -F option and is not a WebSocket connection.\n";

const NOT_FOUND2: &[u8] = b"HTTP/1.1 500 Not Found\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nFailed to open the file on server side.\n";

const BAD_METHOD :&[u8] = b"HTTP/1.1 400 Bad Request\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nHTTP method should be GET\n";

const BAD_URI_FORMAT :&[u8] = b"HTTP/1.1 400 Bad Request\r\nServer: websocat\r\nContent-Type: text/plain\r\nConnection: close\r\n\r\nURI should be an absolute path\n";

pub fn get_static_file_reply(len: Option<u64>, ct: &str) -> Vec<u8> {
    let mut q = Vec::with_capacity(256);
    q.extend_from_slice(b"HTTP/1.1 200 OK\r\nServer: websocat\r\nContent-Type: ");
    q.extend_from_slice(ct.as_bytes());
    q.extend_from_slice(b"\r\n");
    if let Some(x) = len {
        q.extend_from_slice(b"Content-Length: ");
        q.extend_from_slice(format!("{}", x).as_bytes());
        q.extend_from_slice(b"\r\n");
    }
    q.extend_from_slice(b"\r\n");
    q
}

#[cfg_attr(feature = "cargo-clippy", allow(needless_pass_by_value))]
pub fn http_serve(
    p: Peer,
    incoming: Option<Incoming<(Method, RequestUri)>>,
    serve_static_files: Rc<Vec<StaticFile>>,
) -> Box<dyn Future<Item = (), Error = ()>> {
    let mut serve_file = None;
    let content = if serve_static_files.is_empty() {
        BAD_REQUEST.to_vec()
    } else if let Some(inc) = incoming {
        info!("HTTP-serving {:?}", inc.subject);
        if inc.subject.0 == Method::Get {
            match inc.subject.1 {
                AbsolutePath(x) => {
                    let mut reply = None;
                    for sf in &*serve_static_files {
                        if sf.uri == x {
                            match File::open(&sf.file) {
                                Ok(f) => {
                                    let fs = match f.metadata() {
                                        Err(_) => None,
                                        Ok(x) => Some(x.len()),
                                    };
                                    reply = Some(get_static_file_reply(fs, &sf.content_type));
                                    serve_file = Some(f);
                                }
                                Err(_) => {
                                    reply = Some(NOT_FOUND2.to_vec());
                                }
                            }
                        }
                    }
                    reply.unwrap_or_else(|| NOT_FOUND.to_vec())
                }
                _ => BAD_URI_FORMAT.to_vec(),
            }
        } else {
            BAD_METHOD.to_vec()
        }
    } else {
        BAD_REQUEST.to_vec()
    };
    let reply = get_literal_peer_now(content);

    let co = CopyOptions {
        buffer_size: 1024,
        once: false,
        stop_on_reader_zero_read: true,
        skip: false,
        max_ops: None,
    };

    if let Some(f) = serve_file {
        Box::new(
            copy(reply, p.1, co, vec![])
                .map_err(drop)
                .and_then(move |(_len, _, conn)| {
                    let co2 = CopyOptions {
                        buffer_size: 65536,
                        once: false,
                        stop_on_reader_zero_read: true,
                        skip: false,
                        max_ops: None,
                    };
                    let wr = crate::file_peer::ReadFileWrapper(f);
                    copy(wr, conn, co2, vec![]).map(|_| ()).map_err(drop)
                }),
        )
    } else {
        Box::new(copy(reply, p.1, co, vec![]).map(|_| ()).map_err(drop))
    }
}