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