Skip to content

Keep arguments in redirections #2800

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions src/web/builds.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use crate::{
impl_axum_webpage,
web::{
MetaData, ReqVersion,
error::AxumResult,
error::{AxumResult, EscapedURI},
extractors::{DbConnection, Path},
filters, match_version,
page::templates::{RenderRegular, RenderSolid},
Expand Down Expand Up @@ -69,7 +69,7 @@ pub(crate) async fn build_list_handler(
.assume_exact_name()?
.into_canonical_req_version_or_else(|version| {
AxumNope::Redirect(
format!("/crate/{name}/{version}/builds"),
EscapedURI::new(&format!("/crate/{name}/{version}/builds"), None),
CachePolicy::ForeverInCdn,
)
})?
Expand All @@ -93,7 +93,7 @@ pub(crate) async fn build_list_json_handler(
.assume_exact_name()?
.into_canonical_req_version_or_else(|version| {
AxumNope::Redirect(
format!("/crate/{name}/{version}/builds.json"),
EscapedURI::new(&format!("/crate/{name}/{version}/builds.json"), None),
CachePolicy::ForeverInCdn,
)
})?
Expand Down
40 changes: 24 additions & 16 deletions src/web/crate_details.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,7 @@ use crate::{
web::{
MatchedRelease, ReqVersion,
cache::CachePolicy,
encode_url_path,
error::{AxumNope, AxumResult},
error::{AxumNope, AxumResult, EscapedURI},
extractors::{DbConnection, Path},
page::templates::{RenderRegular, RenderSolid, filters},
rustdoc::RustdocHtmlParams,
Expand Down Expand Up @@ -475,7 +474,10 @@ pub(crate) async fn crate_details_handler(
) -> AxumResult<AxumResponse> {
let req_version = params.version.ok_or_else(|| {
AxumNope::Redirect(
format!("/crate/{}/{}", &params.name, ReqVersion::Latest),
EscapedURI::new(
&format!("/crate/{}/{}", &params.name, ReqVersion::Latest),
None,
),
CachePolicy::ForeverInCdn,
)
})?;
Expand All @@ -485,7 +487,7 @@ pub(crate) async fn crate_details_handler(
.assume_exact_name()?
.into_canonical_req_version_or_else(|version| {
AxumNope::Redirect(
format!("/crate/{}/{}", &params.name, version),
EscapedURI::new(&format!("/crate/{}/{}", &params.name, version), None),
CachePolicy::ForeverInCdn,
)
})?;
Expand Down Expand Up @@ -694,23 +696,29 @@ pub(crate) async fn get_all_platforms_inner(
.await?
.into_exactly_named_or_else(|corrected_name, req_version| {
AxumNope::Redirect(
encode_url_path(&format!(
"/platforms/{}/{}/{}",
corrected_name,
req_version,
req_path.join("/")
)),
EscapedURI::new(
&format!(
"/platforms/{}/{}/{}",
corrected_name,
req_version,
req_path.join("/")
),
None,
),
CachePolicy::NoCaching,
)
})?
.into_canonical_req_version_or_else(|version| {
AxumNope::Redirect(
encode_url_path(&format!(
"/platforms/{}/{}/{}",
&params.name,
version,
req_path.join("/")
)),
EscapedURI::new(
&format!(
"/platforms/{}/{}/{}",
&params.name,
version,
req_path.join("/")
),
None,
),
CachePolicy::ForeverInCdn,
)
})?;
Expand Down
34 changes: 27 additions & 7 deletions src/web/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,24 @@ use tracing::error;

use super::AxumErrorPage;

#[derive(Debug)]
pub struct EscapedURI(String);

impl EscapedURI {
pub fn new(path: &str, query: Option<&str>) -> Self {
let mut path = encode_url_path(path);
if let Some(query) = query {
path.push('?');
path.push_str(query);
}
Self(path)
}

pub fn as_str(&self) -> &str {
self.0.as_str()
}
}

#[derive(Debug, thiserror::Error)]
pub enum AxumNope {
#[error("Requested resource not found")]
Expand All @@ -35,7 +53,7 @@ pub enum AxumNope {
#[error("bad request")]
BadRequest(anyhow::Error),
#[error("redirect")]
Redirect(String, CachePolicy),
Redirect(EscapedURI, CachePolicy),
}

// FUTURE: Ideally, the split between the 3 kinds of responses would
Expand Down Expand Up @@ -118,8 +136,8 @@ struct ErrorInfo {
pub status: StatusCode,
}

fn redirect_with_policy(target: String, cache_policy: CachePolicy) -> AxumResponse {
match super::axum_cached_redirect(encode_url_path(&target), cache_policy) {
fn redirect_with_policy(target: EscapedURI, cache_policy: CachePolicy) -> AxumResponse {
match super::axum_cached_redirect(target.0, cache_policy) {
Ok(response) => response.into_response(),
Err(err) => AxumNope::InternalError(err).into_response(),
}
Expand Down Expand Up @@ -215,16 +233,18 @@ pub(crate) type JsonAxumResult<T> = Result<T, JsonAxumNope>;

#[cfg(test)]
mod tests {
use super::{AxumNope, IntoResponse};
use super::{AxumNope, EscapedURI, IntoResponse};
use crate::test::{AxumResponseTestExt, AxumRouterTestExt, async_wrapper};
use crate::web::cache::CachePolicy;
use kuchikiki::traits::TendrilSink;

#[test]
fn test_redirect_error_encodes_url_path() {
let response =
AxumNope::Redirect("/something>".into(), CachePolicy::ForeverInCdnAndBrowser)
.into_response();
let response = AxumNope::Redirect(
EscapedURI::new("/something>", None),
CachePolicy::ForeverInCdnAndBrowser,
)
.into_response();

assert_eq!(response.status(), 302);
assert_eq!(response.headers().get("Location").unwrap(), "/something%3E");
Expand Down
4 changes: 2 additions & 2 deletions src/web/features.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ use crate::{
web::{
MetaData, ReqVersion,
cache::CachePolicy,
error::{AxumNope, AxumResult},
error::{AxumNope, AxumResult, EscapedURI},
extractors::{DbConnection, Path},
filters,
headers::CanonicalUrl,
Expand Down Expand Up @@ -127,7 +127,7 @@ pub(crate) async fn build_features_handler(
.assume_exact_name()?
.into_canonical_req_version_or_else(|version| {
AxumNope::Redirect(
format!("/crate/{}/{}/features", &name, version),
EscapedURI::new(&format!("/crate/{}/{}/features", &name, version), None),
CachePolicy::ForeverInCdn,
)
})?
Expand Down
66 changes: 50 additions & 16 deletions src/web/rustdoc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ use crate::{
crate_details::CrateDetails,
csp::Csp,
encode_url_path,
error::{AxumNope, AxumResult},
error::{AxumNope, AxumResult, EscapedURI},
extractors::{DbConnection, Path},
file::File,
match_version,
Expand Down Expand Up @@ -253,7 +253,11 @@ pub(crate) async fn rustdoc_redirector_handler(
.into_response())
} else {
Ok(axum_cached_redirect(
format!("/crate/{crate_name}/{}", matched_release.req_version),
EscapedURI::new(
&format!("/crate/{crate_name}/{}", matched_release.req_version),
uri.query(),
)
.as_str(),
CachePolicy::ForeverInCdn,
)?
.into_response())
Expand Down Expand Up @@ -369,11 +373,15 @@ pub(crate) async fn rustdoc_html_server_handler(
vers: &Version,
path: &[&str],
cache_policy: CachePolicy,
uri: &Uri,
) -> AxumResult<AxumResponse> {
trace!("redirect");
// Format and parse the redirect url
Ok(axum_cached_redirect(
encode_url_path(&format!("/{}/{}/{}", name, vers, path.join("/"))),
EscapedURI::new(
&format!("/{}/{}/{}", name, vers, path.join("/")),
uri.query(),
)
.as_str(),
cache_policy,
)?
.into_response())
Expand All @@ -392,23 +400,19 @@ pub(crate) async fn rustdoc_html_server_handler(
.await?
.into_exactly_named_or_else(|corrected_name, req_version| {
AxumNope::Redirect(
encode_url_path(&format!(
"/{}/{}/{}",
corrected_name,
req_version,
req_path.join("/")
)),
EscapedURI::new(
&format!("/{}/{}/{}", corrected_name, req_version, req_path.join("/")),
uri.query(),
),
CachePolicy::NoCaching,
)
})?
.into_canonical_req_version_or_else(|version| {
AxumNope::Redirect(
encode_url_path(&format!(
"/{}/{}/{}",
&params.name,
version,
req_path.join("/")
)),
EscapedURI::new(
&format!("/{}/{}/{}", &params.name, version, req_path.join("/")),
None,
),
CachePolicy::ForeverInCdn,
)
})?;
Expand Down Expand Up @@ -439,6 +443,7 @@ pub(crate) async fn rustdoc_html_server_handler(
&krate.version,
&req_path[1..],
CachePolicy::ForeverInCdn,
&uri,
);
}

Expand Down Expand Up @@ -494,6 +499,7 @@ pub(crate) async fn rustdoc_html_server_handler(
&krate.version,
&req_path,
CachePolicy::ForeverInCdn,
&uri,
);
}
}
Expand Down Expand Up @@ -2004,6 +2010,12 @@ mod test {
"/foo-ab/0.0.1/foo_ab/",
)
.await?;
// `-` becomes `_` but we keep the query arguments.
web.assert_redirect_unchecked(
"/foo-ab/0.0.1/foo_ab/?search=a",
"/foo_ab/0.0.1/foo_ab/?search=a",
)
.await?;
Ok(())
})
}
Expand Down Expand Up @@ -2975,4 +2987,26 @@ mod test {
Ok(())
})
}

#[test_case(true)]
#[test_case(false)]
fn test_redirect_with_query_args(archive_storage: bool) {
async_wrapper(|env| async move {
env.fake_release()
.await
.name("fake")
.version("0.0.1")
.archive_storage(archive_storage)
.rustdoc_file("fake/index.html")
.binary(true) // binary => rustdoc_status = false
.create()
.await?;

let web = env.web_app().await;
web.assert_redirect("/fake?a=b", "/crate/fake/latest?a=b")
.await?;

Ok(())
});
}
}
16 changes: 11 additions & 5 deletions src/web/source.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use crate::{
web::{
MetaData, ReqVersion,
cache::CachePolicy,
error::AxumNope,
error::{AxumNope, EscapedURI},
extractors::Path,
file::File as DbFile,
headers::CanonicalUrl,
Expand Down Expand Up @@ -203,16 +203,22 @@ pub(crate) async fn source_browser_handler(
.await?
.into_exactly_named_or_else(|corrected_name, req_version| {
AxumNope::Redirect(
format!(
"/crate/{corrected_name}/{req_version}/source/{}",
params.path
EscapedURI::new(
&format!(
"/crate/{corrected_name}/{req_version}/source/{}",
params.path
),
None,
),
CachePolicy::NoCaching,
)
})?
.into_canonical_req_version_or_else(|version| {
AxumNope::Redirect(
format!("/crate/{}/{version}/source/{}", params.name, params.path),
EscapedURI::new(
&format!("/crate/{}/{version}/source/{}", params.name, params.path),
None,
),
CachePolicy::ForeverInCdn,
)
})?
Expand Down
4 changes: 2 additions & 2 deletions src/web/status.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{cache::CachePolicy, error::AxumNope};
use crate::web::{
ReqVersion,
error::AxumResult,
error::{AxumResult, EscapedURI},
extractors::{DbConnection, Path},
match_version,
};
Expand All @@ -28,7 +28,7 @@ pub(crate) async fn status_handler(
let version = matched_release
.into_canonical_req_version_or_else(|version| {
AxumNope::Redirect(
format!("/crate/{name}/{version}/status.json"),
EscapedURI::new(&format!("/crate/{name}/{version}/status.json"), None),
CachePolicy::NoCaching,
)
})?
Expand Down
Loading