@@ -3,7 +3,7 @@ use crate::db::types::Feature;
33use crate :: {
44 db:: Pool ,
55 impl_webpage,
6- web:: { page:: WebPage , MetaData } ,
6+ web:: { cache :: CachePolicy , page:: WebPage , MetaData } ,
77} ;
88use iron:: { IronResult , Request , Response , Url } ;
99use router:: Router ;
@@ -30,10 +30,10 @@ pub fn build_features_handler(req: &mut Request) -> IronResult<Response> {
3030 let req_version = router. find ( "version" ) ;
3131
3232 let mut conn = extension ! ( req, Pool ) . get ( ) ?;
33- let ( version, version_or_latest) =
33+ let ( version, version_or_latest, is_latest_url ) =
3434 match match_version ( & mut conn, name, req_version) . and_then ( |m| m. assume_exact ( ) ) ? {
35- MatchSemver :: Exact ( ( version, _) ) => ( version. clone ( ) , version) ,
36- MatchSemver :: Latest ( ( version, _) ) => ( version, "latest" . to_string ( ) ) ,
35+ MatchSemver :: Exact ( ( version, _) ) => ( version. clone ( ) , version, false ) ,
36+ MatchSemver :: Latest ( ( version, _) ) => ( version, "latest" . to_string ( ) , true ) ,
3737
3838 MatchSemver :: Semver ( ( version, _) ) => {
3939 let url = ctry ! (
@@ -46,7 +46,7 @@ pub fn build_features_handler(req: &mut Request) -> IronResult<Response> {
4646 ) ) ,
4747 ) ;
4848
49- return Ok ( super :: redirect ( url) ) ;
49+ return Ok ( super :: cached_redirect ( url, CachePolicy :: ForeverInCdn ) ) ;
5050 }
5151 } ;
5252 let rows = ctry ! (
@@ -70,7 +70,7 @@ pub fn build_features_handler(req: &mut Request) -> IronResult<Response> {
7070 default_len = result. 1 ;
7171 }
7272
73- FeaturesPage {
73+ let mut response = FeaturesPage {
7474 metadata : cexpect ! (
7575 req,
7676 MetaData :: from_crate( & mut conn, name, & version, & version_or_latest)
@@ -79,7 +79,13 @@ pub fn build_features_handler(req: &mut Request) -> IronResult<Response> {
7979 default_len,
8080 canonical_url : format ! ( "https://docs.rs/crate/{}/latest/features" , name) ,
8181 }
82- . into_response ( req)
82+ . into_response ( req) ?;
83+ response. extensions . insert :: < CachePolicy > ( if is_latest_url {
84+ CachePolicy :: ForeverInCdn
85+ } else {
86+ CachePolicy :: ForeverInCdnAndStaleInBrowser
87+ } ) ;
88+ Ok ( response)
8389}
8490
8591fn order_features_and_count_default_len ( raw : Vec < Feature > ) -> ( Vec < Feature > , usize ) {
@@ -121,12 +127,9 @@ fn get_feature_map(raw: Vec<Feature>) -> HashMap<String, Feature> {
121127
122128#[ cfg( test) ]
123129mod tests {
130+ use super :: * ;
124131 use crate :: db:: types:: Feature ;
125- use crate :: test:: wrapper;
126- use crate :: web:: features:: {
127- get_feature_map, get_tree_structure_from_default, order_features_and_count_default_len,
128- DEFAULT_NAME ,
129- } ;
132+ use crate :: test:: { assert_cache_control, assert_redirect_cached, wrapper} ;
130133 use reqwest:: StatusCode ;
131134 use std:: collections:: HashMap ;
132135
@@ -246,6 +249,46 @@ mod tests {
246249 assert_eq ! ( features[ 1 ] , non_default) ;
247250 }
248251
252+ #[ test]
253+ fn semver_redirect ( ) {
254+ wrapper ( |env| {
255+ env. fake_release ( )
256+ . name ( "foo" )
257+ . version ( "0.2.1" )
258+ . features ( HashMap :: new ( ) )
259+ . create ( ) ?;
260+
261+ assert_redirect_cached (
262+ "/crate/foo/~0.2/features" ,
263+ "/crate/foo/0.2.1/features" ,
264+ CachePolicy :: ForeverInCdn ,
265+ env. frontend ( ) ,
266+ & env. config ( ) ,
267+ ) ?;
268+ Ok ( ( ) )
269+ } ) ;
270+ }
271+
272+ #[ test]
273+ fn specific_version_correctly_cached ( ) {
274+ wrapper ( |env| {
275+ env. fake_release ( )
276+ . name ( "foo" )
277+ . version ( "0.2.0" )
278+ . features ( HashMap :: new ( ) )
279+ . create ( ) ?;
280+
281+ let resp = env. frontend ( ) . get ( "/crate/foo/0.2.0/features" ) . send ( ) ?;
282+ assert ! ( resp. status( ) . is_success( ) ) ;
283+ assert_cache_control (
284+ & resp,
285+ CachePolicy :: ForeverInCdnAndStaleInBrowser ,
286+ & env. config ( ) ,
287+ ) ;
288+ Ok ( ( ) )
289+ } ) ;
290+ }
291+
249292 #[ test]
250293 fn latest_200 ( ) {
251294 wrapper ( |env| {
@@ -262,6 +305,8 @@ mod tests {
262305 . create ( ) ?;
263306
264307 let resp = env. frontend ( ) . get ( "/crate/foo/latest/features" ) . send ( ) ?;
308+ assert ! ( resp. status( ) . is_success( ) ) ;
309+ assert_cache_control ( & resp, CachePolicy :: ForeverInCdn , & env. config ( ) ) ;
265310 assert ! ( resp. url( ) . as_str( ) . ends_with( "/crate/foo/latest/features" ) ) ;
266311 let body = String :: from_utf8 ( resp. bytes ( ) . unwrap ( ) . to_vec ( ) ) . unwrap ( ) ;
267312 assert ! ( body. contains( "<a href=\" /crate/foo/latest/builds\" " ) ) ;
0 commit comments