1#![cfg_attr(docsrs, feature(doc_cfg))]
2
3use std::{
4 num::{NonZero, NonZeroU64},
5 ops::RangeInclusive,
6};
7
8use bytes::Bytes;
9
10pub mod headers;
11
12use crate::headers::{
13 content_range::{Bound, HttpContentRange, Unsatisfiable},
14 range::HttpRange,
15};
16
17pub fn serve_file_with_http_range(
21 body: Bytes,
22 http_range: Option<HttpRange>,
23) -> Result<BodyRange<Bytes>, UnsatisfiableRange> {
24 let size = u64::try_from(body.len()).expect("we do not support 128bit usize");
25 let size = NonZeroU64::try_from(size).map_err(|_| {
26 UnsatisfiableRange(HttpContentRange::Unsatisfiable(Unsatisfiable::new(size)))
27 })?;
28
29 let content_range = file_range(size, http_range)?;
30
31 let start = usize::try_from(*content_range.range.start()).expect("u64 doesn't fit usize");
32 let end = usize::try_from(*content_range.range.end()).expect("u64 doesn't fit usize");
33
34 Ok(BodyRange {
35 body: body.slice(start..=end),
36 header: content_range.header,
37 })
38}
39
40pub fn file_range(
44 size: NonZero<u64>,
45 http_range: Option<HttpRange>,
46) -> Result<ContentRange, UnsatisfiableRange> {
47 let size = size.get();
48
49 let Some(http_range) = http_range else {
50 let end = size - 1;
51 return Ok(ContentRange {
52 header: None,
53 range: 0..=end,
54 });
55 };
56
57 match http_range {
58 HttpRange::StartingPoint(start) if size > start => {
59 let end = size - 1;
60
61 let content_range =
62 HttpContentRange::Bound(Bound::new(start..=end, Some(size)).unwrap());
63
64 Ok(ContentRange {
65 header: Some(content_range),
66 range: start..=end,
67 })
68 }
69 HttpRange::Range(ordered_range) if size > ordered_range.end() => {
70 let start = ordered_range.start();
71 let end = ordered_range.end();
72
73 let content_range =
74 HttpContentRange::Bound(Bound::new(start..=end, Some(size)).unwrap());
75
76 Ok(ContentRange {
77 header: Some(content_range),
78 range: start..=end,
79 })
80 }
81 HttpRange::Suffix(suffix) if size.checked_sub(suffix).is_some() => {
82 let start = size - suffix;
83 let end = size - 1;
84 let content_range =
85 HttpContentRange::Bound(Bound::new(start..=end, Some(size)).unwrap());
86
87 Ok(ContentRange {
88 header: Some(content_range),
89 range: start..=end,
90 })
91 }
92 _ => {
93 let content_range = HttpContentRange::Unsatisfiable(
94 crate::headers::content_range::Unsatisfiable::new(size),
95 );
96
97 Err(UnsatisfiableRange(content_range))
98 }
99 }
100}
101
102#[derive(Debug, Clone, PartialEq, Eq)]
108pub struct BodyRange<T> {
109 body: T,
110 header: Option<HttpContentRange>,
111}
112
113impl<T> BodyRange<T> {
114 pub fn body(&self) -> &T {
116 &self.body
117 }
118
119 pub fn into_body(self) -> T {
120 self.body
121 }
122
123 pub fn header(&self) -> Option<HttpContentRange> {
126 self.header
127 }
128}
129
130#[derive(Debug, Clone, PartialEq, Eq)]
134pub struct ContentRange {
135 header: Option<HttpContentRange>,
136 range: RangeInclusive<u64>,
137}
138
139impl ContentRange {
140 pub fn header(&self) -> Option<HttpContentRange> {
143 self.header
144 }
145
146 pub fn range(&self) -> &RangeInclusive<u64> {
148 &self.range
149 }
150}
151
152#[derive(Debug, Clone, PartialEq, Eq)]
156pub struct UnsatisfiableRange(HttpContentRange);
157
158impl UnsatisfiableRange {
159 pub fn header(&self) -> HttpContentRange {
161 self.0
162 }
163}
164
165#[cfg(feature = "axum")]
166mod axum {
167 use crate::{BodyRange, UnsatisfiableRange};
168
169 use axum_core::response::{IntoResponse, Response};
170 use bytes::Bytes;
171 use http::{HeaderValue, StatusCode, header::CONTENT_RANGE};
172
173 impl IntoResponse for BodyRange<Bytes> {
174 fn into_response(self) -> Response {
175 match self.header {
176 Some(range) => (
177 StatusCode::PARTIAL_CONTENT,
178 [(CONTENT_RANGE, HeaderValue::from(&range))],
179 self.body,
180 )
181 .into_response(),
182 None => (StatusCode::OK, self.body).into_response(),
183 }
184 }
185 }
186
187 impl IntoResponse for UnsatisfiableRange {
188 fn into_response(self) -> Response {
189 (
190 StatusCode::RANGE_NOT_SATISFIABLE,
191 [(CONTENT_RANGE, HeaderValue::from(&self.0))],
192 )
193 .into_response()
194 }
195 }
196}