http1_spec/
response_head_parser.rs1use std::io::{BufRead, Read as _};
2
3use http::{
4 response::Parts as ResponseParts, HeaderMap, HeaderValue, Response, StatusCode, Version,
5};
6
7use crate::{
8 head_parser::{HeadParseConfig, HeadParseError, HeadParseOutput, HeadParser},
9 ReasonPhrase,
10};
11
12#[derive(Default)]
16pub struct ResponseHeadParser {
17 pub http_version: Version,
18 pub status_code: StatusCode,
19 pub reason_phrase: ReasonPhrase,
20 pub headers: HeaderMap<HeaderValue>,
21 config: HeadParseConfig,
23 state: State,
25 buf: Vec<u8>,
26}
27
28#[derive(Debug, PartialEq, Eq, PartialOrd, Ord)]
29enum State {
30 Idle,
31 HttpVersionParsed,
32 StatusCodeParsed,
33 ReasonPhraseParsed,
34 HeadersParsing,
35}
36impl Default for State {
37 fn default() -> Self {
38 Self::Idle
39 }
40}
41
42impl ResponseHeadParser {
43 pub fn to_response_parts(&self) -> ResponseParts {
44 let (mut parts, _) = Response::new(()).into_parts();
45 parts.status = self.status_code;
46 parts.version = self.http_version;
47 parts.headers = self.headers.to_owned();
48 parts.extensions.insert(self.reason_phrase.to_owned());
49 parts
50 }
51
52 pub fn to_response<B>(&self, body: B) -> Response<B> {
53 let parts = self.to_response_parts();
54 Response::from_parts(parts, body)
55 }
56}
57
58impl HeadParser for ResponseHeadParser {
62 fn new() -> Self {
63 Self::default()
64 }
65 fn with_config(config: HeadParseConfig) -> Self {
66 let buf = Vec::with_capacity(config.buf_capacity());
67 let headers = HeaderMap::with_capacity(config.header_map_capacity());
68 ResponseHeadParser {
69 config,
70 buf,
71 headers,
72 ..Default::default()
73 }
74 }
75
76 fn get_headers(&self) -> &HeaderMap<HeaderValue> {
77 &self.headers
78 }
79 fn get_version(&self) -> &Version {
80 &self.http_version
81 }
82
83 fn parse<R: BufRead>(&mut self, r: &mut R) -> Result<HeadParseOutput, HeadParseError> {
84 let mut take = r.take(0);
85 let mut parsed_num_bytes = 0_usize;
86
87 if self.state < State::HttpVersionParsed {
88 self.buf.clear();
90 match Self::parse_http_version_for_response(&mut take, &mut self.buf)? {
91 Some((http_version, n)) => {
92 self.state = State::HttpVersionParsed;
93
94 self.http_version = http_version;
95 parsed_num_bytes += n;
96 }
97 None => return Ok(HeadParseOutput::Partial(parsed_num_bytes)),
98 }
99 }
100
101 if self.state < State::StatusCodeParsed {
102 self.buf.clear();
104 match Self::parse_status_code(&mut take, &mut self.buf)? {
105 Some((status_code, n)) => {
106 self.state = State::StatusCodeParsed;
107
108 self.status_code = status_code;
109 parsed_num_bytes += n;
110 }
111 None => return Ok(HeadParseOutput::Partial(parsed_num_bytes)),
112 }
113 }
114
115 if self.state < State::ReasonPhraseParsed {
116 self.buf.clear();
118 match Self::parse_reason_phrase(&mut take, &mut self.buf, &self.config)? {
119 Some((reason_phrase, n)) => {
120 self.state = State::ReasonPhraseParsed;
121
122 self.reason_phrase = reason_phrase;
123 parsed_num_bytes += n;
124 }
125 None => return Ok(HeadParseOutput::Partial(parsed_num_bytes)),
126 }
127 }
128
129 if self.state < State::HeadersParsing {
131 self.headers.clear();
132 }
133 loop {
134 if self.state <= State::HeadersParsing {
135 self.buf.clear();
136 match Self::parse_header(&mut take, &mut self.buf, &self.config, &mut self.headers)?
137 {
138 Some((is_all_completed, n)) => {
139 parsed_num_bytes += n;
140
141 if is_all_completed {
142 self.state = State::Idle;
143
144 return Ok(HeadParseOutput::Completed(parsed_num_bytes));
145 } else {
146 self.state = State::HeadersParsing;
147
148 continue;
149 }
150 }
151 None => return Ok(HeadParseOutput::Partial(parsed_num_bytes)),
152 }
153 } else {
154 unreachable!()
155 }
156 }
157 }
158}
159
160#[cfg(test)]
161mod tests {
162 use super::*;
163
164 #[test]
165 fn test_to_response() {
166 let p = ResponseHeadParser {
167 http_version: Version::HTTP_2,
168 status_code: StatusCode::CREATED,
169 reason_phrase: Some(b"MyCreated".to_vec()),
170 headers: {
171 let mut h = HeaderMap::new();
172 h.insert("x-foo", "bar".parse().unwrap());
173 h
174 },
175 ..Default::default()
176 };
177
178 let res = p.to_response("body");
179 assert_eq!(res.version(), Version::HTTP_2);
180 assert_eq!(res.status(), StatusCode::CREATED);
181 assert_eq!(res.headers().get("x-foo").unwrap(), "bar");
182 assert_eq!(
183 res.extensions().get::<ReasonPhrase>().unwrap(),
184 &Some(b"MyCreated".to_vec())
185 );
186 assert_eq!(res.body(), &"body");
187 }
188}