opendal 0.1.11 → 0.1.13

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (248) hide show
  1. checksums.yaml +4 -4
  2. data/Cargo.lock +31 -36
  3. data/Cargo.toml +1 -1
  4. data/DEPENDENCIES.rust.tsv +25 -35
  5. data/README.md +1 -1
  6. data/core/CHANGELOG.md +22 -0
  7. data/core/Cargo.lock +145 -189
  8. data/core/Cargo.toml +95 -95
  9. data/core/DEPENDENCIES.rust.tsv +259 -290
  10. data/core/LICENSE +16 -0
  11. data/core/benches/vs_fs/Cargo.toml +1 -1
  12. data/core/benches/vs_s3/Cargo.toml +1 -1
  13. data/core/core/CHANGELOG.md +6031 -0
  14. data/core/core/Cargo.toml +1 -1
  15. data/core/core/LICENSE +16 -0
  16. data/core/core/src/docs/mod.rs +1 -1
  17. data/core/core/src/docs/upgrade.md +8 -0
  18. data/core/core/src/lib.rs +1 -3
  19. data/core/core/src/raw/futures_util.rs +26 -21
  20. data/core/core/src/raw/http_util/error.rs +9 -0
  21. data/core/core/src/raw/ops.rs +1 -0
  22. data/core/core/src/types/http_transport/mod.rs +30 -2
  23. data/core/core/src/types/http_transport/uri.rs +166 -0
  24. data/core/core/src/types/metadata.rs +1 -0
  25. data/core/core/src/types/mod.rs +2 -0
  26. data/core/http-transports/reqwest/Cargo.toml +1 -1
  27. data/core/http-transports/reqwest/LICENSE +16 -0
  28. data/core/http-transports/reqwest/src/lib.rs +46 -4
  29. data/core/layers/async-backtrace/Cargo.toml +2 -2
  30. data/core/layers/async-backtrace/LICENSE +16 -0
  31. data/core/layers/await-tree/Cargo.toml +2 -2
  32. data/core/layers/await-tree/LICENSE +16 -0
  33. data/core/layers/capability-check/Cargo.toml +2 -2
  34. data/core/layers/capability-check/LICENSE +16 -0
  35. data/core/layers/chaos/Cargo.toml +2 -2
  36. data/core/layers/chaos/LICENSE +16 -0
  37. data/core/layers/concurrent-limit/Cargo.toml +3 -3
  38. data/core/layers/concurrent-limit/LICENSE +16 -0
  39. data/core/layers/dtrace/Cargo.toml +2 -2
  40. data/core/layers/dtrace/LICENSE +16 -0
  41. data/core/layers/fastmetrics/Cargo.toml +3 -3
  42. data/core/layers/fastmetrics/LICENSE +16 -0
  43. data/core/layers/fastrace/Cargo.toml +2 -2
  44. data/core/layers/fastrace/LICENSE +16 -0
  45. data/core/layers/foyer/Cargo.toml +2 -2
  46. data/core/layers/foyer/LICENSE +16 -0
  47. data/core/layers/hotpath/Cargo.toml +2 -2
  48. data/core/layers/hotpath/LICENSE +16 -0
  49. data/core/layers/immutable-index/Cargo.toml +2 -2
  50. data/core/layers/immutable-index/LICENSE +16 -0
  51. data/core/layers/logging/Cargo.toml +2 -2
  52. data/core/layers/logging/LICENSE +16 -0
  53. data/core/layers/metrics/Cargo.toml +3 -3
  54. data/core/layers/metrics/LICENSE +16 -0
  55. data/core/layers/mime-guess/Cargo.toml +2 -2
  56. data/core/layers/mime-guess/LICENSE +16 -0
  57. data/core/layers/observe-metrics-common/Cargo.toml +1 -1
  58. data/core/layers/observe-metrics-common/LICENSE +16 -0
  59. data/core/layers/otelmetrics/Cargo.toml +3 -3
  60. data/core/layers/otelmetrics/LICENSE +16 -0
  61. data/core/layers/oteltrace/Cargo.toml +2 -2
  62. data/core/layers/oteltrace/LICENSE +16 -0
  63. data/core/layers/prometheus/Cargo.toml +3 -3
  64. data/core/layers/prometheus/LICENSE +16 -0
  65. data/core/layers/prometheus-client/Cargo.toml +3 -3
  66. data/core/layers/prometheus-client/LICENSE +16 -0
  67. data/core/layers/retry/Cargo.toml +4 -4
  68. data/core/layers/retry/LICENSE +16 -0
  69. data/core/layers/route/Cargo.toml +3 -3
  70. data/core/layers/route/LICENSE +16 -0
  71. data/core/layers/tail-cut/Cargo.toml +2 -2
  72. data/core/layers/tail-cut/LICENSE +16 -0
  73. data/core/layers/throttle/Cargo.toml +2 -2
  74. data/core/layers/throttle/LICENSE +16 -0
  75. data/core/layers/timeout/Cargo.toml +3 -3
  76. data/core/layers/timeout/LICENSE +16 -0
  77. data/core/layers/tracing/Cargo.toml +2 -2
  78. data/core/layers/tracing/LICENSE +16 -0
  79. data/core/services/aliyun-drive/Cargo.toml +2 -2
  80. data/core/services/aliyun-drive/LICENSE +16 -0
  81. data/core/services/alluxio/Cargo.toml +1 -1
  82. data/core/services/alluxio/LICENSE +16 -0
  83. data/core/services/azblob/Cargo.toml +2 -2
  84. data/core/services/azblob/LICENSE +16 -0
  85. data/core/services/azblob/src/backend.rs +21 -7
  86. data/core/services/azblob/src/config.rs +4 -1
  87. data/core/services/azdls/Cargo.toml +3 -3
  88. data/core/services/azdls/LICENSE +16 -0
  89. data/core/services/azfile/Cargo.toml +2 -2
  90. data/core/services/azfile/LICENSE +16 -0
  91. data/core/services/azure-common/Cargo.toml +1 -1
  92. data/core/services/azure-common/LICENSE +16 -0
  93. data/core/services/b2/Cargo.toml +2 -2
  94. data/core/services/b2/LICENSE +16 -0
  95. data/core/services/cacache/Cargo.toml +1 -1
  96. data/core/services/cacache/LICENSE +16 -0
  97. data/core/services/cloudflare-kv/Cargo.toml +1 -1
  98. data/core/services/cloudflare-kv/LICENSE +16 -0
  99. data/core/services/compfs/Cargo.toml +1 -1
  100. data/core/services/compfs/LICENSE +16 -0
  101. data/core/services/compfs/src/core.rs +27 -11
  102. data/core/services/cos/Cargo.toml +1 -1
  103. data/core/services/cos/LICENSE +16 -0
  104. data/core/services/d1/Cargo.toml +1 -1
  105. data/core/services/d1/LICENSE +16 -0
  106. data/core/services/dashmap/Cargo.toml +1 -1
  107. data/core/services/dashmap/LICENSE +16 -0
  108. data/core/services/dbfs/Cargo.toml +1 -1
  109. data/core/services/dbfs/LICENSE +16 -0
  110. data/core/services/dropbox/Cargo.toml +2 -2
  111. data/core/services/dropbox/LICENSE +16 -0
  112. data/core/services/etcd/Cargo.toml +2 -2
  113. data/core/services/etcd/LICENSE +16 -0
  114. data/core/services/etcd/src/core.rs +3 -3
  115. data/core/services/foundationdb/Cargo.toml +1 -1
  116. data/core/services/foundationdb/LICENSE +16 -0
  117. data/core/services/foyer/Cargo.toml +3 -3
  118. data/core/services/foyer/LICENSE +16 -0
  119. data/core/services/fs/Cargo.toml +1 -1
  120. data/core/services/fs/LICENSE +16 -0
  121. data/core/services/fs/src/core.rs +69 -10
  122. data/core/services/ftp/Cargo.toml +2 -2
  123. data/core/services/ftp/LICENSE +16 -0
  124. data/core/services/ftp/src/core.rs +1 -1
  125. data/core/services/ftp/src/reader.rs +1 -1
  126. data/core/services/ftp/src/writer.rs +1 -1
  127. data/core/services/gcs/Cargo.toml +3 -2
  128. data/core/services/gcs/LICENSE +16 -0
  129. data/core/services/gcs/src/composer.rs +11 -21
  130. data/core/services/gcs/src/core.rs +116 -2
  131. data/core/services/gcs-grpc/Cargo.toml +1 -1
  132. data/core/services/gcs-grpc/proto/google/storage/v2/storage.proto +17 -18
  133. data/core/services/gdrive/Cargo.toml +3 -2
  134. data/core/services/gdrive/LICENSE +16 -0
  135. data/core/services/gdrive/src/docs.md +2 -0
  136. data/core/services/gdrive/src/path_index.rs +109 -18
  137. data/core/services/ghac/Cargo.toml +2 -2
  138. data/core/services/ghac/LICENSE +16 -0
  139. data/core/services/github/Cargo.toml +1 -1
  140. data/core/services/github/LICENSE +16 -0
  141. data/core/services/goosefs/Cargo.toml +13 -4
  142. data/core/services/goosefs/LICENSE +16 -0
  143. data/core/services/goosefs/README.md +11 -0
  144. data/core/services/goosefs/src/backend.rs +376 -49
  145. data/core/services/goosefs/src/config.rs +18 -10
  146. data/core/services/goosefs/src/core.rs +1 -1
  147. data/core/services/goosefs/src/docs.md +72 -3
  148. data/core/services/goosefs/src/reader.rs +22 -89
  149. data/core/services/goosefs/tests/master_addr_resolution.rs +103 -0
  150. data/core/services/gridfs/Cargo.toml +2 -2
  151. data/core/services/gridfs/LICENSE +16 -0
  152. data/core/services/hdfs/Cargo.toml +1 -1
  153. data/core/services/hdfs/LICENSE +16 -0
  154. data/core/services/hdfs-native/Cargo.toml +1 -1
  155. data/core/services/hdfs-native/LICENSE +16 -0
  156. data/core/services/hf/Cargo.toml +3 -3
  157. data/core/services/hf/LICENSE +16 -0
  158. data/core/services/hf/src/backend.rs +77 -9
  159. data/core/services/hf/src/config.rs +58 -0
  160. data/core/services/hf/src/core.rs +695 -318
  161. data/core/services/hf/src/docs.md +29 -0
  162. data/core/services/hf/src/lister.rs +3 -3
  163. data/core/services/hf/src/reader.rs +128 -143
  164. data/core/services/http/Cargo.toml +3 -3
  165. data/core/services/http/LICENSE +16 -0
  166. data/core/services/ipfs/Cargo.toml +1 -1
  167. data/core/services/ipfs/LICENSE +16 -0
  168. data/core/services/ipmfs/Cargo.toml +1 -1
  169. data/core/services/ipmfs/LICENSE +16 -0
  170. data/core/services/koofr/Cargo.toml +2 -2
  171. data/core/services/koofr/LICENSE +16 -0
  172. data/core/services/lakefs/Cargo.toml +1 -1
  173. data/core/services/lakefs/LICENSE +16 -0
  174. data/core/services/memcached/Cargo.toml +2 -2
  175. data/core/services/memcached/LICENSE +16 -0
  176. data/core/services/memcached/src/core.rs +3 -3
  177. data/core/services/mini_moka/Cargo.toml +1 -1
  178. data/core/services/mini_moka/LICENSE +16 -0
  179. data/core/services/moka/Cargo.toml +1 -1
  180. data/core/services/moka/LICENSE +16 -0
  181. data/core/services/mongodb/Cargo.toml +2 -2
  182. data/core/services/mongodb/LICENSE +16 -0
  183. data/core/services/monoiofs/Cargo.toml +2 -2
  184. data/core/services/monoiofs/LICENSE +16 -0
  185. data/core/services/monoiofs/src/core.rs +27 -11
  186. data/core/services/mysql/Cargo.toml +2 -2
  187. data/core/services/mysql/LICENSE +16 -0
  188. data/core/services/obs/Cargo.toml +1 -1
  189. data/core/services/obs/LICENSE +16 -0
  190. data/core/services/onedrive/Cargo.toml +2 -2
  191. data/core/services/onedrive/LICENSE +16 -0
  192. data/core/services/opfs/Cargo.toml +1 -1
  193. data/core/services/opfs/LICENSE +16 -0
  194. data/core/services/oss/Cargo.toml +1 -1
  195. data/core/services/oss/LICENSE +16 -0
  196. data/core/services/pcloud/Cargo.toml +1 -1
  197. data/core/services/pcloud/LICENSE +16 -0
  198. data/core/services/persy/Cargo.toml +1 -1
  199. data/core/services/persy/LICENSE +16 -0
  200. data/core/services/postgresql/Cargo.toml +2 -2
  201. data/core/services/postgresql/LICENSE +16 -0
  202. data/core/services/redb/Cargo.toml +1 -1
  203. data/core/services/redb/LICENSE +16 -0
  204. data/core/services/redis/Cargo.toml +2 -2
  205. data/core/services/redis/LICENSE +16 -0
  206. data/core/services/redis/src/core.rs +1 -1
  207. data/core/services/rocksdb/Cargo.toml +1 -1
  208. data/core/services/rocksdb/LICENSE +16 -0
  209. data/core/services/s3/Cargo.toml +1 -1
  210. data/core/services/s3/LICENSE +16 -0
  211. data/core/services/seafile/Cargo.toml +4 -2
  212. data/core/services/seafile/LICENSE +16 -0
  213. data/core/services/seafile/src/backend.rs +2 -3
  214. data/core/services/seafile/src/core.rs +189 -86
  215. data/core/services/seafile/src/docs.md +2 -0
  216. data/core/services/sftp/Cargo.toml +2 -2
  217. data/core/services/sftp/LICENSE +16 -0
  218. data/core/services/sftp/src/core.rs +1 -1
  219. data/core/services/sftp/src/reader.rs +1 -1
  220. data/core/services/sled/Cargo.toml +1 -1
  221. data/core/services/sled/LICENSE +16 -0
  222. data/core/services/sqlite/Cargo.toml +2 -2
  223. data/core/services/sqlite/LICENSE +16 -0
  224. data/core/services/surrealdb/Cargo.toml +2 -2
  225. data/core/services/surrealdb/LICENSE +16 -0
  226. data/core/services/swift/Cargo.toml +1 -1
  227. data/core/services/swift/LICENSE +16 -0
  228. data/core/services/tikv/Cargo.toml +2 -2
  229. data/core/services/tikv/LICENSE +16 -0
  230. data/core/services/tos/Cargo.toml +1 -1
  231. data/core/services/tos/LICENSE +16 -0
  232. data/core/services/upyun/Cargo.toml +1 -1
  233. data/core/services/upyun/LICENSE +16 -0
  234. data/core/services/vercel-artifacts/Cargo.toml +1 -1
  235. data/core/services/vercel-artifacts/LICENSE +16 -0
  236. data/core/services/vercel-blob/Cargo.toml +1 -1
  237. data/core/services/vercel-blob/LICENSE +16 -0
  238. data/core/services/webdav/Cargo.toml +1 -2
  239. data/core/services/webdav/LICENSE +16 -0
  240. data/core/services/webhdfs/Cargo.toml +2 -2
  241. data/core/services/webhdfs/LICENSE +16 -0
  242. data/core/services/yandex-disk/Cargo.toml +1 -1
  243. data/core/services/yandex-disk/LICENSE +16 -0
  244. data/core/src/lib.rs +1 -3
  245. data/core/testkit/Cargo.toml +4 -4
  246. data/core/testkit/LICENSE +16 -0
  247. data/core/upgrade.md +8 -0
  248. metadata +5 -2
