Skip to content

Commit 3d2d3cb

Browse files
committed
Retain queries and convert :: paths on redirects to rust-lang.org
1 parent e97a5bd commit 3d2d3cb

File tree

3 files changed

+47
-88
lines changed

3 files changed

+47
-88
lines changed

src/web/mod.rs

+7
Original file line numberDiff line numberDiff line change
@@ -750,6 +750,13 @@ mod test {
750750
assert_redirect("/rustdoc", target, web)?;
751751
assert_redirect("/rustdoc/", target, web)?;
752752

753+
// queries are supported
754+
assert_redirect(
755+
"/std?search=foobar",
756+
"https://doc.rust-lang.org/stable/std/?search=foobar",
757+
web,
758+
)?;
759+
753760
Ok(())
754761
})
755762
}

src/web/routes.rs

-41
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@ use iron::{
99
use router::Router;
1010
use std::collections::HashSet;
1111

12-
pub(super) const DOC_RUST_LANG_ORG_REDIRECTS: &[&str] =
13-
&["alloc", "core", "proc_macro", "std", "test"];
14-
1512
// REFACTOR: Break this into smaller initialization functions
1613
pub(super) fn build_routes() -> Routes {
1714
let mut routes = Routes::new();
@@ -168,44 +165,6 @@ pub(super) fn build_routes() -> Routes {
168165
super::rustdoc::rustdoc_html_server_handler,
169166
);
170167

171-
for redirect in DOC_RUST_LANG_ORG_REDIRECTS {
172-
routes.internal_page(
173-
&format!("/{}", redirect),
174-
super::rustdoc::RustLangRedirector::new("stable", redirect),
175-
);
176-
routes.internal_page(
177-
&format!("/{}/", redirect),
178-
super::rustdoc::RustLangRedirector::new("stable", redirect),
179-
);
180-
}
181-
// redirect proc-macro to proc_macro
182-
routes.internal_page(
183-
"/proc-macro",
184-
super::rustdoc::RustLangRedirector::new("stable", "proc_macro"),
185-
);
186-
routes.internal_page(
187-
"/proc-macro/",
188-
super::rustdoc::RustLangRedirector::new("stable", "proc_macro"),
189-
);
190-
// redirect rustc to nightly rustc docs
191-
routes.internal_page(
192-
"/rustc",
193-
super::rustdoc::RustLangRedirector::new("nightly", "nightly-rustc"),
194-
);
195-
routes.internal_page(
196-
"/rustc/",
197-
super::rustdoc::RustLangRedirector::new("nightly", "nightly-rustc"),
198-
);
199-
// redirect rustdoc to nightly rustdoc docs
200-
routes.internal_page(
201-
"/rustdoc",
202-
super::rustdoc::RustLangRedirector::new("nightly", "nightly-rustc/rustdoc"),
203-
);
204-
routes.internal_page(
205-
"/rustdoc/",
206-
super::rustdoc::RustLangRedirector::new("nightly", "nightly-rustc/rustdoc"),
207-
);
208-
209168
routes
210169
}
211170

src/web/rustdoc.rs

+40-47
Original file line numberDiff line numberDiff line change
@@ -19,54 +19,33 @@ use iron::{
1919
Handler, IronResult, Request, Response, Url,
2020
};
2121
use lol_html::errors::RewritingError;
22+
use once_cell::sync::Lazy;
2223
use router::Router;
2324
use serde::Serialize;
24-
use std::{fmt::Write, path::Path};
25-
26-
#[derive(Clone)]
27-
pub struct RustLangRedirector {
28-
url: Url,
29-
}
30-
31-
impl RustLangRedirector {
32-
pub fn new(version: &str, target: &str) -> Self {
33-
let url = iron::url::Url::parse(&format!("https://doc.rust-lang.org/{version}/{target}/"))
34-
.expect("failed to parse rust-lang.org doc URL");
35-
let url = Url::from_generic_url(url).expect("failed to convert url::Url to iron::Url");
36-
37-
Self { url }
38-
}
39-
}
40-
41-
impl iron::Handler for RustLangRedirector {
42-
fn handle(&self, _req: &mut Request) -> IronResult<Response> {
43-
Ok(Response::with((status::Found, Redirect(self.url.clone()))))
44-
}
45-
}
25+
use std::{collections::HashMap, fmt::Write, path::Path};
26+
27+
static DOC_RUST_LANG_ORG_REDIRECTS: Lazy<HashMap<&str, &str>> = Lazy::new(|| {
28+
HashMap::from([
29+
("alloc", "stable/alloc"),
30+
("core", "stable/core"),
31+
("proc_macro", "stable/proc_macro"),
32+
("proc-macro", "stable/proc_macro"),
33+
("std", "stable/std"),
34+
("test", "stable/test"),
35+
("rustc", "nightly/nightly-rustc"),
36+
("rustdoc", "nightly/nightly-rustc/rustdoc"),
37+
])
38+
});
4639

4740
/// Handler called for `/:crate` and `/:crate/:version` URLs. Automatically redirects to the docs
4841
/// or crate details page based on whether the given crate version was successfully built.
4942
pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
5043
fn redirect_to_doc(
5144
req: &Request,
52-
name: &str,
53-
vers: &str,
54-
target: Option<&str>,
55-
target_name: &str,
45+
mut url_str: String,
46+
permanent: bool,
5647
path_in_crate: Option<&str>,
5748
) -> IronResult<Response> {
58-
let mut url_str = if let Some(target) = target {
59-
format!(
60-
"{}/{}/{}/{}/{}/",
61-
redirect_base(req),
62-
name,
63-
vers,
64-
target,
65-
target_name
66-
)
67-
} else {
68-
format!("{}/{}/{}/{}/", redirect_base(req), name, vers, target_name)
69-
};
7049
if let Some(query) = req.url.query() {
7150
url_str.push('?');
7251
url_str.push_str(query);
@@ -75,7 +54,7 @@ pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
7554
url_str.push_str(path);
7655
}
7756
let url = ctry!(req, Url::parse(&url_str));
78-
let (status_code, max_age) = if vers == "latest" {
57+
let (status_code, max_age) = if permanent {
7958
(status::MovedPermanently, 86400)
8059
} else {
8160
(status::Found, 0)
@@ -156,6 +135,12 @@ pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
156135
Some((krate, path)) => (krate.to_string(), Some(path.to_string())),
157136
None => (crate_name.to_string(), None),
158137
};
138+
139+
if let Some(inner_path) = DOC_RUST_LANG_ORG_REDIRECTS.get(crate_name.as_str()) {
140+
let url = format!("https://doc.rust-lang.org/{inner_path}/");
141+
return redirect_to_doc(req, url, false, path_in_crate.as_deref());
142+
}
143+
159144
let req_version = router.find("version");
160145
let mut target = router.find("target");
161146

@@ -197,14 +182,15 @@ pub fn rustdoc_redirector_handler(req: &mut Request) -> IronResult<Response> {
197182

198183
if has_docs {
199184
rendering_time.step("redirect to doc");
200-
redirect_to_doc(
201-
req,
202-
&crate_name,
203-
&version,
204-
target,
205-
&target_name,
206-
path_in_crate.as_deref(),
207-
)
185+
186+
let base = redirect_base(req);
187+
let url_str = if let Some(target) = target {
188+
format!("{base}/{crate_name}/{version}/{target}/{target_name}/")
189+
} else {
190+
format!("{base}/{crate_name}/{version}/{target_name}/")
191+
};
192+
193+
redirect_to_doc(req, url_str, version == "latest", path_in_crate.as_deref())
208194
} else {
209195
rendering_time.step("redirect to crate");
210196
redirect_to_crate(req, &crate_name, &version)
@@ -1790,6 +1776,13 @@ mod test {
17901776
"/some_random_crate/latest/some_random_crate/?search=some::path",
17911777
web,
17921778
)?;
1779+
1780+
assert_redirect(
1781+
"/std::some::path",
1782+
"https://doc.rust-lang.org/stable/std/?search=some::path",
1783+
web,
1784+
)?;
1785+
17931786
Ok(())
17941787
})
17951788
}

0 commit comments

Comments
 (0)