|
3 | 3 | use crate::{
|
4 | 4 | db::Pool,
|
5 | 5 | repositories::RepositoryStatsUpdater,
|
6 |
| - storage::{rustdoc_archive_path, PathNotFoundError}, |
| 6 | + storage::rustdoc_archive_path, |
7 | 7 | utils::{self, spawn_blocking},
|
8 | 8 | web::{
|
9 | 9 | axum_cached_redirect, axum_parse_uri_with_params,
|
@@ -58,6 +58,37 @@ pub(crate) struct RustdocRedirectorParams {
|
58 | 58 | target: Option<String>,
|
59 | 59 | }
|
60 | 60 |
|
| 61 | +/// try to serve a toolchain specific assert from the legacy location. |
| 62 | +/// |
| 63 | +/// Newer rustdoc builds use a specific subfolder on the bucket, |
| 64 | +/// a new `static-root-path` prefix (`/-/rustdoc.static/...`), which |
| 65 | +/// is served via our `static_asset_handler`. |
| 66 | +/// |
| 67 | +/// The legacy location is the root, both on the bucket & the URL |
| 68 | +/// path, which is suboptimal since the route overlaps with other routes. |
| 69 | +/// |
| 70 | +/// See also https://github.com/rust-lang/docs.rs/pull/1889 |
| 71 | +async fn try_serve_legacy_toolchain_asset( |
| 72 | + storage: Arc<Storage>, |
| 73 | + config: Arc<Config>, |
| 74 | + path: impl AsRef<str>, |
| 75 | +) -> AxumResult<AxumResponse> { |
| 76 | + let path = path.as_ref().to_owned(); |
| 77 | + // FIXME: this could be optimized: when a path doesn't exist |
| 78 | + // in storage, we don't need to recheck on every request. |
| 79 | + // Existing files are returned with caching headers, so |
| 80 | + // are cached by the CDN. |
| 81 | + // If cached, it doesn't need to be invalidated, |
| 82 | + // since new nightly versions will always put their |
| 83 | + // toolchain specific resources into the new folder, |
| 84 | + // which is reached via the new handler. |
| 85 | + Ok( |
| 86 | + spawn_blocking(move || File::from_path(&storage, &path, &config)) |
| 87 | + .await |
| 88 | + .map(IntoResponse::into_response)?, |
| 89 | + ) |
| 90 | +} |
| 91 | + |
61 | 92 | /// Handler called for `/:crate` and `/:crate/:version` URLs. Automatically redirects to the docs
|
62 | 93 | /// or crate details page based on whether the given crate version was successfully built.
|
63 | 94 | #[instrument(skip_all)]
|
@@ -93,41 +124,13 @@ pub(crate) async fn rustdoc_redirector_handler(
|
93 | 124 |
|
94 | 125 | // global static assets for older builds are served from the root, which ends up
|
95 | 126 | // in this handler as `params.name`.
|
96 |
| - // Newer builds use a different static-root, and the static assets are served via |
97 |
| - // `static_asset_handler` |
98 | 127 | if let Some((_, extension)) = params.name.rsplit_once('.') {
|
99 | 128 | if ["css", "js", "png", "svg", "woff", "woff2"]
|
100 | 129 | .binary_search(&extension)
|
101 | 130 | .is_ok()
|
102 | 131 | {
|
103 | 132 | rendering_time.step("serve static asset");
|
104 |
| - // FIXME: this could be optimized: when a path doesn't exist |
105 |
| - // in storage, we don't need to recheck on every request. |
106 |
| - // Existing files are returned with caching headers, so |
107 |
| - // are cached by the CDN. |
108 |
| - // If cached, it doesn't need to be invalidated, |
109 |
| - // since new nightly versions will always put their |
110 |
| - // toolchain specific resources into the new folder, |
111 |
| - // which is reached via the new handler. |
112 |
| - return match spawn_blocking({ |
113 |
| - let storage = storage.clone(); |
114 |
| - let name = params.name.clone(); |
115 |
| - let config = config.clone(); |
116 |
| - move || File::from_path(&storage, &name, &config) |
117 |
| - }) |
118 |
| - .await |
119 |
| - { |
120 |
| - Ok(file) => Ok(file.into_response()), |
121 |
| - Err(err) => { |
122 |
| - if matches!(err.downcast_ref(), Some(AxumNope::ResourceNotFound)) |
123 |
| - || matches!(err.downcast_ref(), Some(crate::storage::PathNotFoundError)) |
124 |
| - { |
125 |
| - Err(AxumNope::ResourceNotFound) |
126 |
| - } else { |
127 |
| - Err(AxumNope::InternalError(err)) |
128 |
| - } |
129 |
| - } |
130 |
| - }; |
| 133 | + return try_serve_legacy_toolchain_asset(storage, config, params.name).await; |
131 | 134 | }
|
132 | 135 | }
|
133 | 136 |
|
@@ -210,7 +213,12 @@ pub(crate) async fn rustdoc_redirector_handler(
|
210 | 213 | {
|
211 | 214 | debug!(?target, ?err, "got error serving file");
|
212 | 215 | }
|
213 |
| - return Err(AxumNope::ResourceNotFound); |
| 216 | + // FIXME: we sometimes still get requests for toolchain |
| 217 | + // specific static assets under the crate/version/ path. |
| 218 | + // This is fixed in rustdoc, but pending a rebuild for |
| 219 | + // docs that were affected by this bug. |
| 220 | + // https://github.com/rust-lang/docs.rs/issues/1979 |
| 221 | + return try_serve_legacy_toolchain_asset(storage, config, target).await; |
214 | 222 | }
|
215 | 223 | }
|
216 | 224 | }
|
@@ -912,16 +920,7 @@ pub(crate) async fn static_asset_handler(
|
912 | 920 | ) -> AxumResult<impl IntoResponse> {
|
913 | 921 | let storage_path = format!("{}{}", RUSTDOC_STATIC_STORAGE_PREFIX, path);
|
914 | 922 |
|
915 |
| - Ok(spawn_blocking( |
916 |
| - move || match File::from_path(&storage, &storage_path, &config) { |
917 |
| - Ok(file) => Ok(file), |
918 |
| - Err(err) if err.downcast_ref::<PathNotFoundError>().is_some() => { |
919 |
| - Err(AxumNope::ResourceNotFound.into()) |
920 |
| - } |
921 |
| - Err(err) => Err(AxumNope::InternalError(err).into()), |
922 |
| - }, |
923 |
| - ) |
924 |
| - .await?) |
| 923 | + Ok(spawn_blocking(move || File::from_path(&storage, &storage_path, &config)).await?) |
925 | 924 | }
|
926 | 925 |
|
927 | 926 | #[cfg(test)]
|
@@ -2591,4 +2590,26 @@ mod test {
|
2591 | 2590 | Ok(())
|
2592 | 2591 | });
|
2593 | 2592 | }
|
| 2593 | + |
| 2594 | + #[test] |
| 2595 | + fn fallback_to_root_storage_for_release_js_assets() { |
| 2596 | + // test workaround for https://github.com/rust-lang/docs.rs/issues/1979 |
| 2597 | + wrapper(|env| { |
| 2598 | + env.fake_release() |
| 2599 | + .name("dummy") |
| 2600 | + .version("0.1.0") |
| 2601 | + .archive_storage(true) |
| 2602 | + .create()?; |
| 2603 | + |
| 2604 | + env.storage().store_one("asset.js", *b"content")?; |
| 2605 | + |
| 2606 | + let web = env.frontend(); |
| 2607 | + |
| 2608 | + let response = web.get("/dummy/0.1.0/asset.js").send()?; |
| 2609 | + assert!(response.status().is_success()); |
| 2610 | + assert_eq!(response.text()?, "content"); |
| 2611 | + |
| 2612 | + Ok(()) |
| 2613 | + }) |
| 2614 | + } |
2594 | 2615 | }
|
0 commit comments