@@ -199,3 +199,19 @@
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
201
  limitations under the License.
202
+
203
+
204
+ ------------------------------------------------------------------------------------
205
+ This product bundles third-party components under other open source licenses.
206
+ This section summarizes those components and their licenses. See the referenced
207
+ license files for text of these licenses.
208
+
209
+
210
+ Creative Commons Zero v1.0 Universal
211
+ ------------------------------------
212
+
213
+ bindings/dotnet/.gitignore
214
+
215
+ The above file is derived from VisualStudio.gitignore in
216
+ https://github.com/github/gitignore. Its license text is in
217
+ bindings/dotnet/LICENSE-CC0-1.0.txt.
@@ -32,9 +32,9 @@ version = { workspace = true }
32
32
  all-features = true
33
33
 
34
34
  [dependencies]
35
- opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
35
+ opendal-core = { path = "../../core", version = "0.59.2", default-features = false }
36
36
 
37
- asyncband = { workspace = true }
37
+ asyncband = { workspace = true, features = ["once-cell"] }
38
38
  bytes = { workspace = true }
39
39
  http = { workspace = true }
40
40
  log = { workspace = true }
@@ -42,4 +42,6 @@ serde = { workspace = true, features = ["derive"] }
42
42
  serde_json = { workspace = true }
