|
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}, |
@@ -392,29 +395,39 @@ impl HttpServer { |
392 | 395 | /// The incoming request's scheme and authority |
393 | 396 | /// |
394 | 397 | /// The incoming request's URI is relative to the server, so we need to set the scheme and authority. |
395 | | -/// The `Host` header is used to set the authority. This function will error if no `Host` header is |
396 | | -/// present or if it is not parsable as an `Authority`. |
| 398 | +/// Either the `Host` header or the request's URI's authority is used as the source of truth for the authority. |
| 399 | +/// This function will error if the authority cannot be unambiguously determined. |
397 | 400 | fn set_req_uri(req: &mut Request<Body>, scheme: Scheme) -> anyhow::Result<()> { |
398 | 401 | let uri = req.uri().clone(); |
399 | 402 | let mut parts = uri.into_parts(); |
400 | 403 | let headers = req.headers(); |
401 | | - let host_header = headers |
| 404 | + let header_authority = headers |
402 | 405 | .get(http::header::HOST) |
403 | | - .context("missing 'Host' header")? |
404 | | - .to_str() |
405 | | - .context("'Host' header is not valid UTF-8")?; |
406 | | - let authority = host_header |
407 | | - .parse() |
408 | | - .context("'Host' header contains an invalid authority")?; |
409 | | - // Ensure that if `req.authority` is set, it matches what was in the `Host` header |
410 | | - // https://github.com/hyperium/hyper/issues/1612 |
411 | | - if let Some(a) = parts.authority.as_ref() { |
412 | | - if a != &authority { |
413 | | - return Err(anyhow::anyhow!( |
414 | | - "authority in 'Host' header does not match authority in URI" |
415 | | - )); |
| 406 | + .map(|h| -> anyhow::Result<Authority> { |
| 407 | + let host_header = h.to_str().context("'Host' header is not valid UTF-8")?; |
| 408 | + host_header |
| 409 | + .parse() |
| 410 | + .context("'Host' header contains an invalid authority") |
| 411 | + }) |
| 412 | + .transpose()?; |
| 413 | + let uri_authority = parts.authority; |
| 414 | + |
| 415 | + // Get authority either from request URI or from 'Host' header |
| 416 | + let authority = match (header_authority, uri_authority) { |
| 417 | + (None, None) => bail!("no 'Host' header present in request"), |
| 418 | + (None, Some(a)) => a, |
| 419 | + (Some(a), None) => a, |
| 420 | + (Some(a1), Some(a2)) => { |
| 421 | + // Ensure that if `req.authority` is set, it matches what was in the `Host` header |
| 422 | + // https://github.com/hyperium/hyper/issues/1612 |
| 423 | + if a1 != a2 { |
| 424 | + return Err(anyhow::anyhow!( |
| 425 | + "authority in 'Host' header does not match authority in URI" |
| 426 | + )); |
| 427 | + } |
| 428 | + a1 |
416 | 429 | } |
417 | | - } |
| 430 | + }; |
418 | 431 | parts.scheme = Some(scheme); |
419 | 432 | parts.authority = Some(authority); |
420 | 433 | *req.uri_mut() = Uri::from_parts(parts).unwrap(); |
|
0 commit comments