Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 10 additions & 26 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,45 +15,29 @@ jobs:
rust: [stable, beta, nightly]

steps:
- uses: actions/checkout@v2
- uses: actions-rs/toolchain@v1
- uses: actions/checkout@v3
- uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ matrix.rust }}
profile: minimal
override: true

- name: cargo test --all
uses: actions-rs/cargo@v1
with:
command: test
args: --all
- name: cargo test --benches
if: matrix.rust == 'nightly'
uses: actions-rs/cargo@v1
with:
command: test
args: --benches

components: rustfmt
- run: cargo test --workspace
- if: matrix.rust == 'nightly'
run: cargo test --benches
- name: Check minimal versions
if: matrix.rust == 'nightly'
run: |
cargo clean
cargo update -Z minimal-versions
cargo check
- run: cargo fmt --all --check

MSRV:
runs-on: ubuntu-latest

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v3
- name: Install rust ${{ env.minrust }}
uses: actions-rs/toolchain@v1
uses: dtolnay/rust-toolchain@master
with:
toolchain: ${{ env.minrust }}
profile: minimal
override: true

- name: cargo build
uses: actions-rs/cargo@v1
with:
command: build
- run: cargo build
1 change: 0 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,6 @@ members = [
http = "0.2.0"
headers-core = { version = "0.2", path = "./headers-core" }
base64 = "0.21"
bitflags = "1.0"
bytes = "1"
mime = "0.3.14"
sha1 = "0.10"
Expand Down
82 changes: 63 additions & 19 deletions src/common/cache_control.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use util::{self, csv, Seconds};
use HeaderValue;

/// `Cache-Control` header, defined in [RFC7234](https://tools.ietf.org/html/rfc7234#section-5.2)
/// with extensions in [RFC8246](https://www.rfc-editor.org/rfc/rfc8246)
///
/// The `Cache-Control` header field is used to specify directives for
/// caches along the request/response chain. Such cache directives are
Expand Down Expand Up @@ -43,16 +44,32 @@ pub struct CacheControl {
s_max_age: Option<Seconds>,
}

bitflags! {
struct Flags: u32 {
const NO_CACHE = 0b00000001;
const NO_STORE = 0b00000010;
const NO_TRANSFORM = 0b00000100;
const ONLY_IF_CACHED = 0b00001000;
const MUST_REVALIDATE = 0b00010000;
const PUBLIC = 0b00100000;
const PRIVATE = 0b01000000;
const PROXY_REVALIDATE = 0b10000000;
#[derive(Debug, Clone, PartialEq)]
struct Flags {
bits: u64,
}

impl Flags {
const NO_CACHE: Self = Self { bits: 0b000000001 };
const NO_STORE: Self = Self { bits: 0b000000010 };
const NO_TRANSFORM: Self = Self { bits: 0b000000100 };
const ONLY_IF_CACHED: Self = Self { bits: 0b000001000 };
const MUST_REVALIDATE: Self = Self { bits: 0b000010000 };
const PUBLIC: Self = Self { bits: 0b000100000 };
const PRIVATE: Self = Self { bits: 0b001000000 };
const PROXY_REVALIDATE: Self = Self { bits: 0b010000000 };
const IMMUTABLE: Self = Self { bits: 0b100000000 };

fn empty() -> Self {
Self { bits: 0 }
}

fn contains(&self, flag: Self) -> bool {
(self.bits & flag.bits) != 0
}

fn insert(&mut self, flag: Self) {
self.bits |= flag.bits;
}
}

Expand Down Expand Up @@ -100,6 +117,11 @@ impl CacheControl {
self.flags.contains(Flags::PRIVATE)
}

/// Check if the `immutable` directive is set.
pub fn immutable(&self) -> bool {
self.flags.contains(Flags::IMMUTABLE)
}

/// Get the value of the `max-age` directive if set.
pub fn max_age(&self) -> Option<Duration> {
self.max_age.map(Into::into)
Expand Down Expand Up @@ -158,27 +180,33 @@ impl CacheControl {
self
}

/// Set the `immutable` directive.
pub fn with_immutable(mut self) -> Self {
self.flags.insert(Flags::IMMUTABLE);
self
}

/// Set the `max-age` directive.
pub fn with_max_age(mut self, seconds: Duration) -> Self {
self.max_age = Some(seconds.into());
pub fn with_max_age(mut self, duration: Duration) -> Self {
self.max_age = Some(duration.into());
self
}

/// Set the `max-stale` directive.
pub fn with_max_stale(mut self, seconds: Duration) -> Self {
self.max_stale = Some(seconds.into());
pub fn with_max_stale(mut self, duration: Duration) -> Self {
self.max_stale = Some(duration.into());
self
}

/// Set the `min-fresh` directive.
pub fn with_min_fresh(mut self, seconds: Duration) -> Self {
self.min_fresh = Some(seconds.into());
pub fn with_min_fresh(mut self, duration: Duration) -> Self {
self.min_fresh = Some(duration.into());
self
}

/// Set the `s-maxage` directive.
pub fn with_s_max_age(mut self, seconds: Duration) -> Self {
self.s_max_age = Some(seconds.into());
pub fn with_s_max_age(mut self, duration: Duration) -> Self {
self.s_max_age = Some(duration.into());
self
}
}
Expand Down Expand Up @@ -236,6 +264,9 @@ impl FromIterator<KnownDirective> for FromIter {
Directive::Private => {
cc.flags.insert(Flags::PRIVATE);
}
Directive::Immutable => {
cc.flags.insert(Flags::IMMUTABLE);
}
Directive::ProxyRevalidate => {
cc.flags.insert(Flags::PROXY_REVALIDATE);
}
Expand Down Expand Up @@ -278,6 +309,7 @@ impl<'a> fmt::Display for Fmt<'a> {
if_flag(Flags::MUST_REVALIDATE, Directive::MustRevalidate),
if_flag(Flags::PUBLIC, Directive::Public),
if_flag(Flags::PRIVATE, Directive::Private),
if_flag(Flags::IMMUTABLE, Directive::Immutable),
if_flag(Flags::PROXY_REVALIDATE, Directive::ProxyRevalidate),
self.0
.max_age
Expand Down Expand Up @@ -325,6 +357,7 @@ enum Directive {
MustRevalidate,
Public,
Private,
Immutable,
ProxyRevalidate,
SMaxAge(u64),
}
Expand All @@ -345,6 +378,7 @@ impl fmt::Display for Directive {
Directive::MustRevalidate => "must-revalidate",
Directive::Public => "public",
Directive::Private => "private",
Directive::Immutable => "immutable",
Directive::ProxyRevalidate => "proxy-revalidate",
Directive::SMaxAge(secs) => return write!(f, "s-maxage={}", secs),
},
Expand All @@ -364,6 +398,7 @@ impl FromStr for KnownDirective {
"must-revalidate" => Directive::MustRevalidate,
"public" => Directive::Public,
"private" => Directive::Private,
"immutable" => Directive::Immutable,
"proxy-revalidate" => Directive::ProxyRevalidate,
"" => return Err(()),
_ => match s.find('=') {
Expand Down Expand Up @@ -428,9 +463,18 @@ mod tests {
);
}

#[test]
fn test_immutable() {
let cc = CacheControl::new().with_immutable();
let headers = test_encode(cc.clone());
assert_eq!(headers["cache-control"], "immutable");
assert_eq!(test_decode::<CacheControl>(&["immutable"]).unwrap(), cc);
assert!(cc.immutable());
}

#[test]
fn test_parse_bad_syntax() {
assert_eq!(test_decode::<CacheControl>(&["max-age=lolz"]), None,);
assert_eq!(test_decode::<CacheControl>(&["max-age=lolz"]), None);
}

#[test]
Expand Down
33 changes: 17 additions & 16 deletions src/common/content_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -178,22 +178,23 @@ fn split_in_two(s: &str, separator: char) -> Option<(&str, &str)> {
}

/*
test_header!(test_bytes,
vec![b"bytes 0-499/500"],
Some(ContentRange(ContentRangeSpec::Bytes {
range: Some((0, 499)),
complete_length: Some(500)
})));

test_header!(test_bytes_unknown_len,
vec![b"bytes 0-499/*"],
Some(ContentRange(ContentRangeSpec::Bytes {
range: Some((0, 499)),
complete_length: None
})));

test_header!(test_bytes_unknown_range,
vec![b"bytes */500"],
test_header!(test_bytes,
vec![b"bytes 0-499/500"],
Some(ContentRange(ContentRangeSpec::Bytes {
range: Some((0, 499)),
complete_length: Some(500)
})));

test_header!(test_bytes_unknown_len,
vec![b"bytes 0-499/*"],
Some(ContentRange(ContentRangeSpec::Bytes {
range: Some((0, 499)),
complete_length: None
})));

test_header!(test_bytes_unknown_range,
vec![b"bytes */
500"],
Some(ContentRange(ContentRangeSpec::Bytes {
range: None,
complete_length: Some(500)
Expand Down
19 changes: 19 additions & 0 deletions src/common/content_type.rs
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,16 @@ impl fmt::Display for ContentType {
}
}

impl std::str::FromStr for ContentType {
type Err = ::Error;

fn from_str(s: &str) -> Result<ContentType, Self::Err> {
s.parse::<Mime>()
.map(|m| m.into())
.map_err(|_| ::Error::invalid())
}
}

#[cfg(test)]
mod tests {
use super::super::test_decode;
Expand All @@ -148,6 +158,15 @@ mod tests {
);
}

#[test]
fn from_str() {
assert_eq!(
"application/json".parse::<ContentType>().unwrap(),
ContentType::json(),
);
assert!("invalid-mimetype".parse::<ContentType>().is_err());
}

bench_header!(bench_plain, ContentType, "text/plain");
bench_header!(bench_json, ContentType, "application/json");
bench_header!(
Expand Down
4 changes: 1 addition & 3 deletions src/common/etag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,7 @@ error_type!(InvalidETag);
impl FromStr for ETag {
type Err = InvalidETag;
fn from_str(src: &str) -> Result<Self, Self::Err> {
let val = src
.parse()
.map_err(|_| InvalidETag { _inner: () })?;
let val = src.parse().map_err(|_| InvalidETag { _inner: () })?;

EntityTag::from_owned(val)
.map(ETag)
Expand Down
2 changes: 1 addition & 1 deletion src/common/host.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt;
use std::convert::TryFrom;
use std::fmt;

use http::uri::Authority;

Expand Down
4 changes: 3 additions & 1 deletion src/common/if_range.rs
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,9 @@ impl IfRange {
pub fn is_modified(&self, etag: Option<&ETag>, last_modified: Option<&LastModified>) -> bool {
match self.0 {
IfRange_::Date(since) => last_modified.map(|time| since < time.0).unwrap_or(true),
IfRange_::EntityTag(ref entity) => etag.map(|etag| !etag.0.strong_eq(entity)).unwrap_or(true),
IfRange_::EntityTag(ref entity) => {
etag.map(|etag| !etag.0.strong_eq(entity)).unwrap_or(true)
}
}
}
}
Expand Down
2 changes: 1 addition & 1 deletion src/common/origin.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use std::fmt;
use std::convert::TryFrom;
use std::fmt;

use bytes::Bytes;
use http::uri::{self, Authority, Scheme, Uri};
Expand Down
2 changes: 0 additions & 2 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -73,8 +73,6 @@
//! ```

extern crate base64;
#[macro_use]
extern crate bitflags;
extern crate bytes;
extern crate headers_core;
extern crate http;
Expand Down
13 changes: 5 additions & 8 deletions src/util/entity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -167,9 +167,7 @@ impl EntityTag {
}

pub(crate) fn from_val(val: &HeaderValue) -> Option<EntityTag> {
EntityTag::parse(val.as_bytes()).map(|_entity| {
EntityTag(val.clone())
})
EntityTag::parse(val.as_bytes()).map(|_entity| EntityTag(val.clone()))
}
}

Expand Down Expand Up @@ -239,11 +237,10 @@ impl EntityTagRange {
{
match *self {
EntityTagRange::Any => true,
EntityTagRange::Tags(ref tags) => {
tags.iter()
.flat_map(EntityTag::<&str>::parse)
.any(|tag| func(&tag, entity))
},
EntityTagRange::Tags(ref tags) => tags
.iter()
.flat_map(EntityTag::<&str>::parse)
.any(|tag| func(&tag, entity)),
}
}
}
Expand Down
8 changes: 4 additions & 4 deletions src/util/flat_csv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,8 +120,8 @@ impl<'a, Sep: Separator> FromIterator<&'a HeaderValue> for FlatCsv<Sep> {
buf.extend_from_slice(val.as_bytes());
}

let val =
HeaderValue::from_maybe_shared(buf.freeze()).expect("comma separated HeaderValues are valid");
let val = HeaderValue::from_maybe_shared(buf.freeze())
.expect("comma separated HeaderValues are valid");

val.into()
}
Expand Down Expand Up @@ -151,8 +151,8 @@ impl<Sep: Separator> FromIterator<HeaderValue> for FlatCsv<Sep> {
buf.extend_from_slice(val.as_bytes());
}

let val =
HeaderValue::from_maybe_shared(buf.freeze()).expect("comma separated HeaderValues are valid");
let val = HeaderValue::from_maybe_shared(buf.freeze())
.expect("comma separated HeaderValues are valid");

val.into()
}
Expand Down