1use std::fmt::Debug;
19use std::sync::Arc;
20
21use http::StatusCode;
22use log::debug;
23
24use super::HTTP_SCHEME;
25use super::config::HttpConfig;
26use super::core::{ErrorContext, HttpCore, parse_error};
27use super::reader::*;
28use opendal_core::raw::*;
29use opendal_core::*;
30
31#[doc = include_str!("docs.md")]
33#[derive(Default)]
34pub struct HttpBuilder {
35 pub(super) config: HttpConfig,
36}
37
38impl Debug for HttpBuilder {
39 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
40 f.debug_struct("HttpBuilder")
41 .field("config", &self.config)
42 .finish_non_exhaustive()
43 }
44}
45
46impl HttpBuilder {
47 pub fn endpoint(mut self, endpoint: &str) -> Self {
51 self.config.endpoint = if endpoint.is_empty() {
52 None
53 } else {
54 Some(endpoint.to_string())
55 };
56
57 self
58 }
59
60 pub fn username(mut self, username: &str) -> Self {
64 if !username.is_empty() {
65 self.config.username = Some(username.to_owned());
66 }
67 self
68 }
69
70 pub fn password(mut self, password: &str) -> Self {
74 if !password.is_empty() {
75 self.config.password = Some(password.to_owned());
76 }
77 self
78 }
79
80 pub fn token(mut self, token: &str) -> Self {
84 if !token.is_empty() {
85 self.config.token = Some(token.to_string());
86 }
87 self
88 }
89
90 pub fn root(mut self, root: &str) -> Self {
92 self.config.root = if root.is_empty() {
93 None
94 } else {
95 Some(root.to_string())
96 };
97
98 self
99 }
100}
101
102impl Builder for HttpBuilder {
103 type Config = HttpConfig;
104
105 fn build(self) -> Result<impl Service> {
106 debug!("backend build started: {:?}", self);
107
108 let endpoint = match &self.config.endpoint {
109 Some(v) => v,
110 None => {
111 return Err(Error::new(ErrorKind::ConfigInvalid, "endpoint is empty")
112 .with_context("service", HTTP_SCHEME));
113 }
114 };
115
116 let root = normalize_root(&self.config.root.unwrap_or_default());
117 debug!("backend use root {root}");
118
119 let mut auth = None;
120 if let Some(username) = &self.config.username {
121 auth = Some(format_authorization_by_basic(
122 username,
123 self.config.password.as_deref().unwrap_or_default(),
124 )?);
125 }
126 if let Some(token) = &self.config.token {
127 auth = Some(format_authorization_by_bearer(token)?)
128 }
129
130 let info = ServiceInfo::new(HTTP_SCHEME, &root, "");
131 let capability = Capability {
132 stat: true,
133 stat_with_if_match: true,
134 stat_with_if_none_match: true,
135 stat_with_if_modified_since: true,
136 stat_with_if_unmodified_since: true,
137
138 read: true,
139 read_with_suffix: true,
140
141 read_with_if_match: true,
142 read_with_if_none_match: true,
143 read_with_if_modified_since: true,
144 read_with_if_unmodified_since: true,
145
146 presign: auth.is_none(),
147 presign_read: auth.is_none(),
148 presign_stat: auth.is_none(),
149
150 shared: true,
151
152 ..Default::default()
153 };
154
155 let accessor_info = info;
156
157 let core = Arc::new(HttpCore {
158 info: accessor_info,
159 capability,
160 endpoint: endpoint.to_string(),
161 root,
162 authorization: auth,
163 });
164
165 Ok(HttpBackend { core })
166 }
167}
168
169#[derive(Clone, Debug)]
171pub struct HttpBackend {
172 pub(crate) core: Arc<HttpCore>,
173}
174
175impl Service for HttpBackend {
176 type Reader = oio::StreamReader<HttpReader>;
177 type Writer = ();
178 type Lister = ();
179 type Deleter = ();
180 type Copier = ();
181 type Composer = ();
182
183 fn info(&self) -> ServiceInfo {
184 self.core.info.clone()
185 }
186
187 fn capability(&self) -> Capability {
188 self.core.capability
189 }
190
191 async fn create_dir(
192 &self,
193 _ctx: &OperationContext,
194 _path: &str,
195 _args: OpCreateDir,
196 ) -> Result<RpCreateDir> {
197 Err(Error::new(
198 ErrorKind::Unsupported,
199 "operation is not supported",
200 ))
201 }
202
203 async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
204 if path == "/" {
206 return Ok(RpStat::new(MetadataBuilder::dir().build()));
207 }
208
209 let resp = self.core.http_head(ctx, path, &args).await?;
210
211 let status = resp.status();
212
213 match status {
214 StatusCode::OK => parse_into_metadata(path, resp.headers()).map(RpStat::new),
215 StatusCode::NOT_FOUND | StatusCode::FORBIDDEN if path.ends_with('/') => {
218 Ok(RpStat::new(MetadataBuilder::dir().build()))
219 }
220 _ => Err(parse_error(
221 ErrorContext::new(ServiceOperation("Head")),
222 resp,
223 )),
224 }
225 }
226 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
227 let output: oio::StreamReader<HttpReader> = {
228 Ok(oio::StreamReader::new(HttpReader::new(
229 self.clone(),
230 ctx.clone(),
231 path,
232 args,
233 )))
234 }?;
235
236 Ok(output)
237 }
238
239 fn write(&self, _ctx: &OperationContext, _path: &str, _args: OpWrite) -> Result<Self::Writer> {
240 Err(Error::new(
241 ErrorKind::Unsupported,
242 "operation is not supported",
243 ))
244 }
245
246 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
247 Err(Error::new(
248 ErrorKind::Unsupported,
249 "operation is not supported",
250 ))
251 }
252
253 fn list(&self, _ctx: &OperationContext, _path: &str, _args: OpList) -> Result<Self::Lister> {
254 Err(Error::new(
255 ErrorKind::Unsupported,
256 "operation is not supported",
257 ))
258 }
259
260 fn copy(
261 &self,
262 _ctx: &OperationContext,
263 _from: &str,
264 _to: &str,
265 _args: OpCopy,
266 ) -> Result<Self::Copier> {
267 Err(Error::new(
268 ErrorKind::Unsupported,
269 "operation is not supported",
270 ))
271 }
272
273 async fn rename(
274 &self,
275 _ctx: &OperationContext,
276 _from: &str,
277 _to: &str,
278 _args: OpRename,
279 ) -> Result<RpRename> {
280 Err(Error::new(
281 ErrorKind::Unsupported,
282 "operation is not supported",
283 ))
284 }
285
286 async fn presign(
287 &self,
288 _ctx: &OperationContext,
289 path: &str,
290 args: OpPresign,
291 ) -> Result<RpPresign> {
292 if self.core.has_authorization() {
293 return Err(Error::new(
294 ErrorKind::Unsupported,
295 "Http doesn't support presigned request on backend with authorization",
296 ));
297 }
298
299 let req = match args.operation() {
300 PresignOperation::Stat(v) => self.core.http_head_request(path, v)?,
301 PresignOperation::Read(range, v) => self.core.http_get_request(path, *range, v)?,
302 _ => {
303 return Err(Error::new(
304 ErrorKind::Unsupported,
305 "Http doesn't support presigned write",
306 ));
307 }
308 };
309
310 let (parts, _) = req.into_parts();
311
312 Ok(RpPresign::new(PresignedRequest::new(
313 parts.method,
314 parts.uri,
315 parts.headers,
316 )))
317 }
318}