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