1 | use std::mem::MaybeUninit; |
2 | |
3 | #[cfg (feature = "client" )] |
4 | use std::fmt::{self, Write as _}; |
5 | |
6 | use bytes::Bytes; |
7 | use bytes::BytesMut; |
8 | #[cfg (feature = "client" )] |
9 | use http::header::Entry; |
10 | #[cfg (feature = "server" )] |
11 | use http::header::ValueIter; |
12 | use http::header::{self, HeaderMap, HeaderName, HeaderValue}; |
13 | use http::{Method, StatusCode, Version}; |
14 | use smallvec::{smallvec, smallvec_inline, SmallVec}; |
15 | |
16 | use crate::body::DecodedLength; |
17 | #[cfg (feature = "server" )] |
18 | use crate::common::date; |
19 | use crate::error::Parse; |
20 | use crate::ext::HeaderCaseMap; |
21 | #[cfg (feature = "ffi" )] |
22 | use crate::ext::OriginalHeaderOrder; |
23 | use crate::headers; |
24 | use crate::proto::h1::{ |
25 | Encode, Encoder, Http1Transaction, ParseContext, ParseResult, ParsedMessage, |
26 | }; |
27 | #[cfg (feature = "client" )] |
28 | use crate::proto::RequestHead; |
29 | use crate::proto::{BodyLength, MessageHead, RequestLine}; |
30 | |
31 | pub(crate) const DEFAULT_MAX_HEADERS: usize = 100; |
32 | const AVERAGE_HEADER_SIZE: usize = 30; // totally scientific |
33 | #[cfg (feature = "server" )] |
34 | const MAX_URI_LEN: usize = (u16::MAX - 1) as usize; |
35 | |
36 | macro_rules! header_name { |
37 | ($bytes:expr) => {{ |
38 | { |
39 | match HeaderName::from_bytes($bytes) { |
40 | Ok(name) => name, |
41 | Err(e) => maybe_panic!(e), |
42 | } |
43 | } |
44 | }}; |
45 | } |
46 | |
47 | macro_rules! header_value { |
48 | ($bytes:expr) => {{ |
49 | { |
50 | unsafe { HeaderValue::from_maybe_shared_unchecked($bytes) } |
51 | } |
52 | }}; |
53 | } |
54 | |
55 | macro_rules! maybe_panic { |
56 | ($($arg:tt)*) => ({ |
57 | let _err = ($($arg)*); |
58 | if cfg!(debug_assertions) { |
59 | panic!("{:?}" , _err); |
60 | } else { |
61 | error!("Internal Hyper error, please report {:?}" , _err); |
62 | return Err(Parse::Internal) |
63 | } |
64 | }) |
65 | } |
66 | |
67 | pub(super) fn parse_headers<T>( |
68 | bytes: &mut BytesMut, |
69 | prev_len: Option<usize>, |
70 | ctx: ParseContext<'_>, |
71 | ) -> ParseResult<T::Incoming> |
72 | where |
73 | T: Http1Transaction, |
74 | { |
75 | // If the buffer is empty, don't bother entering the span, it's just noise. |
76 | if bytes.is_empty() { |
77 | return Ok(None); |
78 | } |
79 | |
80 | let _entered: () = trace_span!("parse_headers" ); |
81 | |
82 | if let Some(prev_len: usize) = prev_len { |
83 | if !is_complete_fast(bytes, prev_len) { |
84 | return Ok(None); |
85 | } |
86 | } |
87 | |
88 | T::parse(bytes, ctx) |
89 | } |
90 | |
91 | /// A fast scan for the end of a message. |
92 | /// Used when there was a partial read, to skip full parsing on a |
93 | /// a slow connection. |
94 | fn is_complete_fast(bytes: &[u8], prev_len: usize) -> bool { |
95 | let start: usize = if prev_len < 3 { 0 } else { prev_len - 3 }; |
96 | let bytes: &[u8] = &bytes[start..]; |
97 | |
98 | for (i: usize, b: u8) in bytes.iter().copied().enumerate() { |
99 | if b == b' \r' { |
100 | if bytes[i + 1..].chunks(chunk_size:3).next() == Some(&b" \n\r\n" [..]) { |
101 | return true; |
102 | } |
103 | } else if b == b' \n' && bytes.get(index:i + 1) == Some(&b' \n' ) { |
104 | return true; |
105 | } |
106 | } |
107 | |
108 | false |
109 | } |
110 | |
111 | pub(super) fn encode_headers<T>( |
112 | enc: Encode<'_, T::Outgoing>, |
113 | dst: &mut Vec<u8>, |
114 | ) -> crate::Result<Encoder> |
115 | where |
116 | T: Http1Transaction, |
117 | { |
118 | let _entered: () = trace_span!("encode_headers" ); |
119 | T::encode(enc, dst) |
120 | } |
121 | |
122 | // There are 2 main roles, Client and Server. |
123 | |
124 | #[cfg (feature = "client" )] |
125 | pub(crate) enum Client {} |
126 | |
127 | #[cfg (feature = "server" )] |
128 | pub(crate) enum Server {} |
129 | |
130 | #[cfg (feature = "server" )] |
131 | impl Http1Transaction for Server { |
132 | type Incoming = RequestLine; |
133 | type Outgoing = StatusCode; |
134 | #[cfg (feature = "tracing" )] |
135 | const LOG: &'static str = "{role=server}" ; |
136 | |
137 | fn parse(buf: &mut BytesMut, ctx: ParseContext<'_>) -> ParseResult<RequestLine> { |
138 | debug_assert!(!buf.is_empty(), "parse called with empty buf" ); |
139 | |
140 | let mut keep_alive; |
141 | let is_http_11; |
142 | let subject; |
143 | let version; |
144 | let len; |
145 | let headers_len; |
146 | let method; |
147 | let path_range; |
148 | |
149 | // Both headers_indices and headers are using uninitialized memory, |
150 | // but we *never* read any of it until after httparse has assigned |
151 | // values into it. By not zeroing out the stack memory, this saves |
152 | // a good ~5% on pipeline benchmarks. |
153 | let mut headers_indices: SmallVec<[MaybeUninit<HeaderIndices>; DEFAULT_MAX_HEADERS]> = |
154 | match ctx.h1_max_headers { |
155 | Some(cap) => smallvec![MaybeUninit::uninit(); cap], |
156 | None => smallvec_inline![MaybeUninit::uninit(); DEFAULT_MAX_HEADERS], |
157 | }; |
158 | { |
159 | let mut headers: SmallVec<[MaybeUninit<httparse::Header<'_>>; DEFAULT_MAX_HEADERS]> = |
160 | match ctx.h1_max_headers { |
161 | Some(cap) => smallvec![MaybeUninit::uninit(); cap], |
162 | None => smallvec_inline![MaybeUninit::uninit(); DEFAULT_MAX_HEADERS], |
163 | }; |
164 | trace!(bytes = buf.len(), "Request.parse" ); |
165 | let mut req = httparse::Request::new(&mut []); |
166 | let bytes = buf.as_ref(); |
167 | match req.parse_with_uninit_headers(bytes, &mut headers) { |
168 | Ok(httparse::Status::Complete(parsed_len)) => { |
169 | trace!("Request.parse Complete({})" , parsed_len); |
170 | len = parsed_len; |
171 | let uri = req.path.unwrap(); |
172 | if uri.len() > MAX_URI_LEN { |
173 | return Err(Parse::UriTooLong); |
174 | } |
175 | method = Method::from_bytes(req.method.unwrap().as_bytes())?; |
176 | path_range = Server::record_path_range(bytes, uri); |
177 | version = if req.version.unwrap() == 1 { |
178 | keep_alive = true; |
179 | is_http_11 = true; |
180 | Version::HTTP_11 |
181 | } else { |
182 | keep_alive = false; |
183 | is_http_11 = false; |
184 | Version::HTTP_10 |
185 | }; |
186 | |
187 | record_header_indices(bytes, req.headers, &mut headers_indices)?; |
188 | headers_len = req.headers.len(); |
189 | } |
190 | Ok(httparse::Status::Partial) => return Ok(None), |
191 | Err(err) => { |
192 | return Err(match err { |
193 | // if invalid Token, try to determine if for method or path |
194 | httparse::Error::Token => { |
195 | if req.method.is_none() { |
196 | Parse::Method |
197 | } else { |
198 | debug_assert!(req.path.is_none()); |
199 | Parse::Uri |
200 | } |
201 | } |
202 | other => other.into(), |
203 | }); |
204 | } |
205 | } |
206 | }; |
207 | |
208 | let slice = buf.split_to(len).freeze(); |
209 | let uri = { |
210 | let uri_bytes = slice.slice_ref(&slice[path_range]); |
211 | // TODO(lucab): switch to `Uri::from_shared()` once public. |
212 | http::Uri::from_maybe_shared(uri_bytes)? |
213 | }; |
214 | subject = RequestLine(method, uri); |
215 | |
216 | // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 |
217 | // 1. (irrelevant to Request) |
218 | // 2. (irrelevant to Request) |
219 | // 3. Transfer-Encoding: chunked has a chunked body. |
220 | // 4. If multiple differing Content-Length headers or invalid, close connection. |
221 | // 5. Content-Length header has a sized body. |
222 | // 6. Length 0. |
223 | // 7. (irrelevant to Request) |
224 | |
225 | let mut decoder = DecodedLength::ZERO; |
226 | let mut expect_continue = false; |
227 | let mut con_len = None; |
228 | let mut is_te = false; |
229 | let mut is_te_chunked = false; |
230 | let mut wants_upgrade = subject.0 == Method::CONNECT; |
231 | |
232 | let mut header_case_map = if ctx.preserve_header_case { |
233 | Some(HeaderCaseMap::default()) |
234 | } else { |
235 | None |
236 | }; |
237 | |
238 | #[cfg (feature = "ffi" )] |
239 | let mut header_order = if ctx.preserve_header_order { |
240 | Some(OriginalHeaderOrder::default()) |
241 | } else { |
242 | None |
243 | }; |
244 | |
245 | let mut headers = ctx.cached_headers.take().unwrap_or_default(); |
246 | |
247 | headers.reserve(headers_len); |
248 | |
249 | for header in &headers_indices[..headers_len] { |
250 | // SAFETY: array is valid up to `headers_len` |
251 | let header = unsafe { header.assume_init_ref() }; |
252 | let name = header_name!(&slice[header.name.0..header.name.1]); |
253 | let value = header_value!(slice.slice(header.value.0..header.value.1)); |
254 | |
255 | match name { |
256 | header::TRANSFER_ENCODING => { |
257 | // https://tools.ietf.org/html/rfc7230#section-3.3.3 |
258 | // If Transfer-Encoding header is present, and 'chunked' is |
259 | // not the final encoding, and this is a Request, then it is |
260 | // malformed. A server should respond with 400 Bad Request. |
261 | if !is_http_11 { |
262 | debug!("HTTP/1.0 cannot have Transfer-Encoding header" ); |
263 | return Err(Parse::transfer_encoding_unexpected()); |
264 | } |
265 | is_te = true; |
266 | if headers::is_chunked_(&value) { |
267 | is_te_chunked = true; |
268 | decoder = DecodedLength::CHUNKED; |
269 | } else { |
270 | is_te_chunked = false; |
271 | } |
272 | } |
273 | header::CONTENT_LENGTH => { |
274 | if is_te { |
275 | continue; |
276 | } |
277 | let len = headers::content_length_parse(&value) |
278 | .ok_or_else(Parse::content_length_invalid)?; |
279 | if let Some(prev) = con_len { |
280 | if prev != len { |
281 | debug!( |
282 | "multiple Content-Length headers with different values: [{}, {}]" , |
283 | prev, len, |
284 | ); |
285 | return Err(Parse::content_length_invalid()); |
286 | } |
287 | // we don't need to append this secondary length |
288 | continue; |
289 | } |
290 | decoder = DecodedLength::checked_new(len)?; |
291 | con_len = Some(len); |
292 | } |
293 | header::CONNECTION => { |
294 | // keep_alive was previously set to default for Version |
295 | if keep_alive { |
296 | // HTTP/1.1 |
297 | keep_alive = !headers::connection_close(&value); |
298 | } else { |
299 | // HTTP/1.0 |
300 | keep_alive = headers::connection_keep_alive(&value); |
301 | } |
302 | } |
303 | header::EXPECT => { |
304 | // According to https://datatracker.ietf.org/doc/html/rfc2616#section-14.20 |
305 | // Comparison of expectation values is case-insensitive for unquoted tokens |
306 | // (including the 100-continue token) |
307 | expect_continue = value.as_bytes().eq_ignore_ascii_case(b"100-continue" ); |
308 | } |
309 | header::UPGRADE => { |
310 | // Upgrades are only allowed with HTTP/1.1 |
311 | wants_upgrade = is_http_11; |
312 | } |
313 | |
314 | _ => (), |
315 | } |
316 | |
317 | if let Some(ref mut header_case_map) = header_case_map { |
318 | header_case_map.append(&name, slice.slice(header.name.0..header.name.1)); |
319 | } |
320 | |
321 | #[cfg (feature = "ffi" )] |
322 | if let Some(ref mut header_order) = header_order { |
323 | header_order.append(&name); |
324 | } |
325 | |
326 | headers.append(name, value); |
327 | } |
328 | |
329 | if is_te && !is_te_chunked { |
330 | debug!("request with transfer-encoding header, but not chunked, bad request" ); |
331 | return Err(Parse::transfer_encoding_invalid()); |
332 | } |
333 | |
334 | let mut extensions = http::Extensions::default(); |
335 | |
336 | if let Some(header_case_map) = header_case_map { |
337 | extensions.insert(header_case_map); |
338 | } |
339 | |
340 | #[cfg (feature = "ffi" )] |
341 | if let Some(header_order) = header_order { |
342 | extensions.insert(header_order); |
343 | } |
344 | |
345 | *ctx.req_method = Some(subject.0.clone()); |
346 | |
347 | Ok(Some(ParsedMessage { |
348 | head: MessageHead { |
349 | version, |
350 | subject, |
351 | headers, |
352 | extensions, |
353 | }, |
354 | decode: decoder, |
355 | expect_continue, |
356 | keep_alive, |
357 | wants_upgrade, |
358 | })) |
359 | } |
360 | |
361 | fn encode(mut msg: Encode<'_, Self::Outgoing>, dst: &mut Vec<u8>) -> crate::Result<Encoder> { |
362 | trace!( |
363 | "Server::encode status={:?}, body={:?}, req_method={:?}" , |
364 | msg.head.subject, |
365 | msg.body, |
366 | msg.req_method |
367 | ); |
368 | |
369 | let mut wrote_len = false; |
370 | |
371 | // hyper currently doesn't support returning 1xx status codes as a Response |
372 | // This is because Service only allows returning a single Response, and |
373 | // so if you try to reply with a e.g. 100 Continue, you have no way of |
374 | // replying with the latter status code response. |
375 | let (ret, is_last) = if msg.head.subject == StatusCode::SWITCHING_PROTOCOLS { |
376 | (Ok(()), true) |
377 | } else if msg.req_method == &Some(Method::CONNECT) && msg.head.subject.is_success() { |
378 | // Sending content-length or transfer-encoding header on 2xx response |
379 | // to CONNECT is forbidden in RFC 7231. |
380 | wrote_len = true; |
381 | (Ok(()), true) |
382 | } else if msg.head.subject.is_informational() { |
383 | warn!("response with 1xx status code not supported" ); |
384 | *msg.head = MessageHead::default(); |
385 | msg.head.subject = StatusCode::INTERNAL_SERVER_ERROR; |
386 | msg.body = None; |
387 | (Err(crate::Error::new_user_unsupported_status_code()), true) |
388 | } else { |
389 | (Ok(()), !msg.keep_alive) |
390 | }; |
391 | |
392 | // In some error cases, we don't know about the invalid message until already |
393 | // pushing some bytes onto the `dst`. In those cases, we don't want to send |
394 | // the half-pushed message, so rewind to before. |
395 | let orig_len = dst.len(); |
396 | |
397 | let init_cap = 30 + msg.head.headers.len() * AVERAGE_HEADER_SIZE; |
398 | dst.reserve(init_cap); |
399 | |
400 | let custom_reason_phrase = msg.head.extensions.get::<crate::ext::ReasonPhrase>(); |
401 | |
402 | if msg.head.version == Version::HTTP_11 |
403 | && msg.head.subject == StatusCode::OK |
404 | && custom_reason_phrase.is_none() |
405 | { |
406 | extend(dst, b"HTTP/1.1 200 OK \r\n" ); |
407 | } else { |
408 | match msg.head.version { |
409 | Version::HTTP_10 => extend(dst, b"HTTP/1.0 " ), |
410 | Version::HTTP_11 => extend(dst, b"HTTP/1.1 " ), |
411 | Version::HTTP_2 => { |
412 | debug!("response with HTTP2 version coerced to HTTP/1.1" ); |
413 | extend(dst, b"HTTP/1.1 " ); |
414 | } |
415 | other => panic!("unexpected response version: {:?}" , other), |
416 | } |
417 | |
418 | extend(dst, msg.head.subject.as_str().as_bytes()); |
419 | extend(dst, b" " ); |
420 | |
421 | if let Some(reason) = custom_reason_phrase { |
422 | extend(dst, reason.as_bytes()); |
423 | } else { |
424 | // a reason MUST be written, as many parsers will expect it. |
425 | extend( |
426 | dst, |
427 | msg.head |
428 | .subject |
429 | .canonical_reason() |
430 | .unwrap_or("<none>" ) |
431 | .as_bytes(), |
432 | ); |
433 | } |
434 | |
435 | extend(dst, b" \r\n" ); |
436 | } |
437 | |
438 | let orig_headers; |
439 | let extensions = std::mem::take(&mut msg.head.extensions); |
440 | let orig_headers = match extensions.get::<HeaderCaseMap>() { |
441 | None if msg.title_case_headers => { |
442 | orig_headers = HeaderCaseMap::default(); |
443 | Some(&orig_headers) |
444 | } |
445 | orig_headers => orig_headers, |
446 | }; |
447 | let encoder = if let Some(orig_headers) = orig_headers { |
448 | Self::encode_headers_with_original_case( |
449 | msg, |
450 | dst, |
451 | is_last, |
452 | orig_len, |
453 | wrote_len, |
454 | orig_headers, |
455 | )? |
456 | } else { |
457 | Self::encode_headers_with_lower_case(msg, dst, is_last, orig_len, wrote_len)? |
458 | }; |
459 | |
460 | ret.map(|()| encoder) |
461 | } |
462 | |
463 | fn on_error(err: &crate::Error) -> Option<MessageHead<Self::Outgoing>> { |
464 | use crate::error::Kind; |
465 | let status = match *err.kind() { |
466 | Kind::Parse(Parse::Method) |
467 | | Kind::Parse(Parse::Header(_)) |
468 | | Kind::Parse(Parse::Uri) |
469 | | Kind::Parse(Parse::Version) => StatusCode::BAD_REQUEST, |
470 | Kind::Parse(Parse::TooLarge) => StatusCode::REQUEST_HEADER_FIELDS_TOO_LARGE, |
471 | Kind::Parse(Parse::UriTooLong) => StatusCode::URI_TOO_LONG, |
472 | _ => return None, |
473 | }; |
474 | |
475 | debug!("sending automatic response ({}) for parse error" , status); |
476 | let msg = MessageHead { |
477 | subject: status, |
478 | ..Default::default() |
479 | }; |
480 | Some(msg) |
481 | } |
482 | |
483 | fn is_server() -> bool { |
484 | true |
485 | } |
486 | |
487 | fn update_date() { |
488 | date::update(); |
489 | } |
490 | } |
491 | |
492 | #[cfg (feature = "server" )] |
493 | impl Server { |
494 | fn can_have_body(method: &Option<Method>, status: StatusCode) -> bool { |
495 | Server::can_chunked(method, status) |
496 | } |
497 | |
498 | fn can_chunked(method: &Option<Method>, status: StatusCode) -> bool { |
499 | if method == &Some(Method::HEAD) |
500 | || method == &Some(Method::CONNECT) && status.is_success() |
501 | || status.is_informational() |
502 | { |
503 | false |
504 | } else { |
505 | !matches!(status, StatusCode::NO_CONTENT | StatusCode::NOT_MODIFIED) |
506 | } |
507 | } |
508 | |
509 | fn can_have_content_length(method: &Option<Method>, status: StatusCode) -> bool { |
510 | if status.is_informational() || method == &Some(Method::CONNECT) && status.is_success() { |
511 | false |
512 | } else { |
513 | !matches!(status, StatusCode::NO_CONTENT | StatusCode::NOT_MODIFIED) |
514 | } |
515 | } |
516 | |
517 | fn can_have_implicit_zero_content_length(method: &Option<Method>, status: StatusCode) -> bool { |
518 | Server::can_have_content_length(method, status) && method != &Some(Method::HEAD) |
519 | } |
520 | |
521 | fn encode_headers_with_lower_case( |
522 | msg: Encode<'_, StatusCode>, |
523 | dst: &mut Vec<u8>, |
524 | is_last: bool, |
525 | orig_len: usize, |
526 | wrote_len: bool, |
527 | ) -> crate::Result<Encoder> { |
528 | struct LowercaseWriter; |
529 | |
530 | impl HeaderNameWriter for LowercaseWriter { |
531 | #[inline ] |
532 | fn write_full_header_line( |
533 | &mut self, |
534 | dst: &mut Vec<u8>, |
535 | line: &str, |
536 | _: (HeaderName, &str), |
537 | ) { |
538 | extend(dst, line.as_bytes()) |
539 | } |
540 | |
541 | #[inline ] |
542 | fn write_header_name_with_colon( |
543 | &mut self, |
544 | dst: &mut Vec<u8>, |
545 | name_with_colon: &str, |
546 | _: HeaderName, |
547 | ) { |
548 | extend(dst, name_with_colon.as_bytes()) |
549 | } |
550 | |
551 | #[inline ] |
552 | fn write_header_name(&mut self, dst: &mut Vec<u8>, name: &HeaderName) { |
553 | extend(dst, name.as_str().as_bytes()) |
554 | } |
555 | } |
556 | |
557 | Self::encode_headers(msg, dst, is_last, orig_len, wrote_len, LowercaseWriter) |
558 | } |
559 | |
560 | #[cold ] |
561 | #[inline (never)] |
562 | fn encode_headers_with_original_case( |
563 | msg: Encode<'_, StatusCode>, |
564 | dst: &mut Vec<u8>, |
565 | is_last: bool, |
566 | orig_len: usize, |
567 | wrote_len: bool, |
568 | orig_headers: &HeaderCaseMap, |
569 | ) -> crate::Result<Encoder> { |
570 | struct OrigCaseWriter<'map> { |
571 | map: &'map HeaderCaseMap, |
572 | current: Option<(HeaderName, ValueIter<'map, Bytes>)>, |
573 | title_case_headers: bool, |
574 | } |
575 | |
576 | impl HeaderNameWriter for OrigCaseWriter<'_> { |
577 | #[inline ] |
578 | fn write_full_header_line( |
579 | &mut self, |
580 | dst: &mut Vec<u8>, |
581 | _: &str, |
582 | (name, rest): (HeaderName, &str), |
583 | ) { |
584 | self.write_header_name(dst, &name); |
585 | extend(dst, rest.as_bytes()); |
586 | } |
587 | |
588 | #[inline ] |
589 | fn write_header_name_with_colon( |
590 | &mut self, |
591 | dst: &mut Vec<u8>, |
592 | _: &str, |
593 | name: HeaderName, |
594 | ) { |
595 | self.write_header_name(dst, &name); |
596 | extend(dst, b": " ); |
597 | } |
598 | |
599 | #[inline ] |
600 | fn write_header_name(&mut self, dst: &mut Vec<u8>, name: &HeaderName) { |
601 | let Self { |
602 | map, |
603 | ref mut current, |
604 | title_case_headers, |
605 | } = *self; |
606 | if current.as_ref().map_or(true, |(last, _)| last != name) { |
607 | *current = None; |
608 | } |
609 | let (_, values) = |
610 | current.get_or_insert_with(|| (name.clone(), map.get_all_internal(name))); |
611 | |
612 | if let Some(orig_name) = values.next() { |
613 | extend(dst, orig_name); |
614 | } else if title_case_headers { |
615 | title_case(dst, name.as_str().as_bytes()); |
616 | } else { |
617 | extend(dst, name.as_str().as_bytes()); |
618 | } |
619 | } |
620 | } |
621 | |
622 | let header_name_writer = OrigCaseWriter { |
623 | map: orig_headers, |
624 | current: None, |
625 | title_case_headers: msg.title_case_headers, |
626 | }; |
627 | |
628 | Self::encode_headers(msg, dst, is_last, orig_len, wrote_len, header_name_writer) |
629 | } |
630 | |
631 | #[inline ] |
632 | fn encode_headers<W>( |
633 | msg: Encode<'_, StatusCode>, |
634 | dst: &mut Vec<u8>, |
635 | mut is_last: bool, |
636 | orig_len: usize, |
637 | mut wrote_len: bool, |
638 | mut header_name_writer: W, |
639 | ) -> crate::Result<Encoder> |
640 | where |
641 | W: HeaderNameWriter, |
642 | { |
643 | // In some error cases, we don't know about the invalid message until already |
644 | // pushing some bytes onto the `dst`. In those cases, we don't want to send |
645 | // the half-pushed message, so rewind to before. |
646 | let rewind = |dst: &mut Vec<u8>| { |
647 | dst.truncate(orig_len); |
648 | }; |
649 | |
650 | let mut encoder = Encoder::length(0); |
651 | let mut allowed_trailer_fields: Option<Vec<HeaderValue>> = None; |
652 | let mut wrote_date = false; |
653 | let mut cur_name = None; |
654 | let mut is_name_written = false; |
655 | let mut must_write_chunked = false; |
656 | let mut prev_con_len = None; |
657 | |
658 | macro_rules! handle_is_name_written { |
659 | () => {{ |
660 | if is_name_written { |
661 | // we need to clean up and write the newline |
662 | debug_assert_ne!( |
663 | &dst[dst.len() - 2..], |
664 | b" \r\n" , |
665 | "previous header wrote newline but set is_name_written" |
666 | ); |
667 | |
668 | if must_write_chunked { |
669 | extend(dst, b", chunked \r\n" ); |
670 | } else { |
671 | extend(dst, b" \r\n" ); |
672 | } |
673 | } |
674 | }}; |
675 | } |
676 | |
677 | 'headers: for (opt_name, value) in msg.head.headers.drain() { |
678 | if let Some(n) = opt_name { |
679 | cur_name = Some(n); |
680 | handle_is_name_written!(); |
681 | is_name_written = false; |
682 | } |
683 | let name = cur_name.as_ref().expect("current header name" ); |
684 | match *name { |
685 | header::CONTENT_LENGTH => { |
686 | if wrote_len && !is_name_written { |
687 | warn!("unexpected content-length found, canceling" ); |
688 | rewind(dst); |
689 | return Err(crate::Error::new_user_header()); |
690 | } |
691 | match msg.body { |
692 | Some(BodyLength::Known(known_len)) => { |
693 | // The Body claims to know a length, and |
694 | // the headers are already set. For performance |
695 | // reasons, we are just going to trust that |
696 | // the values match. |
697 | // |
698 | // In debug builds, we'll assert they are the |
699 | // same to help developers find bugs. |
700 | #[cfg (debug_assertions)] |
701 | { |
702 | if let Some(len) = headers::content_length_parse(&value) { |
703 | if msg.req_method != &Some(Method::HEAD) || known_len != 0 { |
704 | assert!( |
705 | len == known_len, |
706 | "payload claims content-length of {}, custom content-length header claims {}" , |
707 | known_len, |
708 | len, |
709 | ); |
710 | } |
711 | } |
712 | } |
713 | |
714 | if !is_name_written { |
715 | encoder = Encoder::length(known_len); |
716 | header_name_writer.write_header_name_with_colon( |
717 | dst, |
718 | "content-length: " , |
719 | header::CONTENT_LENGTH, |
720 | ); |
721 | extend(dst, value.as_bytes()); |
722 | wrote_len = true; |
723 | is_name_written = true; |
724 | } |
725 | continue 'headers; |
726 | } |
727 | Some(BodyLength::Unknown) => { |
728 | // The Body impl didn't know how long the |
729 | // body is, but a length header was included. |
730 | // We have to parse the value to return our |
731 | // Encoder... |
732 | |
733 | if let Some(len) = headers::content_length_parse(&value) { |
734 | if let Some(prev) = prev_con_len { |
735 | if prev != len { |
736 | warn!( |
737 | "multiple Content-Length values found: [{}, {}]" , |
738 | prev, len |
739 | ); |
740 | rewind(dst); |
741 | return Err(crate::Error::new_user_header()); |
742 | } |
743 | debug_assert!(is_name_written); |
744 | continue 'headers; |
745 | } else { |
746 | // we haven't written content-length yet! |
747 | encoder = Encoder::length(len); |
748 | header_name_writer.write_header_name_with_colon( |
749 | dst, |
750 | "content-length: " , |
751 | header::CONTENT_LENGTH, |
752 | ); |
753 | extend(dst, value.as_bytes()); |
754 | wrote_len = true; |
755 | is_name_written = true; |
756 | prev_con_len = Some(len); |
757 | continue 'headers; |
758 | } |
759 | } else { |
760 | warn!("illegal Content-Length value: {:?}" , value); |
761 | rewind(dst); |
762 | return Err(crate::Error::new_user_header()); |
763 | } |
764 | } |
765 | None => { |
766 | // We have no body to actually send, |
767 | // but the headers claim a content-length. |
768 | // There's only 2 ways this makes sense: |
769 | // |
770 | // - The header says the length is `0`. |
771 | // - This is a response to a `HEAD` request. |
772 | if msg.req_method == &Some(Method::HEAD) { |
773 | debug_assert_eq!(encoder, Encoder::length(0)); |
774 | } else { |
775 | if value.as_bytes() != b"0" { |
776 | warn!( |
777 | "content-length value found, but empty body provided: {:?}" , |
778 | value |
779 | ); |
780 | } |
781 | continue 'headers; |
782 | } |
783 | } |
784 | } |
785 | wrote_len = true; |
786 | } |
787 | header::TRANSFER_ENCODING => { |
788 | if wrote_len && !is_name_written { |
789 | warn!("unexpected transfer-encoding found, canceling" ); |
790 | rewind(dst); |
791 | return Err(crate::Error::new_user_header()); |
792 | } |
793 | // check that we actually can send a chunked body... |
794 | if msg.head.version == Version::HTTP_10 |
795 | || !Server::can_chunked(msg.req_method, msg.head.subject) |
796 | { |
797 | continue; |
798 | } |
799 | wrote_len = true; |
800 | // Must check each value, because `chunked` needs to be the |
801 | // last encoding, or else we add it. |
802 | must_write_chunked = !headers::is_chunked_(&value); |
803 | |
804 | if !is_name_written { |
805 | encoder = Encoder::chunked(); |
806 | is_name_written = true; |
807 | header_name_writer.write_header_name_with_colon( |
808 | dst, |
809 | "transfer-encoding: " , |
810 | header::TRANSFER_ENCODING, |
811 | ); |
812 | extend(dst, value.as_bytes()); |
813 | } else { |
814 | extend(dst, b", " ); |
815 | extend(dst, value.as_bytes()); |
816 | } |
817 | continue 'headers; |
818 | } |
819 | header::CONNECTION => { |
820 | if !is_last && headers::connection_close(&value) { |
821 | is_last = true; |
822 | } |
823 | if !is_name_written { |
824 | is_name_written = true; |
825 | header_name_writer.write_header_name_with_colon( |
826 | dst, |
827 | "connection: " , |
828 | header::CONNECTION, |
829 | ); |
830 | extend(dst, value.as_bytes()); |
831 | } else { |
832 | extend(dst, b", " ); |
833 | extend(dst, value.as_bytes()); |
834 | } |
835 | continue 'headers; |
836 | } |
837 | header::DATE => { |
838 | wrote_date = true; |
839 | } |
840 | header::TRAILER => { |
841 | // check that we actually can send a chunked body... |
842 | if msg.head.version == Version::HTTP_10 |
843 | || !Server::can_chunked(msg.req_method, msg.head.subject) |
844 | { |
845 | continue; |
846 | } |
847 | |
848 | if !is_name_written { |
849 | is_name_written = true; |
850 | header_name_writer.write_header_name_with_colon( |
851 | dst, |
852 | "trailer: " , |
853 | header::TRAILER, |
854 | ); |
855 | extend(dst, value.as_bytes()); |
856 | } else { |
857 | extend(dst, b", " ); |
858 | extend(dst, value.as_bytes()); |
859 | } |
860 | |
861 | match allowed_trailer_fields { |
862 | Some(ref mut allowed_trailer_fields) => { |
863 | allowed_trailer_fields.push(value); |
864 | } |
865 | None => { |
866 | allowed_trailer_fields = Some(vec![value]); |
867 | } |
868 | } |
869 | |
870 | continue 'headers; |
871 | } |
872 | _ => (), |
873 | } |
874 | //TODO: this should perhaps instead combine them into |
875 | //single lines, as RFC7230 suggests is preferable. |
876 | |
877 | // non-special write Name and Value |
878 | debug_assert!( |
879 | !is_name_written, |
880 | "{:?} set is_name_written and didn't continue loop" , |
881 | name, |
882 | ); |
883 | header_name_writer.write_header_name(dst, name); |
884 | extend(dst, b": " ); |
885 | extend(dst, value.as_bytes()); |
886 | extend(dst, b" \r\n" ); |
887 | } |
888 | |
889 | handle_is_name_written!(); |
890 | |
891 | if !wrote_len { |
892 | encoder = match msg.body { |
893 | Some(BodyLength::Unknown) => { |
894 | if msg.head.version == Version::HTTP_10 |
895 | || !Server::can_chunked(msg.req_method, msg.head.subject) |
896 | { |
897 | Encoder::close_delimited() |
898 | } else { |
899 | header_name_writer.write_full_header_line( |
900 | dst, |
901 | "transfer-encoding: chunked \r\n" , |
902 | (header::TRANSFER_ENCODING, ": chunked \r\n" ), |
903 | ); |
904 | Encoder::chunked() |
905 | } |
906 | } |
907 | None | Some(BodyLength::Known(0)) => { |
908 | if Server::can_have_implicit_zero_content_length( |
909 | msg.req_method, |
910 | msg.head.subject, |
911 | ) { |
912 | header_name_writer.write_full_header_line( |
913 | dst, |
914 | "content-length: 0 \r\n" , |
915 | (header::CONTENT_LENGTH, ": 0 \r\n" ), |
916 | ) |
917 | } |
918 | Encoder::length(0) |
919 | } |
920 | Some(BodyLength::Known(len)) => { |
921 | if !Server::can_have_content_length(msg.req_method, msg.head.subject) { |
922 | Encoder::length(0) |
923 | } else { |
924 | header_name_writer.write_header_name_with_colon( |
925 | dst, |
926 | "content-length: " , |
927 | header::CONTENT_LENGTH, |
928 | ); |
929 | extend(dst, ::itoa::Buffer::new().format(len).as_bytes()); |
930 | extend(dst, b" \r\n" ); |
931 | Encoder::length(len) |
932 | } |
933 | } |
934 | }; |
935 | } |
936 | |
937 | if !Server::can_have_body(msg.req_method, msg.head.subject) { |
938 | trace!( |
939 | "server body forced to 0; method={:?}, status={:?}" , |
940 | msg.req_method, |
941 | msg.head.subject |
942 | ); |
943 | encoder = Encoder::length(0); |
944 | } |
945 | |
946 | // cached date is much faster than formatting every request |
947 | // don't force the write if disabled |
948 | if !wrote_date && msg.date_header { |
949 | dst.reserve(date::DATE_VALUE_LENGTH + 8); |
950 | header_name_writer.write_header_name_with_colon(dst, "date: " , header::DATE); |
951 | date::extend(dst); |
952 | extend(dst, b" \r\n\r\n" ); |
953 | } else { |
954 | extend(dst, b" \r\n" ); |
955 | } |
956 | |
957 | if encoder.is_chunked() { |
958 | if let Some(allowed_trailer_fields) = allowed_trailer_fields { |
959 | encoder = encoder.into_chunked_with_trailing_fields(allowed_trailer_fields); |
960 | } |
961 | } |
962 | |
963 | Ok(encoder.set_last(is_last)) |
964 | } |
965 | |
966 | /// Helper for zero-copy parsing of request path URI. |
967 | #[inline ] |
968 | fn record_path_range(bytes: &[u8], req_path: &str) -> std::ops::Range<usize> { |
969 | let bytes_ptr = bytes.as_ptr() as usize; |
970 | let start = req_path.as_ptr() as usize - bytes_ptr; |
971 | let end = start + req_path.len(); |
972 | std::ops::Range { start, end } |
973 | } |
974 | } |
975 | |
976 | #[cfg (feature = "server" )] |
977 | trait HeaderNameWriter { |
978 | fn write_full_header_line( |
979 | &mut self, |
980 | dst: &mut Vec<u8>, |
981 | line: &str, |
982 | name_value_pair: (HeaderName, &str), |
983 | ); |
984 | fn write_header_name_with_colon( |
985 | &mut self, |
986 | dst: &mut Vec<u8>, |
987 | name_with_colon: &str, |
988 | name: HeaderName, |
989 | ); |
990 | fn write_header_name(&mut self, dst: &mut Vec<u8>, name: &HeaderName); |
991 | } |
992 | |
993 | #[cfg (feature = "client" )] |
994 | impl Http1Transaction for Client { |
995 | type Incoming = StatusCode; |
996 | type Outgoing = RequestLine; |
997 | #[cfg (feature = "tracing" )] |
998 | const LOG: &'static str = "{role=client}" ; |
999 | |
1000 | fn parse(buf: &mut BytesMut, ctx: ParseContext<'_>) -> ParseResult<StatusCode> { |
1001 | debug_assert!(!buf.is_empty(), "parse called with empty buf" ); |
1002 | |
1003 | // Loop to skip information status code headers (100 Continue, etc). |
1004 | loop { |
1005 | let mut headers_indices: SmallVec<[MaybeUninit<HeaderIndices>; DEFAULT_MAX_HEADERS]> = |
1006 | match ctx.h1_max_headers { |
1007 | Some(cap) => smallvec![MaybeUninit::uninit(); cap], |
1008 | None => smallvec_inline![MaybeUninit::uninit(); DEFAULT_MAX_HEADERS], |
1009 | }; |
1010 | let (len, status, reason, version, headers_len) = { |
1011 | let mut headers: SmallVec< |
1012 | [MaybeUninit<httparse::Header<'_>>; DEFAULT_MAX_HEADERS], |
1013 | > = match ctx.h1_max_headers { |
1014 | Some(cap) => smallvec![MaybeUninit::uninit(); cap], |
1015 | None => smallvec_inline![MaybeUninit::uninit(); DEFAULT_MAX_HEADERS], |
1016 | }; |
1017 | trace!(bytes = buf.len(), "Response.parse" ); |
1018 | let mut res = httparse::Response::new(&mut []); |
1019 | let bytes = buf.as_ref(); |
1020 | match ctx.h1_parser_config.parse_response_with_uninit_headers( |
1021 | &mut res, |
1022 | bytes, |
1023 | &mut headers, |
1024 | ) { |
1025 | Ok(httparse::Status::Complete(len)) => { |
1026 | trace!("Response.parse Complete({})" , len); |
1027 | let status = StatusCode::from_u16(res.code.unwrap())?; |
1028 | |
1029 | let reason = { |
1030 | let reason = res.reason.unwrap(); |
1031 | // Only save the reason phrase if it isn't the canonical reason |
1032 | if Some(reason) != status.canonical_reason() { |
1033 | Some(Bytes::copy_from_slice(reason.as_bytes())) |
1034 | } else { |
1035 | None |
1036 | } |
1037 | }; |
1038 | |
1039 | let version = if res.version.unwrap() == 1 { |
1040 | Version::HTTP_11 |
1041 | } else { |
1042 | Version::HTTP_10 |
1043 | }; |
1044 | record_header_indices(bytes, res.headers, &mut headers_indices)?; |
1045 | let headers_len = res.headers.len(); |
1046 | (len, status, reason, version, headers_len) |
1047 | } |
1048 | Ok(httparse::Status::Partial) => return Ok(None), |
1049 | Err(httparse::Error::Version) if ctx.h09_responses => { |
1050 | trace!("Response.parse accepted HTTP/0.9 response" ); |
1051 | |
1052 | (0, StatusCode::OK, None, Version::HTTP_09, 0) |
1053 | } |
1054 | Err(e) => return Err(e.into()), |
1055 | } |
1056 | }; |
1057 | |
1058 | let mut slice = buf.split_to(len); |
1059 | |
1060 | if ctx |
1061 | .h1_parser_config |
1062 | .obsolete_multiline_headers_in_responses_are_allowed() |
1063 | { |
1064 | for header in &mut headers_indices[..headers_len] { |
1065 | // SAFETY: array is valid up to `headers_len` |
1066 | let header = unsafe { header.assume_init_mut() }; |
1067 | Client::obs_fold_line(&mut slice, header); |
1068 | } |
1069 | } |
1070 | |
1071 | let slice = slice.freeze(); |
1072 | |
1073 | let mut headers = ctx.cached_headers.take().unwrap_or_default(); |
1074 | |
1075 | let mut keep_alive = version == Version::HTTP_11; |
1076 | |
1077 | let mut header_case_map = if ctx.preserve_header_case { |
1078 | Some(HeaderCaseMap::default()) |
1079 | } else { |
1080 | None |
1081 | }; |
1082 | |
1083 | #[cfg (feature = "ffi" )] |
1084 | let mut header_order = if ctx.preserve_header_order { |
1085 | Some(OriginalHeaderOrder::default()) |
1086 | } else { |
1087 | None |
1088 | }; |
1089 | |
1090 | headers.reserve(headers_len); |
1091 | for header in &headers_indices[..headers_len] { |
1092 | // SAFETY: array is valid up to `headers_len` |
1093 | let header = unsafe { header.assume_init_ref() }; |
1094 | let name = header_name!(&slice[header.name.0..header.name.1]); |
1095 | let value = header_value!(slice.slice(header.value.0..header.value.1)); |
1096 | |
1097 | if let header::CONNECTION = name { |
1098 | // keep_alive was previously set to default for Version |
1099 | if keep_alive { |
1100 | // HTTP/1.1 |
1101 | keep_alive = !headers::connection_close(&value); |
1102 | } else { |
1103 | // HTTP/1.0 |
1104 | keep_alive = headers::connection_keep_alive(&value); |
1105 | } |
1106 | } |
1107 | |
1108 | if let Some(ref mut header_case_map) = header_case_map { |
1109 | header_case_map.append(&name, slice.slice(header.name.0..header.name.1)); |
1110 | } |
1111 | |
1112 | #[cfg (feature = "ffi" )] |
1113 | if let Some(ref mut header_order) = header_order { |
1114 | header_order.append(&name); |
1115 | } |
1116 | |
1117 | headers.append(name, value); |
1118 | } |
1119 | |
1120 | let mut extensions = http::Extensions::default(); |
1121 | |
1122 | if let Some(header_case_map) = header_case_map { |
1123 | extensions.insert(header_case_map); |
1124 | } |
1125 | |
1126 | #[cfg (feature = "ffi" )] |
1127 | if let Some(header_order) = header_order { |
1128 | extensions.insert(header_order); |
1129 | } |
1130 | |
1131 | if let Some(reason) = reason { |
1132 | // Safety: httparse ensures that only valid reason phrase bytes are present in this |
1133 | // field. |
1134 | let reason = crate::ext::ReasonPhrase::from_bytes_unchecked(reason); |
1135 | extensions.insert(reason); |
1136 | } |
1137 | |
1138 | let head = MessageHead { |
1139 | version, |
1140 | subject: status, |
1141 | headers, |
1142 | extensions, |
1143 | }; |
1144 | if let Some((decode, is_upgrade)) = Client::decoder(&head, ctx.req_method)? { |
1145 | return Ok(Some(ParsedMessage { |
1146 | head, |
1147 | decode, |
1148 | expect_continue: false, |
1149 | // a client upgrade means the connection can't be used |
1150 | // again, as it is definitely upgrading. |
1151 | keep_alive: keep_alive && !is_upgrade, |
1152 | wants_upgrade: is_upgrade, |
1153 | })); |
1154 | } |
1155 | |
1156 | if head.subject.is_informational() { |
1157 | if let Some(callback) = ctx.on_informational { |
1158 | callback.call(head.into_response(())); |
1159 | } |
1160 | } |
1161 | |
1162 | // Parsing a 1xx response could have consumed the buffer, check if |
1163 | // it is empty now... |
1164 | if buf.is_empty() { |
1165 | return Ok(None); |
1166 | } |
1167 | } |
1168 | } |
1169 | |
1170 | fn encode(msg: Encode<'_, Self::Outgoing>, dst: &mut Vec<u8>) -> crate::Result<Encoder> { |
1171 | trace!( |
1172 | "Client::encode method={:?}, body={:?}" , |
1173 | msg.head.subject.0, |
1174 | msg.body |
1175 | ); |
1176 | |
1177 | *msg.req_method = Some(msg.head.subject.0.clone()); |
1178 | |
1179 | let body = Client::set_length(msg.head, msg.body); |
1180 | |
1181 | let init_cap = 30 + msg.head.headers.len() * AVERAGE_HEADER_SIZE; |
1182 | dst.reserve(init_cap); |
1183 | |
1184 | extend(dst, msg.head.subject.0.as_str().as_bytes()); |
1185 | extend(dst, b" " ); |
1186 | //TODO: add API to http::Uri to encode without std::fmt |
1187 | let _ = write!(FastWrite(dst), " {} " , msg.head.subject.1); |
1188 | |
1189 | match msg.head.version { |
1190 | Version::HTTP_10 => extend(dst, b"HTTP/1.0" ), |
1191 | Version::HTTP_11 => extend(dst, b"HTTP/1.1" ), |
1192 | Version::HTTP_2 => { |
1193 | debug!("request with HTTP2 version coerced to HTTP/1.1" ); |
1194 | extend(dst, b"HTTP/1.1" ); |
1195 | } |
1196 | other => panic!("unexpected request version: {:?}" , other), |
1197 | } |
1198 | extend(dst, b" \r\n" ); |
1199 | |
1200 | if let Some(orig_headers) = msg.head.extensions.get::<HeaderCaseMap>() { |
1201 | write_headers_original_case( |
1202 | &msg.head.headers, |
1203 | orig_headers, |
1204 | dst, |
1205 | msg.title_case_headers, |
1206 | ); |
1207 | } else if msg.title_case_headers { |
1208 | write_headers_title_case(&msg.head.headers, dst); |
1209 | } else { |
1210 | write_headers(&msg.head.headers, dst); |
1211 | } |
1212 | |
1213 | extend(dst, b" \r\n" ); |
1214 | msg.head.headers.clear(); //TODO: remove when switching to drain() |
1215 | |
1216 | Ok(body) |
1217 | } |
1218 | |
1219 | fn on_error(_err: &crate::Error) -> Option<MessageHead<Self::Outgoing>> { |
1220 | // we can't tell the server about any errors it creates |
1221 | None |
1222 | } |
1223 | |
1224 | fn is_client() -> bool { |
1225 | true |
1226 | } |
1227 | } |
1228 | |
1229 | #[cfg (feature = "client" )] |
1230 | impl Client { |
1231 | /// Returns Some(length, wants_upgrade) if successful. |
1232 | /// |
1233 | /// Returns None if this message head should be skipped (like a 100 status). |
1234 | fn decoder( |
1235 | inc: &MessageHead<StatusCode>, |
1236 | method: &mut Option<Method>, |
1237 | ) -> Result<Option<(DecodedLength, bool)>, Parse> { |
1238 | // According to https://tools.ietf.org/html/rfc7230#section-3.3.3 |
1239 | // 1. HEAD responses, and Status 1xx, 204, and 304 cannot have a body. |
1240 | // 2. Status 2xx to a CONNECT cannot have a body. |
1241 | // 3. Transfer-Encoding: chunked has a chunked body. |
1242 | // 4. If multiple differing Content-Length headers or invalid, close connection. |
1243 | // 5. Content-Length header has a sized body. |
1244 | // 6. (irrelevant to Response) |
1245 | // 7. Read till EOF. |
1246 | |
1247 | match inc.subject.as_u16() { |
1248 | 101 => { |
1249 | return Ok(Some((DecodedLength::ZERO, true))); |
1250 | } |
1251 | 100 | 102..=199 => { |
1252 | trace!("ignoring informational response: {}" , inc.subject.as_u16()); |
1253 | return Ok(None); |
1254 | } |
1255 | 204 | 304 => return Ok(Some((DecodedLength::ZERO, false))), |
1256 | _ => (), |
1257 | } |
1258 | match *method { |
1259 | Some(Method::HEAD) => { |
1260 | return Ok(Some((DecodedLength::ZERO, false))); |
1261 | } |
1262 | Some(Method::CONNECT) => { |
1263 | if let 200..=299 = inc.subject.as_u16() { |
1264 | return Ok(Some((DecodedLength::ZERO, true))); |
1265 | } |
1266 | } |
1267 | Some(_) => {} |
1268 | None => { |
1269 | trace!("Client::decoder is missing the Method" ); |
1270 | } |
1271 | } |
1272 | |
1273 | if inc.headers.contains_key(header::TRANSFER_ENCODING) { |
1274 | // https://tools.ietf.org/html/rfc7230#section-3.3.3 |
1275 | // If Transfer-Encoding header is present, and 'chunked' is |
1276 | // not the final encoding, and this is a Request, then it is |
1277 | // malformed. A server should respond with 400 Bad Request. |
1278 | if inc.version == Version::HTTP_10 { |
1279 | debug!("HTTP/1.0 cannot have Transfer-Encoding header" ); |
1280 | Err(Parse::transfer_encoding_unexpected()) |
1281 | } else if headers::transfer_encoding_is_chunked(&inc.headers) { |
1282 | Ok(Some((DecodedLength::CHUNKED, false))) |
1283 | } else { |
1284 | trace!("not chunked, read till eof" ); |
1285 | Ok(Some((DecodedLength::CLOSE_DELIMITED, false))) |
1286 | } |
1287 | } else if let Some(len) = headers::content_length_parse_all(&inc.headers) { |
1288 | Ok(Some((DecodedLength::checked_new(len)?, false))) |
1289 | } else if inc.headers.contains_key(header::CONTENT_LENGTH) { |
1290 | debug!("illegal Content-Length header" ); |
1291 | Err(Parse::content_length_invalid()) |
1292 | } else { |
1293 | trace!("neither Transfer-Encoding nor Content-Length" ); |
1294 | Ok(Some((DecodedLength::CLOSE_DELIMITED, false))) |
1295 | } |
1296 | } |
1297 | fn set_length(head: &mut RequestHead, body: Option<BodyLength>) -> Encoder { |
1298 | let body = if let Some(body) = body { |
1299 | body |
1300 | } else { |
1301 | head.headers.remove(header::TRANSFER_ENCODING); |
1302 | return Encoder::length(0); |
1303 | }; |
1304 | |
1305 | // HTTP/1.0 doesn't know about chunked |
1306 | let can_chunked = head.version == Version::HTTP_11; |
1307 | let headers = &mut head.headers; |
1308 | |
1309 | // If the user already set specific headers, we should respect them, regardless |
1310 | // of what the Body knows about itself. They set them for a reason. |
1311 | |
1312 | // Because of the borrow checker, we can't check the for an existing |
1313 | // Content-Length header while holding an `Entry` for the Transfer-Encoding |
1314 | // header, so unfortunately, we must do the check here, first. |
1315 | |
1316 | let existing_con_len = headers::content_length_parse_all(headers); |
1317 | let mut should_remove_con_len = false; |
1318 | |
1319 | if !can_chunked { |
1320 | // Chunked isn't legal, so if it is set, we need to remove it. |
1321 | if headers.remove(header::TRANSFER_ENCODING).is_some() { |
1322 | trace!("removing illegal transfer-encoding header" ); |
1323 | } |
1324 | |
1325 | return if let Some(len) = existing_con_len { |
1326 | Encoder::length(len) |
1327 | } else if let BodyLength::Known(len) = body { |
1328 | set_content_length(headers, len) |
1329 | } else { |
1330 | // HTTP/1.0 client requests without a content-length |
1331 | // cannot have any body at all. |
1332 | Encoder::length(0) |
1333 | }; |
1334 | } |
1335 | |
1336 | // If the user set a transfer-encoding, respect that. Let's just |
1337 | // make sure `chunked` is the final encoding. |
1338 | let encoder = match headers.entry(header::TRANSFER_ENCODING) { |
1339 | Entry::Occupied(te) => { |
1340 | should_remove_con_len = true; |
1341 | if headers::is_chunked(te.iter()) { |
1342 | Some(Encoder::chunked()) |
1343 | } else { |
1344 | warn!("user provided transfer-encoding does not end in 'chunked'" ); |
1345 | |
1346 | // There's a Transfer-Encoding, but it doesn't end in 'chunked'! |
1347 | // An example that could trigger this: |
1348 | // |
1349 | // Transfer-Encoding: gzip |
1350 | // |
1351 | // This can be bad, depending on if this is a request or a |
1352 | // response. |
1353 | // |
1354 | // - A request is illegal if there is a `Transfer-Encoding` |
1355 | // but it doesn't end in `chunked`. |
1356 | // - A response that has `Transfer-Encoding` but doesn't |
1357 | // end in `chunked` isn't illegal, it just forces this |
1358 | // to be close-delimited. |
1359 | // |
1360 | // We can try to repair this, by adding `chunked` ourselves. |
1361 | |
1362 | headers::add_chunked(te); |
1363 | Some(Encoder::chunked()) |
1364 | } |
1365 | } |
1366 | Entry::Vacant(te) => { |
1367 | if let Some(len) = existing_con_len { |
1368 | Some(Encoder::length(len)) |
1369 | } else if let BodyLength::Unknown = body { |
1370 | // GET, HEAD, and CONNECT almost never have bodies. |
1371 | // |
1372 | // So instead of sending a "chunked" body with a 0-chunk, |
1373 | // assume no body here. If you *must* send a body, |
1374 | // set the headers explicitly. |
1375 | match head.subject.0 { |
1376 | Method::GET | Method::HEAD | Method::CONNECT => Some(Encoder::length(0)), |
1377 | _ => { |
1378 | te.insert(HeaderValue::from_static("chunked" )); |
1379 | Some(Encoder::chunked()) |
1380 | } |
1381 | } |
1382 | } else { |
1383 | None |
1384 | } |
1385 | } |
1386 | }; |
1387 | |
1388 | let encoder = encoder.map(|enc| { |
1389 | if enc.is_chunked() { |
1390 | let allowed_trailer_fields: Vec<HeaderValue> = |
1391 | headers.get_all(header::TRAILER).iter().cloned().collect(); |
1392 | |
1393 | if !allowed_trailer_fields.is_empty() { |
1394 | return enc.into_chunked_with_trailing_fields(allowed_trailer_fields); |
1395 | } |
1396 | } |
1397 | |
1398 | enc |
1399 | }); |
1400 | |
1401 | // This is because we need a second mutable borrow to remove |
1402 | // content-length header. |
1403 | if let Some(encoder) = encoder { |
1404 | if should_remove_con_len && existing_con_len.is_some() { |
1405 | headers.remove(header::CONTENT_LENGTH); |
1406 | } |
1407 | return encoder; |
1408 | } |
1409 | |
1410 | // User didn't set transfer-encoding, AND we know body length, |
1411 | // so we can just set the Content-Length automatically. |
1412 | |
1413 | let len = if let BodyLength::Known(len) = body { |
1414 | len |
1415 | } else { |
1416 | unreachable!("BodyLength::Unknown would set chunked" ); |
1417 | }; |
1418 | |
1419 | set_content_length(headers, len) |
1420 | } |
1421 | |
1422 | fn obs_fold_line(all: &mut [u8], idx: &mut HeaderIndices) { |
1423 | // If the value has obs-folded text, then in-place shift the bytes out |
1424 | // of here. |
1425 | // |
1426 | // https://httpwg.org/specs/rfc9112.html#line.folding |
1427 | // |
1428 | // > A user agent that receives an obs-fold MUST replace each received |
1429 | // > obs-fold with one or more SP octets prior to interpreting the |
1430 | // > field value. |
1431 | // |
1432 | // This means strings like "\r\n\t foo" must replace the "\r\n\t " with |
1433 | // a single space. |
1434 | |
1435 | let buf = &mut all[idx.value.0..idx.value.1]; |
1436 | |
1437 | // look for a newline, otherwise bail out |
1438 | let first_nl = match buf.iter().position(|b| *b == b' \n' ) { |
1439 | Some(i) => i, |
1440 | None => return, |
1441 | }; |
1442 | |
1443 | // not on standard slices because whatever, sigh |
1444 | fn trim_start(mut s: &[u8]) -> &[u8] { |
1445 | while let [first, rest @ ..] = s { |
1446 | if first.is_ascii_whitespace() { |
1447 | s = rest; |
1448 | } else { |
1449 | break; |
1450 | } |
1451 | } |
1452 | s |
1453 | } |
1454 | |
1455 | fn trim_end(mut s: &[u8]) -> &[u8] { |
1456 | while let [rest @ .., last] = s { |
1457 | if last.is_ascii_whitespace() { |
1458 | s = rest; |
1459 | } else { |
1460 | break; |
1461 | } |
1462 | } |
1463 | s |
1464 | } |
1465 | |
1466 | fn trim(s: &[u8]) -> &[u8] { |
1467 | trim_start(trim_end(s)) |
1468 | } |
1469 | |
1470 | // TODO(perf): we could do the moves in-place, but this is so uncommon |
1471 | // that it shouldn't matter. |
1472 | let mut unfolded = trim_end(&buf[..first_nl]).to_vec(); |
1473 | for line in buf[first_nl + 1..].split(|b| *b == b' \n' ) { |
1474 | unfolded.push(b' ' ); |
1475 | unfolded.extend_from_slice(trim(line)); |
1476 | } |
1477 | buf[..unfolded.len()].copy_from_slice(&unfolded); |
1478 | idx.value.1 = idx.value.0 + unfolded.len(); |
1479 | } |
1480 | } |
1481 | |
1482 | #[cfg (feature = "client" )] |
1483 | fn set_content_length(headers: &mut HeaderMap, len: u64) -> Encoder { |
1484 | // At this point, there should not be a valid Content-Length |
1485 | // header. However, since we'll be indexing in anyways, we can |
1486 | // warn the user if there was an existing illegal header. |
1487 | // |
1488 | // Or at least, we can in theory. It's actually a little bit slower, |
1489 | // so perhaps only do that while the user is developing/testing. |
1490 | |
1491 | if cfg!(debug_assertions) { |
1492 | match headers.entry(header::CONTENT_LENGTH) { |
1493 | Entry::Occupied(mut cl) => { |
1494 | // Internal sanity check, we should have already determined |
1495 | // that the header was illegal before calling this function. |
1496 | debug_assert!(headers::content_length_parse_all_values(cl.iter()).is_none()); |
1497 | // Uh oh, the user set `Content-Length` headers, but set bad ones. |
1498 | // This would be an illegal message anyways, so let's try to repair |
1499 | // with our known good length. |
1500 | error!("user provided content-length header was invalid" ); |
1501 | |
1502 | cl.insert(HeaderValue::from(len)); |
1503 | Encoder::length(len) |
1504 | } |
1505 | Entry::Vacant(cl) => { |
1506 | cl.insert(HeaderValue::from(len)); |
1507 | Encoder::length(len) |
1508 | } |
1509 | } |
1510 | } else { |
1511 | headers.insert(header::CONTENT_LENGTH, HeaderValue::from(len)); |
1512 | Encoder::length(len) |
1513 | } |
1514 | } |
1515 | |
1516 | #[derive (Clone, Copy)] |
1517 | struct HeaderIndices { |
1518 | name: (usize, usize), |
1519 | value: (usize, usize), |
1520 | } |
1521 | |
1522 | fn record_header_indices( |
1523 | bytes: &[u8], |
1524 | headers: &[httparse::Header<'_>], |
1525 | indices: &mut [MaybeUninit<HeaderIndices>], |
1526 | ) -> Result<(), crate::error::Parse> { |
1527 | let bytes_ptr: usize = bytes.as_ptr() as usize; |
1528 | |
1529 | for (header: &Header<'_>, indices: &mut MaybeUninit) in headers.iter().zip(indices.iter_mut()) { |
1530 | if header.name.len() >= (1 << 16) { |
1531 | debug!("header name larger than 64kb: {:?}" , header.name); |
1532 | return Err(crate::error::Parse::TooLarge); |
1533 | } |
1534 | let name_start: usize = header.name.as_ptr() as usize - bytes_ptr; |
1535 | let name_end: usize = name_start + header.name.len(); |
1536 | let value_start: usize = header.value.as_ptr() as usize - bytes_ptr; |
1537 | let value_end: usize = value_start + header.value.len(); |
1538 | |
1539 | indices.write(val:HeaderIndices { |
1540 | name: (name_start, name_end), |
1541 | value: (value_start, value_end), |
1542 | }); |
1543 | } |
1544 | |
1545 | Ok(()) |
1546 | } |
1547 | |
1548 | // Write header names as title case. The header name is assumed to be ASCII. |
1549 | fn title_case(dst: &mut Vec<u8>, name: &[u8]) { |
1550 | dst.reserve(additional:name.len()); |
1551 | |
1552 | // Ensure first character is uppercased |
1553 | let mut prev: u8 = b'-' ; |
1554 | for &(mut c: u8) in name { |
1555 | if prev == b'-' { |
1556 | c.make_ascii_uppercase(); |
1557 | } |
1558 | dst.push(c); |
1559 | prev = c; |
1560 | } |
1561 | } |
1562 | |
1563 | pub(crate) fn write_headers_title_case(headers: &HeaderMap, dst: &mut Vec<u8>) { |
1564 | for (name: &HeaderName, value: &HeaderValue) in headers { |
1565 | title_case(dst, name.as_str().as_bytes()); |
1566 | extend(dst, data:b": " ); |
1567 | extend(dst, data:value.as_bytes()); |
1568 | extend(dst, data:b" \r\n" ); |
1569 | } |
1570 | } |
1571 | |
1572 | pub(crate) fn write_headers(headers: &HeaderMap, dst: &mut Vec<u8>) { |
1573 | for (name: &HeaderName, value: &HeaderValue) in headers { |
1574 | extend(dst, data:name.as_str().as_bytes()); |
1575 | extend(dst, data:b": " ); |
1576 | extend(dst, data:value.as_bytes()); |
1577 | extend(dst, data:b" \r\n" ); |
1578 | } |
1579 | } |
1580 | |
1581 | #[cold ] |
1582 | #[cfg (feature = "client" )] |
1583 | fn write_headers_original_case( |
1584 | headers: &HeaderMap, |
1585 | orig_case: &HeaderCaseMap, |
1586 | dst: &mut Vec<u8>, |
1587 | title_case_headers: bool, |
1588 | ) { |
1589 | // For each header name/value pair, there may be a value in the casemap |
1590 | // that corresponds to the HeaderValue. So, we iterator all the keys, |
1591 | // and for each one, try to pair the originally cased name with the value. |
1592 | // |
1593 | // TODO: consider adding http::HeaderMap::entries() iterator |
1594 | for name in headers.keys() { |
1595 | let mut names = orig_case.get_all(name); |
1596 | |
1597 | for value in headers.get_all(name) { |
1598 | if let Some(orig_name) = names.next() { |
1599 | extend(dst, orig_name.as_ref()); |
1600 | } else if title_case_headers { |
1601 | title_case(dst, name.as_str().as_bytes()); |
1602 | } else { |
1603 | extend(dst, name.as_str().as_bytes()); |
1604 | } |
1605 | |
1606 | // Wanted for curl test cases that send `X-Custom-Header:\r\n` |
1607 | if value.is_empty() { |
1608 | extend(dst, b": \r\n" ); |
1609 | } else { |
1610 | extend(dst, b": " ); |
1611 | extend(dst, value.as_bytes()); |
1612 | extend(dst, b" \r\n" ); |
1613 | } |
1614 | } |
1615 | } |
1616 | } |
1617 | |
1618 | #[cfg (feature = "client" )] |
1619 | struct FastWrite<'a>(&'a mut Vec<u8>); |
1620 | |
1621 | #[cfg (feature = "client" )] |
1622 | impl fmt::Write for FastWrite<'_> { |
1623 | #[inline ] |
1624 | fn write_str(&mut self, s: &str) -> fmt::Result { |
1625 | extend(self.0, data:s.as_bytes()); |
1626 | Ok(()) |
1627 | } |
1628 | |
1629 | #[inline ] |
1630 | fn write_fmt(&mut self, args: fmt::Arguments<'_>) -> fmt::Result { |
1631 | fmt::write(self, args) |
1632 | } |
1633 | } |
1634 | |
1635 | #[inline ] |
1636 | fn extend(dst: &mut Vec<u8>, data: &[u8]) { |
1637 | dst.extend_from_slice(data); |
1638 | } |
1639 | |
1640 | #[cfg (test)] |
1641 | mod tests { |
1642 | use bytes::BytesMut; |
1643 | |
1644 | use super::*; |
1645 | |
1646 | #[cfg (feature = "server" )] |
1647 | #[test ] |
1648 | fn test_parse_request() { |
1649 | let _ = pretty_env_logger::try_init(); |
1650 | let mut raw = BytesMut::from("GET /echo HTTP/1.1 \r\nHost: hyper.rs \r\n\r\n" ); |
1651 | let mut method = None; |
1652 | let msg = Server::parse( |
1653 | &mut raw, |
1654 | ParseContext { |
1655 | cached_headers: &mut None, |
1656 | req_method: &mut method, |
1657 | h1_parser_config: Default::default(), |
1658 | h1_max_headers: None, |
1659 | preserve_header_case: false, |
1660 | #[cfg (feature = "ffi" )] |
1661 | preserve_header_order: false, |
1662 | h09_responses: false, |
1663 | #[cfg (feature = "client" )] |
1664 | on_informational: &mut None, |
1665 | }, |
1666 | ) |
1667 | .unwrap() |
1668 | .unwrap(); |
1669 | assert_eq!(raw.len(), 0); |
1670 | assert_eq!(msg.head.subject.0, crate::Method::GET); |
1671 | assert_eq!(msg.head.subject.1, "/echo" ); |
1672 | assert_eq!(msg.head.version, crate::Version::HTTP_11); |
1673 | assert_eq!(msg.head.headers.len(), 1); |
1674 | assert_eq!(msg.head.headers["Host" ], "hyper.rs" ); |
1675 | assert_eq!(method, Some(crate::Method::GET)); |
1676 | } |
1677 | |
1678 | #[test ] |
1679 | fn test_parse_response() { |
1680 | let _ = pretty_env_logger::try_init(); |
1681 | let mut raw = BytesMut::from("HTTP/1.1 200 OK \r\nContent-Length: 0 \r\n\r\n" ); |
1682 | let ctx = ParseContext { |
1683 | cached_headers: &mut None, |
1684 | req_method: &mut Some(crate::Method::GET), |
1685 | h1_parser_config: Default::default(), |
1686 | h1_max_headers: None, |
1687 | preserve_header_case: false, |
1688 | #[cfg (feature = "ffi" )] |
1689 | preserve_header_order: false, |
1690 | h09_responses: false, |
1691 | #[cfg (feature = "client" )] |
1692 | on_informational: &mut None, |
1693 | }; |
1694 | let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); |
1695 | assert_eq!(raw.len(), 0); |
1696 | assert_eq!(msg.head.subject, crate::StatusCode::OK); |
1697 | assert_eq!(msg.head.version, crate::Version::HTTP_11); |
1698 | assert_eq!(msg.head.headers.len(), 1); |
1699 | assert_eq!(msg.head.headers["Content-Length" ], "0" ); |
1700 | } |
1701 | |
1702 | #[cfg (feature = "server" )] |
1703 | #[test ] |
1704 | fn test_parse_request_errors() { |
1705 | let mut raw = BytesMut::from("GET htt:p// HTTP/1.1 \r\nHost: hyper.rs \r\n\r\n" ); |
1706 | let ctx = ParseContext { |
1707 | cached_headers: &mut None, |
1708 | req_method: &mut None, |
1709 | h1_parser_config: Default::default(), |
1710 | h1_max_headers: None, |
1711 | preserve_header_case: false, |
1712 | #[cfg (feature = "ffi" )] |
1713 | preserve_header_order: false, |
1714 | h09_responses: false, |
1715 | #[cfg (feature = "client" )] |
1716 | on_informational: &mut None, |
1717 | }; |
1718 | Server::parse(&mut raw, ctx).unwrap_err(); |
1719 | } |
1720 | |
1721 | const H09_RESPONSE: &str = "Baguettes are super delicious, don't you agree?" ; |
1722 | |
1723 | #[test ] |
1724 | fn test_parse_response_h09_allowed() { |
1725 | let _ = pretty_env_logger::try_init(); |
1726 | let mut raw = BytesMut::from(H09_RESPONSE); |
1727 | let ctx = ParseContext { |
1728 | cached_headers: &mut None, |
1729 | req_method: &mut Some(crate::Method::GET), |
1730 | h1_parser_config: Default::default(), |
1731 | h1_max_headers: None, |
1732 | preserve_header_case: false, |
1733 | #[cfg (feature = "ffi" )] |
1734 | preserve_header_order: false, |
1735 | h09_responses: true, |
1736 | #[cfg (feature = "client" )] |
1737 | on_informational: &mut None, |
1738 | }; |
1739 | let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); |
1740 | assert_eq!(raw, H09_RESPONSE); |
1741 | assert_eq!(msg.head.subject, crate::StatusCode::OK); |
1742 | assert_eq!(msg.head.version, crate::Version::HTTP_09); |
1743 | assert_eq!(msg.head.headers.len(), 0); |
1744 | } |
1745 | |
1746 | #[test ] |
1747 | fn test_parse_response_h09_rejected() { |
1748 | let _ = pretty_env_logger::try_init(); |
1749 | let mut raw = BytesMut::from(H09_RESPONSE); |
1750 | let ctx = ParseContext { |
1751 | cached_headers: &mut None, |
1752 | req_method: &mut Some(crate::Method::GET), |
1753 | h1_parser_config: Default::default(), |
1754 | h1_max_headers: None, |
1755 | preserve_header_case: false, |
1756 | #[cfg (feature = "ffi" )] |
1757 | preserve_header_order: false, |
1758 | h09_responses: false, |
1759 | #[cfg (feature = "client" )] |
1760 | on_informational: &mut None, |
1761 | }; |
1762 | Client::parse(&mut raw, ctx).unwrap_err(); |
1763 | assert_eq!(raw, H09_RESPONSE); |
1764 | } |
1765 | |
1766 | const RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON: &str = |
1767 | "HTTP/1.1 200 OK \r\nAccess-Control-Allow-Credentials : true \r\n\r\n" ; |
1768 | |
1769 | #[test ] |
1770 | fn test_parse_allow_response_with_spaces_before_colons() { |
1771 | use httparse::ParserConfig; |
1772 | |
1773 | let _ = pretty_env_logger::try_init(); |
1774 | let mut raw = BytesMut::from(RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); |
1775 | let mut h1_parser_config = ParserConfig::default(); |
1776 | h1_parser_config.allow_spaces_after_header_name_in_responses(true); |
1777 | let ctx = ParseContext { |
1778 | cached_headers: &mut None, |
1779 | req_method: &mut Some(crate::Method::GET), |
1780 | h1_parser_config, |
1781 | h1_max_headers: None, |
1782 | preserve_header_case: false, |
1783 | #[cfg (feature = "ffi" )] |
1784 | preserve_header_order: false, |
1785 | h09_responses: false, |
1786 | #[cfg (feature = "client" )] |
1787 | on_informational: &mut None, |
1788 | }; |
1789 | let msg = Client::parse(&mut raw, ctx).unwrap().unwrap(); |
1790 | assert_eq!(raw.len(), 0); |
1791 | assert_eq!(msg.head.subject, crate::StatusCode::OK); |
1792 | assert_eq!(msg.head.version, crate::Version::HTTP_11); |
1793 | assert_eq!(msg.head.headers.len(), 1); |
1794 | assert_eq!(msg.head.headers["Access-Control-Allow-Credentials" ], "true" ); |
1795 | } |
1796 | |
1797 | #[test ] |
1798 | fn test_parse_reject_response_with_spaces_before_colons() { |
1799 | let _ = pretty_env_logger::try_init(); |
1800 | let mut raw = BytesMut::from(RESPONSE_WITH_WHITESPACE_BETWEEN_HEADER_NAME_AND_COLON); |
1801 | let ctx = ParseContext { |
1802 | cached_headers: &mut None, |
1803 | req_method: &mut Some(crate::Method::GET), |
1804 | h1_parser_config: Default::default(), |
1805 | h1_max_headers: None, |
1806 | preserve_header_case: false, |
1807 | #[cfg (feature = "ffi" )] |
1808 | preserve_header_order: false, |
1809 | h09_responses: false, |
1810 | #[cfg (feature = "client" )] |
1811 | on_informational: &mut None, |
1812 | }; |
1813 | Client::parse(&mut raw, ctx).unwrap_err(); |
1814 | } |
1815 | |
1816 | #[cfg (feature = "server" )] |
1817 | #[test ] |
1818 | fn test_parse_preserve_header_case_in_request() { |
1819 | let mut raw = |
1820 | BytesMut::from("GET / HTTP/1.1 \r\nHost: hyper.rs \r\nX-BREAD: baguette \r\n\r\n" ); |
1821 | let ctx = ParseContext { |
1822 | cached_headers: &mut None, |
1823 | req_method: &mut None, |
1824 | h1_parser_config: Default::default(), |
1825 | h1_max_headers: None, |
1826 | preserve_header_case: true, |
1827 | #[cfg (feature = "ffi" )] |
1828 | preserve_header_order: false, |
1829 | h09_responses: false, |
1830 | #[cfg (feature = "client" )] |
1831 | on_informational: &mut None, |
1832 | }; |
1833 | let parsed_message = Server::parse(&mut raw, ctx).unwrap().unwrap(); |
1834 | let orig_headers = parsed_message |
1835 | .head |
1836 | .extensions |
1837 | .get::<HeaderCaseMap>() |
1838 | .unwrap(); |
1839 | assert_eq!( |
1840 | orig_headers |
1841 | .get_all_internal(&HeaderName::from_static("host" )) |
1842 | .collect::<Vec<_>>(), |
1843 | vec![&Bytes::from("Host" )] |
1844 | ); |
1845 | assert_eq!( |
1846 | orig_headers |
1847 | .get_all_internal(&HeaderName::from_static("x-bread" )) |
1848 | .collect::<Vec<_>>(), |
1849 | vec![&Bytes::from("X-BREAD" )] |
1850 | ); |
1851 | } |
1852 | |
1853 | #[cfg (feature = "server" )] |
1854 | #[test ] |
1855 | fn test_decoder_request() { |
1856 | fn parse(s: &str) -> ParsedMessage<RequestLine> { |
1857 | let mut bytes = BytesMut::from(s); |
1858 | Server::parse( |
1859 | &mut bytes, |
1860 | ParseContext { |
1861 | cached_headers: &mut None, |
1862 | req_method: &mut None, |
1863 | h1_parser_config: Default::default(), |
1864 | h1_max_headers: None, |
1865 | preserve_header_case: false, |
1866 | #[cfg (feature = "ffi" )] |
1867 | preserve_header_order: false, |
1868 | h09_responses: false, |
1869 | #[cfg (feature = "client" )] |
1870 | on_informational: &mut None, |
1871 | }, |
1872 | ) |
1873 | .expect("parse ok" ) |
1874 | .expect("parse complete" ) |
1875 | } |
1876 | |
1877 | fn parse_err(s: &str, comment: &str) -> crate::error::Parse { |
1878 | let mut bytes = BytesMut::from(s); |
1879 | Server::parse( |
1880 | &mut bytes, |
1881 | ParseContext { |
1882 | cached_headers: &mut None, |
1883 | req_method: &mut None, |
1884 | h1_parser_config: Default::default(), |
1885 | h1_max_headers: None, |
1886 | preserve_header_case: false, |
1887 | #[cfg (feature = "ffi" )] |
1888 | preserve_header_order: false, |
1889 | h09_responses: false, |
1890 | #[cfg (feature = "client" )] |
1891 | on_informational: &mut None, |
1892 | }, |
1893 | ) |
1894 | .expect_err(comment) |
1895 | } |
1896 | |
1897 | // no length or transfer-encoding means 0-length body |
1898 | assert_eq!( |
1899 | parse( |
1900 | "\ |
1901 | GET / HTTP/1.1 \r\n\ |
1902 | \r\n\ |
1903 | " |
1904 | ) |
1905 | .decode, |
1906 | DecodedLength::ZERO |
1907 | ); |
1908 | |
1909 | assert_eq!( |
1910 | parse( |
1911 | "\ |
1912 | POST / HTTP/1.1 \r\n\ |
1913 | \r\n\ |
1914 | " |
1915 | ) |
1916 | .decode, |
1917 | DecodedLength::ZERO |
1918 | ); |
1919 | |
1920 | // transfer-encoding: chunked |
1921 | assert_eq!( |
1922 | parse( |
1923 | "\ |
1924 | POST / HTTP/1.1 \r\n\ |
1925 | transfer-encoding: chunked \r\n\ |
1926 | \r\n\ |
1927 | " |
1928 | ) |
1929 | .decode, |
1930 | DecodedLength::CHUNKED |
1931 | ); |
1932 | |
1933 | assert_eq!( |
1934 | parse( |
1935 | "\ |
1936 | POST / HTTP/1.1 \r\n\ |
1937 | transfer-encoding: gzip, chunked \r\n\ |
1938 | \r\n\ |
1939 | " |
1940 | ) |
1941 | .decode, |
1942 | DecodedLength::CHUNKED |
1943 | ); |
1944 | |
1945 | assert_eq!( |
1946 | parse( |
1947 | "\ |
1948 | POST / HTTP/1.1 \r\n\ |
1949 | transfer-encoding: gzip \r\n\ |
1950 | transfer-encoding: chunked \r\n\ |
1951 | \r\n\ |
1952 | " |
1953 | ) |
1954 | .decode, |
1955 | DecodedLength::CHUNKED |
1956 | ); |
1957 | |
1958 | // content-length |
1959 | assert_eq!( |
1960 | parse( |
1961 | "\ |
1962 | POST / HTTP/1.1 \r\n\ |
1963 | content-length: 10 \r\n\ |
1964 | \r\n\ |
1965 | " |
1966 | ) |
1967 | .decode, |
1968 | DecodedLength::new(10) |
1969 | ); |
1970 | |
1971 | // transfer-encoding and content-length = chunked |
1972 | assert_eq!( |
1973 | parse( |
1974 | "\ |
1975 | POST / HTTP/1.1 \r\n\ |
1976 | content-length: 10 \r\n\ |
1977 | transfer-encoding: chunked \r\n\ |
1978 | \r\n\ |
1979 | " |
1980 | ) |
1981 | .decode, |
1982 | DecodedLength::CHUNKED |
1983 | ); |
1984 | |
1985 | assert_eq!( |
1986 | parse( |
1987 | "\ |
1988 | POST / HTTP/1.1 \r\n\ |
1989 | transfer-encoding: chunked \r\n\ |
1990 | content-length: 10 \r\n\ |
1991 | \r\n\ |
1992 | " |
1993 | ) |
1994 | .decode, |
1995 | DecodedLength::CHUNKED |
1996 | ); |
1997 | |
1998 | assert_eq!( |
1999 | parse( |
2000 | "\ |
2001 | POST / HTTP/1.1 \r\n\ |
2002 | transfer-encoding: gzip \r\n\ |
2003 | content-length: 10 \r\n\ |
2004 | transfer-encoding: chunked \r\n\ |
2005 | \r\n\ |
2006 | " |
2007 | ) |
2008 | .decode, |
2009 | DecodedLength::CHUNKED |
2010 | ); |
2011 | |
2012 | // multiple content-lengths of same value are fine |
2013 | assert_eq!( |
2014 | parse( |
2015 | "\ |
2016 | POST / HTTP/1.1 \r\n\ |
2017 | content-length: 10 \r\n\ |
2018 | content-length: 10 \r\n\ |
2019 | \r\n\ |
2020 | " |
2021 | ) |
2022 | .decode, |
2023 | DecodedLength::new(10) |
2024 | ); |
2025 | |
2026 | // multiple content-lengths with different values is an error |
2027 | parse_err( |
2028 | "\ |
2029 | POST / HTTP/1.1 \r\n\ |
2030 | content-length: 10 \r\n\ |
2031 | content-length: 11 \r\n\ |
2032 | \r\n\ |
2033 | " , |
2034 | "multiple content-lengths" , |
2035 | ); |
2036 | |
2037 | // content-length with prefix is not allowed |
2038 | parse_err( |
2039 | "\ |
2040 | POST / HTTP/1.1 \r\n\ |
2041 | content-length: +10 \r\n\ |
2042 | \r\n\ |
2043 | " , |
2044 | "prefixed content-length" , |
2045 | ); |
2046 | |
2047 | // transfer-encoding that isn't chunked is an error |
2048 | parse_err( |
2049 | "\ |
2050 | POST / HTTP/1.1 \r\n\ |
2051 | transfer-encoding: gzip \r\n\ |
2052 | \r\n\ |
2053 | " , |
2054 | "transfer-encoding but not chunked" , |
2055 | ); |
2056 | |
2057 | parse_err( |
2058 | "\ |
2059 | POST / HTTP/1.1 \r\n\ |
2060 | transfer-encoding: chunked, gzip \r\n\ |
2061 | \r\n\ |
2062 | " , |
2063 | "transfer-encoding doesn't end in chunked" , |
2064 | ); |
2065 | |
2066 | parse_err( |
2067 | "\ |
2068 | POST / HTTP/1.1 \r\n\ |
2069 | transfer-encoding: chunked \r\n\ |
2070 | transfer-encoding: afterlol \r\n\ |
2071 | \r\n\ |
2072 | " , |
2073 | "transfer-encoding multiple lines doesn't end in chunked" , |
2074 | ); |
2075 | |
2076 | // http/1.0 |
2077 | |
2078 | assert_eq!( |
2079 | parse( |
2080 | "\ |
2081 | POST / HTTP/1.0 \r\n\ |
2082 | content-length: 10 \r\n\ |
2083 | \r\n\ |
2084 | " |
2085 | ) |
2086 | .decode, |
2087 | DecodedLength::new(10) |
2088 | ); |
2089 | |
2090 | // 1.0 doesn't understand chunked, so its an error |
2091 | parse_err( |
2092 | "\ |
2093 | POST / HTTP/1.0 \r\n\ |
2094 | transfer-encoding: chunked \r\n\ |
2095 | \r\n\ |
2096 | " , |
2097 | "1.0 chunked" , |
2098 | ); |
2099 | } |
2100 | |
2101 | #[test ] |
2102 | fn test_decoder_response() { |
2103 | fn parse(s: &str) -> ParsedMessage<StatusCode> { |
2104 | parse_with_method(s, Method::GET) |
2105 | } |
2106 | |
2107 | fn parse_ignores(s: &str) { |
2108 | let mut bytes = BytesMut::from(s); |
2109 | assert!(Client::parse( |
2110 | &mut bytes, |
2111 | ParseContext { |
2112 | cached_headers: &mut None, |
2113 | req_method: &mut Some(Method::GET), |
2114 | h1_parser_config: Default::default(), |
2115 | h1_max_headers: None, |
2116 | preserve_header_case: false, |
2117 | #[cfg(feature = "ffi" )] |
2118 | preserve_header_order: false, |
2119 | h09_responses: false, |
2120 | #[cfg(feature = "client" )] |
2121 | on_informational: &mut None, |
2122 | } |
2123 | ) |
2124 | .expect("parse ok" ) |
2125 | .is_none()) |
2126 | } |
2127 | |
2128 | fn parse_with_method(s: &str, m: Method) -> ParsedMessage<StatusCode> { |
2129 | let mut bytes = BytesMut::from(s); |
2130 | Client::parse( |
2131 | &mut bytes, |
2132 | ParseContext { |
2133 | cached_headers: &mut None, |
2134 | req_method: &mut Some(m), |
2135 | h1_parser_config: Default::default(), |
2136 | h1_max_headers: None, |
2137 | preserve_header_case: false, |
2138 | #[cfg (feature = "ffi" )] |
2139 | preserve_header_order: false, |
2140 | h09_responses: false, |
2141 | #[cfg (feature = "client" )] |
2142 | on_informational: &mut None, |
2143 | }, |
2144 | ) |
2145 | .expect("parse ok" ) |
2146 | .expect("parse complete" ) |
2147 | } |
2148 | |
2149 | fn parse_err(s: &str) -> crate::error::Parse { |
2150 | let mut bytes = BytesMut::from(s); |
2151 | Client::parse( |
2152 | &mut bytes, |
2153 | ParseContext { |
2154 | cached_headers: &mut None, |
2155 | req_method: &mut Some(Method::GET), |
2156 | h1_parser_config: Default::default(), |
2157 | h1_max_headers: None, |
2158 | preserve_header_case: false, |
2159 | #[cfg (feature = "ffi" )] |
2160 | preserve_header_order: false, |
2161 | h09_responses: false, |
2162 | #[cfg (feature = "client" )] |
2163 | on_informational: &mut None, |
2164 | }, |
2165 | ) |
2166 | .expect_err("parse should err" ) |
2167 | } |
2168 | |
2169 | // no content-length or transfer-encoding means close-delimited |
2170 | assert_eq!( |
2171 | parse( |
2172 | "\ |
2173 | HTTP/1.1 200 OK \r\n\ |
2174 | \r\n\ |
2175 | " |
2176 | ) |
2177 | .decode, |
2178 | DecodedLength::CLOSE_DELIMITED |
2179 | ); |
2180 | |
2181 | // 204 and 304 never have a body |
2182 | assert_eq!( |
2183 | parse( |
2184 | "\ |
2185 | HTTP/1.1 204 No Content \r\n\ |
2186 | \r\n\ |
2187 | " |
2188 | ) |
2189 | .decode, |
2190 | DecodedLength::ZERO |
2191 | ); |
2192 | |
2193 | assert_eq!( |
2194 | parse( |
2195 | "\ |
2196 | HTTP/1.1 304 Not Modified \r\n\ |
2197 | \r\n\ |
2198 | " |
2199 | ) |
2200 | .decode, |
2201 | DecodedLength::ZERO |
2202 | ); |
2203 | |
2204 | // content-length |
2205 | assert_eq!( |
2206 | parse( |
2207 | "\ |
2208 | HTTP/1.1 200 OK \r\n\ |
2209 | content-length: 8 \r\n\ |
2210 | \r\n\ |
2211 | " |
2212 | ) |
2213 | .decode, |
2214 | DecodedLength::new(8) |
2215 | ); |
2216 | |
2217 | assert_eq!( |
2218 | parse( |
2219 | "\ |
2220 | HTTP/1.1 200 OK \r\n\ |
2221 | content-length: 8 \r\n\ |
2222 | content-length: 8 \r\n\ |
2223 | \r\n\ |
2224 | " |
2225 | ) |
2226 | .decode, |
2227 | DecodedLength::new(8) |
2228 | ); |
2229 | |
2230 | parse_err( |
2231 | "\ |
2232 | HTTP/1.1 200 OK \r\n\ |
2233 | content-length: 8 \r\n\ |
2234 | content-length: 9 \r\n\ |
2235 | \r\n\ |
2236 | " , |
2237 | ); |
2238 | |
2239 | parse_err( |
2240 | "\ |
2241 | HTTP/1.1 200 OK \r\n\ |
2242 | content-length: +8 \r\n\ |
2243 | \r\n\ |
2244 | " , |
2245 | ); |
2246 | |
2247 | // transfer-encoding: chunked |
2248 | assert_eq!( |
2249 | parse( |
2250 | "\ |
2251 | HTTP/1.1 200 OK \r\n\ |
2252 | transfer-encoding: chunked \r\n\ |
2253 | \r\n\ |
2254 | " |
2255 | ) |
2256 | .decode, |
2257 | DecodedLength::CHUNKED |
2258 | ); |
2259 | |
2260 | // transfer-encoding not-chunked is close-delimited |
2261 | assert_eq!( |
2262 | parse( |
2263 | "\ |
2264 | HTTP/1.1 200 OK \r\n\ |
2265 | transfer-encoding: yolo \r\n\ |
2266 | \r\n\ |
2267 | " |
2268 | ) |
2269 | .decode, |
2270 | DecodedLength::CLOSE_DELIMITED |
2271 | ); |
2272 | |
2273 | // transfer-encoding and content-length = chunked |
2274 | assert_eq!( |
2275 | parse( |
2276 | "\ |
2277 | HTTP/1.1 200 OK \r\n\ |
2278 | content-length: 10 \r\n\ |
2279 | transfer-encoding: chunked \r\n\ |
2280 | \r\n\ |
2281 | " |
2282 | ) |
2283 | .decode, |
2284 | DecodedLength::CHUNKED |
2285 | ); |
2286 | |
2287 | // HEAD can have content-length, but not body |
2288 | assert_eq!( |
2289 | parse_with_method( |
2290 | "\ |
2291 | HTTP/1.1 200 OK \r\n\ |
2292 | content-length: 8 \r\n\ |
2293 | \r\n\ |
2294 | " , |
2295 | Method::HEAD |
2296 | ) |
2297 | .decode, |
2298 | DecodedLength::ZERO |
2299 | ); |
2300 | |
2301 | // CONNECT with 200 never has body |
2302 | { |
2303 | let msg = parse_with_method( |
2304 | "\ |
2305 | HTTP/1.1 200 OK \r\n\ |
2306 | \r\n\ |
2307 | " , |
2308 | Method::CONNECT, |
2309 | ); |
2310 | assert_eq!(msg.decode, DecodedLength::ZERO); |
2311 | assert!(!msg.keep_alive, "should be upgrade" ); |
2312 | assert!(msg.wants_upgrade, "should be upgrade" ); |
2313 | } |
2314 | |
2315 | // CONNECT receiving non 200 can have a body |
2316 | assert_eq!( |
2317 | parse_with_method( |
2318 | "\ |
2319 | HTTP/1.1 400 Bad Request \r\n\ |
2320 | \r\n\ |
2321 | " , |
2322 | Method::CONNECT |
2323 | ) |
2324 | .decode, |
2325 | DecodedLength::CLOSE_DELIMITED |
2326 | ); |
2327 | |
2328 | // 1xx status codes |
2329 | parse_ignores( |
2330 | "\ |
2331 | HTTP/1.1 100 Continue \r\n\ |
2332 | \r\n\ |
2333 | " , |
2334 | ); |
2335 | |
2336 | parse_ignores( |
2337 | "\ |
2338 | HTTP/1.1 103 Early Hints \r\n\ |
2339 | \r\n\ |
2340 | " , |
2341 | ); |
2342 | |
2343 | // 101 upgrade not supported yet |
2344 | { |
2345 | let msg = parse( |
2346 | "\ |
2347 | HTTP/1.1 101 Switching Protocols \r\n\ |
2348 | \r\n\ |
2349 | " , |
2350 | ); |
2351 | assert_eq!(msg.decode, DecodedLength::ZERO); |
2352 | assert!(!msg.keep_alive, "should be last" ); |
2353 | assert!(msg.wants_upgrade, "should be upgrade" ); |
2354 | } |
2355 | |
2356 | // http/1.0 |
2357 | assert_eq!( |
2358 | parse( |
2359 | "\ |
2360 | HTTP/1.0 200 OK \r\n\ |
2361 | \r\n\ |
2362 | " |
2363 | ) |
2364 | .decode, |
2365 | DecodedLength::CLOSE_DELIMITED |
2366 | ); |
2367 | |
2368 | // 1.0 doesn't understand chunked |
2369 | parse_err( |
2370 | "\ |
2371 | HTTP/1.0 200 OK \r\n\ |
2372 | transfer-encoding: chunked \r\n\ |
2373 | \r\n\ |
2374 | " , |
2375 | ); |
2376 | |
2377 | // keep-alive |
2378 | assert!( |
2379 | parse( |
2380 | "\ |
2381 | HTTP/1.1 200 OK \r\n\ |
2382 | content-length: 0 \r\n\ |
2383 | \r\n\ |
2384 | " |
2385 | ) |
2386 | .keep_alive, |
2387 | "HTTP/1.1 keep-alive is default" |
2388 | ); |
2389 | |
2390 | assert!( |
2391 | !parse( |
2392 | "\ |
2393 | HTTP/1.1 200 OK \r\n\ |
2394 | content-length: 0 \r\n\ |
2395 | connection: foo, close, bar \r\n\ |
2396 | \r\n\ |
2397 | " |
2398 | ) |
2399 | .keep_alive, |
2400 | "connection close is always close" |
2401 | ); |
2402 | |
2403 | assert!( |
2404 | !parse( |
2405 | "\ |
2406 | HTTP/1.0 200 OK \r\n\ |
2407 | content-length: 0 \r\n\ |
2408 | \r\n\ |
2409 | " |
2410 | ) |
2411 | .keep_alive, |
2412 | "HTTP/1.0 close is default" |
2413 | ); |
2414 | |
2415 | assert!( |
2416 | parse( |
2417 | "\ |
2418 | HTTP/1.0 200 OK \r\n\ |
2419 | content-length: 0 \r\n\ |
2420 | connection: foo, keep-alive, bar \r\n\ |
2421 | \r\n\ |
2422 | " |
2423 | ) |
2424 | .keep_alive, |
2425 | "connection keep-alive is always keep-alive" |
2426 | ); |
2427 | } |
2428 | |
2429 | #[cfg (feature = "client" )] |
2430 | #[test ] |
2431 | fn test_client_obs_fold_line() { |
2432 | fn unfold(src: &str) -> String { |
2433 | let mut buf = src.as_bytes().to_vec(); |
2434 | let mut idx = HeaderIndices { |
2435 | name: (0, 0), |
2436 | value: (0, buf.len()), |
2437 | }; |
2438 | Client::obs_fold_line(&mut buf, &mut idx); |
2439 | String::from_utf8(buf[idx.value.0..idx.value.1].to_vec()).unwrap() |
2440 | } |
2441 | |
2442 | assert_eq!(unfold("a normal line" ), "a normal line" ,); |
2443 | |
2444 | assert_eq!(unfold("obs \r\n fold \r\n\t line" ), "obs fold line" ,); |
2445 | } |
2446 | |
2447 | #[test ] |
2448 | fn test_client_request_encode_title_case() { |
2449 | use crate::proto::BodyLength; |
2450 | use http::header::HeaderValue; |
2451 | |
2452 | let mut head = MessageHead::default(); |
2453 | head.headers |
2454 | .insert("content-length" , HeaderValue::from_static("10" )); |
2455 | head.headers |
2456 | .insert("content-type" , HeaderValue::from_static("application/json" )); |
2457 | head.headers.insert("*-*" , HeaderValue::from_static("o_o" )); |
2458 | |
2459 | let mut vec = Vec::new(); |
2460 | Client::encode( |
2461 | Encode { |
2462 | head: &mut head, |
2463 | body: Some(BodyLength::Known(10)), |
2464 | #[cfg (feature = "server" )] |
2465 | keep_alive: true, |
2466 | req_method: &mut None, |
2467 | title_case_headers: true, |
2468 | #[cfg (feature = "server" )] |
2469 | date_header: true, |
2470 | }, |
2471 | &mut vec, |
2472 | ) |
2473 | .unwrap(); |
2474 | |
2475 | assert_eq!(vec, b"GET / HTTP/1.1 \r\nContent-Length: 10 \r\nContent-Type: application/json \r\n*-*: o_o \r\n\r\n" .to_vec()); |
2476 | } |
2477 | |
2478 | #[test ] |
2479 | fn test_client_request_encode_orig_case() { |
2480 | use crate::proto::BodyLength; |
2481 | use http::header::{HeaderValue, CONTENT_LENGTH}; |
2482 | |
2483 | let mut head = MessageHead::default(); |
2484 | head.headers |
2485 | .insert("content-length" , HeaderValue::from_static("10" )); |
2486 | head.headers |
2487 | .insert("content-type" , HeaderValue::from_static("application/json" )); |
2488 | |
2489 | let mut orig_headers = HeaderCaseMap::default(); |
2490 | orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH" .into()); |
2491 | head.extensions.insert(orig_headers); |
2492 | |
2493 | let mut vec = Vec::new(); |
2494 | Client::encode( |
2495 | Encode { |
2496 | head: &mut head, |
2497 | body: Some(BodyLength::Known(10)), |
2498 | #[cfg (feature = "server" )] |
2499 | keep_alive: true, |
2500 | req_method: &mut None, |
2501 | title_case_headers: false, |
2502 | #[cfg (feature = "server" )] |
2503 | date_header: true, |
2504 | }, |
2505 | &mut vec, |
2506 | ) |
2507 | .unwrap(); |
2508 | |
2509 | assert_eq!( |
2510 | &*vec, |
2511 | b"GET / HTTP/1.1 \r\nCONTENT-LENGTH: 10 \r\ncontent-type: application/json \r\n\r\n" |
2512 | .as_ref(), |
2513 | ); |
2514 | } |
2515 | #[test ] |
2516 | fn test_client_request_encode_orig_and_title_case() { |
2517 | use crate::proto::BodyLength; |
2518 | use http::header::{HeaderValue, CONTENT_LENGTH}; |
2519 | |
2520 | let mut head = MessageHead::default(); |
2521 | head.headers |
2522 | .insert("content-length" , HeaderValue::from_static("10" )); |
2523 | head.headers |
2524 | .insert("content-type" , HeaderValue::from_static("application/json" )); |
2525 | |
2526 | let mut orig_headers = HeaderCaseMap::default(); |
2527 | orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH" .into()); |
2528 | head.extensions.insert(orig_headers); |
2529 | |
2530 | let mut vec = Vec::new(); |
2531 | Client::encode( |
2532 | Encode { |
2533 | head: &mut head, |
2534 | body: Some(BodyLength::Known(10)), |
2535 | #[cfg (feature = "server" )] |
2536 | keep_alive: true, |
2537 | req_method: &mut None, |
2538 | title_case_headers: true, |
2539 | #[cfg (feature = "server" )] |
2540 | date_header: true, |
2541 | }, |
2542 | &mut vec, |
2543 | ) |
2544 | .unwrap(); |
2545 | |
2546 | assert_eq!( |
2547 | &*vec, |
2548 | b"GET / HTTP/1.1 \r\nCONTENT-LENGTH: 10 \r\nContent-Type: application/json \r\n\r\n" |
2549 | .as_ref(), |
2550 | ); |
2551 | } |
2552 | |
2553 | #[cfg (feature = "server" )] |
2554 | #[test ] |
2555 | fn test_server_encode_connect_method() { |
2556 | let mut head = MessageHead::default(); |
2557 | |
2558 | let mut vec = Vec::new(); |
2559 | let encoder = Server::encode( |
2560 | Encode { |
2561 | head: &mut head, |
2562 | body: None, |
2563 | keep_alive: true, |
2564 | req_method: &mut Some(Method::CONNECT), |
2565 | title_case_headers: false, |
2566 | date_header: true, |
2567 | }, |
2568 | &mut vec, |
2569 | ) |
2570 | .unwrap(); |
2571 | |
2572 | assert!(encoder.is_last()); |
2573 | } |
2574 | |
2575 | #[cfg (feature = "server" )] |
2576 | #[test ] |
2577 | fn test_server_response_encode_title_case() { |
2578 | use crate::proto::BodyLength; |
2579 | use http::header::HeaderValue; |
2580 | |
2581 | let mut head = MessageHead::default(); |
2582 | head.headers |
2583 | .insert("content-length" , HeaderValue::from_static("10" )); |
2584 | head.headers |
2585 | .insert("content-type" , HeaderValue::from_static("application/json" )); |
2586 | head.headers |
2587 | .insert("weird--header" , HeaderValue::from_static("" )); |
2588 | |
2589 | let mut vec = Vec::new(); |
2590 | Server::encode( |
2591 | Encode { |
2592 | head: &mut head, |
2593 | body: Some(BodyLength::Known(10)), |
2594 | keep_alive: true, |
2595 | req_method: &mut None, |
2596 | title_case_headers: true, |
2597 | date_header: true, |
2598 | }, |
2599 | &mut vec, |
2600 | ) |
2601 | .unwrap(); |
2602 | |
2603 | let expected_response = |
2604 | b"HTTP/1.1 200 OK \r\nContent-Length: 10 \r\nContent-Type: application/json \r\nWeird--Header: \r\n" ; |
2605 | |
2606 | assert_eq!(&vec[..expected_response.len()], &expected_response[..]); |
2607 | } |
2608 | |
2609 | #[cfg (feature = "server" )] |
2610 | #[test ] |
2611 | fn test_server_response_encode_orig_case() { |
2612 | use crate::proto::BodyLength; |
2613 | use http::header::{HeaderValue, CONTENT_LENGTH}; |
2614 | |
2615 | let mut head = MessageHead::default(); |
2616 | head.headers |
2617 | .insert("content-length" , HeaderValue::from_static("10" )); |
2618 | head.headers |
2619 | .insert("content-type" , HeaderValue::from_static("application/json" )); |
2620 | |
2621 | let mut orig_headers = HeaderCaseMap::default(); |
2622 | orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH" .into()); |
2623 | head.extensions.insert(orig_headers); |
2624 | |
2625 | let mut vec = Vec::new(); |
2626 | Server::encode( |
2627 | Encode { |
2628 | head: &mut head, |
2629 | body: Some(BodyLength::Known(10)), |
2630 | keep_alive: true, |
2631 | req_method: &mut None, |
2632 | title_case_headers: false, |
2633 | date_header: true, |
2634 | }, |
2635 | &mut vec, |
2636 | ) |
2637 | .unwrap(); |
2638 | |
2639 | let expected_response = |
2640 | b"HTTP/1.1 200 OK \r\nCONTENT-LENGTH: 10 \r\ncontent-type: application/json \r\ndate: " ; |
2641 | |
2642 | assert_eq!(&vec[..expected_response.len()], &expected_response[..]); |
2643 | } |
2644 | |
2645 | #[cfg (feature = "server" )] |
2646 | #[test ] |
2647 | fn test_server_response_encode_orig_and_title_case() { |
2648 | use crate::proto::BodyLength; |
2649 | use http::header::{HeaderValue, CONTENT_LENGTH}; |
2650 | |
2651 | let mut head = MessageHead::default(); |
2652 | head.headers |
2653 | .insert("content-length" , HeaderValue::from_static("10" )); |
2654 | head.headers |
2655 | .insert("content-type" , HeaderValue::from_static("application/json" )); |
2656 | |
2657 | let mut orig_headers = HeaderCaseMap::default(); |
2658 | orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH" .into()); |
2659 | head.extensions.insert(orig_headers); |
2660 | |
2661 | let mut vec = Vec::new(); |
2662 | Server::encode( |
2663 | Encode { |
2664 | head: &mut head, |
2665 | body: Some(BodyLength::Known(10)), |
2666 | keep_alive: true, |
2667 | req_method: &mut None, |
2668 | title_case_headers: true, |
2669 | date_header: true, |
2670 | }, |
2671 | &mut vec, |
2672 | ) |
2673 | .unwrap(); |
2674 | |
2675 | // this will also test that the date does exist |
2676 | let expected_response = |
2677 | b"HTTP/1.1 200 OK \r\nCONTENT-LENGTH: 10 \r\nContent-Type: application/json \r\nDate: " ; |
2678 | |
2679 | assert_eq!(&vec[..expected_response.len()], &expected_response[..]); |
2680 | } |
2681 | |
2682 | #[cfg (feature = "server" )] |
2683 | #[test ] |
2684 | fn test_disabled_date_header() { |
2685 | use crate::proto::BodyLength; |
2686 | use http::header::{HeaderValue, CONTENT_LENGTH}; |
2687 | |
2688 | let mut head = MessageHead::default(); |
2689 | head.headers |
2690 | .insert("content-length" , HeaderValue::from_static("10" )); |
2691 | head.headers |
2692 | .insert("content-type" , HeaderValue::from_static("application/json" )); |
2693 | |
2694 | let mut orig_headers = HeaderCaseMap::default(); |
2695 | orig_headers.insert(CONTENT_LENGTH, "CONTENT-LENGTH" .into()); |
2696 | head.extensions.insert(orig_headers); |
2697 | |
2698 | let mut vec = Vec::new(); |
2699 | Server::encode( |
2700 | Encode { |
2701 | head: &mut head, |
2702 | body: Some(BodyLength::Known(10)), |
2703 | keep_alive: true, |
2704 | req_method: &mut None, |
2705 | title_case_headers: true, |
2706 | date_header: false, |
2707 | }, |
2708 | &mut vec, |
2709 | ) |
2710 | .unwrap(); |
2711 | |
2712 | let expected_response = |
2713 | b"HTTP/1.1 200 OK \r\nCONTENT-LENGTH: 10 \r\nContent-Type: application/json \r\n\r\n" ; |
2714 | |
2715 | assert_eq!(&vec, &expected_response); |
2716 | } |
2717 | |
2718 | #[test ] |
2719 | fn parse_header_htabs() { |
2720 | let mut bytes = BytesMut::from("HTTP/1.1 200 OK \r\nserver: hello \tworld \r\n\r\n" ); |
2721 | let parsed = Client::parse( |
2722 | &mut bytes, |
2723 | ParseContext { |
2724 | cached_headers: &mut None, |
2725 | req_method: &mut Some(Method::GET), |
2726 | h1_parser_config: Default::default(), |
2727 | h1_max_headers: None, |
2728 | preserve_header_case: false, |
2729 | #[cfg (feature = "ffi" )] |
2730 | preserve_header_order: false, |
2731 | h09_responses: false, |
2732 | #[cfg (feature = "client" )] |
2733 | on_informational: &mut None, |
2734 | }, |
2735 | ) |
2736 | .expect("parse ok" ) |
2737 | .expect("parse complete" ); |
2738 | |
2739 | assert_eq!(parsed.head.headers["server" ], "hello \tworld" ); |
2740 | } |
2741 | |
2742 | #[cfg (feature = "server" )] |
2743 | #[test ] |
2744 | fn parse_too_large_headers() { |
2745 | fn gen_req_with_headers(num: usize) -> String { |
2746 | let mut req = String::from("GET / HTTP/1.1 \r\n" ); |
2747 | for i in 0..num { |
2748 | req.push_str(&format!("key{i}: val{i} \r\n" )); |
2749 | } |
2750 | req.push_str(" \r\n" ); |
2751 | req |
2752 | } |
2753 | fn gen_resp_with_headers(num: usize) -> String { |
2754 | let mut req = String::from("HTTP/1.1 200 OK \r\n" ); |
2755 | for i in 0..num { |
2756 | req.push_str(&format!("key{i}: val{i} \r\n" )); |
2757 | } |
2758 | req.push_str(" \r\n" ); |
2759 | req |
2760 | } |
2761 | fn parse(max_headers: Option<usize>, gen_size: usize, should_success: bool) { |
2762 | { |
2763 | // server side |
2764 | let mut bytes = BytesMut::from(gen_req_with_headers(gen_size).as_str()); |
2765 | let result = Server::parse( |
2766 | &mut bytes, |
2767 | ParseContext { |
2768 | cached_headers: &mut None, |
2769 | req_method: &mut None, |
2770 | h1_parser_config: Default::default(), |
2771 | h1_max_headers: max_headers, |
2772 | preserve_header_case: false, |
2773 | #[cfg (feature = "ffi" )] |
2774 | preserve_header_order: false, |
2775 | h09_responses: false, |
2776 | #[cfg (feature = "client" )] |
2777 | on_informational: &mut None, |
2778 | }, |
2779 | ); |
2780 | if should_success { |
2781 | result.expect("parse ok" ).expect("parse complete" ); |
2782 | } else { |
2783 | result.expect_err("parse should err" ); |
2784 | } |
2785 | } |
2786 | { |
2787 | // client side |
2788 | let mut bytes = BytesMut::from(gen_resp_with_headers(gen_size).as_str()); |
2789 | let result = Client::parse( |
2790 | &mut bytes, |
2791 | ParseContext { |
2792 | cached_headers: &mut None, |
2793 | req_method: &mut None, |
2794 | h1_parser_config: Default::default(), |
2795 | h1_max_headers: max_headers, |
2796 | preserve_header_case: false, |
2797 | #[cfg (feature = "ffi" )] |
2798 | preserve_header_order: false, |
2799 | h09_responses: false, |
2800 | #[cfg (feature = "client" )] |
2801 | on_informational: &mut None, |
2802 | }, |
2803 | ); |
2804 | if should_success { |
2805 | result.expect("parse ok" ).expect("parse complete" ); |
2806 | } else { |
2807 | result.expect_err("parse should err" ); |
2808 | } |
2809 | } |
2810 | } |
2811 | |
2812 | // check generator |
2813 | assert_eq!( |
2814 | gen_req_with_headers(0), |
2815 | String::from("GET / HTTP/1.1 \r\n\r\n" ) |
2816 | ); |
2817 | assert_eq!( |
2818 | gen_req_with_headers(1), |
2819 | String::from("GET / HTTP/1.1 \r\nkey0: val0 \r\n\r\n" ) |
2820 | ); |
2821 | assert_eq!( |
2822 | gen_req_with_headers(2), |
2823 | String::from("GET / HTTP/1.1 \r\nkey0: val0 \r\nkey1: val1 \r\n\r\n" ) |
2824 | ); |
2825 | assert_eq!( |
2826 | gen_req_with_headers(3), |
2827 | String::from("GET / HTTP/1.1 \r\nkey0: val0 \r\nkey1: val1 \r\nkey2: val2 \r\n\r\n" ) |
2828 | ); |
2829 | |
2830 | // default max_headers is 100, so |
2831 | // |
2832 | // - less than or equal to 100, accepted |
2833 | // |
2834 | parse(None, 0, true); |
2835 | parse(None, 1, true); |
2836 | parse(None, 50, true); |
2837 | parse(None, 99, true); |
2838 | parse(None, 100, true); |
2839 | // |
2840 | // - more than 100, rejected |
2841 | // |
2842 | parse(None, 101, false); |
2843 | parse(None, 102, false); |
2844 | parse(None, 200, false); |
2845 | |
2846 | // max_headers is 0, parser will reject any headers |
2847 | // |
2848 | // - without header, accepted |
2849 | // |
2850 | parse(Some(0), 0, true); |
2851 | // |
2852 | // - with header(s), rejected |
2853 | // |
2854 | parse(Some(0), 1, false); |
2855 | parse(Some(0), 100, false); |
2856 | |
2857 | // max_headers is 200 |
2858 | // |
2859 | // - less than or equal to 200, accepted |
2860 | // |
2861 | parse(Some(200), 0, true); |
2862 | parse(Some(200), 1, true); |
2863 | parse(Some(200), 100, true); |
2864 | parse(Some(200), 200, true); |
2865 | // |
2866 | // - more than 200, rejected |
2867 | // |
2868 | parse(Some(200), 201, false); |
2869 | parse(Some(200), 210, false); |
2870 | } |
2871 | |
2872 | #[test ] |
2873 | fn test_is_complete_fast() { |
2874 | let s = b"GET / HTTP/1.1 \r\na: b \r\n\r\n" ; |
2875 | for n in 0..s.len() { |
2876 | assert!(is_complete_fast(s, n), "{:?}; {}" , s, n); |
2877 | } |
2878 | let s = b"GET / HTTP/1.1 \na: b \n\n" ; |
2879 | for n in 0..s.len() { |
2880 | assert!(is_complete_fast(s, n)); |
2881 | } |
2882 | |
2883 | // Not |
2884 | let s = b"GET / HTTP/1.1 \r\na: b \r\n\r" ; |
2885 | for n in 0..s.len() { |
2886 | assert!(!is_complete_fast(s, n)); |
2887 | } |
2888 | let s = b"GET / HTTP/1.1 \na: b \n" ; |
2889 | for n in 0..s.len() { |
2890 | assert!(!is_complete_fast(s, n)); |
2891 | } |
2892 | } |
2893 | |
2894 | #[test ] |
2895 | fn test_write_headers_orig_case_empty_value() { |
2896 | let mut headers = HeaderMap::new(); |
2897 | let name = http::header::HeaderName::from_static("x-empty" ); |
2898 | headers.insert(&name, "" .parse().expect("parse empty" )); |
2899 | let mut orig_cases = HeaderCaseMap::default(); |
2900 | orig_cases.insert(name, Bytes::from_static(b"X-EmptY" )); |
2901 | |
2902 | let mut dst = Vec::new(); |
2903 | super::write_headers_original_case(&headers, &orig_cases, &mut dst, false); |
2904 | |
2905 | assert_eq!( |
2906 | dst, b"X-EmptY: \r\n" , |
2907 | "there should be no space between the colon and CRLF" |
2908 | ); |
2909 | } |
2910 | |
2911 | #[test ] |
2912 | fn test_write_headers_orig_case_multiple_entries() { |
2913 | let mut headers = HeaderMap::new(); |
2914 | let name = http::header::HeaderName::from_static("x-empty" ); |
2915 | headers.insert(&name, "a" .parse().unwrap()); |
2916 | headers.append(&name, "b" .parse().unwrap()); |
2917 | |
2918 | let mut orig_cases = HeaderCaseMap::default(); |
2919 | orig_cases.insert(name.clone(), Bytes::from_static(b"X-Empty" )); |
2920 | orig_cases.append(name, Bytes::from_static(b"X-EMPTY" )); |
2921 | |
2922 | let mut dst = Vec::new(); |
2923 | super::write_headers_original_case(&headers, &orig_cases, &mut dst, false); |
2924 | |
2925 | assert_eq!(dst, b"X-Empty: a \r\nX-EMPTY: b \r\n" ); |
2926 | } |
2927 | |
2928 | #[cfg (feature = "nightly" )] |
2929 | use test::Bencher; |
2930 | |
2931 | #[cfg (feature = "nightly" )] |
2932 | #[bench ] |
2933 | fn bench_parse_incoming(b: &mut Bencher) { |
2934 | let mut raw = BytesMut::from( |
2935 | &b"GET /super_long_uri/and_whatever?what_should_we_talk_about/\ |
2936 | I_wonder/Hard_to_write_in_an_uri_after_all/you_have_to_make\ |
2937 | _up_the_punctuation_yourself/how_fun_is_that?test=foo&test1=\ |
2938 | foo1&test2=foo2&test3=foo3&test4=foo4 HTTP/1.1 \r\nHost: \ |
2939 | hyper.rs \r\nAccept: a lot of things \r\nAccept-Charset: \ |
2940 | utf8 \r\nAccept-Encoding: * \r\nAccess-Control-Allow-\ |
2941 | Credentials: None \r\nAccess-Control-Allow-Origin: None \r\n\ |
2942 | Access-Control-Allow-Methods: None \r\nAccess-Control-Allow-\ |
2943 | Headers: None \r\nContent-Encoding: utf8 \r\nContent-Security-\ |
2944 | Policy: None \r\nContent-Type: text/html \r\nOrigin: hyper\ |
2945 | \r\nSec-Websocket-Extensions: It looks super important! \r\n\ |
2946 | Sec-Websocket-Origin: hyper \r\nSec-Websocket-Version: 4.3 \r\ |
2947 | \nStrict-Transport-Security: None \r\nUser-Agent: hyper \r\n\ |
2948 | X-Content-Duration: None \r\nX-Content-Security-Policy: None\ |
2949 | \r\nX-DNSPrefetch-Control: None \r\nX-Frame-Options: \ |
2950 | Something important obviously \r\nX-Requested-With: Nothing\ |
2951 | \r\n\r\n" [..], |
2952 | ); |
2953 | let len = raw.len(); |
2954 | let mut headers = Some(HeaderMap::new()); |
2955 | |
2956 | b.bytes = len as u64; |
2957 | b.iter(|| { |
2958 | let mut msg = Server::parse( |
2959 | &mut raw, |
2960 | ParseContext { |
2961 | cached_headers: &mut headers, |
2962 | req_method: &mut None, |
2963 | h1_parser_config: Default::default(), |
2964 | h1_max_headers: None, |
2965 | preserve_header_case: false, |
2966 | #[cfg (feature = "ffi" )] |
2967 | preserve_header_order: false, |
2968 | h09_responses: false, |
2969 | #[cfg (feature = "client" )] |
2970 | on_informational: &mut None, |
2971 | }, |
2972 | ) |
2973 | .unwrap() |
2974 | .unwrap(); |
2975 | ::test::black_box(&msg); |
2976 | |
2977 | // Remove all references pointing into BytesMut. |
2978 | msg.head.headers.clear(); |
2979 | headers = Some(msg.head.headers); |
2980 | std::mem::take(&mut msg.head.subject); |
2981 | |
2982 | restart(&mut raw, len); |
2983 | }); |
2984 | |
2985 | fn restart(b: &mut BytesMut, len: usize) { |
2986 | b.reserve(1); |
2987 | unsafe { |
2988 | b.set_len(len); |
2989 | } |
2990 | } |
2991 | } |
2992 | |
2993 | #[cfg (feature = "nightly" )] |
2994 | #[bench ] |
2995 | fn bench_parse_short(b: &mut Bencher) { |
2996 | let s = &b"GET / HTTP/1.1 \r\nHost: localhost:8080 \r\n\r\n" [..]; |
2997 | let mut raw = BytesMut::from(s); |
2998 | let len = raw.len(); |
2999 | let mut headers = Some(HeaderMap::new()); |
3000 | |
3001 | b.bytes = len as u64; |
3002 | b.iter(|| { |
3003 | let mut msg = Server::parse( |
3004 | &mut raw, |
3005 | ParseContext { |
3006 | cached_headers: &mut headers, |
3007 | req_method: &mut None, |
3008 | h1_parser_config: Default::default(), |
3009 | h1_max_headers: None, |
3010 | preserve_header_case: false, |
3011 | #[cfg (feature = "ffi" )] |
3012 | preserve_header_order: false, |
3013 | h09_responses: false, |
3014 | #[cfg (feature = "client" )] |
3015 | on_informational: &mut None, |
3016 | }, |
3017 | ) |
3018 | .unwrap() |
3019 | .unwrap(); |
3020 | ::test::black_box(&msg); |
3021 | msg.head.headers.clear(); |
3022 | headers = Some(msg.head.headers); |
3023 | restart(&mut raw, len); |
3024 | }); |
3025 | |
3026 | fn restart(b: &mut BytesMut, len: usize) { |
3027 | b.reserve(1); |
3028 | unsafe { |
3029 | b.set_len(len); |
3030 | } |
3031 | } |
3032 | } |
3033 | |
3034 | #[cfg (feature = "nightly" )] |
3035 | #[bench ] |
3036 | fn bench_server_encode_headers_preset(b: &mut Bencher) { |
3037 | use crate::proto::BodyLength; |
3038 | use http::header::HeaderValue; |
3039 | |
3040 | let len = 108; |
3041 | b.bytes = len as u64; |
3042 | |
3043 | let mut head = MessageHead::default(); |
3044 | let mut headers = HeaderMap::new(); |
3045 | headers.insert("content-length" , HeaderValue::from_static("10" )); |
3046 | headers.insert("content-type" , HeaderValue::from_static("application/json" )); |
3047 | |
3048 | b.iter(|| { |
3049 | let mut vec = Vec::new(); |
3050 | head.headers = headers.clone(); |
3051 | Server::encode( |
3052 | Encode { |
3053 | head: &mut head, |
3054 | body: Some(BodyLength::Known(10)), |
3055 | keep_alive: true, |
3056 | req_method: &mut Some(Method::GET), |
3057 | title_case_headers: false, |
3058 | date_header: true, |
3059 | }, |
3060 | &mut vec, |
3061 | ) |
3062 | .unwrap(); |
3063 | assert_eq!(vec.len(), len); |
3064 | ::test::black_box(vec); |
3065 | }) |
3066 | } |
3067 | |
3068 | #[cfg (feature = "nightly" )] |
3069 | #[bench ] |
3070 | fn bench_server_encode_no_headers(b: &mut Bencher) { |
3071 | use crate::proto::BodyLength; |
3072 | |
3073 | let len = 76; |
3074 | b.bytes = len as u64; |
3075 | |
3076 | let mut head = MessageHead::default(); |
3077 | let mut vec = Vec::with_capacity(128); |
3078 | |
3079 | b.iter(|| { |
3080 | Server::encode( |
3081 | Encode { |
3082 | head: &mut head, |
3083 | body: Some(BodyLength::Known(10)), |
3084 | keep_alive: true, |
3085 | req_method: &mut Some(Method::GET), |
3086 | title_case_headers: false, |
3087 | date_header: true, |
3088 | }, |
3089 | &mut vec, |
3090 | ) |
3091 | .unwrap(); |
3092 | assert_eq!(vec.len(), len); |
3093 | ::test::black_box(&vec); |
3094 | |
3095 | vec.clear(); |
3096 | }) |
3097 | } |
3098 | } |
3099 | |