ehttpd_range/
responseext.rs1use crate::rangeext::RangeExt;
4use ehttpd::{
5 bytes::{Data, DataSliceExt, Source},
6 error,
7 error::Error,
8 http::{Response, ResponseExt},
9};
10use std::{
11 fs::File,
12 io::{BufReader, Read, Seek, SeekFrom},
13 ops::{Range, RangeBounds, RangeInclusive},
14};
15
16pub trait ResponseRangeExt
18where
19 Self: Sized,
20{
21 fn new_206_partial_content() -> Self;
23
24 fn set_accept_ranges_bytes(&mut self);
26 fn set_accept_ranges_none(&mut self);
28
29 fn set_content_range<T>(&mut self, range: T, total: u64) -> Result<(), Error>
31 where
32 T: RangeBounds<u64>;
33
34 fn set_body_data_range<T, R>(&mut self, data: T, range: R) -> Result<(), Error>
40 where
41 T: Into<Data>,
42 R: RangeBounds<usize>;
43 fn set_body_file_range<T, R>(&mut self, file: T, range: R) -> Result<(), Error>
49 where
50 T: Into<File>,
51 R: RangeBounds<u64>;
52}
53impl<const HEADER_SIZE_MAX: usize> ResponseRangeExt for Response<HEADER_SIZE_MAX> {
54 fn new_206_partial_content() -> Self {
55 Self::new_status_reason(206, "Partial Content")
56 }
57
58 fn set_accept_ranges_bytes(&mut self) {
59 self.set_field("Accept-Ranges", "bytes")
60 }
61 fn set_accept_ranges_none(&mut self) {
62 self.set_field("Accept-Ranges", "none")
63 }
64
65 fn set_content_range<T>(&mut self, range: T, total: u64) -> Result<(), Error>
66 where
67 T: RangeBounds<u64>,
68 {
69 let range = RangeInclusive::from_range_bounds(range, 0, total)
71 .ok_or_else(|| error!("Range would exceed total limit"))?;
72 let range_string = format!("bytes {}-{}/{total}", range.start(), range.end());
73
74 self.set_field("Content-Range", range_string);
76 Ok(())
77 }
78
79 fn set_body_data_range<T, R>(&mut self, data: T, range: R) -> Result<(), Error>
80 where
81 T: Into<Data>,
82 R: RangeBounds<usize>,
83 {
84 if !self.status.eq(b"206") {
86 return Err(error!("Response is not a 206 response"));
87 }
88
89 let data: Data = data.into();
91 let Range { start, end } =
92 Range::from_range_bounds(range, 0, data.len()).ok_or_else(|| error!("Range would exceed data size"))?;
93 let subdata = data.subcopy(start..end).expect("range would exceed data size");
94
95 self.set_content_range(start as u64..end as u64, data.len() as u64)?;
97 self.set_body_data(subdata);
98 Ok(())
99 }
100 fn set_body_file_range<T, R>(&mut self, file: T, range: R) -> Result<(), Error>
101 where
102 T: Into<File>,
103 R: RangeBounds<u64>,
104 {
105 if !self.status.eq(b"206") {
107 return Err(error!("Response is not a 206 response"));
108 }
109
110 let mut file: File = file.into();
112 let file_size = file.metadata()?.len();
113 let Range { start, end } =
114 Range::from_range_bounds(range, 0, file_size).ok_or_else(|| error!("Range would exceed file size"))?;
115
116 let len = end.saturating_sub(start);
118 file.seek(SeekFrom::Start(start))?;
119 let file = file.take(end.saturating_sub(start));
120
121 self.set_content_range(start..end, file_size)?;
123 self.set_content_length(len);
124
125 let file = BufReader::new(file);
127 self.body = Source::new(file);
128 Ok(())
129 }
130}