@@ -2,6 +2,8 @@ use fastly::http::{Method, StatusCode};
22use fastly:: { Error , Request , Response } ;
33use log:: { info, warn, LevelFilter } ;
44use log_fastly:: Logger ;
5+ use once_cell:: sync:: Lazy ;
6+ use regex:: Regex ;
57use serde_json:: json;
68use time:: OffsetDateTime ;
79
@@ -74,6 +76,7 @@ fn handle_request(config: &Config, mut request: Request) -> Result<Response, Err
7476
7577 set_ttl ( config, & mut request) ;
7678 rewrite_urls_with_plus_character ( & mut request) ;
79+ rewrite_download_urls ( & mut request) ;
7780
7881 // Database dump is too big to cache on Fastly
7982 if request. get_url_str ( ) . ends_with ( "db-dump.tar.gz" ) {
@@ -127,6 +130,28 @@ fn rewrite_urls_with_plus_character(request: &mut Request) {
127130 }
128131}
129132
133+ /// Rewrite `/crates/{crate}/{version}/download` URLs to
134+ /// `/crates/{crate}/{crate}-{version}.crate`
135+ ///
136+ /// cargo versions before 1.24 don't support placeholders in the `dl` field
137+ /// of the index, so we need to rewrite the download URL to point to the
138+ /// crate file instead.
139+ fn rewrite_download_urls ( request : & mut Request ) {
140+ static RE : Lazy < Regex > = Lazy :: new ( || {
141+ Regex :: new ( r"^/crates/(?P<crate>[^/]+)/(?P<version>[^/]+)/download$" ) . unwrap ( )
142+ } ) ;
143+
144+ let url = request. get_url_mut ( ) ;
145+ let path = url. path ( ) ;
146+
147+ if let Some ( captures) = RE . captures ( path) {
148+ let krate = captures. name ( "crate" ) . unwrap ( ) . as_str ( ) ;
149+ let version = captures. name ( "version" ) . unwrap ( ) . as_str ( ) ;
150+ let new_path = format ! ( "/crates/{krate}/{krate}-{version}.crate" ) ;
151+ url. set_path ( & new_path) ;
152+ }
153+ }
154+
130155/// Redirect request to CloudFront
131156///
132157/// As of early 2023, certain files are too large to be served through Fastly. One of those is the
@@ -199,3 +224,34 @@ fn build_and_send_log(log_line: LogLineV1Builder, config: &Config) {
199224 }
200225 } ;
201226}
227+
228+ #[ cfg( test) ]
229+ mod tests {
230+ use super :: * ;
231+
232+ #[ test]
233+ fn test_rewrite_download_urls ( ) {
234+ fn test ( url : & str , expected : & str ) {
235+ let mut request = Request :: get ( url) ;
236+ rewrite_download_urls ( & mut request) ;
237+ assert_eq ! ( request. get_url_str( ) , expected) ;
238+ }
239+
240+ test (
241+ "https://static.crates.io/unrelated" ,
242+ "https://static.crates.io/unrelated" ,
243+ ) ;
244+ test (
245+ "https://static.crates.io/crates/serde/serde-1.0.0.crate" ,
246+ "https://static.crates.io/crates/serde/serde-1.0.0.crate" ,
247+ ) ;
248+ test (
249+ "https://static.crates.io/crates/serde/1.0.0/download" ,
250+ "https://static.crates.io/crates/serde/serde-1.0.0.crate" ,
251+ ) ;
252+ test (
253+ "https://static.crates.io/crates/serde/1.0.0-alpha.1+foo-bar/download" ,
254+ "https://static.crates.io/crates/serde/serde-1.0.0-alpha.1+foo-bar.crate" ,
255+ ) ;
256+ }
257+ }
0 commit comments