1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
use std::collections::HashMap;

#[derive(Debug)]
pub struct Request {
	pub path: String,
	pub method: Method,
	pub slugs: HashMap<String, String>,
	pub headers: HashMap<String, String>,
}

#[derive(Debug, PartialEq, Eq)]
pub enum Method {
	GET,
	POST,
	PUT,
	DELETE,
	HEAD,
	OPTIONS,
	TRACE,
	CONNECT,
	PATCH,
}

fn parse_method(buffer: &[u8]) -> Option<(Method, usize)> {
	let method_end = buffer.iter().position(|&byte| byte == b' ')?;
	let method = match &buffer[..method_end] {
		b"GET" => Method::GET,
		b"POST" => Method::POST,
		b"PUT" => Method::PUT,
		b"DELETE" => Method::DELETE,
		b"HEAD" => Method::HEAD,
		b"OPTIONS" => Method::OPTIONS,
		b"TRACE" => Method::TRACE,
		b"CONNECT" => Method::CONNECT,
		b"PATCH" => Method::PATCH,
		_ => {
			return None;
		}
	};

	Some((method, method_end + 1))
}

fn parse_path(buffer: &[u8]) -> Option<(String, usize)> {
	let path_end = buffer.iter().position(|&byte| byte == b' ')?;
	let path = String::from_utf8_lossy(&buffer[1..path_end]).to_string();

	Some((path, path_end + 1))
}

fn is_http101(buffer: &[u8]) -> Option<usize> {
	let http_end = buffer.iter().position(|&w| w == b'\r')?;

	match &buffer[..http_end] {
		b"HTTP/1.1" => Some(http_end + 2),
		_ => None,
	}
}

fn parse_headers(buffer: &[u8]) -> HashMap<String, String> {
	let mut headers: HashMap<String, String> = HashMap::new();

	let mut trimmer = 0;

	while let Some(line_end) = buffer[trimmer..].iter().position(|&byte| byte == b'\r') {
		if let Some(separator) = buffer[trimmer..trimmer + line_end].iter().position(|&byte| byte == b':') {
			let key = String::from_utf8_lossy(&buffer[trimmer..trimmer + separator]).to_string();
			let value = String::from_utf8_lossy(&buffer[trimmer + separator + 2..trimmer + line_end]).to_string();
			headers.insert(key, value);
		}

		trimmer += line_end + 2;
	}

	headers
}

impl Request {
	pub fn new(buffer: [u8; super::BUFFER_SIZE]) -> Option<Self> {
		let mut trimmer = 0;

		let (method, method_end) = parse_method(&buffer)?;
		trimmer += method_end;

		let (path, path_end) = parse_path(&buffer[trimmer..])?;
		trimmer += path_end;

		let http_end = is_http101(&buffer[trimmer..])?;
		trimmer += http_end;

		let headers = parse_headers(&buffer[trimmer..]);

		Some(Request {
			method,
			path,
			headers,
			slugs: HashMap::new(),
		})
	}

	pub fn bearer_token(&self) -> Option<String> {
		match self.headers.get("authorization") {
			Some(authorization) => {
				let mut authorization = authorization.split(" ");

				if authorization.next()? != "bearer" {
					return None;
				}

				authorization.next().map(|token| token.to_string())
			}
			None => None,
		}
	}
}