@@ -6,12 +6,192 @@ use std::path::Path;
6
6
#[ cfg( any( not( feature = "curl-backend" ) , not( feature = "reqwest-rustls-tls" ) , not( feature = "reqwest-native-tls" ) ) ) ]
7
7
use anyhow:: anyhow;
8
8
use anyhow:: { Context , Result } ;
9
+ use sha2:: Sha256 ;
9
10
use thiserror:: Error ;
11
+ #[ cfg( any( feature = "reqwest-rustls-tls" , feature = "reqwest-native-tls" ) ) ]
12
+ use tracing:: info;
10
13
use url:: Url ;
11
14
15
+ use crate :: { errors:: RustupError , process:: Process , utils:: Notification } ;
16
+
12
17
#[ cfg( test) ]
13
18
mod tests;
14
19
20
+ pub ( crate ) async fn download_file (
21
+ url : & Url ,
22
+ path : & Path ,
23
+ hasher : Option < & mut Sha256 > ,
24
+ notify_handler : & dyn Fn ( Notification < ' _ > ) ,
25
+ process : & Process ,
26
+ ) -> Result < ( ) > {
27
+ download_file_with_resume ( url, path, hasher, false , & notify_handler, process) . await
28
+ }
29
+
30
+ pub ( crate ) async fn download_file_with_resume (
31
+ url : & Url ,
32
+ path : & Path ,
33
+ hasher : Option < & mut Sha256 > ,
34
+ resume_from_partial : bool ,
35
+ notify_handler : & dyn Fn ( Notification < ' _ > ) ,
36
+ process : & Process ,
37
+ ) -> Result < ( ) > {
38
+ use crate :: download:: DownloadError as DEK ;
39
+ match download_file_ (
40
+ url,
41
+ path,
42
+ hasher,
43
+ resume_from_partial,
44
+ notify_handler,
45
+ process,
46
+ )
47
+ . await
48
+ {
49
+ Ok ( _) => Ok ( ( ) ) ,
50
+ Err ( e) => {
51
+ if e. downcast_ref :: < std:: io:: Error > ( ) . is_some ( ) {
52
+ return Err ( e) ;
53
+ }
54
+ let is_client_error = match e. downcast_ref :: < DEK > ( ) {
55
+ // Specifically treat the bad partial range error as not our
56
+ // fault in case it was something odd which happened.
57
+ Some ( DEK :: HttpStatus ( 416 ) ) => false ,
58
+ Some ( DEK :: HttpStatus ( 400 ..=499 ) ) | Some ( DEK :: FileNotFound ) => true ,
59
+ _ => false ,
60
+ } ;
61
+ Err ( e) . with_context ( || {
62
+ if is_client_error {
63
+ RustupError :: DownloadNotExists {
64
+ url : url. clone ( ) ,
65
+ path : path. to_path_buf ( ) ,
66
+ }
67
+ } else {
68
+ RustupError :: DownloadingFile {
69
+ url : url. clone ( ) ,
70
+ path : path. to_path_buf ( ) ,
71
+ }
72
+ }
73
+ } )
74
+ }
75
+ }
76
+ }
77
+
78
+ async fn download_file_ (
79
+ url : & Url ,
80
+ path : & Path ,
81
+ hasher : Option < & mut Sha256 > ,
82
+ resume_from_partial : bool ,
83
+ notify_handler : & dyn Fn ( Notification < ' _ > ) ,
84
+ process : & Process ,
85
+ ) -> Result < ( ) > {
86
+ #[ cfg( any( feature = "reqwest-rustls-tls" , feature = "reqwest-native-tls" ) ) ]
87
+ use crate :: download:: { Backend , Event , TlsBackend } ;
88
+ use sha2:: Digest ;
89
+ use std:: cell:: RefCell ;
90
+
91
+ notify_handler ( Notification :: DownloadingFile ( url, path) ) ;
92
+
93
+ let hasher = RefCell :: new ( hasher) ;
94
+
95
+ // This callback will write the download to disk and optionally
96
+ // hash the contents, then forward the notification up the stack
97
+ let callback: & dyn Fn ( Event < ' _ > ) -> anyhow:: Result < ( ) > = & |msg| {
98
+ if let Event :: DownloadDataReceived ( data) = msg {
99
+ if let Some ( h) = hasher. borrow_mut ( ) . as_mut ( ) {
100
+ h. update ( data) ;
101
+ }
102
+ }
103
+
104
+ match msg {
105
+ Event :: DownloadContentLengthReceived ( len) => {
106
+ notify_handler ( Notification :: DownloadContentLengthReceived ( len) ) ;
107
+ }
108
+ Event :: DownloadDataReceived ( data) => {
109
+ notify_handler ( Notification :: DownloadDataReceived ( data) ) ;
110
+ }
111
+ Event :: ResumingPartialDownload => {
112
+ notify_handler ( Notification :: ResumingPartialDownload ) ;
113
+ }
114
+ }
115
+
116
+ Ok ( ( ) )
117
+ } ;
118
+
119
+ // Download the file
120
+
121
+ // Keep the curl env var around for a bit
122
+ let use_curl_backend = process. var_os ( "RUSTUP_USE_CURL" ) . map ( |it| it != "0" ) ;
123
+ let use_rustls = process. var_os ( "RUSTUP_USE_RUSTLS" ) . map ( |it| it != "0" ) ;
124
+
125
+ let backend = match ( use_curl_backend, use_rustls) {
126
+ // If environment specifies a backend that's unavailable, error out
127
+ #[ cfg( not( feature = "reqwest-rustls-tls" ) ) ]
128
+ ( _, Some ( true ) ) => {
129
+ return Err ( anyhow ! (
130
+ "RUSTUP_USE_RUSTLS is set, but this rustup distribution was not built with the reqwest-rustls-tls feature"
131
+ ) ) ;
132
+ }
133
+ #[ cfg( not( feature = "reqwest-native-tls" ) ) ]
134
+ ( _, Some ( false ) ) => {
135
+ return Err ( anyhow ! (
136
+ "RUSTUP_USE_RUSTLS is set to false, but this rustup distribution was not built with the reqwest-native-tls feature"
137
+ ) ) ;
138
+ }
139
+ #[ cfg( not( feature = "curl-backend" ) ) ]
140
+ ( Some ( true ) , _) => {
141
+ return Err ( anyhow ! (
142
+ "RUSTUP_USE_CURL is set, but this rustup distribution was not built with the curl-backend feature"
143
+ ) ) ;
144
+ }
145
+
146
+ // Positive selections, from least preferred to most preferred
147
+ #[ cfg( feature = "curl-backend" ) ]
148
+ ( Some ( true ) , None ) => Backend :: Curl ,
149
+ #[ cfg( feature = "reqwest-native-tls" ) ]
150
+ ( _, Some ( false ) ) => {
151
+ if use_curl_backend == Some ( true ) {
152
+ info ! (
153
+ "RUSTUP_USE_CURL is set and RUSTUP_USE_RUSTLS is set to off, using reqwest with native-tls"
154
+ ) ;
155
+ }
156
+ Backend :: Reqwest ( TlsBackend :: NativeTls )
157
+ }
158
+ #[ cfg( feature = "reqwest-rustls-tls" ) ]
159
+ _ => {
160
+ if use_curl_backend == Some ( true ) {
161
+ info ! (
162
+ "both RUSTUP_USE_CURL and RUSTUP_USE_RUSTLS are set, using reqwest with rustls"
163
+ ) ;
164
+ }
165
+ Backend :: Reqwest ( TlsBackend :: Rustls )
166
+ }
167
+
168
+ // Falling back if only one backend is available
169
+ #[ cfg( all( not( feature = "reqwest-rustls-tls" ) , feature = "reqwest-native-tls" ) ) ]
170
+ _ => Backend :: Reqwest ( TlsBackend :: NativeTls ) ,
171
+ #[ cfg( all(
172
+ not( feature = "reqwest-rustls-tls" ) ,
173
+ not( feature = "reqwest-native-tls" ) ,
174
+ feature = "curl-backend"
175
+ ) ) ]
176
+ _ => Backend :: Curl ,
177
+ } ;
178
+
179
+ notify_handler ( match backend {
180
+ #[ cfg( feature = "curl-backend" ) ]
181
+ Backend :: Curl => Notification :: UsingCurl ,
182
+ #[ cfg( any( feature = "reqwest-rustls-tls" , feature = "reqwest-native-tls" ) ) ]
183
+ Backend :: Reqwest ( _) => Notification :: UsingReqwest ,
184
+ } ) ;
185
+
186
+ let res = backend
187
+ . download_to_path ( url, path, resume_from_partial, Some ( callback) )
188
+ . await ;
189
+
190
+ notify_handler ( Notification :: DownloadFinished ) ;
191
+
192
+ res
193
+ }
194
+
15
195
/// User agent header value for HTTP request.
16
196
/// See: https://github.com/rust-lang/rustup/issues/2860.
17
197
#[ cfg( feature = "curl-backend" ) ]
@@ -29,15 +209,15 @@ const REQWEST_RUSTLS_TLS_USER_AGENT: &str =
29
209
concat ! ( "rustup/" , env!( "CARGO_PKG_VERSION" ) , " (reqwest; rustls)" ) ;
30
210
31
211
#[ derive( Debug , Copy , Clone ) ]
32
- pub enum Backend {
212
+ enum Backend {
33
213
#[ cfg( feature = "curl-backend" ) ]
34
214
Curl ,
35
215
#[ cfg( any( feature = "reqwest-rustls-tls" , feature = "reqwest-native-tls" ) ) ]
36
216
Reqwest ( TlsBackend ) ,
37
217
}
38
218
39
219
impl Backend {
40
- pub async fn download_to_path (
220
+ async fn download_to_path (
41
221
self ,
42
222
url : & Url ,
43
223
path : & Path ,
@@ -171,7 +351,7 @@ impl Backend {
171
351
172
352
#[ cfg( any( feature = "reqwest-rustls-tls" , feature = "reqwest-native-tls" ) ) ]
173
353
#[ derive( Debug , Copy , Clone ) ]
174
- pub enum TlsBackend {
354
+ enum TlsBackend {
175
355
#[ cfg( feature = "reqwest-rustls-tls" ) ]
176
356
Rustls ,
177
357
#[ cfg( feature = "reqwest-native-tls" ) ]
@@ -198,7 +378,7 @@ impl TlsBackend {
198
378
}
199
379
200
380
#[ derive( Debug , Copy , Clone ) ]
201
- pub enum Event < ' a > {
381
+ enum Event < ' a > {
202
382
ResumingPartialDownload ,
203
383
/// Received the Content-Length of the to-be downloaded data.
204
384
DownloadContentLengthReceived ( u64 ) ,
@@ -211,7 +391,7 @@ type DownloadCallback<'a> = &'a dyn Fn(Event<'_>) -> Result<()>;
211
391
/// Download via libcurl; encrypt with the native (or OpenSSl) TLS
212
392
/// stack via libcurl
213
393
#[ cfg( feature = "curl-backend" ) ]
214
- pub mod curl {
394
+ mod curl {
215
395
use std:: cell:: RefCell ;
216
396
use std:: str;
217
397
use std:: time:: Duration ;
@@ -222,7 +402,7 @@ pub mod curl {
222
402
223
403
use super :: { DownloadError , Event } ;
224
404
225
- pub fn download (
405
+ pub ( super ) fn download (
226
406
url : & Url ,
227
407
resume_from : u64 ,
228
408
callback : & dyn Fn ( Event < ' _ > ) -> Result < ( ) > ,
@@ -323,7 +503,7 @@ pub mod curl {
323
503
}
324
504
325
505
#[ cfg( any( feature = "reqwest-rustls-tls" , feature = "reqwest-native-tls" ) ) ]
326
- pub mod reqwest_be {
506
+ mod reqwest_be {
327
507
use std:: io;
328
508
#[ cfg( feature = "reqwest-rustls-tls" ) ]
329
509
use std:: sync:: Arc ;
@@ -342,7 +522,7 @@ pub mod reqwest_be {
342
522
343
523
use super :: { DownloadError , Event } ;
344
524
345
- pub async fn download (
525
+ pub ( super ) async fn download (
346
526
url : & Url ,
347
527
resume_from : u64 ,
348
528
callback : & dyn Fn ( Event < ' _ > ) -> Result < ( ) > ,
@@ -487,7 +667,7 @@ pub mod reqwest_be {
487
667
}
488
668
489
669
#[ derive( Debug , Error ) ]
490
- pub enum DownloadError {
670
+ enum DownloadError {
491
671
#[ error( "http request returned an unsuccessful status code: {0}" ) ]
492
672
HttpStatus ( u32 ) ,
493
673
#[ error( "file not found" ) ]
0 commit comments