ehttpd_range/
rangeresponse.rs1use crate::rangeext::RangeExt;
4use ehttpd::bytes::{Data, Source};
5use ehttpd::err;
6use ehttpd::error::Error;
7use ehttpd::http::Response;
8use std::fs::File;
9use std::io::{BufReader, Read, Seek, SeekFrom};
10use std::ops::{Range, RangeBounds, RangeInclusive};
11
12pub trait RangeResponse
14where
15 Self: Sized,
16{
17 fn new_206_partial_content() -> Self;
19
20 fn set_accept_ranges_bytes(&mut self);
22 fn set_accept_ranges_none(&mut self);
24
25 fn set_content_range<T>(&mut self, range: T, total: u64) -> Result<(), Error>
27 where
28 T: RangeBounds<u64>;
29
30 fn set_body_data_range<T, R>(&mut self, data: T, range: R) -> Result<(), Error>
36 where
37 T: Into<Data>,
38 R: RangeBounds<usize>;
39 fn set_body_file_range<T, R>(&mut self, file: T, range: R) -> Result<(), Error>
45 where
46 T: Into<File>,
47 R: RangeBounds<u64>;
48}
49impl<const HEADER_SIZE_MAX: usize> RangeResponse for Response<HEADER_SIZE_MAX> {
50 fn new_206_partial_content() -> Self {
51 Self::new_status_reason(206, "Partial Content")
52 }
53
54 fn set_accept_ranges_bytes(&mut self) {
55 self.set_field("Accept-Ranges", "bytes")
56 }
57 fn set_accept_ranges_none(&mut self) {
58 self.set_field("Accept-Ranges", "none")
59 }
60
61 fn set_content_range<T>(&mut self, range: T, total: u64) -> Result<(), Error>
62 where
63 T: RangeBounds<u64>,
64 {
65 let range =
67 RangeInclusive::from_range_bounds(range, 0, total).ok_or_else(|| err!("Range would exceed total limit"))?;
68 let range_string = format!("bytes {}-{}/{total}", range.start(), range.end());
69
70 self.set_field("Content-Range", range_string);
72 Ok(())
73 }
74
75 fn set_body_data_range<T, R>(&mut self, data: T, range: R) -> Result<(), Error>
76 where
77 T: Into<Data>,
78 R: RangeBounds<usize>,
79 {
80 if !self.status.eq(b"206") {
82 return Err(err!("Response is not a 206 response"));
83 }
84
85 let data: Data = data.into();
87 let Range { start, end } =
88 Range::from_range_bounds(range, 0, data.len()).ok_or_else(|| err!("Range would exceed data size"))?;
89 let subdata = data.subcopy(start..end).expect("range would exceed data size");
90
91 self.set_content_range(start as u64..end as u64, data.len() as u64)?;
93 self.set_body_data(subdata);
94 Ok(())
95 }
96 fn set_body_file_range<T, R>(&mut self, file: T, range: R) -> Result<(), Error>
97 where
98 T: Into<File>,
99 R: RangeBounds<u64>,
100 {
101 if !self.status.eq(b"206") {
103 return Err(err!("Response is not a 206 response"));
104 }
105
106 let mut file: File = file.into();
108 let file_size = file.metadata()?.len();
109 let Range { start, end } =
110 Range::from_range_bounds(range, 0, file_size).ok_or_else(|| err!("Range would exceed file size"))?;
111
112 let len = end.saturating_sub(start);
114 file.seek(SeekFrom::Start(start))?;
115 let file = file.take(end.saturating_sub(start));
116
117 self.set_content_range(start..end, file_size)?;
119 self.set_content_length(len);
120
121 let file = BufReader::new(file);
123 self.body = Source::new(file);
124 Ok(())
125 }
126}