From 2c029e65c54753c0b31a7ab3e3fd4eb18df02b69 Mon Sep 17 00:00:00 2001 From: Yury Yarashevich Date: Tue, 23 Apr 2024 18:01:28 +0200 Subject: [PATCH] Fix request pseudo-header construction for CONNECT & OPTION methods CONNECT & OPTIONS request has special requirements regarding :path & :scheme pseudo-header which were not met. Construction of pseudo-header was fixed to not include :path & :scheme fields for CONNECT method. Empty :path field for OPTIONS requests now translates to '*' value sent in :path field. CONNECT request changes were tested against server based on hyper 1.2. --- src/frame/headers.rs | 44 ++++++++++++++++++++++++++++---------------- 1 file changed, 28 insertions(+), 16 deletions(-) diff --git a/src/frame/headers.rs b/src/frame/headers.rs index e9b163e5..a45bb04b 100644 --- a/src/frame/headers.rs +++ b/src/frame/headers.rs @@ -554,32 +554,44 @@ impl Pseudo { pub fn request(method: Method, uri: Uri, protocol: Option) -> Self { let parts = uri::Parts::from(uri); - let mut path = parts - .path_and_query - .map(|v| BytesStr::from(v.as_str())) - .unwrap_or(BytesStr::from_static("")); - - match method { - Method::OPTIONS | Method::CONNECT => {} - _ if path.is_empty() => { - path = BytesStr::from_static("/"); - } - _ => {} - } + let (scheme, path) = if method == Method::CONNECT { + // CONNECT requests MUST NOT include :scheme & :path pseudo-header fields + // See: https://datatracker.ietf.org/doc/html/rfc9113#section-8.5 + (None, None) + } else { + let path = parts + .path_and_query + .map(|v| BytesStr::from(v.as_str())) + .unwrap_or(BytesStr::from_static("")); + + let path = if !path.is_empty() { + path + } else { + if method == Method::OPTIONS { + // An OPTIONS request for an "http" or "https" URI that does not include a path component; + // these MUST include a ":path" pseudo-header field with a value of '*' (see Section 7.1 of [HTTP]). + // See: https://datatracker.ietf.org/doc/html/rfc9113#section-8.3.1 + // TODO: Validate URI is "http" or "https". + BytesStr::from_static("*") + } else { + BytesStr::from_static("/") + } + }; + + (parts.scheme, Some(path).filter(|p| !p.is_empty())) + }; let mut pseudo = Pseudo { method: Some(method), scheme: None, authority: None, - path: Some(path).filter(|p| !p.is_empty()), + path, protocol, status: None, }; // If the URI includes a scheme component, add it to the pseudo headers - // - // TODO: Scheme must be set... - if let Some(scheme) = parts.scheme { + if let Some(scheme) = scheme { pseudo.set_scheme(scheme); }