makepad_http/
utils.rs

1use std::net::{TcpStream, Shutdown, SocketAddr};
2use std::io::BufReader;
3use std::io::prelude::*;
4
5pub fn write_bytes_to_tcp_stream_no_error(tcp_stream: &mut TcpStream, bytes: &[u8]) -> bool {
6    let bytes_total = bytes.len();
7    let mut bytes_left = bytes_total;
8    while bytes_left > 0 {
9        let buf = &bytes[(bytes_total - bytes_left)..bytes_total];
10        if let Ok(bytes_written) = tcp_stream.write(buf) {
11            if bytes_written == 0 {
12                return true
13            }
14            bytes_left -= bytes_written;
15        }
16        else {
17            return true
18        }
19    }
20    false
21}
22
23pub fn http_error_out(mut tcp_stream: TcpStream, code: usize) {
24    write_bytes_to_tcp_stream_no_error(&mut tcp_stream, format!("HTTP/1.1 {}\r\n\r\n", code).as_bytes());
25    let _ = tcp_stream.shutdown(Shutdown::Both);
26}
27
28
29pub fn split_header_line<'a>(inp: &'a str, what: &str) -> Option<&'a str> {
30    let mut what_lc = what.to_string();
31    what_lc.make_ascii_lowercase();
32    let mut inp_lc = inp.to_string();
33    inp_lc.make_ascii_lowercase();
34    if inp_lc.starts_with(&what_lc) {
35        return Some(&inp[what.len()..(inp.len() - 2)])
36    }
37    None
38}
39
40pub fn parse_url_path(url: &str) -> Option<(String, Option<String>)> {
41    
42    // find the end_of_name skipping everything else
43    let end_of_name = url.find(' ');
44    end_of_name ?;
45    let end_of_name = end_of_name.unwrap();
46    let mut search = None;
47    let end_of_name = if let Some(q) = url.find('?') {
48        search = Some(url[q..].to_string());
49        end_of_name.min(q)
50    }else {end_of_name};
51    
52    let mut url = url[0..end_of_name].to_string();
53    
54    if url.ends_with('/') {
55        url.push_str("index.html");
56    }
57    
58    Some((url, search))
59}
60
61#[derive(Debug)]
62pub struct HttpServerHeaders {
63    pub addr: SocketAddr,
64    pub lines: Vec<String>,
65    pub verb: String,
66    pub path: String,
67    pub path_no_slash: String,
68    pub search: Option<String>,
69    pub content_length: Option<u64>,
70    pub accept_encoding: Option<String>,
71    pub sec_websocket_key: Option<String>
72}
73
74impl HttpServerHeaders {
75    pub fn from_tcp_stream(tcp_stream: &mut TcpStream) -> Option<HttpServerHeaders> {
76        let addr = tcp_stream.peer_addr().unwrap();
77        let mut reader = BufReader::new(tcp_stream);
78        
79        let mut lines = Vec::new();
80        let mut content_length = None;
81        let mut accept_encoding = None;
82        let mut sec_websocket_key = None;
83        let mut line = String::new();
84        
85        while reader.read_line(&mut line).is_ok() { // TODO replace this with a non-line read
86            if line == "\r\n" { // the newline
87                break;
88            }
89            if let Some(v) = split_header_line(&line, "Content-Length: ") {
90                content_length = Some(if let Ok(v) = v.parse() {v} else {
91                    return None
92                });
93            }
94            if let Some(v) = split_header_line(&line, "Accept-Encoding: ") {
95                accept_encoding = Some(v.to_string());
96            }
97            if let Some(v) = split_header_line(&line, "sec-websocket-key: ") {
98                sec_websocket_key = Some(v.to_string());
99            }
100            if line.len() > 4096 || lines.len() > 4096 { // some overflow protection
101                return None
102            }
103            lines.push(line.clone());
104            line.clear();
105        }
106        if lines.len() <2 {
107            return None;
108        }
109        let verb;
110        let path;
111        if let Some(v) = split_header_line(&lines[0], "GET ") {
112            verb = "GET";
113            path = parse_url_path(v)
114        }
115        else if let Some(v) = split_header_line(&lines[0], "POST ") {
116            verb = "POST";
117            path = parse_url_path(v)
118        }
119        else if let Some(v) = split_header_line(&lines[0], "PUT ") {
120            verb = "PUT";
121            path = parse_url_path(v)
122        }
123        else if let Some(v) = split_header_line(&lines[0], "DELETE ") {
124            verb = "DELETE";
125            path = parse_url_path(v)
126        }
127        else {
128            return None
129        }
130        path.as_ref() ?;
131        let path = path.unwrap();
132        
133        Some(HttpServerHeaders {
134            addr,
135            verb: verb.to_string(),
136            path_no_slash: path.0[1..].to_string(),
137            path: path.0,
138            search: path.1,
139            lines,
140            content_length,
141            accept_encoding,
142            sec_websocket_key
143        })
144    }
145}