mirror of
https://github.com/fafhrd91/actix-web
synced 2024-12-04 20:31:55 +01:00
add method for getting accept type preference (#1793)
This commit is contained in:
parent
2f11ef089b
commit
70f4747a23
@ -3,6 +3,7 @@
|
|||||||
## Unreleased - 2020-xx-xx
|
## Unreleased - 2020-xx-xx
|
||||||
### Added
|
### Added
|
||||||
* HttpResponse builders for 1xx status codes. [#1768]
|
* HttpResponse builders for 1xx status codes. [#1768]
|
||||||
|
* `Accept::mime_precedence` and `Accept::mime_preference`. [#1793]
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
* Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767]
|
* Started dropping `transfer-encoding: chunked` and `Content-Length` for 1XX and 204 responses. [#1767]
|
||||||
@ -12,6 +13,7 @@
|
|||||||
|
|
||||||
[#1767]: https://github.com/actix/actix-web/pull/1767
|
[#1767]: https://github.com/actix/actix-web/pull/1767
|
||||||
[#1768]: https://github.com/actix/actix-web/pull/1768
|
[#1768]: https://github.com/actix/actix-web/pull/1768
|
||||||
|
[#1793]: https://github.com/actix/actix-web/pull/1793
|
||||||
|
|
||||||
|
|
||||||
## 2.1.0 - 2020-10-30
|
## 2.1.0 - 2020-10-30
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
use std::cmp::Ordering;
|
||||||
|
|
||||||
use mime::Mime;
|
use mime::Mime;
|
||||||
|
|
||||||
use crate::header::{qitem, QualityItem};
|
use crate::header::{qitem, QualityItem};
|
||||||
@ -97,14 +99,14 @@ header! {
|
|||||||
test_header!(
|
test_header!(
|
||||||
test1,
|
test1,
|
||||||
vec![b"audio/*; q=0.2, audio/basic"],
|
vec![b"audio/*; q=0.2, audio/basic"],
|
||||||
Some(HeaderField(vec![
|
Some(Accept(vec![
|
||||||
QualityItem::new("audio/*".parse().unwrap(), q(200)),
|
QualityItem::new("audio/*".parse().unwrap(), q(200)),
|
||||||
qitem("audio/basic".parse().unwrap()),
|
qitem("audio/basic".parse().unwrap()),
|
||||||
])));
|
])));
|
||||||
test_header!(
|
test_header!(
|
||||||
test2,
|
test2,
|
||||||
vec![b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"],
|
vec![b"text/plain; q=0.5, text/html, text/x-dvi; q=0.8, text/x-c"],
|
||||||
Some(HeaderField(vec![
|
Some(Accept(vec![
|
||||||
QualityItem::new(mime::TEXT_PLAIN, q(500)),
|
QualityItem::new(mime::TEXT_PLAIN, q(500)),
|
||||||
qitem(mime::TEXT_HTML),
|
qitem(mime::TEXT_HTML),
|
||||||
QualityItem::new(
|
QualityItem::new(
|
||||||
@ -138,23 +140,148 @@ header! {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Accept {
|
impl Accept {
|
||||||
/// A constructor to easily create `Accept: */*`.
|
/// Construct `Accept: */*`.
|
||||||
pub fn star() -> Accept {
|
pub fn star() -> Accept {
|
||||||
Accept(vec![qitem(mime::STAR_STAR)])
|
Accept(vec![qitem(mime::STAR_STAR)])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create `Accept: application/json`.
|
/// Construct `Accept: application/json`.
|
||||||
pub fn json() -> Accept {
|
pub fn json() -> Accept {
|
||||||
Accept(vec![qitem(mime::APPLICATION_JSON)])
|
Accept(vec![qitem(mime::APPLICATION_JSON)])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create `Accept: text/*`.
|
/// Construct `Accept: text/*`.
|
||||||
pub fn text() -> Accept {
|
pub fn text() -> Accept {
|
||||||
Accept(vec![qitem(mime::TEXT_STAR)])
|
Accept(vec![qitem(mime::TEXT_STAR)])
|
||||||
}
|
}
|
||||||
|
|
||||||
/// A constructor to easily create `Accept: image/*`.
|
/// Construct `Accept: image/*`.
|
||||||
pub fn image() -> Accept {
|
pub fn image() -> Accept {
|
||||||
Accept(vec![qitem(mime::IMAGE_STAR)])
|
Accept(vec![qitem(mime::IMAGE_STAR)])
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Construct `Accept: text/html`.
|
||||||
|
pub fn html() -> Accept {
|
||||||
|
Accept(vec![qitem(mime::TEXT_HTML)])
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns a sorted list of mime types from highest to lowest preference, accounting for
|
||||||
|
/// [q-factor weighting] and specificity.
|
||||||
|
///
|
||||||
|
/// [q-factor weighting]: https://tools.ietf.org/html/rfc7231#section-5.3.2
|
||||||
|
pub fn mime_precedence(&self) -> Vec<Mime> {
|
||||||
|
let mut types = self.0.clone();
|
||||||
|
|
||||||
|
// use stable sort so items with equal q-factor and specificity retain listed order
|
||||||
|
types.sort_by(|a, b| {
|
||||||
|
// sort by q-factor descending
|
||||||
|
b.quality.cmp(&a.quality).then_with(|| {
|
||||||
|
// use specificity rules on mime types with
|
||||||
|
// same q-factor (eg. text/html > text/* > */*)
|
||||||
|
|
||||||
|
// subtypes are not comparable if main type is star, so return
|
||||||
|
match (a.item.type_(), b.item.type_()) {
|
||||||
|
(mime::STAR, mime::STAR) => return Ordering::Equal,
|
||||||
|
|
||||||
|
// a is sorted after b
|
||||||
|
(mime::STAR, _) => return Ordering::Greater,
|
||||||
|
|
||||||
|
// a is sorted before b
|
||||||
|
(_, mime::STAR) => return Ordering::Less,
|
||||||
|
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
// in both these match expressions, the returned ordering appears
|
||||||
|
// inverted because sort is high-to-low ("descending") precedence
|
||||||
|
match (a.item.subtype(), b.item.subtype()) {
|
||||||
|
(mime::STAR, mime::STAR) => Ordering::Equal,
|
||||||
|
|
||||||
|
// a is sorted after b
|
||||||
|
(mime::STAR, _) => Ordering::Greater,
|
||||||
|
|
||||||
|
// a is sorted before b
|
||||||
|
(_, mime::STAR) => Ordering::Less,
|
||||||
|
|
||||||
|
_ => Ordering::Equal,
|
||||||
|
}
|
||||||
|
})
|
||||||
|
});
|
||||||
|
|
||||||
|
types.into_iter().map(|qitem| qitem.item).collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Extracts the most preferable mime type, accounting for [q-factor weighting].
|
||||||
|
///
|
||||||
|
/// If no q-factors are provided, the first mime type is chosen. Note that items without
|
||||||
|
/// q-factors are given the maximum preference value.
|
||||||
|
///
|
||||||
|
/// Returns `None` if contained list is empty.
|
||||||
|
///
|
||||||
|
/// [q-factor weighting]: https://tools.ietf.org/html/rfc7231#section-5.3.2
|
||||||
|
pub fn mime_preference(&self) -> Option<Mime> {
|
||||||
|
let types = self.mime_precedence();
|
||||||
|
types.first().cloned()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use super::*;
|
||||||
|
use crate::header::q;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mime_precedence() {
|
||||||
|
let test = Accept(vec![]);
|
||||||
|
assert!(test.mime_precedence().is_empty());
|
||||||
|
|
||||||
|
let test = Accept(vec![qitem(mime::APPLICATION_JSON)]);
|
||||||
|
assert_eq!(test.mime_precedence(), vec!(mime::APPLICATION_JSON));
|
||||||
|
|
||||||
|
let test = Accept(vec![
|
||||||
|
qitem(mime::TEXT_HTML),
|
||||||
|
"application/xhtml+xml".parse().unwrap(),
|
||||||
|
QualityItem::new("application/xml".parse().unwrap(), q(0.9)),
|
||||||
|
QualityItem::new(mime::STAR_STAR, q(0.8)),
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
test.mime_precedence(),
|
||||||
|
vec![
|
||||||
|
mime::TEXT_HTML,
|
||||||
|
"application/xhtml+xml".parse().unwrap(),
|
||||||
|
"application/xml".parse().unwrap(),
|
||||||
|
mime::STAR_STAR,
|
||||||
|
]
|
||||||
|
);
|
||||||
|
|
||||||
|
let test = Accept(vec![
|
||||||
|
qitem(mime::STAR_STAR),
|
||||||
|
qitem(mime::IMAGE_STAR),
|
||||||
|
qitem(mime::IMAGE_PNG),
|
||||||
|
]);
|
||||||
|
assert_eq!(
|
||||||
|
test.mime_precedence(),
|
||||||
|
vec![mime::IMAGE_PNG, mime::IMAGE_STAR, mime::STAR_STAR]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_mime_preference() {
|
||||||
|
let test = Accept(vec![
|
||||||
|
qitem(mime::TEXT_HTML),
|
||||||
|
"application/xhtml+xml".parse().unwrap(),
|
||||||
|
QualityItem::new("application/xml".parse().unwrap(), q(0.9)),
|
||||||
|
QualityItem::new(mime::STAR_STAR, q(0.8)),
|
||||||
|
]);
|
||||||
|
assert_eq!(test.mime_preference(), Some(mime::TEXT_HTML));
|
||||||
|
|
||||||
|
let test = Accept(vec![
|
||||||
|
QualityItem::new("video/*".parse().unwrap(), q(0.8)),
|
||||||
|
qitem(mime::IMAGE_PNG),
|
||||||
|
QualityItem::new(mime::STAR_STAR, q(0.5)),
|
||||||
|
qitem(mime::IMAGE_SVG),
|
||||||
|
QualityItem::new(mime::IMAGE_STAR, q(0.8)),
|
||||||
|
]);
|
||||||
|
assert_eq!(test.mime_preference(), Some(mime::IMAGE_PNG));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -18,7 +18,7 @@ use self::internal::IntoQuality;
|
|||||||
///
|
///
|
||||||
/// [RFC7231 Section 5.3.1](https://tools.ietf.org/html/rfc7231#section-5.3.1)
|
/// [RFC7231 Section 5.3.1](https://tools.ietf.org/html/rfc7231#section-5.3.1)
|
||||||
/// gives more information on quality values in HTTP header fields.
|
/// gives more information on quality values in HTTP header fields.
|
||||||
#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)]
|
#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)]
|
||||||
pub struct Quality(u16);
|
pub struct Quality(u16);
|
||||||
|
|
||||||
impl Default for Quality {
|
impl Default for Quality {
|
||||||
@ -121,7 +121,7 @@ fn from_f32(f: f32) -> Quality {
|
|||||||
/// Convenience function to wrap a value in a `QualityItem`
|
/// Convenience function to wrap a value in a `QualityItem`
|
||||||
/// Sets `q` to the default 1.0
|
/// Sets `q` to the default 1.0
|
||||||
pub fn qitem<T>(item: T) -> QualityItem<T> {
|
pub fn qitem<T>(item: T) -> QualityItem<T> {
|
||||||
QualityItem::new(item, Default::default())
|
QualityItem::new(item, Quality::default())
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Convenience function to create a `Quality` from a float or integer.
|
/// Convenience function to create a `Quality` from a float or integer.
|
||||||
|
Loading…
Reference in New Issue
Block a user