opendal/services/http/
backend.rs1use std::fmt::Debug;
19use std::fmt::Formatter;
20use std::sync::Arc;
21
22use http::Response;
23use http::StatusCode;
24use log::debug;
25
26use super::core::HttpCore;
27use super::error::parse_error;
28use crate::raw::*;
29use crate::services::HttpConfig;
30use crate::*;
31
32impl Configurator for HttpConfig {
33 type Builder = HttpBuilder;
34
35 #[allow(deprecated)]
36 fn into_builder(self) -> Self::Builder {
37 HttpBuilder {
38 config: self,
39 http_client: None,
40 }
41 }
42}
43
44#[doc = include_str!("docs.md")]
46#[derive(Default)]
47pub struct HttpBuilder {
48 config: HttpConfig,
49
50 #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` instead")]
51 http_client: Option<HttpClient>,
52}
53
54impl Debug for HttpBuilder {
55 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
56 let mut de = f.debug_struct("HttpBuilder");
57
58 de.field("config", &self.config).finish()
59 }
60}
61
62impl HttpBuilder {
63 pub fn endpoint(mut self, endpoint: &str) -> Self {
67 self.config.endpoint = if endpoint.is_empty() {
68 None
69 } else {
70 Some(endpoint.to_string())
71 };
72
73 self
74 }
75
76 pub fn username(mut self, username: &str) -> Self {
80 if !username.is_empty() {
81 self.config.username = Some(username.to_owned());
82 }
83 self
84 }
85
86 pub fn password(mut self, password: &str) -> Self {
90 if !password.is_empty() {
91 self.config.password = Some(password.to_owned());
92 }
93 self
94 }
95
96 pub fn token(mut self, token: &str) -> Self {
100 if !token.is_empty() {
101 self.config.token = Some(token.to_string());
102 }
103 self
104 }
105
106 pub fn root(mut self, root: &str) -> Self {
108 self.config.root = if root.is_empty() {
109 None
110 } else {
111 Some(root.to_string())
112 };
113
114 self
115 }
116
117 #[deprecated(since = "0.53.0", note = "Use `Operator::update_http_client` instead")]
124 #[allow(deprecated)]
125 pub fn http_client(mut self, client: HttpClient) -> Self {
126 self.http_client = Some(client);
127 self
128 }
129}
130
131impl Builder for HttpBuilder {
132 const SCHEME: Scheme = Scheme::Http;
133 type Config = HttpConfig;
134
135 fn build(self) -> Result<impl Access> {
136 debug!("backend build started: {:?}", &self);
137
138 let endpoint = match &self.config.endpoint {
139 Some(v) => v,
140 None => {
141 return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty")
142 .with_context("service", Scheme::Http))
143 }
144 };
145
146 let root = normalize_root(&self.config.root.unwrap_or_default());
147 debug!("backend use root {}", root);
148
149 let mut auth = None;
150 if let Some(username) = &self.config.username {
151 auth = Some(format_authorization_by_basic(
152 username,
153 self.config.password.as_deref().unwrap_or_default(),
154 )?);
155 }
156 if let Some(token) = &self.config.token {
157 auth = Some(format_authorization_by_bearer(token)?)
158 }
159
160 let info = AccessorInfo::default();
161 info.set_scheme(Scheme::Http)
162 .set_root(&root)
163 .set_native_capability(Capability {
164 stat: true,
165 stat_with_if_match: true,
166 stat_with_if_none_match: true,
167 stat_has_cache_control: true,
168 stat_has_content_length: true,
169 stat_has_content_type: true,
170 stat_has_content_encoding: true,
171 stat_has_content_range: true,
172 stat_has_etag: true,
173 stat_has_content_md5: true,
174 stat_has_last_modified: true,
175 stat_has_content_disposition: true,
176
177 read: true,
178
179 read_with_if_match: true,
180 read_with_if_none_match: true,
181
182 presign: auth.is_none(),
183 presign_read: auth.is_none(),
184 presign_stat: auth.is_none(),
185
186 shared: true,
187
188 ..Default::default()
189 });
190
191 #[allow(deprecated)]
193 if let Some(client) = self.http_client {
194 info.update_http_client(|_| client);
195 }
196
197 let accessor_info = Arc::new(info);
198
199 let core = Arc::new(HttpCore {
200 info: accessor_info,
201 endpoint: endpoint.to_string(),
202 root,
203 authorization: auth,
204 });
205
206 Ok(HttpBackend { core })
207 }
208}
209
210#[derive(Clone)]
212pub struct HttpBackend {
213 core: Arc<HttpCore>,
214}
215
216impl Debug for HttpBackend {
217 fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
218 f.debug_struct("HttpBackend")
219 .field("core", &self.core)
220 .finish()
221 }
222}
223
224impl Access for HttpBackend {
225 type Reader = HttpBody;
226 type Writer = ();
227 type Lister = ();
228 type Deleter = ();
229 type BlockingReader = ();
230 type BlockingWriter = ();
231 type BlockingLister = ();
232 type BlockingDeleter = ();
233
234 fn info(&self) -> Arc<AccessorInfo> {
235 self.core.info.clone()
236 }
237
238 async fn stat(&self, path: &str, args: OpStat) -> Result<RpStat> {
239 if path == "/" {
241 return Ok(RpStat::new(Metadata::new(EntryMode::DIR)));
242 }
243
244 let resp = self.core.http_head(path, &args).await?;
245
246 let status = resp.status();
247
248 match status {
249 StatusCode::OK => parse_into_metadata(path, resp.headers()).map(RpStat::new),
250 StatusCode::NOT_FOUND | StatusCode::FORBIDDEN if path.ends_with('/') => {
253 Ok(RpStat::new(Metadata::new(EntryMode::DIR)))
254 }
255 _ => Err(parse_error(resp)),
256 }
257 }
258
259 async fn read(&self, path: &str, args: OpRead) -> Result<(RpRead, Self::Reader)> {
260 let resp = self.core.http_get(path, args.range(), &args).await?;
261
262 let status = resp.status();
263
264 match status {
265 StatusCode::OK | StatusCode::PARTIAL_CONTENT => {
266 Ok((RpRead::default(), resp.into_body()))
267 }
268 _ => {
269 let (part, mut body) = resp.into_parts();
270 let buf = body.to_buffer().await?;
271 Err(parse_error(Response::from_parts(part, buf)))
272 }
273 }
274 }
275
276 async fn presign(&self, path: &str, args: OpPresign) -> Result<RpPresign> {
277 if self.core.has_authorization() {
278 return Err(Error::new(
279 ErrorKind::Unsupported,
280 "Http doesn't support presigned request on backend with authorization",
281 ));
282 }
283
284 let req = match args.operation() {
285 PresignOperation::Stat(v) => self.core.http_head_request(path, v)?,
286 PresignOperation::Read(v) => {
287 self.core.http_get_request(path, BytesRange::default(), v)?
288 }
289 _ => {
290 return Err(Error::new(
291 ErrorKind::Unsupported,
292 "Http doesn't support presigned write",
293 ))
294 }
295 };
296
297 let (parts, _) = req.into_parts();
298
299 Ok(RpPresign::new(PresignedRequest::new(
300 parts.method,
301 parts.uri,
302 parts.headers,
303 )))
304 }
305}