43
43
 
44
44
  [dev-dependencies]
45
+ asyncband = { workspace = true, features = ["mutex", "semaphore"] }
46
+ futures = { workspace = true }
45
47
  tokio = { workspace = true, features = ["macros", "rt-multi-thread"] }
@@ -199,3 +199,19 @@
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
201
  limitations under the License.
202
+
203
+
204
+ ------------------------------------------------------------------------------------
205
+ This product bundles third-party components under other open source licenses.
206
+ This section summarizes those components and their licenses. See the referenced
207
+ license files for text of these licenses.
208
+
209
+
210
+ Creative Commons Zero v1.0 Universal
211
+ ------------------------------------
212
+
213
+ bindings/dotnet/.gitignore
214
+
215
+ The above file is derived from VisualStudio.gitignore in
216
+ https://github.com/github/gitignore. Its license text is in
217
+ bindings/dotnet/LICENSE-CC0-1.0.txt.
@@ -18,13 +18,12 @@
18
18
  use std::fmt::Debug;
19
19
  use std::sync::Arc;
20
20
 
21
- use asyncband::rwlock::RwLock;
21
+ use asyncband::once::OnceCell;
22
22
  use log::debug;
23
23
 
24
24
  use super::SEAFILE_SCHEME;
25
25
  use super::config::SeafileConfig;
