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