1#![doc = include_str!("../README.md")]
19#![cfg_attr(docsrs, feature(doc_cfg))]
20#![cfg_attr(docsrs, doc(auto_cfg))]
21#![deny(missing_docs)]
22use std::future::Future;
23use std::pin::Pin;
24use std::sync::Arc;
25use std::task::Context;
26use std::task::Poll;
27
28use asyncband::semaphore::OwnedSemaphorePermit;
29use asyncband::semaphore::Semaphore;
30use futures::Stream;
31use futures::StreamExt;
32use opendal_core::raw::*;
33use opendal_core::*;
34
35pub trait ConcurrentLimitSemaphore: Send + Sync + Clone + Unpin + 'static {
38 type Permit: Send + Sync + 'static;
41
42 fn acquire(&self) -> impl Future<Output = Self::Permit> + MaybeSend;
44}
45
46impl ConcurrentLimitSemaphore for Arc<Semaphore> {
47 type Permit = OwnedSemaphorePermit;
48
49 async fn acquire(&self) -> Self::Permit {
50 self.clone().acquire_owned(1).await
51 }
52}
53
54#[derive(Clone)]
97pub struct ConcurrentLimitLayer<S: ConcurrentLimitSemaphore = Arc<Semaphore>> {
98 operation_semaphore: S,
99 http_semaphore: Option<S>,
100}
101
102impl<S: ConcurrentLimitSemaphore> std::fmt::Debug for ConcurrentLimitLayer<S> {
103 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
104 f.debug_struct("ConcurrentLimitLayer")
105 .field("has_http_limit", &self.http_semaphore.is_some())
106 .finish_non_exhaustive()
107 }
108}
109
110impl ConcurrentLimitLayer<Arc<Semaphore>> {
111 pub fn new(permits: usize) -> Self {
116 Self::with_semaphore(Arc::new(Semaphore::new(permits)))
117 }
118
119 pub fn with_http_concurrent_limit(self, permits: usize) -> Self {
126 self.with_http_semaphore(Arc::new(Semaphore::new(permits)))
127 }
128}
129
130impl<S: ConcurrentLimitSemaphore> ConcurrentLimitLayer<S> {
131 pub fn with_semaphore(operation_semaphore: S) -> Self {
141 Self {
142 operation_semaphore,
143 http_semaphore: None,
144 }
145 }
146
147 pub fn with_http_semaphore(mut self, semaphore: S) -> Self {
149 self.http_semaphore = Some(semaphore);
150 self
151 }
152}
153
154impl<S: ConcurrentLimitSemaphore> Layer for ConcurrentLimitLayer<S>
155where
156 S::Permit: Send + Sync + 'static + Unpin,
157{
158 fn apply_service(&self, inner: Servicer) -> Servicer {
159 Arc::new(self.layer(inner))
160 }
161
162 fn apply_context(&self, _srv: Servicer, inner: OperationContext) -> OperationContext {
163 let transport = HttpTransporter::new(ConcurrentLimitHttpTransport::<S> {
166 inner: inner.http_transport().clone(),
167 http_semaphore: self.http_semaphore.clone(),
168 });
169 inner.with_http_transport(transport)
170 }
171}
172
173impl<S: ConcurrentLimitSemaphore> ConcurrentLimitLayer<S>
174where
175 S::Permit: Send + Sync + 'static + Unpin,
176{
177 fn layer(&self, inner: Servicer) -> ConcurrentLimitService<S> {
178 ConcurrentLimitService {
179 inner,
180 semaphore: self.operation_semaphore.clone(),
181 }
182 }
183}
184
185#[doc(hidden)]
186pub struct ConcurrentLimitHttpTransport<S: ConcurrentLimitSemaphore> {
187 inner: HttpTransporter,
188 http_semaphore: Option<S>,
189}
190
191impl<S: ConcurrentLimitSemaphore> HttpTransport for ConcurrentLimitHttpTransport<S>
192where
193 S::Permit: Unpin,
194{
195 async fn fetch(&self, req: http::Request<Buffer>) -> Result<http::Response<HttpBody>> {
196 let Some(semaphore) = self.http_semaphore.clone() else {
197 return self.inner.fetch(req).await;
198 };
199
200 let permit = semaphore.acquire().await;
201
202 let resp = self.inner.fetch(req).await?;
203 let (parts, body) = resp.into_parts();
204 let body = body.map_inner(|s| {
205 Box::new(ConcurrentLimitStream::<_, S::Permit> {
206 inner: s,
207 _permit: permit,
208 })
209 });
210 Ok(http::Response::from_parts(parts, body))
211 }
212}
213
214struct ConcurrentLimitStream<S, P> {
215 inner: S,
216 _permit: P,
218}
219
220impl<S, P> Stream for ConcurrentLimitStream<S, P>
221where
222 S: Stream<Item = Result<Buffer>> + Unpin + 'static,
223 P: Unpin,
224{
225 type Item = Result<Buffer>;
226
227 fn poll_next(self: Pin<&mut Self>, cx: &mut Context<'_>) -> Poll<Option<Self::Item>> {
228 let this = self.get_mut();
230 this.inner.poll_next_unpin(cx)
231 }
232}
233
234#[doc(hidden)]
235#[derive(Clone)]
236pub struct ConcurrentLimitService<S: ConcurrentLimitSemaphore> {
237 inner: Servicer,
238 semaphore: S,
239}
240
241impl<S: ConcurrentLimitSemaphore> std::fmt::Debug for ConcurrentLimitService<S> {
242 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
243 f.debug_struct("ConcurrentLimitService")
244 .field("inner", &self.inner)
245 .finish_non_exhaustive()
246 }
247}
248
249impl<S: ConcurrentLimitSemaphore> Service for ConcurrentLimitService<S>
250where
251 S::Permit: Send + Sync + 'static + Unpin,
252{
253 type Reader = ConcurrentLimitReader<oio::Reader, S>;
254 type Writer = ConcurrentLimitWrapper<oio::Writer, S>;
255 type Lister = ConcurrentLimitWrapper<oio::Lister, S>;
256 type Deleter = ConcurrentLimitWrapper<oio::Deleter, S>;
257 type Copier = ConcurrentLimitWrapper<oio::Copier, S>;
258 type Composer = ConcurrentLimitWrapper<oio::Composer, S>;
259
260 fn info(&self) -> ServiceInfo {
261 self.inner.info()
262 }
263
264 fn capability(&self) -> Capability {
265 self.inner.capability()
266 }
267
268 async fn create_dir(
269 &self,
270 ctx: &OperationContext,
271 path: &str,
272 args: OpCreateDir,
273 ) -> Result<RpCreateDir> {
274 let _permit = self.semaphore.acquire().await;
275 self.inner.create_dir(ctx, path, args).await
276 }
277
278 fn read(&self, ctx: &OperationContext, path: &str, args: OpRead) -> Result<Self::Reader> {
279 self.inner
280 .read(ctx, path, args)
281 .map(|r| ConcurrentLimitReader::new(r, self.semaphore.clone()))
282 }
283
284 fn write(&self, ctx: &OperationContext, path: &str, args: OpWrite) -> Result<Self::Writer> {
285 self.inner
286 .write(ctx, path, args)
287 .map(|w| ConcurrentLimitWrapper::new(w, self.semaphore.clone()))
288 }
289
290 fn copy(
291 &self,
292 ctx: &OperationContext,
293 from: &str,
294 to: &str,
295 args: OpCopy,
296 ) -> Result<Self::Copier> {
297 self.inner
298 .copy(ctx, from, to, args)
299 .map(|c| ConcurrentLimitWrapper::new(c, self.semaphore.clone()))
300 }
301
302 fn compose(&self, ctx: &OperationContext, to: &str, args: OpCompose) -> Result<Self::Composer> {
303 self.inner
304 .compose(ctx, to, args)
305 .map(|c| ConcurrentLimitWrapper::new(c, self.semaphore.clone()))
306 }
307
308 async fn rename(
309 &self,
310 ctx: &OperationContext,
311 from: &str,
312 to: &str,
313 args: OpRename,
314 ) -> Result<RpRename> {
315 let _permit = self.semaphore.acquire().await;
316 self.inner.rename(ctx, from, to, args).await
317 }
318
319 async fn restore(
320 &self,
321 ctx: &OperationContext,
322 path: &str,
323 args: OpRestore,
324 ) -> Result<RpRestore> {
325 let _permit = self.semaphore.acquire().await;
326 self.inner.restore(ctx, path, args).await
327 }
328
329 async fn stat(&self, ctx: &OperationContext, path: &str, args: OpStat) -> Result<RpStat> {
330 let _permit = self.semaphore.acquire().await;
331 self.inner.stat(ctx, path, args).await
332 }
333
334 fn delete(&self, ctx: &OperationContext) -> Result<Self::Deleter> {
335 self.inner
336 .delete(ctx)
337 .map(|w| ConcurrentLimitWrapper::new(w, self.semaphore.clone()))
338 }
339
340 fn list(&self, ctx: &OperationContext, path: &str, args: OpList) -> Result<Self::Lister> {
341 self.inner
342 .list(ctx, path, args)
343 .map(|s| ConcurrentLimitWrapper::new(s, self.semaphore.clone()))
344 }
345
346 async fn presign(
347 &self,
348 ctx: &OperationContext,
349 path: &str,
350 args: OpPresign,
351 ) -> Result<RpPresign> {
352 let _permit = self.semaphore.acquire().await;
353 self.inner.presign(ctx, path, args).await
354 }
355}
356
357#[doc(hidden)]
358pub struct ConcurrentLimitReader<R, S> {
359 inner: R,
360 semaphore: S,
361}
362
363impl<R, S> ConcurrentLimitReader<R, S> {
364 fn new(inner: R, semaphore: S) -> Self {
365 Self { inner, semaphore }
366 }
367}
368
369impl<R: oio::Read, S: ConcurrentLimitSemaphore> oio::Read for ConcurrentLimitReader<R, S>
370where
371 S::Permit: Send + Sync + 'static + Unpin,
372{
373 async fn open(&self, range: BytesRange) -> Result<(RpRead, Box<dyn oio::ReadStreamDyn>)> {
374 let permit = self.semaphore.acquire().await;
375 let (rp, stream) = self.inner.open(range).await?;
376 Ok((
377 rp,
378 Box::new(ConcurrentLimitWrapper::new_with_permit(
379 stream,
380 self.semaphore.clone(),
381 permit,
382 )) as Box<dyn oio::ReadStreamDyn>,
383 ))
384 }
385
386 async fn read(&self, range: BytesRange) -> Result<(RpRead, Buffer)> {
387 let _permit = self.semaphore.acquire().await;
388 self.inner.read(range).await
389 }
390}
391
392#[doc(hidden)]
393pub struct ConcurrentLimitWrapper<R, S: ConcurrentLimitSemaphore> {
394 inner: R,
395 semaphore: S,
396 permit: Option<S::Permit>,
398}
399
400impl<R, S: ConcurrentLimitSemaphore> ConcurrentLimitWrapper<R, S> {
401 fn new(inner: R, semaphore: S) -> Self {
402 Self {
403 inner,
404 semaphore,
405 permit: None,
406 }
407 }
408
409 fn new_with_permit(inner: R, semaphore: S, permit: S::Permit) -> Self {
410 Self {
411 inner,
412 semaphore,
413 permit: Some(permit),
414 }
415 }
416
417 async fn acquire(&mut self) {
418 if self.permit.is_none() {
419 self.permit = Some(self.semaphore.acquire().await);
420 }
421 }
422}
423
424impl<R: oio::ReadStream, S: ConcurrentLimitSemaphore> oio::ReadStream
425 for ConcurrentLimitWrapper<R, S>
426where
427 S::Permit: Send + Sync + 'static + Unpin,
428{
429 async fn read(&mut self) -> Result<Buffer> {
430 self.acquire().await;
431 self.inner.read().await
432 }
433}
434
435impl<R: oio::Read, S: ConcurrentLimitSemaphore> oio::Read for ConcurrentLimitWrapper<R, S>
436where
437 S::Permit: Send + Sync + 'static + Unpin,
438{
439 async fn open(&self, range: BytesRange) -> Result<(RpRead, Box<dyn oio::ReadStreamDyn>)> {
440 self.inner.open(range).await
441 }
442
443 async fn read(&self, range: BytesRange) -> Result<(RpRead, Buffer)> {
444 self.inner.read(range).await
445 }
446}
447
448impl<R: oio::Write, S: ConcurrentLimitSemaphore> oio::Write for ConcurrentLimitWrapper<R, S>
449where
450 S::Permit: Send + Sync + 'static + Unpin,
451{
452 async fn write(&mut self, bs: Buffer) -> Result<()> {
453 self.acquire().await;
454 self.inner.write(bs).await
455 }
456
457 async fn copy_from(&mut self, path: &str, args: OpRead, range: BytesRange) -> Result<()> {
458 self.acquire().await;
459 self.inner.copy_from(path, args, range).await
460 }
461
462 async fn close(&mut self) -> Result<Metadata> {
463 self.acquire().await;
464 self.inner.close().await
465 }
466
467 async fn abort(&mut self) -> Result<()> {
468 self.acquire().await;
469 self.inner.abort().await
470 }
471}
472
473impl<R: oio::List, S: ConcurrentLimitSemaphore> oio::List for ConcurrentLimitWrapper<R, S>
474where
475 S::Permit: Send + Sync + 'static + Unpin,
476{
477 async fn next(&mut self) -> Result<Option<oio::Entry>> {
478 self.acquire().await;
479 self.inner.next().await
480 }
481}
482
483impl<R: oio::Delete, S: ConcurrentLimitSemaphore> oio::Delete for ConcurrentLimitWrapper<R, S>
484where
485 S::Permit: Send + Sync + 'static + Unpin,
486{
487 async fn delete(&mut self, path: &str, args: OpDelete) -> Result<()> {
488 self.acquire().await;
489 self.inner.delete(path, args).await
490 }
491
492 async fn close(&mut self) -> Result<()> {
493 self.acquire().await;
494 self.inner.close().await
495 }
496}
497
498impl<C: oio::Copy, S: ConcurrentLimitSemaphore> oio::Copy for ConcurrentLimitWrapper<C, S>
499where
500 S::Permit: Send + Sync + 'static + Unpin,
501{
502 async fn next(&mut self) -> Result<Option<usize>> {
503 self.acquire().await;
504 self.inner.next().await
505 }
506
507 async fn close(&mut self) -> Result<Metadata> {
508 self.acquire().await;
509 self.inner.close().await
510 }
511
512 async fn abort(&mut self) -> Result<()> {
513 self.acquire().await;
514 self.inner.abort().await
515 }
516}
517
518impl<C: oio::Compose, S: ConcurrentLimitSemaphore> oio::Compose for ConcurrentLimitWrapper<C, S>
519where
520 S::Permit: Send + Sync + 'static + Unpin,
521{
522 async fn compose(&mut self, path: &str, args: OpRead) -> Result<()> {
523 self.acquire().await;
524 self.inner.compose(path, args).await
525 }
526
527 async fn close(&mut self) -> Result<Metadata> {
528 self.acquire().await;
529 self.inner.close().await
530 }
531}
532
533#[cfg(test)]
534mod tests {
535 use super::*;
536 use opendal_core::Operator;
537 use opendal_core::services;
538 use std::future::pending;
539 use std::sync::Arc;
540 use std::time::Duration;
541 use tokio::time::timeout;
542
543 use futures::stream;
544 use http::Response;
545
546 #[tokio::test]
547 async fn operation_semaphore_can_be_shared() {
548 let semaphore = Arc::new(Semaphore::new(1));
549 let layer = ConcurrentLimitLayer::with_semaphore(semaphore.clone());
550
551 let permit = semaphore.clone().acquire_owned(1).await;
552
553 let op = Operator::new(services::Memory::default())
554 .expect("operator must build")
555 .layer(layer);
556
557 let blocked = timeout(Duration::from_millis(50), op.stat("any")).await;
558 assert!(
559 blocked.is_err(),
560 "operation should be limited by shared semaphore"
561 );
562
563 drop(permit);
564
565 let completed = timeout(Duration::from_millis(50), op.stat("any")).await;
566 assert!(
567 completed.is_ok(),
568 "operation should proceed once permit is released"
569 );
570 }
571
572 #[tokio::test]
573 async fn operation_semaphore_limits_copy_and_rename() {
574 #[derive(Clone, Debug)]
575 struct CopyRenameBackend {
576 info: ServiceInfo,
577 capability: Capability,
578 }
579
580 impl Service for CopyRenameBackend {
581 type Reader = ();
582 type Writer = ();
583 type Lister = ();
584 type Deleter = ();
585 type Copier = ();
586 type Composer = ();
587
588 fn info(&self) -> ServiceInfo {
589 self.info.clone()
590 }
591
592 fn capability(&self) -> Capability {
593 self.capability
594 }
595
596 async fn create_dir(
597 &self,
598 _: &OperationContext,
599 _: &str,
600 _: OpCreateDir,
601 ) -> Result<RpCreateDir> {
602 Err(Error::new(
603 ErrorKind::Unsupported,
604 "operation is not supported",
605 ))
606 }
607
608 async fn stat(&self, _: &OperationContext, _: &str, _: OpStat) -> Result<RpStat> {
609 Err(Error::new(
610 ErrorKind::Unsupported,
611 "operation is not supported",
612 ))
613 }
614
615 fn read(&self, _ctx: &OperationContext, _: &str, _: OpRead) -> Result<Self::Reader> {
616 Err(Error::new(
617 ErrorKind::Unsupported,
618 "operation is not supported",
619 ))
620 }
621
622 fn write(&self, _: &OperationContext, _: &str, _: OpWrite) -> Result<Self::Writer> {
623 Err(Error::new(
624 ErrorKind::Unsupported,
625 "operation is not supported",
626 ))
627 }
628
629 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
630 Err(Error::new(
631 ErrorKind::Unsupported,
632 "operation is not supported",
633 ))
634 }
635
636 fn list(&self, _ctx: &OperationContext, _: &str, _: OpList) -> Result<Self::Lister> {
637 Err(Error::new(
638 ErrorKind::Unsupported,
639 "operation is not supported",
640 ))
641 }
642
643 fn copy(
644 &self,
645 _: &OperationContext,
646 _: &str,
647 _: &str,
648 _: OpCopy,
649 ) -> Result<Self::Copier> {
650 Ok(())
651 }
652
653 async fn rename(
654 &self,
655 _: &OperationContext,
656 _: &str,
657 _: &str,
658 _: OpRename,
659 ) -> Result<RpRename> {
660 Ok(RpRename::default())
661 }
662
663 async fn presign(
664 &self,
665 _: &OperationContext,
666 _: &str,
667 _: OpPresign,
668 ) -> Result<RpPresign> {
669 Err(Error::new(
670 ErrorKind::Unsupported,
671 "operation is not supported",
672 ))
673 }
674 }
675
676 let semaphore = Arc::new(Semaphore::new(1));
677 let layer = ConcurrentLimitLayer::with_semaphore(semaphore.clone());
678 let capability = Capability {
679 copy: true,
680 rename: true,
681 ..Default::default()
682 };
683 let op = Operator::from_parts(
684 OperationContext::default(),
685 Arc::new(CopyRenameBackend {
686 info: ServiceInfo::with_scheme("mock"),
687 capability,
688 }),
689 )
690 .layer(layer);
691
692 let permit = semaphore.clone().acquire_owned(1).await;
693
694 let copy = timeout(Duration::from_millis(50), op.copy("from", "to")).await;
695 assert!(copy.is_err(), "copy should wait for the operation permit");
696
697 let rename = timeout(Duration::from_millis(50), op.rename("from", "to")).await;
698 assert!(
699 rename.is_err(),
700 "rename should wait for the operation permit"
701 );
702
703 drop(permit);
704
705 timeout(Duration::from_millis(50), op.copy("from", "to"))
706 .await
707 .expect("copy should proceed once permit is released")
708 .expect("copy should succeed");
709 timeout(Duration::from_millis(50), op.rename("from", "to"))
710 .await
711 .expect("rename should proceed once permit is released")
712 .expect("rename should succeed");
713 }
714
715 #[tokio::test]
716 async fn operation_semaphore_held_until_copier_dropped() {
717 #[derive(Debug)]
718 struct PendingCopier;
719
720 impl oio::Copy for PendingCopier {
721 async fn next(&mut self) -> Result<Option<usize>> {
722 pending().await
723 }
724
725 async fn close(&mut self) -> Result<Metadata> {
726 pending().await
727 }
728
729 async fn abort(&mut self) -> Result<()> {
730 Ok(())
731 }
732 }
733
734 #[derive(Clone, Debug)]
735 struct CopierBackend {
736 info: ServiceInfo,
737 capability: Capability,
738 }
739
740 impl Service for CopierBackend {
741 type Reader = ();
742 type Writer = ();
743 type Lister = ();
744 type Deleter = ();
745 type Copier = PendingCopier;
746 type Composer = ();
747
748 fn info(&self) -> ServiceInfo {
749 self.info.clone()
750 }
751
752 fn capability(&self) -> Capability {
753 self.capability
754 }
755
756 async fn create_dir(
757 &self,
758 _: &OperationContext,
759 _: &str,
760 _: OpCreateDir,
761 ) -> Result<RpCreateDir> {
762 Err(Error::new(
763 ErrorKind::Unsupported,
764 "operation is not supported",
765 ))
766 }
767
768 fn read(&self, _ctx: &OperationContext, _: &str, _: OpRead) -> Result<Self::Reader> {
769 Err(Error::new(
770 ErrorKind::Unsupported,
771 "operation is not supported",
772 ))
773 }
774
775 fn write(&self, _: &OperationContext, _: &str, _: OpWrite) -> Result<Self::Writer> {
776 Err(Error::new(
777 ErrorKind::Unsupported,
778 "operation is not supported",
779 ))
780 }
781
782 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
783 Err(Error::new(
784 ErrorKind::Unsupported,
785 "operation is not supported",
786 ))
787 }
788
789 fn list(&self, _ctx: &OperationContext, _: &str, _: OpList) -> Result<Self::Lister> {
790 Err(Error::new(
791 ErrorKind::Unsupported,
792 "operation is not supported",
793 ))
794 }
795
796 fn copy(
797 &self,
798 _: &OperationContext,
799 _: &str,
800 _: &str,
801 _: OpCopy,
802 ) -> Result<Self::Copier> {
803 Ok(PendingCopier)
804 }
805
806 async fn stat(&self, _: &OperationContext, _: &str, _: OpStat) -> Result<RpStat> {
807 Ok(RpStat::new(MetadataBuilder::file(0).build()))
808 }
809
810 async fn rename(
811 &self,
812 _: &OperationContext,
813 _: &str,
814 _: &str,
815 _: OpRename,
816 ) -> Result<RpRename> {
817 Err(Error::new(
818 ErrorKind::Unsupported,
819 "operation is not supported",
820 ))
821 }
822
823 async fn presign(
824 &self,
825 _: &OperationContext,
826 _: &str,
827 _: OpPresign,
828 ) -> Result<RpPresign> {
829 Err(Error::new(
830 ErrorKind::Unsupported,
831 "operation is not supported",
832 ))
833 }
834 }
835
836 let semaphore = Arc::new(Semaphore::new(1));
837 let layer = ConcurrentLimitLayer::with_semaphore(semaphore.clone());
838 let capability = Capability {
839 copy: true,
840 stat: true,
841 ..Default::default()
842 };
843 let op = Operator::from_parts(
844 OperationContext::default(),
845 Arc::new(CopierBackend {
846 info: ServiceInfo::with_scheme("mock"),
847 capability,
848 }),
849 )
850 .layer(layer);
851
852 let mut copier = timeout(Duration::from_millis(50), op.copier("from", "to"))
853 .await
854 .expect("copier setup should not block")
855 .expect("copier should be created");
856
857 let copy = timeout(Duration::from_millis(50), copier.next()).await;
858 assert!(copy.is_err(), "copy body should remain pending");
859
860 let blocked = timeout(Duration::from_millis(50), op.stat("any")).await;
863 assert!(
864 blocked.is_err(),
865 "stat should wait while the copier holds the permit"
866 );
867
868 drop(copier);
869
870 timeout(Duration::from_millis(50), op.stat("any"))
871 .await
872 .expect("stat should proceed once the copier is dropped")
873 .expect("stat should succeed");
874 }
875
876 #[tokio::test]
877 async fn concurrent_chunked_read_with_http_limit() {
878 use opendal_core::raw::*;
879
880 struct EchoTransport;
881
882 impl HttpTransport for EchoTransport {
883 async fn fetch(&self, req: http::Request<Buffer>) -> Result<http::Response<HttpBody>> {
884 let data = req.into_body();
885 let len = data.len() as u64;
886 let body =
887 HttpBody::new(Box::pin(stream::once(async move { Ok(data) })), Some(len));
888 Ok(http::Response::builder()
889 .status(http::StatusCode::OK)
890 .body(body)
891 .unwrap())
892 }
893 }
894
895 #[derive(Clone, Debug)]
896 struct HttpBackend {
897 info: ServiceInfo,
898 capability: Capability,
899 content: Buffer,
900 }
901
902 pub struct HttpReader {
904 backend: HttpBackend,
905 ctx: OperationContext,
906 }
907
908 impl HttpReader {
909 fn new(backend: HttpBackend, ctx: OperationContext, _: &str, _: OpRead) -> Self {
910 Self { backend, ctx }
911 }
912 }
913
914 impl oio::StreamRead for HttpReader {
915 async fn open(
916 &self,
917 range: BytesRange,
918 ) -> Result<(RpRead, Box<dyn oio::ReadStreamDyn>)> {
919 let backend = &self.backend;
920 let start = range.offset() as usize;
921 let data = match range.size() {
922 Some(sz) => backend.content.slice(start..start + sz as usize),
923 None => backend.content.slice(start..),
924 };
925 let req = http::Request::get("http://fake").body(data).unwrap();
926 let resp = self.ctx.http_transport().fetch(req).await?;
927 let rp = RpRead::new({
928 let metadata = MetadataBuilder::file(backend.content.len() as u64);
929 metadata.build()
930 });
931 let stream = resp.into_body();
932
933 Ok((rp, Box::new(stream) as Box<dyn oio::ReadStreamDyn>))
934 }
935 }
936
937 impl Service for HttpBackend {
938 type Reader = oio::StreamReader<HttpReader>;
939 type Writer = ();
940 type Lister = ();
941 type Deleter = ();
942 type Copier = ();
943 type Composer = ();
944
945 fn info(&self) -> ServiceInfo {
946 self.info.clone()
947 }
948
949 fn capability(&self) -> Capability {
950 self.capability
951 }
952
953 async fn create_dir(
954 &self,
955 _: &OperationContext,
956 _: &str,
957 _: OpCreateDir,
958 ) -> Result<RpCreateDir> {
959 Err(Error::new(
960 ErrorKind::Unsupported,
961 "operation is not supported",
962 ))
963 }
964
965 fn read(
966 &self,
967 ctx: &OperationContext,
968 path: &str,
969 args: OpRead,
970 ) -> Result<Self::Reader> {
971 Ok(oio::StreamReader::new(HttpReader::new(
972 self.clone(),
973 ctx.clone(),
974 path,
975 args,
976 )))
977 }
978
979 async fn stat(&self, _: &OperationContext, _: &str, _: OpStat) -> Result<RpStat> {
980 Ok(RpStat::new({
981 let metadata = MetadataBuilder::file(self.content.len() as u64);
982 metadata.build()
983 }))
984 }
985
986 fn write(&self, _: &OperationContext, _: &str, _: OpWrite) -> Result<Self::Writer> {
987 Err(Error::new(
988 ErrorKind::Unsupported,
989 "operation is not supported",
990 ))
991 }
992
993 fn delete(&self, _ctx: &OperationContext) -> Result<Self::Deleter> {
994 Err(Error::new(
995 ErrorKind::Unsupported,
996 "operation is not supported",
997 ))
998 }
999
1000 fn list(&self, _ctx: &OperationContext, _: &str, _: OpList) -> Result<Self::Lister> {
1001 Err(Error::new(
1002 ErrorKind::Unsupported,
1003 "operation is not supported",
1004 ))
1005 }
1006
1007 fn copy(
1008 &self,
1009 _: &OperationContext,
1010 _: &str,
1011 _: &str,
1012 _: OpCopy,
1013 ) -> Result<Self::Copier> {
1014 Err(Error::new(
1015 ErrorKind::Unsupported,
1016 "operation is not supported",
1017 ))
1018 }
1019
1020 async fn rename(
1021 &self,
1022 _: &OperationContext,
1023 _: &str,
1024 _: &str,
1025 _: OpRename,
1026 ) -> Result<RpRename> {
1027 Err(Error::new(
1028 ErrorKind::Unsupported,
1029 "operation is not supported",
1030 ))
1031 }
1032
1033 async fn presign(
1034 &self,
1035 _: &OperationContext,
1036 _: &str,
1037 _: OpPresign,
1038 ) -> Result<RpPresign> {
1039 Err(Error::new(
1040 ErrorKind::Unsupported,
1041 "operation is not supported",
1042 ))
1043 }
1044 }
1045
1046 let content = Buffer::from(vec![0u8; 4096]);
1047 let op = Operator::from_parts(
1048 OperationContext::default(),
1049 Arc::new(HttpBackend {
1050 info: ServiceInfo::with_scheme("mock"),
1051 capability: Capability {
1052 read: true,
1053 stat: true,
1054 ..Default::default()
1055 },
1056 content: content.clone(),
1057 }),
1058 )
1059 .with_context(
1060 OperationContext::new().with_http_transport(HttpTransporter::new(EchoTransport)),
1061 )
1062 .layer(ConcurrentLimitLayer::new(1024).with_http_concurrent_limit(2));
1063
1064 let result = timeout(Duration::from_secs(5), async {
1066 op.reader_with("test")
1067 .chunk(256)
1068 .concurrent(4)
1069 .await
1070 .expect("reader must build")
1071 .read(..)
1072 .await
1073 })
1074 .await;
1075
1076 let buf = result
1077 .expect("read must not deadlock (timeout)")
1078 .expect("read must succeed");
1079 assert_eq!(buf.to_bytes(), content.to_bytes());
1080 }
1081
1082 #[tokio::test]
1083 async fn http_semaphore_holds_until_body_dropped() {
1084 struct DummyTransport;
1085
1086 impl HttpTransport for DummyTransport {
1087 async fn fetch(&self, _req: http::Request<Buffer>) -> Result<Response<HttpBody>> {
1088 let body = HttpBody::new(stream::empty(), None);
1089 Ok(Response::builder()
1090 .status(http::StatusCode::OK)
1091 .body(body)
1092 .expect("response must build"))
1093 }
1094 }
1095
1096 let semaphore = Arc::new(Semaphore::new(1));
1097 let layer = ConcurrentLimitLayer::new(1).with_http_semaphore(semaphore.clone());
1098 let fetcher = ConcurrentLimitHttpTransport::<Arc<Semaphore>> {
1099 inner: HttpTransporter::new(DummyTransport),
1100 http_semaphore: layer.http_semaphore.clone(),
1101 };
1102
1103 let request = http::Request::builder()
1104 .uri("http://example.invalid/")
1105 .body(Buffer::new())
1106 .expect("request must build");
1107 let _resp = fetcher
1108 .fetch(request)
1109 .await
1110 .expect("first fetch should succeed");
1111
1112 let request = http::Request::builder()
1113 .uri("http://example.invalid/")
1114 .body(Buffer::new())
1115 .expect("request must build");
1116 let blocked = timeout(Duration::from_millis(50), fetcher.fetch(request)).await;
1117 assert!(
1118 blocked.is_err(),
1119 "http fetch should block while the body holds the permit"
1120 );
1121 }
1122}