| 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 | |