mirror of
https://github.com/fafhrd91/actix-web
synced 2025-02-17 10:13:30 +01:00
re-instate Range typed header (#2485)
Co-authored-by: RideWindX <ridewindx@gmail.com>
This commit is contained in:
parent
4c9ca7196d
commit
d89c706cd6
@ -4,6 +4,7 @@
|
|||||||
### Added
|
### Added
|
||||||
* Methods on `AcceptLanguage`: `ranked` and `preference`. [#2480]
|
* Methods on `AcceptLanguage`: `ranked` and `preference`. [#2480]
|
||||||
* `AcceptEncoding` typed header. [#2482]
|
* `AcceptEncoding` typed header. [#2482]
|
||||||
|
* `Range` typed header. [#2485]
|
||||||
* `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
|
* `HttpResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
|
||||||
* `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
|
* `ServiceResponse::map_into_{left,right}_body` and `HttpResponse::map_into_boxed_body`. [#2468]
|
||||||
|
|
||||||
@ -21,6 +22,7 @@
|
|||||||
[#2480]: https://github.com/actix/actix-web/pull/2480
|
[#2480]: https://github.com/actix/actix-web/pull/2480
|
||||||
[#2482]: https://github.com/actix/actix-web/pull/2482
|
[#2482]: https://github.com/actix/actix-web/pull/2482
|
||||||
[#2484]: https://github.com/actix/actix-web/pull/2484
|
[#2484]: https://github.com/actix/actix-web/pull/2484
|
||||||
|
[#2485]: https://github.com/actix/actix-web/pull/2485
|
||||||
|
|
||||||
|
|
||||||
## 4.0.0-beta.13 - 2021-11-30
|
## 4.0.0-beta.13 - 2021-11-30
|
||||||
|
@ -40,7 +40,7 @@ mod if_unmodified_since;
|
|||||||
mod last_modified;
|
mod last_modified;
|
||||||
mod macros;
|
mod macros;
|
||||||
mod preference;
|
mod preference;
|
||||||
// mod range;
|
mod range;
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
pub(crate) use macros::common_header_test;
|
pub(crate) use macros::common_header_test;
|
||||||
@ -68,7 +68,7 @@ pub use self::if_range::IfRange;
|
|||||||
pub use self::if_unmodified_since::IfUnmodifiedSince;
|
pub use self::if_unmodified_since::IfUnmodifiedSince;
|
||||||
pub use self::last_modified::LastModified;
|
pub use self::last_modified::LastModified;
|
||||||
pub use self::preference::Preference;
|
pub use self::preference::Preference;
|
||||||
//pub use self::range::{Range, ByteRangeSpec};
|
pub use self::range::{ByteRangeSpec, Range};
|
||||||
|
|
||||||
/// Format writer ([`fmt::Write`]) for a [`BytesMut`].
|
/// Format writer ([`fmt::Write`]) for a [`BytesMut`].
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
@ -77,10 +77,12 @@ struct Writer {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Writer {
|
impl Writer {
|
||||||
|
/// Constructs new bytes writer.
|
||||||
pub fn new() -> Writer {
|
pub fn new() -> Writer {
|
||||||
Writer::default()
|
Writer::default()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Splits bytes out of writer, leaving writer buffer empty.
|
||||||
pub fn take(&mut self) -> Bytes {
|
pub fn take(&mut self) -> Bytes {
|
||||||
self.buf.split().freeze()
|
self.buf.split().freeze()
|
||||||
}
|
}
|
||||||
|
@ -1,11 +1,12 @@
|
|||||||
// TODO: reinstate module
|
|
||||||
|
|
||||||
use std::{
|
use std::{
|
||||||
fmt::{self, Display},
|
cmp,
|
||||||
|
fmt::{self, Display, Write},
|
||||||
str::FromStr,
|
str::FromStr,
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{parsing::from_one_raw_str, Header, Raw};
|
use actix_http::{error::ParseError, header, HttpMessage};
|
||||||
|
|
||||||
|
use super::{Header, HeaderName, HeaderValue, IntoHeaderValue, InvalidHeaderValue, Writer};
|
||||||
|
|
||||||
/// `Range` header, defined
|
/// `Range` header, defined
|
||||||
/// in [RFC 7233 §3.1](https://datatracker.ietf.org/doc/html/rfc7233#section-3.1)
|
/// in [RFC 7233 §3.1](https://datatracker.ietf.org/doc/html/rfc7233#section-3.1)
|
||||||
@ -16,7 +17,7 @@ use super::{parsing::from_one_raw_str, Header, Raw};
|
|||||||
///
|
///
|
||||||
/// # ABNF
|
/// # ABNF
|
||||||
/// ```plain
|
/// ```plain
|
||||||
/// Range = byte-ranges-specifier / other-ranges-specifier
|
/// Range = byte-ranges-specifier / other-ranges-specifier
|
||||||
/// other-ranges-specifier = other-range-unit "=" other-range-set
|
/// other-ranges-specifier = other-range-unit "=" other-range-set
|
||||||
/// other-range-set = 1*VCHAR
|
/// other-range-set = 1*VCHAR
|
||||||
///
|
///
|
||||||
@ -25,13 +26,15 @@ use super::{parsing::from_one_raw_str, Header, Raw};
|
|||||||
/// byte-ranges-specifier = bytes-unit "=" byte-range-set
|
/// byte-ranges-specifier = bytes-unit "=" byte-range-set
|
||||||
/// byte-range-set = 1#(byte-range-spec / suffix-byte-range-spec)
|
/// byte-range-set = 1#(byte-range-spec / suffix-byte-range-spec)
|
||||||
/// byte-range-spec = first-byte-pos "-" [last-byte-pos]
|
/// byte-range-spec = first-byte-pos "-" [last-byte-pos]
|
||||||
|
/// suffix-byte-range-spec = "-" suffix-length
|
||||||
|
/// suffix-length = 1*DIGIT
|
||||||
/// first-byte-pos = 1*DIGIT
|
/// first-byte-pos = 1*DIGIT
|
||||||
/// last-byte-pos = 1*DIGIT
|
/// last-byte-pos = 1*DIGIT
|
||||||
/// ```
|
/// ```
|
||||||
///
|
///
|
||||||
/// # Example Values
|
/// # Example Values
|
||||||
/// * `bytes=1000-`
|
/// * `bytes=1000-`
|
||||||
/// * `bytes=-2000`
|
/// * `bytes=-50`
|
||||||
/// * `bytes=0-1,30-40`
|
/// * `bytes=0-1,30-40`
|
||||||
/// * `bytes=0-10,20-90,-100`
|
/// * `bytes=0-10,20-90,-100`
|
||||||
/// * `custom_unit=0-123`
|
/// * `custom_unit=0-123`
|
||||||
@ -39,81 +42,81 @@ use super::{parsing::from_one_raw_str, Header, Raw};
|
|||||||
///
|
///
|
||||||
/// # Examples
|
/// # Examples
|
||||||
/// ```
|
/// ```
|
||||||
/// use hyper::header::{Headers, Range, ByteRangeSpec};
|
/// use actix_web::http::header::{Range, ByteRangeSpec};
|
||||||
|
/// use actix_web::HttpResponse;
|
||||||
///
|
///
|
||||||
/// let mut headers = Headers::new();
|
/// let mut builder = HttpResponse::Ok();
|
||||||
/// headers.set(Range::Bytes(
|
/// builder.insert_header(Range::Bytes(
|
||||||
/// vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::AllFrom(200)]
|
/// vec![ByteRangeSpec::FromTo(1, 100), ByteRangeSpec::From(200)]
|
||||||
/// ));
|
/// ));
|
||||||
///
|
/// builder.insert_header(Range::Unregistered("letters".to_owned(), "a-f".to_owned()));
|
||||||
/// headers.clear();
|
/// builder.insert_header(Range::bytes(1, 100));
|
||||||
/// headers.set(Range::Unregistered("letters".to_owned(), "a-f".to_owned()));
|
/// builder.insert_header(Range::bytes_multi(vec![(1, 100), (200, 300)]));
|
||||||
/// ```
|
|
||||||
///
|
|
||||||
/// ```
|
|
||||||
/// use hyper::header::{Headers, Range};
|
|
||||||
///
|
|
||||||
/// let mut headers = Headers::new();
|
|
||||||
/// headers.set(Range::bytes(1, 100));
|
|
||||||
///
|
|
||||||
/// headers.clear();
|
|
||||||
/// headers.set(Range::bytes_multi(vec![(1, 100), (200, 300)]));
|
|
||||||
/// ```
|
/// ```
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
#[derive(PartialEq, Clone, Debug)]
|
||||||
pub enum Range {
|
pub enum Range {
|
||||||
/// Byte range
|
/// Byte range.
|
||||||
Bytes(Vec<ByteRangeSpec>),
|
Bytes(Vec<ByteRangeSpec>),
|
||||||
|
|
||||||
/// Custom range, with unit not registered at IANA
|
/// Custom range, with unit not registered at IANA.
|
||||||
|
///
|
||||||
/// (`other-range-unit`: String , `other-range-set`: String)
|
/// (`other-range-unit`: String , `other-range-set`: String)
|
||||||
Unregistered(String, String),
|
Unregistered(String, String),
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Each `Range::Bytes` header can contain one or more `ByteRangeSpecs`.
|
/// A range of bytes to fetch.
|
||||||
/// Each `ByteRangeSpec` defines a range of bytes to fetch
|
///
|
||||||
#[derive(PartialEq, Clone, Debug)]
|
/// Each [`Range::Bytes`] header can contain one or more `ByteRangeSpec`s.
|
||||||
|
#[derive(Debug, Clone, PartialEq, Eq)]
|
||||||
pub enum ByteRangeSpec {
|
pub enum ByteRangeSpec {
|
||||||
/// Get all bytes between x and y ("x-y")
|
/// All bytes from `x` to `y`, inclusive.
|
||||||
|
///
|
||||||
|
/// Serialized as `x-y`.
|
||||||
|
///
|
||||||
|
/// Example: `bytes=500-999` would represent the second 500 bytes.
|
||||||
FromTo(u64, u64),
|
FromTo(u64, u64),
|
||||||
|
|
||||||
/// Get all bytes starting from x ("x-")
|
/// All bytes starting from `x`, inclusive.
|
||||||
AllFrom(u64),
|
///
|
||||||
|
/// Serialized as `x-`.
|
||||||
|
///
|
||||||
|
/// Example: For a file of 1000 bytes, `bytes=950-` would represent bytes 950-999, inclusive.
|
||||||
|
From(u64),
|
||||||
|
|
||||||
/// Get last x bytes ("-x")
|
/// The last `y` bytes, inclusive.
|
||||||
|
///
|
||||||
|
/// Using the spec terminology, this is `suffix-byte-range-spec`. Serialized as `-y`.
|
||||||
|
///
|
||||||
|
/// Example: For a file of 1000 bytes, `bytes=-50` is equivalent to `bytes=950-`.
|
||||||
Last(u64),
|
Last(u64),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ByteRangeSpec {
|
impl ByteRangeSpec {
|
||||||
/// Given the full length of the entity, attempt to normalize the byte range
|
/// Given the full length of the entity, attempt to normalize the byte range into an satisfiable
|
||||||
/// into an satisfiable end-inclusive (from, to) range.
|
/// end-inclusive `(from, to)` range.
|
||||||
///
|
///
|
||||||
/// The resulting range is guaranteed to be a satisfiable range within the
|
/// The resulting range is guaranteed to be a satisfiable range within the bounds
|
||||||
/// bounds of `0 <= from <= to < full_length`.
|
/// of `0 <= from <= to < full_length`.
|
||||||
///
|
///
|
||||||
/// If the byte range is deemed unsatisfiable, `None` is returned.
|
/// If the byte range is deemed unsatisfiable, `None` is returned. An unsatisfiable range is
|
||||||
/// An unsatisfiable range is generally cause for a server to either reject
|
/// generally cause for a server to either reject the client request with a
|
||||||
/// the client request with a `416 Range Not Satisfiable` status code, or to
|
/// `416 Range Not Satisfiable` status code, or to simply ignore the range header and serve the
|
||||||
/// simply ignore the range header and serve the full entity using a `200
|
/// full entity using a `200 OK` status code.
|
||||||
/// OK` status code.
|
|
||||||
///
|
///
|
||||||
/// This function closely follows [RFC 7233 §2.1].
|
/// This function closely follows [RFC 7233 §2.1]. As such, it considers ranges to be
|
||||||
/// As such, it considers ranges to be satisfiable if they meet the
|
/// satisfiable if they meet the following conditions:
|
||||||
/// following conditions:
|
|
||||||
///
|
///
|
||||||
/// > If a valid byte-range-set includes at least one byte-range-spec with
|
/// > If a valid byte-range-set includes at least one byte-range-spec with a first-byte-pos that
|
||||||
/// a first-byte-pos that is less than the current length of the
|
/// is less than the current length of the representation, or at least one
|
||||||
/// representation, or at least one suffix-byte-range-spec with a
|
/// suffix-byte-range-spec with a non-zero suffix-length, then the byte-range-set
|
||||||
/// non-zero suffix-length, then the byte-range-set is satisfiable.
|
/// is satisfiable. Otherwise, the byte-range-set is unsatisfiable.
|
||||||
/// Otherwise, the byte-range-set is unsatisfiable.
|
|
||||||
///
|
///
|
||||||
/// The function also computes remainder ranges based on the RFC:
|
/// The function also computes remainder ranges based on the RFC:
|
||||||
///
|
///
|
||||||
/// > If the last-byte-pos value is
|
/// > If the last-byte-pos value is absent, or if the value is greater than or equal to the
|
||||||
/// absent, or if the value is greater than or equal to the current
|
/// current length of the representation data, the byte range is interpreted as the remainder
|
||||||
/// length of the representation data, the byte range is interpreted as
|
/// of the representation (i.e., the server replaces the value of last-byte-pos with a value
|
||||||
/// the remainder of the representation (i.e., the server replaces the
|
/// that is one less than the current length of the selected representation).
|
||||||
/// value of last-byte-pos with a value that is one less than the current
|
|
||||||
/// length of the selected representation).
|
|
||||||
///
|
///
|
||||||
/// [RFC 7233 §2.1]: https://datatracker.ietf.org/doc/html/rfc7233
|
/// [RFC 7233 §2.1]: https://datatracker.ietf.org/doc/html/rfc7233
|
||||||
pub fn to_satisfiable_range(&self, full_length: u64) -> Option<(u64, u64)> {
|
pub fn to_satisfiable_range(&self, full_length: u64) -> Option<(u64, u64)> {
|
||||||
@ -121,26 +124,28 @@ impl ByteRangeSpec {
|
|||||||
if full_length == 0 {
|
if full_length == 0 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
match self {
|
|
||||||
&ByteRangeSpec::FromTo(from, to) => {
|
match *self {
|
||||||
|
ByteRangeSpec::FromTo(from, to) => {
|
||||||
if from < full_length && from <= to {
|
if from < full_length && from <= to {
|
||||||
Some((from, ::std::cmp::min(to, full_length - 1)))
|
Some((from, cmp::min(to, full_length - 1)))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&ByteRangeSpec::AllFrom(from) => {
|
|
||||||
|
ByteRangeSpec::From(from) => {
|
||||||
if from < full_length {
|
if from < full_length {
|
||||||
Some((from, full_length - 1))
|
Some((from, full_length - 1))
|
||||||
} else {
|
} else {
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
&ByteRangeSpec::Last(last) => {
|
|
||||||
|
ByteRangeSpec::Last(last) => {
|
||||||
if last > 0 {
|
if last > 0 {
|
||||||
// From the RFC: If the selected representation is shorter
|
// From the RFC: If the selected representation is shorter than the specified
|
||||||
// than the specified suffix-length,
|
// suffix-length, the entire representation is used.
|
||||||
// the entire representation is used.
|
|
||||||
if last > full_length {
|
if last > full_length {
|
||||||
Some((0, full_length - 1))
|
Some((0, full_length - 1))
|
||||||
} else {
|
} else {
|
||||||
@ -155,48 +160,53 @@ impl ByteRangeSpec {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Range {
|
impl Range {
|
||||||
/// Get the most common byte range header ("bytes=from-to")
|
/// Constructs a common byte range header.
|
||||||
|
///
|
||||||
|
/// Eg: `bytes=from-to`
|
||||||
pub fn bytes(from: u64, to: u64) -> Range {
|
pub fn bytes(from: u64, to: u64) -> Range {
|
||||||
Range::Bytes(vec![ByteRangeSpec::FromTo(from, to)])
|
Range::Bytes(vec![ByteRangeSpec::FromTo(from, to)])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Get byte range header with multiple subranges
|
/// Constructs a byte range header with multiple subranges.
|
||||||
/// ("bytes=from1-to1,from2-to2,fromX-toX")
|
///
|
||||||
|
/// Eg: `bytes=from1-to1,from2-to2,fromX-toX`
|
||||||
pub fn bytes_multi(ranges: Vec<(u64, u64)>) -> Range {
|
pub fn bytes_multi(ranges: Vec<(u64, u64)>) -> Range {
|
||||||
Range::Bytes(
|
Range::Bytes(
|
||||||
ranges
|
ranges
|
||||||
.iter()
|
.into_iter()
|
||||||
.map(|r| ByteRangeSpec::FromTo(r.0, r.1))
|
.map(|(from, to)| ByteRangeSpec::FromTo(from, to))
|
||||||
.collect(),
|
.collect(),
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for ByteRangeSpec {
|
impl fmt::Display for ByteRangeSpec {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match *self {
|
||||||
ByteRangeSpec::FromTo(from, to) => write!(f, "{}-{}", from, to),
|
ByteRangeSpec::FromTo(from, to) => write!(f, "{}-{}", from, to),
|
||||||
ByteRangeSpec::Last(pos) => write!(f, "-{}", pos),
|
ByteRangeSpec::Last(pos) => write!(f, "-{}", pos),
|
||||||
ByteRangeSpec::AllFrom(pos) => write!(f, "{}-", pos),
|
ByteRangeSpec::From(pos) => write!(f, "{}-", pos),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Display for Range {
|
impl fmt::Display for Range {
|
||||||
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
|
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match *self {
|
match self {
|
||||||
Range::Bytes(ref ranges) => {
|
Range::Bytes(ranges) => {
|
||||||
write!(f, "bytes=")?;
|
write!(f, "bytes=")?;
|
||||||
|
|
||||||
for (i, range) in ranges.iter().enumerate() {
|
for (i, range) in ranges.iter().enumerate() {
|
||||||
if i != 0 {
|
if i != 0 {
|
||||||
f.write_str(",")?;
|
f.write_str(",")?;
|
||||||
}
|
}
|
||||||
|
|
||||||
Display::fmt(range, f)?;
|
Display::fmt(range, f)?;
|
||||||
}
|
}
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
Range::Unregistered(ref unit, ref range_str) => {
|
|
||||||
|
Range::Unregistered(unit, range_str) => {
|
||||||
write!(f, "{}={}", unit, range_str)
|
write!(f, "{}={}", unit, range_str)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -204,89 +214,118 @@ impl fmt::Display for Range {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for Range {
|
impl FromStr for Range {
|
||||||
type Err = ::Error;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> ::Result<Range> {
|
fn from_str(s: &str) -> Result<Range, ParseError> {
|
||||||
let mut iter = s.splitn(2, '=');
|
let (unit, val) = s.split_once('=').ok_or(ParseError::Header)?;
|
||||||
|
|
||||||
match (iter.next(), iter.next()) {
|
match (unit, val) {
|
||||||
(Some("bytes"), Some(ranges)) => {
|
("bytes", ranges) => {
|
||||||
let ranges = from_comma_delimited(ranges);
|
let ranges = from_comma_delimited(ranges);
|
||||||
|
|
||||||
if ranges.is_empty() {
|
if ranges.is_empty() {
|
||||||
return Err(::Error::Header);
|
return Err(ParseError::Header);
|
||||||
}
|
}
|
||||||
|
|
||||||
Ok(Range::Bytes(ranges))
|
Ok(Range::Bytes(ranges))
|
||||||
}
|
}
|
||||||
(Some(unit), Some(range_str)) if unit != "" && range_str != "" => {
|
|
||||||
Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned()))
|
(_, "") => Err(ParseError::Header),
|
||||||
}
|
("", _) => Err(ParseError::Header),
|
||||||
_ => Err(::Error::Header),
|
|
||||||
|
(unit, range_str) => Ok(Range::Unregistered(unit.to_owned(), range_str.to_owned())),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl FromStr for ByteRangeSpec {
|
impl FromStr for ByteRangeSpec {
|
||||||
type Err = ::Error;
|
type Err = ParseError;
|
||||||
|
|
||||||
fn from_str(s: &str) -> ::Result<ByteRangeSpec> {
|
fn from_str(s: &str) -> Result<ByteRangeSpec, ParseError> {
|
||||||
let mut parts = s.splitn(2, '-');
|
let (start, end) = s.split_once('-').ok_or(ParseError::Header)?;
|
||||||
|
|
||||||
match (parts.next(), parts.next()) {
|
match (start, end) {
|
||||||
(Some(""), Some(end)) => end
|
("", end) => end
|
||||||
.parse()
|
.parse()
|
||||||
.or(Err(::Error::Header))
|
.or(Err(ParseError::Header))
|
||||||
.map(ByteRangeSpec::Last),
|
.map(ByteRangeSpec::Last),
|
||||||
(Some(start), Some("")) => start
|
|
||||||
|
(start, "") => start
|
||||||
.parse()
|
.parse()
|
||||||
.or(Err(::Error::Header))
|
.or(Err(ParseError::Header))
|
||||||
.map(ByteRangeSpec::AllFrom),
|
.map(ByteRangeSpec::From),
|
||||||
(Some(start), Some(end)) => match (start.parse(), end.parse()) {
|
|
||||||
|
(start, end) => match (start.parse(), end.parse()) {
|
||||||
(Ok(start), Ok(end)) if start <= end => Ok(ByteRangeSpec::FromTo(start, end)),
|
(Ok(start), Ok(end)) if start <= end => Ok(ByteRangeSpec::FromTo(start, end)),
|
||||||
_ => Err(::Error::Header),
|
_ => Err(ParseError::Header),
|
||||||
},
|
},
|
||||||
_ => Err(::Error::Header),
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Header for Range {
|
impl Header for Range {
|
||||||
fn header_name() -> &'static str {
|
fn name() -> HeaderName {
|
||||||
static NAME: &'static str = "Range";
|
header::RANGE
|
||||||
NAME
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fn parse_header(raw: &Raw) -> ::Result<Range> {
|
#[inline]
|
||||||
from_one_raw_str(raw)
|
fn parse<T: HttpMessage>(msg: &T) -> Result<Self, ParseError> {
|
||||||
|
header::from_one_raw_str(msg.headers().get(&Self::name()))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn fmt_header(&self, f: &mut ::header::Formatter) -> fmt::Result {
|
impl IntoHeaderValue for Range {
|
||||||
f.fmt_line(self)
|
type Error = InvalidHeaderValue;
|
||||||
|
|
||||||
|
fn try_into_value(self) -> Result<HeaderValue, Self::Error> {
|
||||||
|
let mut wrt = Writer::new();
|
||||||
|
let _ = write!(wrt, "{}", self);
|
||||||
|
HeaderValue::from_maybe_shared(wrt.take())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Parses 0 or more items out of a comma delimited string, ignoring invalid items.
|
||||||
|
fn from_comma_delimited<T: FromStr>(s: &str) -> Vec<T> {
|
||||||
|
s.split(',')
|
||||||
|
.filter_map(|x| match x.trim() {
|
||||||
|
"" => None,
|
||||||
|
y => Some(y),
|
||||||
|
})
|
||||||
|
.filter_map(|x| x.parse().ok())
|
||||||
|
.collect()
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use actix_http::{test::TestRequest, Request};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
fn req(s: &str) -> Request {
|
||||||
|
TestRequest::default()
|
||||||
|
.insert_header((header::RANGE, s))
|
||||||
|
.finish()
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_bytes_range_valid() {
|
fn test_parse_bytes_range_valid() {
|
||||||
let r: Range = Header::parse_header(&"bytes=1-100".into()).unwrap();
|
let r: Range = Header::parse(&req("bytes=1-100")).unwrap();
|
||||||
let r2: Range = Header::parse_header(&"bytes=1-100,-".into()).unwrap();
|
let r2: Range = Header::parse(&req("bytes=1-100,-")).unwrap();
|
||||||
let r3 = Range::bytes(1, 100);
|
let r3 = Range::bytes(1, 100);
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
assert_eq!(r2, r3);
|
assert_eq!(r2, r3);
|
||||||
|
|
||||||
let r: Range = Header::parse_header(&"bytes=1-100,200-".into()).unwrap();
|
let r: Range = Header::parse(&req("bytes=1-100,200-")).unwrap();
|
||||||
let r2: Range = Header::parse_header(&"bytes= 1-100 , 101-xxx, 200- ".into()).unwrap();
|
let r2: Range = Header::parse(&req("bytes= 1-100 , 101-xxx, 200- ")).unwrap();
|
||||||
let r3 = Range::Bytes(vec![
|
let r3 = Range::Bytes(vec![
|
||||||
ByteRangeSpec::FromTo(1, 100),
|
ByteRangeSpec::FromTo(1, 100),
|
||||||
ByteRangeSpec::AllFrom(200),
|
ByteRangeSpec::From(200),
|
||||||
]);
|
]);
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
assert_eq!(r2, r3);
|
assert_eq!(r2, r3);
|
||||||
|
|
||||||
let r: Range = Header::parse_header(&"bytes=1-100,-100".into()).unwrap();
|
let r: Range = Header::parse(&req("bytes=1-100,-100")).unwrap();
|
||||||
let r2: Range = Header::parse_header(&"bytes=1-100, ,,-100".into()).unwrap();
|
let r2: Range = Header::parse(&req("bytes=1-100, ,,-100")).unwrap();
|
||||||
let r3 = Range::Bytes(vec![
|
let r3 = Range::Bytes(vec![
|
||||||
ByteRangeSpec::FromTo(1, 100),
|
ByteRangeSpec::FromTo(1, 100),
|
||||||
ByteRangeSpec::Last(100),
|
ByteRangeSpec::Last(100),
|
||||||
@ -294,71 +333,65 @@ mod tests {
|
|||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
assert_eq!(r2, r3);
|
assert_eq!(r2, r3);
|
||||||
|
|
||||||
let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap();
|
let r: Range = Header::parse(&req("custom=1-100,-100")).unwrap();
|
||||||
let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned());
|
let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned());
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_unregistered_range_valid() {
|
fn test_parse_unregistered_range_valid() {
|
||||||
let r: Range = Header::parse_header(&"custom=1-100,-100".into()).unwrap();
|
let r: Range = Header::parse(&req("custom=1-100,-100")).unwrap();
|
||||||
let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned());
|
let r2 = Range::Unregistered("custom".to_owned(), "1-100,-100".to_owned());
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
|
|
||||||
let r: Range = Header::parse_header(&"custom=abcd".into()).unwrap();
|
let r: Range = Header::parse(&req("custom=abcd")).unwrap();
|
||||||
let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned());
|
let r2 = Range::Unregistered("custom".to_owned(), "abcd".to_owned());
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
|
|
||||||
let r: Range = Header::parse_header(&"custom=xxx-yyy".into()).unwrap();
|
let r: Range = Header::parse(&req("custom=xxx-yyy")).unwrap();
|
||||||
let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned());
|
let r2 = Range::Unregistered("custom".to_owned(), "xxx-yyy".to_owned());
|
||||||
assert_eq!(r, r2);
|
assert_eq!(r, r2);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_parse_invalid() {
|
fn test_parse_invalid() {
|
||||||
let r: ::Result<Range> = Header::parse_header(&"bytes=1-a,-".into());
|
let r: Result<Range, ParseError> = Header::parse(&req("bytes=1-a,-"));
|
||||||
assert_eq!(r.ok(), None);
|
assert_eq!(r.ok(), None);
|
||||||
|
|
||||||
let r: ::Result<Range> = Header::parse_header(&"bytes=1-2-3".into());
|
let r: Result<Range, ParseError> = Header::parse(&req("bytes=1-2-3"));
|
||||||
assert_eq!(r.ok(), None);
|
assert_eq!(r.ok(), None);
|
||||||
|
|
||||||
let r: ::Result<Range> = Header::parse_header(&"abc".into());
|
let r: Result<Range, ParseError> = Header::parse(&req("abc"));
|
||||||
assert_eq!(r.ok(), None);
|
assert_eq!(r.ok(), None);
|
||||||
|
|
||||||
let r: ::Result<Range> = Header::parse_header(&"bytes=1-100=".into());
|
let r: Result<Range, ParseError> = Header::parse(&req("bytes=1-100="));
|
||||||
assert_eq!(r.ok(), None);
|
assert_eq!(r.ok(), None);
|
||||||
|
|
||||||
let r: ::Result<Range> = Header::parse_header(&"bytes=".into());
|
let r: Result<Range, ParseError> = Header::parse(&req("bytes="));
|
||||||
assert_eq!(r.ok(), None);
|
assert_eq!(r.ok(), None);
|
||||||
|
|
||||||
let r: ::Result<Range> = Header::parse_header(&"custom=".into());
|
let r: Result<Range, ParseError> = Header::parse(&req("custom="));
|
||||||
assert_eq!(r.ok(), None);
|
assert_eq!(r.ok(), None);
|
||||||
|
|
||||||
let r: ::Result<Range> = Header::parse_header(&"=1-100".into());
|
let r: Result<Range, ParseError> = Header::parse(&req("=1-100"));
|
||||||
assert_eq!(r.ok(), None);
|
assert_eq!(r.ok(), None);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_fmt() {
|
fn test_fmt() {
|
||||||
use header::Headers;
|
let range = Range::Bytes(vec![
|
||||||
|
|
||||||
let mut headers = Headers::new();
|
|
||||||
|
|
||||||
headers.set(Range::Bytes(vec![
|
|
||||||
ByteRangeSpec::FromTo(0, 1000),
|
ByteRangeSpec::FromTo(0, 1000),
|
||||||
ByteRangeSpec::AllFrom(2000),
|
ByteRangeSpec::From(2000),
|
||||||
]));
|
]);
|
||||||
assert_eq!(&headers.to_string(), "Range: bytes=0-1000,2000-\r\n");
|
assert_eq!(&range.to_string(), "bytes=0-1000,2000-");
|
||||||
|
|
||||||
headers.clear();
|
let range = Range::Bytes(vec![]);
|
||||||
headers.set(Range::Bytes(vec![]));
|
|
||||||
|
|
||||||
assert_eq!(&headers.to_string(), "Range: bytes=\r\n");
|
assert_eq!(&range.to_string(), "bytes=");
|
||||||
|
|
||||||
headers.clear();
|
let range = Range::Unregistered("custom".to_owned(), "1-xxx".to_owned());
|
||||||
headers.set(Range::Unregistered("custom".to_owned(), "1-xxx".to_owned()));
|
|
||||||
|
|
||||||
assert_eq!(&headers.to_string(), "Range: custom=1-xxx\r\n");
|
assert_eq!(&range.to_string(), "custom=1-xxx");
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -379,17 +412,11 @@ mod tests {
|
|||||||
assert_eq!(None, ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3));
|
assert_eq!(None, ByteRangeSpec::FromTo(2, 1).to_satisfiable_range(3));
|
||||||
assert_eq!(None, ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0));
|
assert_eq!(None, ByteRangeSpec::FromTo(0, 0).to_satisfiable_range(0));
|
||||||
|
|
||||||
assert_eq!(
|
assert_eq!(Some((0, 2)), ByteRangeSpec::From(0).to_satisfiable_range(3));
|
||||||
Some((0, 2)),
|
assert_eq!(Some((2, 2)), ByteRangeSpec::From(2).to_satisfiable_range(3));
|
||||||
ByteRangeSpec::AllFrom(0).to_satisfiable_range(3)
|
assert_eq!(None, ByteRangeSpec::From(3).to_satisfiable_range(3));
|
||||||
);
|
assert_eq!(None, ByteRangeSpec::From(5).to_satisfiable_range(3));
|
||||||
assert_eq!(
|
assert_eq!(None, ByteRangeSpec::From(0).to_satisfiable_range(0));
|
||||||
Some((2, 2)),
|
|
||||||
ByteRangeSpec::AllFrom(2).to_satisfiable_range(3)
|
|
||||||
);
|
|
||||||
assert_eq!(None, ByteRangeSpec::AllFrom(3).to_satisfiable_range(3));
|
|
||||||
assert_eq!(None, ByteRangeSpec::AllFrom(5).to_satisfiable_range(3));
|
|
||||||
assert_eq!(None, ByteRangeSpec::AllFrom(0).to_satisfiable_range(0));
|
|
||||||
|
|
||||||
assert_eq!(Some((1, 2)), ByteRangeSpec::Last(2).to_satisfiable_range(3));
|
assert_eq!(Some((1, 2)), ByteRangeSpec::Last(2).to_satisfiable_range(3));
|
||||||
assert_eq!(Some((2, 2)), ByteRangeSpec::Last(1).to_satisfiable_range(3));
|
assert_eq!(Some((2, 2)), ByteRangeSpec::Last(1).to_satisfiable_range(3));
|
||||||
|
Loading…
x
Reference in New Issue
Block a user