1
1
use std:: collections:: BTreeMap ;
2
2
use std:: fmt:: Debug ;
3
- use std:: io;
4
3
use std:: path:: PathBuf ;
5
4
use std:: str:: FromStr ;
6
5
@@ -16,7 +15,9 @@ use tracing::{info_span, instrument, trace, warn, Instrument};
16
15
use url:: Url ;
17
16
18
17
use distribution_filename:: { DistFilename , SourceDistFilename , WheelFilename } ;
19
- use distribution_types:: { BuiltDist , File , FileLocation , IndexUrl , IndexUrls , Name } ;
18
+ use distribution_types:: {
19
+ BuiltDist , File , FileLocation , IndexCapabilities , IndexUrl , IndexUrls , Name ,
20
+ } ;
20
21
use install_wheel_rs:: metadata:: { find_archive_dist_info, is_metadata_entry} ;
21
22
use pep440_rs:: Version ;
22
23
use pep508_rs:: MarkerEnvironment ;
@@ -147,7 +148,7 @@ impl<'a> RegistryClientBuilder<'a> {
147
148
}
148
149
149
150
impl < ' a > TryFrom < BaseClientBuilder < ' a > > for RegistryClientBuilder < ' a > {
150
- type Error = io:: Error ;
151
+ type Error = std :: io:: Error ;
151
152
152
153
fn try_from ( value : BaseClientBuilder < ' a > ) -> Result < Self , Self :: Error > {
153
154
Ok ( Self {
@@ -402,7 +403,11 @@ impl RegistryClient {
402
403
/// 2. From a remote wheel by partial zip reading
403
404
/// 3. From a (temp) download of a remote wheel (this is a fallback, the webserver should support range requests)
404
405
#[ instrument( skip_all, fields( % built_dist) ) ]
405
- pub async fn wheel_metadata ( & self , built_dist : & BuiltDist ) -> Result < Metadata23 , Error > {
406
+ pub async fn wheel_metadata (
407
+ & self ,
408
+ built_dist : & BuiltDist ,
409
+ capabilities : & IndexCapabilities ,
410
+ ) -> Result < Metadata23 , Error > {
406
411
let metadata = match & built_dist {
407
412
BuiltDist :: Registry ( wheels) => {
408
413
#[ derive( Debug , Clone ) ]
@@ -451,7 +456,7 @@ impl RegistryClient {
451
456
. await ?
452
457
}
453
458
WheelLocation :: Url ( url) => {
454
- self . wheel_metadata_registry ( & wheel. index , & wheel. file , & url)
459
+ self . wheel_metadata_registry ( & wheel. index , & wheel. file , & url, capabilities )
455
460
. await ?
456
461
}
457
462
}
@@ -460,7 +465,9 @@ impl RegistryClient {
460
465
self . wheel_metadata_no_pep658 (
461
466
& wheel. filename ,
462
467
& wheel. url ,
468
+ None ,
463
469
WheelCache :: Url ( & wheel. url ) ,
470
+ capabilities,
464
471
)
465
472
. await ?
466
473
}
@@ -489,6 +496,7 @@ impl RegistryClient {
489
496
index : & IndexUrl ,
490
497
file : & File ,
491
498
url : & Url ,
499
+ capabilities : & IndexCapabilities ,
492
500
) -> Result < Metadata23 , Error > {
493
501
// If the metadata file is available at its own url (PEP 658), download it from there.
494
502
let filename = WheelFilename :: from_str ( & file. filename ) . map_err ( ErrorKind :: WheelFilename ) ?;
@@ -536,8 +544,14 @@ impl RegistryClient {
536
544
// If we lack PEP 658 support, try using HTTP range requests to read only the
537
545
// `.dist-info/METADATA` file from the zip, and if that also fails, download the whole wheel
538
546
// into the cache and read from there
539
- self . wheel_metadata_no_pep658 ( & filename, url, WheelCache :: Index ( index) )
540
- . await
547
+ self . wheel_metadata_no_pep658 (
548
+ & filename,
549
+ url,
550
+ Some ( index) ,
551
+ WheelCache :: Index ( index) ,
552
+ capabilities,
553
+ )
554
+ . await
541
555
}
542
556
}
543
557
@@ -546,7 +560,9 @@ impl RegistryClient {
546
560
& self ,
547
561
filename : & ' data WheelFilename ,
548
562
url : & ' data Url ,
563
+ index : Option < & ' data IndexUrl > ,
549
564
cache_shard : WheelCache < ' data > ,
565
+ capabilities : & ' data IndexCapabilities ,
550
566
) -> Result < Metadata23 , Error > {
551
567
let cache_entry = self . cache . entry (
552
568
CacheBucket :: Wheels ,
@@ -562,72 +578,80 @@ impl RegistryClient {
562
578
Connectivity :: Offline => CacheControl :: AllowStale ,
563
579
} ;
564
580
565
- let req = self
566
- . uncached_client ( url)
567
- . head ( url. clone ( ) )
568
- . header (
569
- "accept-encoding" ,
570
- http:: HeaderValue :: from_static ( "identity" ) ,
571
- )
572
- . build ( )
573
- . map_err ( ErrorKind :: from) ?;
581
+ // Attempt to fetch via a range request.
582
+ if index. map_or ( true , |index| capabilities. supports_range_requests ( index) ) {
583
+ let req = self
584
+ . uncached_client ( url)
585
+ . head ( url. clone ( ) )
586
+ . header (
587
+ "accept-encoding" ,
588
+ http:: HeaderValue :: from_static ( "identity" ) ,
589
+ )
590
+ . build ( )
591
+ . map_err ( ErrorKind :: from) ?;
574
592
575
- // Copy authorization headers from the HEAD request to subsequent requests
576
- let mut headers = HeaderMap :: default ( ) ;
577
- if let Some ( authorization) = req. headers ( ) . get ( "authorization" ) {
578
- headers. append ( "authorization" , authorization. clone ( ) ) ;
579
- }
593
+ // Copy authorization headers from the HEAD request to subsequent requests
594
+ let mut headers = HeaderMap :: default ( ) ;
595
+ if let Some ( authorization) = req. headers ( ) . get ( "authorization" ) {
596
+ headers. append ( "authorization" , authorization. clone ( ) ) ;
597
+ }
580
598
581
- // This response callback is special, we actually make a number of subsequent requests to
582
- // fetch the file from the remote zip.
583
- let read_metadata_range_request = |response : Response | {
584
- async {
585
- let mut reader = AsyncHttpRangeReader :: from_head_response (
586
- self . uncached_client ( url) . clone ( ) ,
587
- response,
588
- url. clone ( ) ,
589
- headers,
599
+ // This response callback is special, we actually make a number of subsequent requests to
600
+ // fetch the file from the remote zip.
601
+ let read_metadata_range_request = |response : Response | {
602
+ async {
603
+ let mut reader = AsyncHttpRangeReader :: from_head_response (
604
+ self . uncached_client ( url) . clone ( ) ,
605
+ response,
606
+ url. clone ( ) ,
607
+ headers,
608
+ )
609
+ . await
610
+ . map_err ( ErrorKind :: AsyncHttpRangeReader ) ?;
611
+ trace ! ( "Getting metadata for {filename} by range request" ) ;
612
+ let text = wheel_metadata_from_remote_zip ( filename, & mut reader) . await ?;
613
+ let metadata = Metadata23 :: parse_metadata ( text. as_bytes ( ) ) . map_err ( |err| {
614
+ Error :: from ( ErrorKind :: MetadataParseError (
615
+ filename. clone ( ) ,
616
+ url. to_string ( ) ,
617
+ Box :: new ( err) ,
618
+ ) )
619
+ } ) ?;
620
+ Ok :: < Metadata23 , CachedClientError < Error > > ( metadata)
621
+ }
622
+ . boxed_local ( )
623
+ . instrument ( info_span ! ( "read_metadata_range_request" , wheel = %filename) )
624
+ } ;
625
+
626
+ let result = self
627
+ . cached_client ( )
628
+ . get_serde (
629
+ req,
630
+ & cache_entry,
631
+ cache_control,
632
+ read_metadata_range_request,
590
633
)
591
634
. await
592
- . map_err ( ErrorKind :: AsyncHttpRangeReader ) ?;
593
- trace ! ( "Getting metadata for {filename} by range request" ) ;
594
- let text = wheel_metadata_from_remote_zip ( filename, & mut reader) . await ?;
595
- let metadata = Metadata23 :: parse_metadata ( text. as_bytes ( ) ) . map_err ( |err| {
596
- Error :: from ( ErrorKind :: MetadataParseError (
597
- filename. clone ( ) ,
598
- url. to_string ( ) ,
599
- Box :: new ( err) ,
600
- ) )
601
- } ) ?;
602
- Ok :: < Metadata23 , CachedClientError < Error > > ( metadata)
603
- }
604
- . boxed_local ( )
605
- . instrument ( info_span ! ( "read_metadata_range_request" , wheel = %filename) )
606
- } ;
635
+ . map_err ( crate :: Error :: from) ;
607
636
608
- let result = self
609
- . cached_client ( )
610
- . get_serde (
611
- req,
612
- & cache_entry,
613
- cache_control,
614
- read_metadata_range_request,
615
- )
616
- . await
617
- . map_err ( crate :: Error :: from) ;
618
-
619
- match result {
620
- Ok ( metadata) => return Ok ( metadata) ,
621
- Err ( err) => {
622
- if err. is_http_range_requests_unsupported ( ) {
623
- // The range request version failed. Fall back to streaming the file to search
624
- // for the METADATA file.
625
- warn ! ( "Range requests not supported for {filename}; streaming wheel" ) ;
626
- } else {
627
- return Err ( err) ;
637
+ match result {
638
+ Ok ( metadata) => return Ok ( metadata) ,
639
+ Err ( err) => {
640
+ if err. is_http_range_requests_unsupported ( ) {
641
+ // The range request version failed. Fall back to streaming the file to search
642
+ // for the METADATA file.
643
+ warn ! ( "Range requests not supported for {filename}; streaming wheel" ) ;
644
+
645
+ // Mark the index as not supporting range requests.
646
+ if let Some ( index) = index {
647
+ capabilities. set_supports_range_requests ( index. clone ( ) , false ) ;
648
+ }
649
+ } else {
650
+ return Err ( err) ;
651
+ }
628
652
}
629
- }
630
- } ;
653
+ } ;
654
+ }
631
655
632
656
// Create a request to stream the file.
633
657
let req = self
0 commit comments