Skip to content

Commit b4de77c

Browse files
committed
use the cloudfront-forwarded-proto header during redirects
All the docs.rs redirects are absolute, including the protocol and the host name. This is fine if docs.rs is exposed directly on the Internet, but the production instance sits behind CloudFront, which terminates TLS connections. This means the application thinks it's serving HTTP requests instead of HTTPS ones, and it issues wrong redirects. This fixes the issue by taking the Cloudfront-Forwarded-Proto header into account when building the redirect URL.
1 parent e608440 commit b4de77c

File tree

4 files changed

+38
-39
lines changed

4 files changed

+38
-39
lines changed

src/web/crate_details.rs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33

44
use super::pool::Pool;
5-
use super::{MetaData, duration_to_str, match_version, render_markdown, MatchVersion};
5+
use super::{MetaData, duration_to_str, match_version, render_markdown, MatchVersion, redirect_base};
66
use super::error::Nope;
77
use super::page::Page;
88
use iron::prelude::*;
@@ -240,10 +240,8 @@ pub fn crate_details_handler(req: &mut Request) -> IronResult<Response> {
240240
.to_resp("crate_details")
241241
}
242242
MatchVersion::Semver(version) => {
243-
let url = ctry!(Url::parse(&format!("{}://{}:{}/crate/{}/{}",
244-
req.url.scheme(),
245-
req.url.host(),
246-
req.url.port(),
243+
let url = ctry!(Url::parse(&format!("{}/crate/{}/{}",
244+
redirect_base(req),
247245
name,
248246
version)[..]));
249247

src/web/mod.rs

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -466,6 +466,24 @@ fn redirect(url: Url) -> Response {
466466
resp
467467
}
468468

469+
pub fn redirect_base(req: &Request) -> String {
470+
// Try to get the scheme from CloudFront first, and then from iron
471+
let scheme = req.headers
472+
.get_raw("cloudfront-forwarded-proto")
473+
.and_then(|values| values.get(0))
474+
.and_then(|value| std::str::from_utf8(value).ok())
475+
.filter(|proto| *proto == "http" || *proto == "https")
476+
.unwrap_or_else(|| req.url.scheme());
477+
478+
// Only include the port if it's needed
479+
let port = req.url.port();
480+
if port == 80 {
481+
format!("{}://{}", scheme, req.url.host())
482+
} else {
483+
format!("{}://{}:{}", scheme, req.url.host(), port)
484+
}
485+
}
486+
469487
fn style_css_handler(_: &mut Request) -> IronResult<Response> {
470488
let mut response = Response::with((status::Ok, STYLE_CSS));
471489
let cache = vec![CacheDirective::Public,
@@ -495,10 +513,7 @@ fn ico_handler(req: &mut Request) -> IronResult<Response> {
495513
} else {
496514
// if we're looking for something like "favicon-20190317-1.35.0-nightly-c82834e2b.ico",
497515
// redirect to the plain one so that the above branch can trigger with the correct filename
498-
let url = ctry!(Url::parse(&format!("{}://{}:{}/favicon.ico",
499-
req.url.scheme(),
500-
req.url.host(),
501-
req.url.port())[..]));
516+
let url = ctry!(Url::parse(&format!("{}/favicon.ico", redirect_base(req))[..]));
502517

503518
Ok(redirect(url))
504519
}

src/web/releases.rs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Releases web handlers
22
33

4-
use super::{duration_to_str, match_version};
4+
use super::{duration_to_str, match_version, redirect_base};
55
use super::error::Nope;
66
use super::page::Page;
77
use super::pool::Pool;
@@ -468,10 +468,8 @@ pub fn search_handler(req: &mut Request) -> IronResult<Response> {
468468
let name: String = rows.get(0).get(0);
469469
let version: String = rows.get(0).get(1);
470470
let target_name: String = rows.get(0).get(2);
471-
let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/{}/{}",
472-
req.url.scheme(),
473-
req.url.host(),
474-
req.url.port(),
471+
let url = ctry!(Url::parse(&format!("{}/{}/{}/{}",
472+
redirect_base(req),
475473
name,
476474
version,
477475
target_name)));
@@ -504,17 +502,13 @@ pub fn search_handler(req: &mut Request) -> IronResult<Response> {
504502
}
505503
};
506504
let url = if rustdoc_status {
507-
ctry!(Url::parse(&format!("{}://{}:{}/{}/{}",
508-
req.url.scheme(),
509-
req.url.host(),
510-
req.url.port(),
505+
ctry!(Url::parse(&format!("{}/{}/{}",
506+
redirect_base(req),
511507
query,
512508
version)[..]))
513509
} else {
514-
ctry!(Url::parse(&format!("{}://{}:{}/crate/{}/{}",
515-
req.url.scheme(),
516-
req.url.host(),
517-
req.url.port(),
510+
ctry!(Url::parse(&format!("{}/crate/{}/{}",
511+
redirect_base(req),
518512
query,
519513
version)[..]))
520514
};

src/web/rustdoc.rs

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
use super::pool::Pool;
55
use super::file::File;
6-
use super::latest_version;
6+
use super::{latest_version, redirect_base};
77
use super::crate_details::CrateDetails;
88
use iron::prelude::*;
99
use iron::{status, Url};
@@ -75,10 +75,8 @@ pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
7575
vers: &str,
7676
target_name: &str)
7777
-> IronResult<Response> {
78-
let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/{}/{}/",
79-
req.url.scheme(),
80-
req.url.host(),
81-
req.url.port(),
78+
let url = ctry!(Url::parse(&format!("{}/{}/{}/{}/",
79+
redirect_base(req),
8280
name,
8381
vers,
8482
target_name)[..]));
@@ -92,10 +90,8 @@ pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
9290
name: &str,
9391
vers: &str)
9492
-> IronResult<Response> {
95-
let url = ctry!(Url::parse(&format!("{}://{}:{}/crate/{}/{}",
96-
req.url.scheme(),
97-
req.url.host(),
98-
req.url.port(),
93+
let url = ctry!(Url::parse(&format!("{}/crate/{}/{}",
94+
redirect_base(req),
9995
name,
10096
vers)[..]));
10197

@@ -188,10 +184,8 @@ pub fn rustdoc_html_server_handler(req: &mut Request) -> IronResult<Response> {
188184
// to prevent cloudfront caching the wrong artifacts on URLs with loose semver
189185
// versions, redirect the browser to the returned version instead of loading it
190186
// immediately
191-
let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/{}/{}",
192-
req.url.scheme(),
193-
req.url.host(),
194-
req.url.port(),
187+
let url = ctry!(Url::parse(&format!("{}/{}/{}/{}",
188+
redirect_base(req),
195189
name,
196190
v,
197191
req_path.join("/"))[..]));
@@ -297,10 +291,8 @@ pub fn badge_handler(req: &mut Request) -> IronResult<Response> {
297291
}
298292
}
299293
MatchVersion::Semver(version) => {
300-
let url = ctry!(Url::parse(&format!("{}://{}:{}/{}/badge.svg?version={}",
301-
req.url.scheme(),
302-
req.url.host(),
303-
req.url.port(),
294+
let url = ctry!(Url::parse(&format!("{}/{}/badge.svg?version={}",
295+
redirect_base(req),
304296
name,
305297
version)[..]));
306298

0 commit comments

Comments
 (0)