26
26
  use super::core::SeafileCore;
27
- use super::core::SeafileSigner;
28
27
  use super::core::parse_dir_detail;
29
28
  use super::core::parse_file_detail;
30
29
  use super::deleter::SeafileDeleter;
@@ -178,7 +177,7 @@ impl Builder for SeafileBuilder {
178
177
  username,
179
178
  password,
180
179
  repo_name: self.config.repo_name.clone(),
181
- signer: Arc::new(RwLock::new(SeafileSigner::default())),
180
+ auth_info: Arc::new(OnceCell::new()),
182
181
  }),
183
182
  })
184
183
  }
@@ -18,7 +18,7 @@
18
18
  use std::fmt::Debug;
19
19
  use std::sync::Arc;
20
20
 
21
- use asyncband::rwlock::RwLock;
21
+ use asyncband::once::OnceCell;
22
22
  use bytes::Buf;
23
23
  use bytes::Bytes;
24
24
  use http::Request;
@@ -46,8 +46,8 @@ pub struct SeafileCore {
46
46
  /// The repo name of this backend.
47
47
  pub repo_name: String,
48
48
 
49
- /// signer of this backend.
50
- pub signer: Arc<RwLock<SeafileSigner>>,
49
+ /// Authentication and library information published after both requests succeed.
50
+ pub auth_info: Arc<OnceCell<AuthInfo>>,
51
51
  }
52
52
 
