|
1 | | -use super::{markdown, match_version, redirect_base, MatchSemver, MetaData}; |
2 | | -use crate::utils::{get_correct_docsrs_style_file, report_error}; |
| 1 | +use super::error::spawn_blocking_web; |
| 2 | +use super::{markdown, match_version, MatchSemver, MetaData}; |
| 3 | +use crate::utils::{get_correct_docsrs_style_file, report_error, spawn_blocking}; |
3 | 4 | use crate::{ |
4 | 5 | db::Pool, |
5 | | - impl_webpage, |
| 6 | + impl_axum_webpage, |
6 | 7 | repositories::RepositoryStatsUpdater, |
7 | | - web::{cache::CachePolicy, page::WebPage}, |
| 8 | + web::{ |
| 9 | + cache::CachePolicy, |
| 10 | + error::{AxumNope, AxumResult}, |
| 11 | + }, |
| 12 | +}; |
| 13 | +use anyhow::{anyhow, Context as _}; |
| 14 | +use axum::{ |
| 15 | + extract::{Extension, Path}, |
| 16 | + response::{IntoResponse, Response as AxumResponse}, |
8 | 17 | }; |
9 | | -use anyhow::anyhow; |
10 | 18 | use chrono::{DateTime, Utc}; |
11 | | -use iron::prelude::*; |
12 | | -use iron::Url; |
13 | 19 | use postgres::GenericClient; |
14 | | -use router::Router; |
| 20 | +use serde::Deserialize; |
15 | 21 | use serde::{ser::Serializer, Serialize}; |
16 | 22 | use serde_json::Value; |
| 23 | +use std::sync::Arc; |
17 | 24 |
|
18 | 25 | // TODO: Add target name and versions |
19 | 26 |
|
@@ -293,75 +300,85 @@ struct CrateDetailsPage { |
293 | 300 | details: CrateDetails, |
294 | 301 | } |
295 | 302 |
|
296 | | -impl_webpage! { |
| 303 | +impl_axum_webpage! { |
297 | 304 | CrateDetailsPage = "crate/details.html", |
298 | 305 | } |
299 | 306 |
|
300 | | -pub fn crate_details_handler(req: &mut Request) -> IronResult<Response> { |
301 | | - let router = extension!(req, Router); |
302 | | - // this handler must always called with a crate name |
303 | | - let name = cexpect!(req, router.find("name")); |
304 | | - let req_version = router.find("version"); |
| 307 | +#[derive(Deserialize, Clone, Debug)] |
| 308 | +pub(crate) struct CrateDetailHandlerParams { |
| 309 | + name: String, |
| 310 | + version: Option<String>, |
| 311 | +} |
305 | 312 |
|
306 | | - if req_version.is_none() { |
307 | | - let url = ctry!( |
308 | | - req, |
309 | | - Url::parse(&format!("{}/crate/{}/latest", redirect_base(req), name,)), |
310 | | - ); |
311 | | - return Ok(super::cached_redirect(url, CachePolicy::ForeverInCdn)); |
| 313 | +#[tracing::instrument] |
| 314 | +pub(crate) async fn crate_details_handler( |
| 315 | + Path(params): Path<CrateDetailHandlerParams>, |
| 316 | + Extension(pool): Extension<Pool>, |
| 317 | + Extension(repository_stats_updater): Extension<Arc<RepositoryStatsUpdater>>, |
| 318 | +) -> AxumResult<AxumResponse> { |
| 319 | + // this handler must always called with a crate name |
| 320 | + if params.version.is_none() { |
| 321 | + return Ok(super::axum_cached_redirect( |
| 322 | + &format!("/crate/{}/latest", params.name), |
| 323 | + CachePolicy::ForeverInCdn, |
| 324 | + ) |
| 325 | + .into_response()); |
312 | 326 | } |
313 | 327 |
|
314 | | - let mut conn = extension!(req, Pool).get()?; |
| 328 | + let found_version = spawn_blocking_web({ |
| 329 | + let pool = pool.clone(); |
| 330 | + let params = params.clone(); |
| 331 | + move || { |
| 332 | + let mut conn = pool.get().context("could not get connection from pool")?; |
| 333 | + match_version(&mut conn, ¶ms.name, params.version.as_deref()) |
| 334 | + .and_then(|m| m.assume_exact()) |
| 335 | + .map_err(Into::<AxumNope>::into) |
| 336 | + } |
| 337 | + }) |
| 338 | + .await?; |
315 | 339 |
|
316 | | - let found_version = |
317 | | - match_version(&mut conn, name, req_version).and_then(|m| m.assume_exact())?; |
318 | 340 | let (version, version_or_latest, is_latest_url) = match found_version { |
319 | 341 | MatchSemver::Exact((version, _)) => (version.clone(), version, false), |
320 | 342 | MatchSemver::Latest((version, _)) => (version, "latest".to_string(), true), |
321 | 343 | MatchSemver::Semver((version, _)) => { |
322 | | - let url = ctry!( |
323 | | - req, |
324 | | - Url::parse(&format!( |
325 | | - "{}/crate/{}/{}", |
326 | | - redirect_base(req), |
327 | | - name, |
328 | | - version |
329 | | - )), |
330 | | - ); |
331 | | - |
332 | | - return Ok(super::cached_redirect(url, CachePolicy::ForeverInCdn)); |
| 344 | + return Ok(super::axum_cached_redirect( |
| 345 | + &format!("/crate/{}/{}", ¶ms.name, version), |
| 346 | + CachePolicy::ForeverInCdn, |
| 347 | + ) |
| 348 | + .into_response()); |
333 | 349 | } |
334 | 350 | }; |
335 | 351 |
|
336 | | - let updater = extension!(req, RepositoryStatsUpdater); |
337 | | - let details = cexpect!( |
338 | | - req, |
339 | | - ctry!( |
340 | | - req, |
341 | | - CrateDetails::new( |
342 | | - &mut *conn, |
343 | | - name, |
344 | | - &version, |
345 | | - &version_or_latest, |
346 | | - Some(updater) |
347 | | - ) |
| 352 | + let details = spawn_blocking(move || { |
| 353 | + let mut conn = pool.get()?; |
| 354 | + CrateDetails::new( |
| 355 | + &mut *conn, |
| 356 | + ¶ms.name, |
| 357 | + &version, |
| 358 | + &version_or_latest, |
| 359 | + Some(&repository_stats_updater), |
348 | 360 | ) |
349 | | - ); |
350 | | - |
351 | | - let mut res = CrateDetailsPage { details }.into_response(req)?; |
352 | | - res.extensions.insert::<CachePolicy>(if is_latest_url { |
353 | | - CachePolicy::ForeverInCdn |
354 | | - } else { |
355 | | - CachePolicy::ForeverInCdnAndStaleInBrowser |
356 | | - }); |
357 | | - Ok(res) |
| 361 | + }) |
| 362 | + .await? |
| 363 | + .ok_or(AxumNope::VersionNotFound)?; |
| 364 | + |
| 365 | + let mut res = CrateDetailsPage { details }.into_response(); |
| 366 | + res.extensions_mut() |
| 367 | + .insert::<CachePolicy>(if is_latest_url { |
| 368 | + CachePolicy::ForeverInCdn |
| 369 | + } else { |
| 370 | + CachePolicy::ForeverInCdnAndStaleInBrowser |
| 371 | + }); |
| 372 | + Ok(res.into_response()) |
358 | 373 | } |
359 | 374 |
|
360 | 375 | #[cfg(test)] |
361 | 376 | mod tests { |
362 | 377 | use super::*; |
363 | 378 | use crate::index::api::CrateOwner; |
364 | | - use crate::test::{assert_cache_control, assert_redirect_cached, wrapper, TestDatabase}; |
| 379 | + use crate::test::{ |
| 380 | + assert_cache_control, assert_redirect, assert_redirect_cached, wrapper, TestDatabase, |
| 381 | + }; |
365 | 382 | use anyhow::{Context, Error}; |
366 | 383 | use kuchiki::traits::TendrilSink; |
367 | 384 | use std::collections::HashMap; |
@@ -1035,13 +1052,7 @@ mod tests { |
1035 | 1052 | assert!(body.contains("<a href=\"/crate/dummy/latest/source/\"")); |
1036 | 1053 | assert!(body.contains("<a href=\"/crate/dummy/latest\"")); |
1037 | 1054 |
|
1038 | | - assert_redirect_cached( |
1039 | | - "/crate/dummy/latest/", |
1040 | | - "/crate/dummy/latest", |
1041 | | - CachePolicy::NoCaching, |
1042 | | - web, |
1043 | | - &env.config(), |
1044 | | - )?; |
| 1055 | + assert_redirect("/crate/dummy/latest/", "/crate/dummy/latest", web)?; |
1045 | 1056 | assert_redirect_cached( |
1046 | 1057 | "/crate/dummy", |
1047 | 1058 | "/crate/dummy/latest", |
|
0 commit comments