|
1 | 1 | use std::{collections::HashMap, future::Future, io::IsTerminal, net::SocketAddr, sync::Arc}; |
2 | 2 |
|
3 | | -use anyhow::Context; |
4 | | -use http::{uri::Scheme, Request, Response, StatusCode, Uri}; |
| 3 | +use anyhow::{bail, Context}; |
| 4 | +use http::{ |
| 5 | + uri::{Authority, Scheme}, |
| 6 | + Request, Response, StatusCode, Uri, |
| 7 | +}; |
5 | 8 | use http_body_util::BodyExt; |
6 | 9 | use hyper::{ |
7 | 10 | body::{Bytes, Incoming}, |
@@ -359,29 +362,39 @@ impl HttpServer { |
359 | 362 | /// The incoming request's scheme and authority |
360 | 363 | /// |
361 | 364 | /// The incoming request's URI is relative to the server, so we need to set the scheme and authority. |
362 | | -/// The `Host` header is used to set the authority. This function will error if no `Host` header is |
363 | | -/// present or if it is not parsable as an `Authority`. |
| 365 | +/// Either the `Host` header or the request's URI's authority is used as the source of truth for the authority. |
| 366 | +/// This function will error if the authority cannot be unambiguously determined. |
364 | 367 | fn set_req_uri(req: &mut Request<Body>, scheme: Scheme) -> anyhow::Result<()> { |
365 | 368 | let uri = req.uri().clone(); |
366 | 369 | let mut parts = uri.into_parts(); |
367 | 370 | let headers = req.headers(); |
368 | | - let host_header = headers |
| 371 | + let header_authority = headers |
369 | 372 | .get(http::header::HOST) |
370 | | - .context("missing 'Host' header")? |
371 | | - .to_str() |
372 | | - .context("'Host' header is not valid UTF-8")?; |
373 | | - let authority = host_header |
374 | | - .parse() |
375 | | - .context("'Host' header contains an invalid authority")?; |
376 | | - // Ensure that if `req.authority` is set, it matches what was in the `Host` header |
377 | | - // https://github.com/hyperium/hyper/issues/1612 |
378 | | - if let Some(a) = parts.authority.as_ref() { |
379 | | - if a != &authority { |
380 | | - return Err(anyhow::anyhow!( |
381 | | - "authority in 'Host' header does not match authority in URI" |
382 | | - )); |
| 373 | + .map(|h| -> anyhow::Result<Authority> { |
| 374 | + let host_header = h.to_str().context("'Host' header is not valid UTF-8")?; |
| 375 | + host_header |
| 376 | + .parse() |
| 377 | + .context("'Host' header contains an invalid authority") |
| 378 | + }) |
| 379 | + .transpose()?; |
| 380 | + let uri_authority = parts.authority; |
| 381 | + |
| 382 | + // Get authority either from request URI or from 'Host' header |
| 383 | + let authority = match (header_authority, uri_authority) { |
| 384 | + (None, None) => bail!("no 'Host' header present in request"), |
| 385 | + (None, Some(a)) => a, |
| 386 | + (Some(a), None) => a, |
| 387 | + (Some(a1), Some(a2)) => { |
| 388 | + // Ensure that if `req.authority` is set, it matches what was in the `Host` header |
| 389 | + // https://github.com/hyperium/hyper/issues/1612 |
| 390 | + if a1 != a2 { |
| 391 | + return Err(anyhow::anyhow!( |
| 392 | + "authority in 'Host' header does not match authority in URI" |
| 393 | + )); |
| 394 | + } |
| 395 | + a1 |
383 | 396 | } |
384 | | - } |
| 397 | + }; |
385 | 398 | parts.scheme = Some(scheme); |
386 | 399 | parts.authority = Some(authority); |
387 | 400 | *req.uri_mut() = Uri::from_parts(parts).unwrap(); |
|
0 commit comments