1use std::io::{Read, Write};
8
9use super::camera::StreamOpen;
10
11#[derive(Debug, PartialEq, Eq)]
13pub enum CopyEnd {
14 Eof,
16 Cancelled,
18 CapReached,
20 Errored,
24}
25
26#[derive(Debug, Default, PartialEq, Eq)]
28pub struct RecordStats {
29 pub bytes: u64,
31 pub connections: u32,
33 pub failures: u32,
35}
36
37pub fn copy_until(
43 reader: &mut dyn Read,
44 sink: &mut dyn Write,
45 cancel: &dyn Fn() -> bool,
46 cap_remaining: u64,
47 buf: &mut [u8],
48) -> (CopyEnd, u64) {
49 let mut written: u64 = 0;
50 loop {
51 if cancel() {
52 return (CopyEnd::Cancelled, written);
53 }
54 if written >= cap_remaining {
55 return (CopyEnd::CapReached, written);
56 }
57 let n = match reader.read(buf) {
58 Ok(0) => return (CopyEnd::Eof, written),
59 Ok(n) => n,
60 Err(_) => return (CopyEnd::Errored, written),
61 };
62 let take = (n as u64).min(cap_remaining - written) as usize;
64 if sink.write_all(&buf[..take]).is_err() {
65 return (CopyEnd::Errored, written);
66 }
67 written += take as u64;
68 if take < n {
69 return (CopyEnd::CapReached, written);
70 }
71 }
72}
73
74pub fn record_loop(
81 open: &StreamOpen,
82 sink: &mut dyn Write,
83 cancel: &dyn Fn() -> bool,
84 max_bytes: u64,
85 buf_size: usize,
86 backoff: &dyn Fn(u32),
87) -> RecordStats {
88 let mut stats = RecordStats::default();
89 let mut buf = vec![0u8; buf_size.max(1)];
90 let mut attempts: u32 = 0;
91 loop {
92 if cancel() || stats.bytes >= max_bytes {
93 return stats;
94 }
95 if attempts > 0 {
96 backoff(attempts);
97 if cancel() {
98 return stats; }
100 }
101 attempts += 1;
102 match (open)() {
103 Ok(mut opened) => {
104 stats.connections += 1;
105 let (end, n) = copy_until(
106 &mut *opened.reader,
107 sink,
108 cancel,
109 max_bytes - stats.bytes,
110 &mut buf,
111 );
112 stats.bytes += n; match end {
114 CopyEnd::Eof => {} CopyEnd::Errored => stats.failures += 1, CopyEnd::Cancelled | CopyEnd::CapReached => return stats,
117 }
118 }
119 Err(_) => stats.failures += 1, }
121 }
122}
123
124#[cfg(test)]
125mod tests {
126 use super::*;
127 use crate::server::camera::OpenedCameraStream;
128 use std::io::Cursor;
129 use std::sync::Arc;
130 use std::sync::atomic::{AtomicUsize, Ordering};
131
132 fn never() -> bool {
133 false
134 }
135
136 #[test]
139 fn copy_until_writes_everything_then_reports_eof() {
140 let mut r = Cursor::new(b"hello world".to_vec());
141 let mut sink: Vec<u8> = Vec::new();
142 let mut buf = [0u8; 4];
143 let (end, n) = copy_until(&mut r, &mut sink, &never, 1_000, &mut buf);
144 assert_eq!(end, CopyEnd::Eof);
145 assert_eq!(n, 11);
146 assert_eq!(sink, b"hello world");
147 }
148
149 #[test]
150 fn copy_until_stops_on_cancel_without_reading() {
151 let mut r = Cursor::new(b"data".to_vec());
152 let mut sink: Vec<u8> = Vec::new();
153 let mut buf = [0u8; 8];
154 let (end, n) = copy_until(&mut r, &mut sink, &|| true, 1_000, &mut buf);
155 assert_eq!(end, CopyEnd::Cancelled);
156 assert_eq!(n, 0);
157 assert!(sink.is_empty());
158 }
159
160 struct BytesThenErr {
162 data: Vec<u8>,
163 pos: usize,
164 }
165 impl Read for BytesThenErr {
166 fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
167 if self.pos >= self.data.len() {
168 return Err(std::io::Error::new(
169 std::io::ErrorKind::ConnectionReset,
170 "drop",
171 ));
172 }
173 let n = buf.len().min(self.data.len() - self.pos);
174 buf[..n].copy_from_slice(&self.data[self.pos..self.pos + n]);
175 self.pos += n;
176 Ok(n)
177 }
178 }
179
180 #[test]
181 fn copy_until_keeps_bytes_written_before_a_read_error() {
182 let mut r = BytesThenErr {
185 data: b"abc".to_vec(),
186 pos: 0,
187 };
188 let mut sink: Vec<u8> = Vec::new();
189 let mut buf = [0u8; 2];
190 let (end, n) = copy_until(&mut r, &mut sink, &never, 1_000, &mut buf);
191 assert_eq!(end, CopyEnd::Errored);
192 assert_eq!(n, 3);
193 assert_eq!(sink, b"abc");
194 }
195
196 #[test]
197 fn copy_until_never_writes_past_the_cap() {
198 let mut r = Cursor::new(b"0123456789".to_vec());
199 let mut sink: Vec<u8> = Vec::new();
200 let mut buf = [0u8; 8];
201 let (end, n) = copy_until(&mut r, &mut sink, &never, 4, &mut buf);
202 assert_eq!(end, CopyEnd::CapReached);
203 assert_eq!(n, 4);
204 assert_eq!(sink, b"0123");
205 }
206
207 fn seq_opener(entries: Vec<Option<Vec<u8>>>) -> (StreamOpen, Arc<AtomicUsize>) {
213 let idx = Arc::new(AtomicUsize::new(0));
214 let counter = idx.clone();
215 let open: StreamOpen = Arc::new(move || {
216 let i = idx.fetch_add(1, Ordering::SeqCst);
217 match entries.get(i) {
218 Some(Some(data)) => Ok(OpenedCameraStream {
219 content_type: "multipart/x-mixed-replace".to_string(),
220 reader: Box::new(Cursor::new(data.clone())),
221 }),
222 _ => Err("no stream".to_string()),
223 }
224 });
225 (open, counter)
226 }
227
228 #[test]
229 fn record_loop_reconnects_after_each_eof() {
230 let (open, _) = seq_opener(vec![Some(b"aaa".to_vec()), Some(b"bbb".to_vec())]);
234 let mut sink: Vec<u8> = Vec::new();
235 let stats = record_loop(&open, &mut sink, &never, 6, 8, &|_| {});
236 assert_eq!(stats.connections, 2);
237 assert_eq!(stats.failures, 0);
238 assert_eq!(sink, b"aaabbb");
239 }
240
241 #[test]
242 fn record_loop_counts_a_failed_open_and_retries() {
243 let (open, _) = seq_opener(vec![None, Some(b"ok".to_vec())]);
245 let mut sink: Vec<u8> = Vec::new();
246 let stats = record_loop(&open, &mut sink, &never, 2, 8, &|_| {});
247 assert_eq!(stats.failures, 1);
248 assert_eq!(stats.connections, 1);
249 assert_eq!(sink, b"ok");
250 }
251
252 #[test]
253 fn record_loop_stops_immediately_when_cancelled_up_front() {
254 let (open, _idx) = seq_opener(vec![Some(b"x".to_vec())]);
255 let mut sink: Vec<u8> = Vec::new();
256 let stats = record_loop(&open, &mut sink, &|| true, 1_000, 8, &|_| {});
257 assert_eq!(stats.connections, 0);
258 assert_eq!(stats.bytes, 0);
259 assert!(sink.is_empty());
260 }
261
262 #[test]
263 fn record_loop_honours_the_byte_cap() {
264 let (open, _idx) = seq_opener(vec![Some(vec![7u8; 100])]);
267 let mut sink: Vec<u8> = Vec::new();
268 let stats = record_loop(&open, &mut sink, &never, 10, 8, &|_| {});
269 assert_eq!(stats.bytes, 10);
270 assert_eq!(stats.connections, 1);
271 assert_eq!(sink.len(), 10);
272 }
273}