53
53
  impl Debug for SeafileCore {
@@ -71,96 +71,85 @@ impl SeafileCore {
71
71
  ctx.http_transport().send(req).await
72
72
  }
73
73
 
74
- /// get auth info
74
+ /// Initialize authentication and library information, retrying incomplete attempts.
75
75
  pub async fn get_auth_info(&self, ctx: &OperationContext) -> Result<AuthInfo> {
76
- {
77
- let signer = self.signer.read().await;
76
+ let auth_info = self
77
+ .auth_info
78
+ .get_or_try_init(async || {
79
+ let body = format!(
80
+ "username={}&password={}",
81
+ percent_encode_path(&self.username),
82
+ percent_encode_path(&self.password)
83
+ );
84
+ let req = Request::post(format!("{}/api2/auth-token/", self.endpoint))
85
+ .header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
86
+ .body(Buffer::from(Bytes::from(body)))
87
+ .map_err(new_request_build_error)?;
88
+
89
+ let resp = ctx.http_transport().send(req).await?;
90
+ let status = resp.status();
91
+
92
+ let token = match status {
93
+ StatusCode::OK => {
94
+ let resp_body = resp.into_body();
95
+ let auth_response: AuthTokenResponse =
96
+ serde_json::from_reader(resp_body.reader())
97
+ .map_err(new_json_deserialize_error)?;
98
+ auth_response.token
99
+ }
100
+ _ => {
101
+ return Err(parse_error(
102
+ ErrorContext::new(ServiceOperation("AuthToken")),
103
+ resp,
104
+ ));
105
+ }
106
+ };
78
107
 
79
- if !signer.auth_info.token.is_empty() {
80
- let auth_info = signer.auth_info.clone();
81
- return Ok(auth_info.clone());
82
- }
83
- }
108
+ let url = format!("{}/api2/repos", self.endpoint);
84
109
 
85
- {
86
- let mut signer = self.signer.write().await;
87
- let body = format!(
88
- "username={}&password={}",
89
- percent_encode_path(&self.username),
90
- percent_encode_path(&self.password)
91
- );
92
- let req = Request::post(format!("{}/api2/auth-token/", self.endpoint))
93
- .header(header::CONTENT_TYPE, "application/x-www-form-urlencoded")
94
- .body(Buffer::from(Bytes::from(body)))
95
- .map_err(new_request_build_error)?;
96
-
97
- let resp = ctx.http_transport().send(req).await?;
98
- let status = resp.status();
99
-
100
- match status {
101
- StatusCode::OK => {
102
- let resp_body = resp.into_body();
103
- let auth_response: AuthTokenResponse =
104
- serde_json::from_reader(resp_body.reader())
105
- .map_err(new_json_deserialize_error)?;
106
- signer.auth_info = AuthInfo {
107
- token: auth_response.token,
108
- repo_id: "".to_string(),
109
- };
110
- }
111
- _ => {
112
- return Err(parse_error(
113
- ErrorContext::new(ServiceOperation("AuthToken")),
114
- resp,
115
- ));
116
- }
117
- }
118
-
119
- let url = format!("{}/api2/repos", self.endpoint);
110
+ let req = Request::get(url)
111
+ .header(header::AUTHORIZATION, format!("Token {token}"))
112
+ .body(Buffer::new())
113
+ .map_err(new_request_build_error)?;
120
114
 
121
- let req = Request::get(url)
122
- .header(
123
- header::AUTHORIZATION,
124
- format!("Token {}", signer.auth_info.token),
125
- )
126
- .body(Buffer::new())
127
- .map_err(new_request_build_error)?;
115
+ let resp = ctx.http_transport().send(req).await?;
128
116
 
129
- let resp = ctx.http_transport().send(req).await?;
117
+ let status = resp.status();
130
118
 
131
- let status = resp.status();
119
+ let mut repo_id = String::new();
120
+ match status {
121
+ StatusCode::OK => {
122
+ let resp_body = resp.into_body();
123
+ let list_library_response: Vec<ListLibraryResponse> =
124
+ serde_json::from_reader(resp_body.reader())
125
+ .map_err(new_json_deserialize_error)?;
132
126
 
133
- match status {
134
- StatusCode::OK => {
135
- let resp_body = resp.into_body();
136
- let list_library_response: Vec<ListLibraryResponse> =
137
- serde_json::from_reader(resp_body.reader())
138
- .map_err(new_json_deserialize_error)?;
127
+ for library in list_library_response {
128
+ if library.name == self.repo_name {
129
+ repo_id = library.id;
130
+ break;
131
+ }
132
+ }
139
133
 
140
- for library in list_library_response {
141
- if library.name == self.repo_name {
142
- signer.auth_info.repo_id = library.id;
143
- break;
134
+ // repo not found
135
+ if repo_id.is_empty() {
136
+ return Err(Error::new(
137
+ ErrorKind::NotFound,
138
+ format!("repo {} not found", self.repo_name),
139
+ ));
144
140
  }
145
141
  }
146
-
147
- // repo not found
148
- if signer.auth_info.repo_id.is_empty() {
149
- return Err(Error::new(
150
- ErrorKind::NotFound,
151
- format!("repo {} not found", self.repo_name),
142
+ _ => {
143
+ return Err(parse_error(
144
+ ErrorContext::new(ServiceOperation("ListRepositories")),
145
+ resp,
152
146
  ));
153
147
  }
154
148
  }
155
- _ => {
156
- return Err(parse_error(
157
- ErrorContext::new(ServiceOperation("ListRepositories")),
158
- resp,
159
- ));
160
- }
161
- }
162
- Ok(signer.auth_info.clone())
163
- }
149
+ Ok(AuthInfo { repo_id, token })
150
+ })
151
+ .await?;
152
+ Ok(auth_info.clone())
164
153
  }
165
154
  }
166
155
 
@@ -520,12 +509,7 @@ pub fn parse_file_detail(file_detail: FileDetail) -> Result<Metadata> {
520
509
  Ok(md.build())
521
510
  }
522
511
 
523
- #[derive(Clone, Default)]
524
- pub struct SeafileSigner {
525
- pub auth_info: AuthInfo,
526
- }
527
-
528
- #[derive(Clone, Default)]
512
+ #[derive(Clone)]
529
513
  pub struct AuthInfo {
530
514
  /// The repo id of this auth info.
531
515
  pub repo_id: String,
@@ -598,10 +582,129 @@ pub(crate) fn parse_error(ctx: ErrorContext, resp: Response<Buffer>) -> Error {
598
582
 
599
583
  #[cfg(test)]
600
584
  mod tests {
585
+ use std::collections::VecDeque;
586
+ use std::future::Future;
587
+ use std::task::Context;
588
+ use std::task::Waker;
589
+
590
+ use asyncband::mutex::Mutex;
591
+ use asyncband::semaphore::Semaphore;
601
592
  use http::StatusCode;
602
593
 
603
594
  use super::*;
604
595
 
596
+ #[derive(Clone)]
597
+ struct AuthTransport {
598
+ responses: Arc<Mutex<VecDeque<(&'static str, &'static str)>>>,
599
+ repositories: Arc<Semaphore>,
600
+ }
601
+
602
+ impl AuthTransport {
603
+ fn new(responses: &[(&'static str, &'static str)]) -> Self {
604
+ Self {
605
+ responses: Arc::new(Mutex::new(responses.iter().copied().collect())),
606
+ repositories: Arc::new(Semaphore::new(0)),
607
+ }
608
+ }
609
+ }
610
+
611
+ impl HttpTransport for AuthTransport {
612
+ async fn fetch(&self, req: Request<Buffer>) -> Result<Response<HttpBody>> {
613
+ let (path, body) = self.responses.lock().await.pop_front().unwrap();
614
+ assert_eq!(req.uri().path(), path);
615
+ if path == "/api2/repos" {
616
+ drop(self.repositories.acquire(1).await);
617
+ }
618
+ Ok(Response::builder()
619
+ .body(HttpBody::new(
620
+ futures::stream::iter([Ok(Buffer::from(Bytes::from_static(body.as_bytes())))]),
621
+ Some(body.len() as u64),
622
+ ))
623
+ .unwrap())
624
+ }
625
+ }
626
+
627
+ fn auth_core(transport: AuthTransport) -> (SeafileCore, OperationContext) {
628
+ let core = SeafileCore {
629
+ info: ServiceInfo::new("seafile", "/", ""),
630
+ capability: Capability::default(),
631
+ root: "/".to_string(),
632
+ endpoint: "http://example.com".to_string(),
633
+ username: "user".to_string(),
634
+ password: "password".to_string(),
635
+ repo_name: "test".to_string(),
636
+ auth_info: Arc::new(OnceCell::new()),
637
+ };
638
+ let ctx = OperationContext::new().with_http_transport(HttpTransporter::new(transport));
639
+ (core, ctx)
640
+ }
641
+
642
+ #[tokio::test]
643
+ async fn test_auth_info_retries_after_missing_repository() {
644
+ let transport = AuthTransport::new(&[
645
+ ("/api2/auth-token/", r#"{"token":"first"}"#),
646
+ ("/api2/repos", "[]"),
647
+ ("/api2/auth-token/", r#"{"token":"second"}"#),
648
+ ("/api2/repos", r#"[{"name":"test","id":"repo-id"}]"#),
649
+ ]);
650
+ transport.repositories.release(1);
651
+ let (core, ctx) = auth_core(transport.clone());
652
+
653
+ let err = core.get_auth_info(&ctx).await.err().unwrap();
654
+ assert_eq!(err.kind(), ErrorKind::NotFound);
655
+ let auth = core.get_auth_info(&ctx).await.unwrap();
656
+ assert_eq!(auth.repo_id, "repo-id");
657
+ assert_eq!(auth.token, "second");
658
+ assert!(transport.responses.lock().await.is_empty());
659
+ }
660
+
661
+ #[tokio::test]
662
+ async fn test_auth_info_retries_after_initialization_is_cancelled() {
663
+ let transport = AuthTransport::new(&[
664
+ ("/api2/auth-token/", r#"{"token":"first"}"#),
665
+ ("/api2/repos", r#"[{"name":"test","id":"repo-id"}]"#),
666
+ ("/api2/auth-token/", r#"{"token":"second"}"#),
667
+ ("/api2/repos", r#"[{"name":"test","id":"repo-id"}]"#),
668
+ ]);
669
+ let (core, ctx) = auth_core(transport.clone());
670
+ let mut first = Box::pin(core.get_auth_info(&ctx));
671
+ let mut cx = Context::from_waker(Waker::noop());
672
+ assert!(first.as_mut().poll(&mut cx).is_pending());
673
+ drop(first);
674
+
675
+ transport.repositories.release(1);
676
+ let auth = core.get_auth_info(&ctx).await.unwrap();
677
+ assert_eq!(auth.repo_id, "repo-id");
678
+ assert_eq!(auth.token, "second");
679
+ assert!(transport.responses.lock().await.is_empty());
680
+ }
681
+
682
+ #[tokio::test]
683
+ async fn test_auth_info_shares_complete_initialization() {
684
+ let transport = AuthTransport::new(&[
685
+ ("/api2/auth-token/", r#"{"token":"shared"}"#),
686
+ ("/api2/repos", r#"[{"name":"test","id":"repo-id"}]"#),
687
+ ]);
688
+ let (core, ctx) = auth_core(transport.clone());
689
+ let mut first = Box::pin(core.get_auth_info(&ctx));
690
+ let mut second = Box::pin(core.get_auth_info(&ctx));
691
+ let mut cancelled = Box::pin(core.get_auth_info(&ctx));
692
+ let mut cx = Context::from_waker(Waker::noop());
693
+ assert!(first.as_mut().poll(&mut cx).is_pending());
694
+ assert!(second.as_mut().poll(&mut cx).is_pending());
695
+ assert!(cancelled.as_mut().poll(&mut cx).is_pending());
696
+ drop(cancelled);
697
+
698
+ transport.repositories.release(1);
699
+ let (first, second) = tokio::join!(first, second);
700
+ for auth in [first, second, core.get_auth_info(&ctx).await] {
701
+ let auth = auth.unwrap();
702
+ assert_eq!(auth.repo_id, "repo-id");
703
+ assert_eq!(auth.token, "shared");
704
+ }
705
+ assert!(transport.responses.lock().await.is_empty());
706
+ }
707
+
605
708
  #[tokio::test]
606
709
  async fn test_parse_error() {
607
710
  let err_res = vec![
@@ -21,6 +21,8 @@ Use [`crate::SeafileConfig`] for serializable configuration and this builder's
21
21
  methods for direct construction. The field and method documentation defines
22
22
  accepted values, defaults, and environment interaction.
23
23
 
24
+ Authentication and library lookup are initialized together on first use. Failed or cancelled initialization can be retried by a subsequent operation; successful initialization is shared by an operator and its clones.
25
+
24
26
  ## Example
25
27
 
26
28
  ### Via Builder
@@ -32,10 +32,10 @@ version = { workspace = true }
32
32
  all-features = true
33
33
 
34
34
  [dependencies]
35
- opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
35
+ opendal-core = { path = "../../core", version = "0.59.2", default-features = false }
36
36
 
37
+ asyncband = { workspace = true, features = ["pool"] }
37
38
  bytes = { workspace = true }
38
- fastpool = "1.0.2"
39
39
  futures = { workspace = true }
40
40
  log = { workspace = true }
41
41
  openssh = "0.11.0"
@@ -199,3 +199,19 @@
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
201
  limitations under the License.
202
+
203
+
204
+ ------------------------------------------------------------------------------------
205
+ This product bundles third-party components under other open source licenses.
206
+ This section summarizes those components and their licenses. See the referenced
207
+ license files for text of these licenses.
208
+
209
+
210
+ Creative Commons Zero v1.0 Universal
211
+ ------------------------------------
212
+
213
+ bindings/dotnet/.gitignore
214
+
215
+ The above file is derived from VisualStudio.gitignore in
216
+ https://github.com/github/gitignore. Its license text is in
217
+ bindings/dotnet/LICENSE-CC0-1.0.txt.
@@ -15,7 +15,7 @@
15
15
  // specific language governing permissions and limitations
16
16
  // under the License.
17
17
 
18
- use fastpool::{ManageObject, ObjectStatus, bounded};
18
+ use asyncband::pool::{ManageObject, ObjectStatus, bounded};
19
19
  use log::debug;
20
20
  use opendal_core::raw::*;
21
21
  use opendal_core::*;
@@ -22,8 +22,8 @@ use super::core::is_sftp_failure;
22
22
  use super::core::parse_sftp_error;
23
23
  use super::lister::SftpLister;
24
24
  use super::writer::SftpWriter;
25
+ use asyncband::pool::bounded;
25
26
  use bytes::BytesMut;
26
- use fastpool::bounded;
27
27
  use opendal_core::raw::*;
28
28
  use opendal_core::*;
29
29
  use openssh_sftp_client::file::File;
@@ -32,7 +32,7 @@ version = { workspace = true }
32
32
  all-features = true
33
33
 
34
34
  [dependencies]
35
- opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
35
+ opendal-core = { path = "../../core", version = "0.59.2", default-features = false }
36
36
  serde = { workspace = true, features = ["derive"] }
37
37
  sled = "0.34.7"
38
38
 
@@ -199,3 +199,19 @@
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
201
  limitations under the License.
202
+
203
+
204
+ ------------------------------------------------------------------------------------
205
+ This product bundles third-party components under other open source licenses.
206
+ This section summarizes those components and their licenses. See the referenced
207
+ license files for text of these licenses.
208
+
209
+
210
+ Creative Commons Zero v1.0 Universal
211
+ ------------------------------------
212
+
213
+ bindings/dotnet/.gitignore
214
+
215
+ The above file is derived from VisualStudio.gitignore in
216
+ https://github.com/github/gitignore. Its license text is in
217
+ bindings/dotnet/LICENSE-CC0-1.0.txt.
@@ -32,8 +32,8 @@ version = { workspace = true }
32
32
  all-features = true
33
33
 
34
34
  [dependencies]
35
- asyncband = { workspace = true }
36
- opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
35
+ asyncband = { workspace = true, features = ["once-cell"] }
36
+ opendal-core = { path = "../../core", version = "0.59.2", default-features = false }
37
37
  serde = { workspace = true, features = ["derive"] }
38
38
  sqlx = { version = "0.8.0", features = ["runtime-tokio-rustls", "sqlite"] }
39
39
 
@@ -199,3 +199,19 @@
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
201
  limitations under the License.
202
+
203
+
204
+ ------------------------------------------------------------------------------------
205
+ This product bundles third-party components under other open source licenses.
206
+ This section summarizes those components and their licenses. See the referenced
207
+ license files for text of these licenses.
208
+
209
+
210
+ Creative Commons Zero v1.0 Universal
211
+ ------------------------------------
212
+
213
+ bindings/dotnet/.gitignore
214
+
215
+ The above file is derived from VisualStudio.gitignore in
216
+ https://github.com/github/gitignore. Its license text is in
217
+ bindings/dotnet/LICENSE-CC0-1.0.txt.
@@ -32,8 +32,8 @@ version = { workspace = true }
32
32
  all-features = true
33
33
 
34
34
  [dependencies]
35
- asyncband = { workspace = true }
36
- opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
35
+ asyncband = { workspace = true, features = ["once-cell"] }
36
+ opendal-core = { path = "../../core", version = "0.59.2", default-features = false }
37
37
  serde = { workspace = true, features = ["derive"] }
38
38
  surrealdb = { version = "3", features = ["protocol-http"] }
39
39
 
@@ -199,3 +199,19 @@
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
201
  limitations under the License.
202
+
203
+
204
+ ------------------------------------------------------------------------------------
205
+ This product bundles third-party components under other open source licenses.
206
+ This section summarizes those components and their licenses. See the referenced
207
+ license files for text of these licenses.
208
+
209
+
210
+ Creative Commons Zero v1.0 Universal
211
+ ------------------------------------
212
+
213
+ bindings/dotnet/.gitignore
214
+
215
+ The above file is derived from VisualStudio.gitignore in
216
+ https://github.com/github/gitignore. Its license text is in
217
+ bindings/dotnet/LICENSE-CC0-1.0.txt.
@@ -37,7 +37,7 @@ bytes = { workspace = true }
37
37
  hmac = "0.13.0"
38
38
  http = { workspace = true }
39
39
  log = { workspace = true }
40
- opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
40
+ opendal-core = { path = "../../core", version = "0.59.2", default-features = false }
41
41
  percent-encoding = "2"
42
42
  quick-xml = { workspace = true, features = ["serialize", "overlapped-lists"] }
43
43
  serde = { workspace = true, features = ["derive"] }
@@ -199,3 +199,19 @@
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
201
  limitations under the License.
202
+
203
+
204
+ ------------------------------------------------------------------------------------
205
+ This product bundles third-party components under other open source licenses.
206
+ This section summarizes those components and their licenses. See the referenced
207
+ license files for text of these licenses.
208
+
209
+
210
+ Creative Commons Zero v1.0 Universal
211
+ ------------------------------------
212
+
213
+ bindings/dotnet/.gitignore
214
+
215
+ The above file is derived from VisualStudio.gitignore in
216
+ https://github.com/github/gitignore. Its license text is in
217
+ bindings/dotnet/LICENSE-CC0-1.0.txt.
@@ -32,8 +32,8 @@ version = { workspace = true }
32
32
  all-features = true
33
33
 
34
34
  [dependencies]
35
- asyncband = { workspace = true }
36
- opendal-core = { path = "../../core", version = "0.59.0", default-features = false }
35
+ asyncband = { workspace = true, features = ["once-cell"] }
36
+ opendal-core = { path = "../../core", version = "0.59.2", default-features = false }
37
37
  serde = { workspace = true, features = ["derive"] }
38
38
  tikv-client = { version = "0.4.0", default-features = false }
39
39
 
@@ -199,3 +199,19 @@
199
199
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
200
  See the License for the specific language governing permissions and
201
201
  limitations under the License.
202
+
203
+
204
+ ------------------------------------------------------------------------------------
205
+ This product bundles third-party components under other open source licenses.
206
+ This section summarizes those components and their licenses. See the referenced
207
+ license files for text of these licenses.
208
+
209
+
210
+ Creative Commons Zero v1.0 Universal
211
+ ------------------------------------
212
+
213
+ bindings/dotnet/.gitignore
214
+
215
+ The above file is derived from VisualStudio.gitignore in
216
+ https://github.com/github/gitignore. Its license text is in
217
+ bindings/dotnet/LICENSE-CC0-1.0.txt.
@@ -34,7 +34,7 @@ all-features = true
34
34
  [dependencies]
35
35
  bytes = { workspace = true }
36
36
  http = { workspace = true }
37
- opendal-core = { path = "../../core", version = "0.59.0", default-features = false, features = [
37
+ opendal-core = { path = "../../core", version = "0.59.2", default-features = false, features = [
38
38
  "reqsign",
39
39
  ] }
40
40
  quick-xml = { workspace = true, features = ["serialize", "overlapped-lists"] }