|
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