1use crate::{ByteRange, ObjectPath, ObjectRead, StorageError, StorageFailure, StoredObject};
4
5pub const BUFFERED_READ_LIMIT: u64 = 32 * 1024 * 1024;
7
8#[async_trait::async_trait]
11pub trait ObjectBody: Send {
12 async fn read(&mut self, output: &mut [u8]) -> Result<usize, StorageError>;
14}
15
16pub struct ObjectStream<'a> {
20 metadata: StoredObject,
21 state: StreamState<'a>,
22 received: crate::ByteProgress,
24 delivered: u64,
26}
27
28enum StreamState<'a> {
29 Receiving {
30 remaining: u64,
31 body: Box<dyn ObjectBody + 'a>,
32 download: Option<crate::transfer_budget::DownloadLease>,
33 },
34 Complete,
35 Failed,
36}
37
38impl<'a> ObjectStream<'a> {
39 pub fn metadata(&self) -> &StoredObject {
41 &self.metadata
42 }
43
44 pub fn new(
46 metadata: StoredObject,
47 range: Option<ByteRange>,
48 body: impl ObjectBody + 'a,
49 ) -> Result<Self, StorageError> {
50 let remaining = metadata.read_length(&metadata.path, range)?;
51 Ok(Self {
52 metadata,
53 state: StreamState::Receiving { remaining, body: Box::new(body), download: None },
54 received: crate::ByteProgress::unread(),
55 delivered: 0,
56 })
57 }
58
59 pub(crate) fn check(
60 &self,
61 path: &ObjectPath,
62 range: Option<ByteRange>,
63 ) -> Result<(), StorageError> {
64 let expected = self.metadata.read_length(path, range)?;
65 match &self.state {
66 StreamState::Receiving { remaining, .. } if *remaining == expected => Ok(()),
67 _ => Err(StorageFailure::Protocol
68 .with_source("provider returned an already consumed response")),
69 }
70 }
71
72 pub async fn read(&mut self, output: &mut [u8]) -> Result<usize, StorageError> {
74 if output.is_empty() {
75 return Ok(0);
76 }
77 let mut response = std::mem::replace(&mut self.state, StreamState::Failed);
78 let (remaining, body) = match &mut response {
79 StreamState::Receiving { remaining, body, .. } => (remaining, body),
80 StreamState::Complete => {
81 self.state = StreamState::Complete;
82 return Ok(0);
83 }
84 StreamState::Failed => {
85 return Err(StorageFailure::Protocol.with_source("response already failed"))
86 }
87 };
88 let count = body.read(output).await?;
91 if count > output.len() || count as u64 > *remaining {
92 return Err(
93 StorageFailure::Protocol.with_source("response exceeds its declared length")
94 );
95 }
96 if count == 0 {
97 if *remaining != 0 {
98 return Err(StorageFailure::Protocol
99 .with_source("response ended before its declared length"));
100 }
101 self.state = StreamState::Complete;
102 } else {
103 *remaining -= count as u64;
104 self.state = response;
105 self.delivered += count as u64;
106 self.received.moved(self.delivered);
107 }
108 Ok(count)
109 }
110
111 pub async fn collect(mut self, maximum: u64) -> Result<ObjectRead, StorageError> {
114 let remaining = match &self.state {
115 StreamState::Receiving { remaining, .. } => *remaining,
116 StreamState::Complete => 0,
117 StreamState::Failed => {
118 return Err(StorageFailure::Protocol.with_source("response already failed"))
119 }
120 };
121 if remaining > maximum {
122 return Err(StorageFailure::Protocol
123 .with_source("object exceeds the consumer's buffered-read bound"));
124 }
125 let capacity = usize::try_from(remaining)
126 .map_err(|error| StorageFailure::Protocol.with_source(error))?;
127 let mut bytes = Vec::with_capacity(capacity);
128 let mut chunk = vec![0; 64 * 1024];
129 loop {
130 let length = self.read(&mut chunk).await?;
131 if length == 0 {
132 break;
133 }
134 bytes.extend_from_slice(&chunk[..length]);
135 }
136 Ok(ObjectRead { metadata: self.metadata, bytes })
137 }
138}
139
140struct CompletingBody<'a, F> {
141 body: Box<dyn ObjectBody + 'a>,
142 completion: Option<F>,
143}
144
145#[async_trait::async_trait]
146impl<F: std::future::Future<Output = Result<(), StorageError>> + Send> ObjectBody
147 for CompletingBody<'_, F>
148{
149 async fn read(&mut self, output: &mut [u8]) -> Result<usize, StorageError> {
150 let length = self.body.read(output).await?;
151 if length == 0 {
152 if let Some(completion) = self.completion.take() {
153 completion.await?;
154 }
155 }
156 Ok(length)
157 }
158}
159
160impl<'a> ObjectStream<'a> {
161 pub(crate) fn with_download(mut self, lease: crate::transfer_budget::DownloadLease) -> Self {
163 if let StreamState::Receiving { download, .. } = &mut self.state {
164 *download = Some(lease);
165 }
166 self
167 }
168
169 pub(crate) fn heard_by(mut self, received: crate::ByteProgress) -> Self {
172 self.received = received;
173 self
174 }
175
176 pub(crate) fn counted(mut self, tally: &'a crate::request_tally::RequestTally) -> Self {
178 self.state = match self.state {
179 StreamState::Receiving { remaining, body, download } => StreamState::Receiving {
180 remaining,
181 body: Box::new(CountedBody { body, tally }),
182 download,
183 },
184 state => state,
185 };
186 self
187 }
188
189 pub(crate) fn after(
191 mut self,
192 completion: impl std::future::Future<Output = Result<(), StorageError>> + Send + 'a,
193 ) -> Self {
194 self.state = match self.state {
195 StreamState::Receiving { remaining, body, download } => StreamState::Receiving {
196 remaining,
197 body: Box::new(CompletingBody { body, completion: Some(completion) }),
198 download,
199 },
200 state => state,
201 };
202 self
203 }
204}
205
206struct CountedBody<'a> {
207 body: Box<dyn ObjectBody + 'a>,
208 tally: &'a crate::request_tally::RequestTally,
209}
210
211#[async_trait::async_trait]
212impl ObjectBody for CountedBody<'_> {
213 async fn read(&mut self, output: &mut [u8]) -> Result<usize, StorageError> {
214 self.tally.receive(self.body.read(output)).await
215 }
216}
217
218#[async_trait::async_trait]
219impl<T: AsRef<[u8]> + Send> ObjectBody for std::io::Cursor<T> {
220 async fn read(&mut self, output: &mut [u8]) -> Result<usize, StorageError> {
221 Ok(std::io::Read::read(self, output).expect("read from a memory cursor"))
222 }
223}
224
225#[cfg(test)]
226#[path = "object_stream_tests.rs"]
227mod tests;