iris_core/protocols/stream/http/
transaction.rs1use anyhow::{bail, Result};
8use httparse::{Request, Response, EMPTY_HEADER};
9use serde::Serialize;
10
11#[derive(Debug, Default, Serialize, Clone)]
13pub struct HttpRequest {
14 pub method: Option<String>,
15 pub uri: Option<String>,
16 pub version: Option<String>,
17 pub user_agent: Option<String>,
18 pub cookie: Option<String>,
19 pub host: Option<String>,
20 pub content_length: Option<usize>,
21 pub content_type: Option<String>,
22 pub transfer_encoding: Option<String>,
23 }
26
27impl HttpRequest {
28 pub(crate) fn parse_from(data: &[u8]) -> Result<Self> {
29 let mut request = HttpRequest::default();
30
31 const NUM_OF_HEADERS: usize = 20;
32 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
33 let mut req = Request::new(&mut headers[..]);
34 let status = req.parse(data);
35 if status.is_err() {
36 bail!("error");
37 }
38
39 if let Some(method) = req.method {
40 request.method = Some(method.to_owned());
41 }
42 if let Some(uri) = req.path {
43 request.uri = Some(uri.to_owned());
44 }
45 if let Some(version) = req.version {
46 request.version = Some(format!("HTTP/1.{}", version));
47 }
48 for hdr in &headers {
49 let name = hdr.name.to_lowercase();
50 match name.as_ref() {
51 "user-agent" => {
52 let s = String::from_utf8_lossy(hdr.value).into_owned();
53 request.user_agent = Some(s);
54 }
55 "cookie" => {
56 let s = String::from_utf8_lossy(hdr.value).into_owned();
57 request.cookie = Some(s);
58 }
59 "host" => {
60 let s = String::from_utf8_lossy(hdr.value);
61 request.host = Some(s.to_string());
62 }
63 "content-length" => {
64 if let Ok(s) = std::str::from_utf8(hdr.value) {
65 if let Ok(length) = str::parse::<usize>(s) {
66 request.content_length = Some(length);
67 }
68 }
69 }
70 "content-type" => {
71 let s = String::from_utf8_lossy(hdr.value).into_owned();
72 request.content_type = Some(s);
73 }
74 "transfer-encoding" => {
75 let s = String::from_utf8_lossy(hdr.value).to_lowercase();
76 request.transfer_encoding = Some(s);
77 }
84 _ => (),
85 }
86 }
87 Ok(request)
88 }
89}
90
91#[derive(Debug, Default, Serialize, Clone)]
93pub struct HttpResponse {
94 pub version: Option<String>,
95 pub status_code: Option<u16>,
96 pub status_msg: Option<String>,
97 pub content_length: Option<usize>,
98 pub content_type: Option<String>,
99 pub transfer_encoding: Option<String>,
100 }
104
105impl HttpResponse {
106 pub(crate) fn parse_from(data: &[u8]) -> Result<(Self, usize)> {
107 let mut response = HttpResponse::default();
108
109 const NUM_OF_HEADERS: usize = 20;
110 let mut headers = [EMPTY_HEADER; NUM_OF_HEADERS];
111 let mut resp = Response::new(&mut headers[..]);
112 let status = resp.parse(data);
113 if status.is_err() {
114 bail!("error");
115 }
116
117 if let Some(version) = resp.version {
118 response.version = Some(format!("HTTP/1.{}", version));
119 }
120 if let Some(code) = resp.code {
121 response.status_code = Some(code);
122 }
123 if let Some(reason) = resp.reason {
124 response.status_msg = Some(reason.to_owned());
125 }
126
127 for hdr in &headers {
128 let name = hdr.name.to_lowercase();
129 match name.as_ref() {
130 "content-length" => {
131 if let Ok(s) = std::str::from_utf8(hdr.value) {
132 if let Ok(length) = str::parse::<usize>(s) {
133 response.content_length = Some(length);
137 }
141 }
142 }
143 "content-type" => {
144 let s = String::from_utf8_lossy(hdr.value).into_owned();
145 response.content_type = Some(s);
146 }
147 "transfer-encoding" => {
148 let s = String::from_utf8_lossy(hdr.value).to_lowercase();
149 response.transfer_encoding = Some(s);
150 }
157 _ => (),
158 }
159 }
160 let consumed = match status.unwrap() {
161 httparse::Status::Complete(nbytes) => nbytes,
162 httparse::Status::Partial => data.len(),
163 };
164 Ok((response, consumed))
165 }
166}