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