fix(upload,download): refresh access token before each parallel transfer task
The access token obtained at command startup could expire during a long transfer session (e.g. uploading thousands of files or large files), causing subsequent requests to fail with 401 Unauthorized. Root cause: load_cache_with_token_refresh was called only once, and the resulting MDRSConnection — including its now-stale token — was shared across all parallel tasks via Arc. There was no mechanism to update the token in the shared instance after creation. Fix: - Add MDRSConnection::with_token(&self, token) that creates a new connection struct reusing the caller's HTTP client (cheap Arc clone, shares the connection pool) but carrying a fresh Bearer token. - In upload.rs and download.rs, call load_cache_with_token_refresh inside each tokio::spawn task body, then create a task-local connection via conn.with_token(fresh_token) before transferring the file. The shared reqwest::Client (connection pool) is preserved. cp.rs is not changed: it uses only short server-side API calls with no parallel tasks, so token expiry during a cp operation is negligible. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -110,9 +110,17 @@ pub async fn download(
|
||||
}
|
||||
let url = make_absolute_url(&conn, &f.download_url);
|
||||
let conn = conn.clone();
|
||||
let remote = remote.clone();
|
||||
futs.push(tokio::spawn(async move {
|
||||
// Refresh the access token if it has expired or is about to
|
||||
// expire. conn.with_token() reuses the shared HTTP client
|
||||
// (connection pool) while supplying a fresh Bearer token.
|
||||
let task_conn = match load_cache_with_token_refresh(&remote).await {
|
||||
Ok(c) => conn.with_token(c.token.access),
|
||||
Err(e) => { eprintln!("Error: {}", e); return; }
|
||||
};
|
||||
let dest_str = dest_path.to_string_lossy().to_string();
|
||||
match conn.download_file(&url, &dest_str).await {
|
||||
match task_conn.download_file(&url, &dest_str).await {
|
||||
Ok(_) => println!("{}", dest_path.display()),
|
||||
Err(_) => {
|
||||
eprintln!("Failed: {}", dest_path.display());
|
||||
|
||||
@@ -102,8 +102,16 @@ pub async fn upload(
|
||||
let folder_id = remote_id.clone();
|
||||
let remote_path_prefix = folder_detail.path.clone();
|
||||
let fname = filename.clone();
|
||||
let remote = remote.clone();
|
||||
futs.push(tokio::spawn(async move {
|
||||
match conn.upload_file(&folder_id, &file_path_str).await {
|
||||
// Refresh the access token if it has expired or is about to
|
||||
// expire. conn.with_token() reuses the shared HTTP client
|
||||
// (connection pool) while supplying a fresh Bearer token.
|
||||
let task_conn = match load_cache_with_token_refresh(&remote).await {
|
||||
Ok(c) => conn.with_token(c.token.access),
|
||||
Err(e) => { eprintln!("Error: {}", e); return; }
|
||||
};
|
||||
match task_conn.upload_file(&folder_id, &file_path_str).await {
|
||||
Ok(_) => println!("{}{}", remote_path_prefix, fname),
|
||||
Err(e) => eprintln!("Error: {}", e),
|
||||
}
|
||||
|
||||
@@ -41,6 +41,21 @@ impl MDRSConnection {
|
||||
}
|
||||
}
|
||||
|
||||
/// Create a new connection that shares the HTTP client (and its connection
|
||||
/// pool) with the receiver but uses a fresh access token. Useful for
|
||||
/// spawning per-task connections without allocating a new connection pool
|
||||
/// for every concurrent task.
|
||||
///
|
||||
/// `reqwest::Client` wraps an internal `Arc`; cloning it is cheap and
|
||||
/// keeps the shared pool intact.
|
||||
pub fn with_token(&self, access_token: String) -> Self {
|
||||
MDRSConnection {
|
||||
url: self.url.clone(),
|
||||
client: self.client.clone(),
|
||||
token: Some(access_token),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn build_url(&self, path: &str) -> String {
|
||||
format!("{}/{}", self.url.trim_end_matches('/'), path)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user