kern 1.8.3

General library for Rust
Documentation
use core::str;
use std::collections::HashMap;
use std::io::Write;
use std::net::TcpStream;

use crate::http::common::{HttpMethod, ReadWrite};
use crate::{Fail, Result};

use super::url::{Url, url_encode};

#[cfg(feature = "tls")]
use {
    rustls::{ClientConfig, ClientConnection, StreamOwned},
    std::sync::Arc,
};

pub fn send_content_length(stream: &mut impl Write, body: &[u8]) -> Result<()> {
    send_header(stream, "Content-Length", &body.len().to_string()).or_else(Fail::from)
}

pub fn end_headers(stream: &mut impl Write) -> Result<()> {
    stream.write_all("\r\n".as_bytes()).or_else(Fail::from)
}

pub fn send_main_header(
    stream: &mut impl Write,
    method: HttpMethod,
    url: &Url,
    query: &HashMap<String, String>,
) -> Result<()> {
    stream.write_all(method.as_str().as_bytes())?;
    stream.write_all(" /".as_bytes())?;
    stream.write_all(url_encode(url.path, true).as_bytes())?;

    for (i, (name, value)) in query.iter().enumerate() {
        stream.write_all(&[if i == 0 { b'?' } else { b'&' }])?;
        stream.write_all(url_encode(name, false).as_bytes())?;
        stream.write_all(b"=")?;
        stream.write_all(url_encode(value, false).as_bytes())?;
    }

    stream.write_all(" HTTP/1.1\r\nHost: ".as_bytes())?;
    stream.write_all(url.server_name.as_bytes())?;
    stream.write_all("\r\n".as_bytes())?;
    Ok(())
}

pub fn send_header(stream: &mut impl Write, name: &str, value: &str) -> Result<()> {
    stream.write_all(name.as_bytes())?;
    stream.write_all(": ".as_bytes())?;
    stream.write_all(value.as_bytes())?;
    stream.write_all("\r\n".as_bytes()).or_else(Fail::from)
}

pub fn connect(
    #[cfg(feature = "tls")] config: Arc<ClientConfig>,
    url: &Url,
) -> Result<Box<dyn ReadWrite>> {
    let stream = TcpStream::connect(&url.addr)?;

    let stream: Box<dyn ReadWrite> = if url.secure {
        #[cfg(not(feature = "tls"))]
        return Fail::from("tls feature not enabled");

        #[cfg(feature = "tls")]
        {
            let server_name = url.server_name.to_string().try_into()?;
            let conn = ClientConnection::new(config, server_name).or_else(Fail::from)?;
            Box::new(StreamOwned::new(conn, stream))
        }
    } else {
        Box::new(stream)
    };

    Ok(stream)
}