Skip to content

Commit b2e3059

Browse files
committed
Add server::Allow
1 parent 82e3d5e commit b2e3059

File tree

3 files changed

+212
-0
lines changed

3 files changed

+212
-0
lines changed

src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -123,6 +123,7 @@ pub mod content;
123123
pub mod headers;
124124
pub mod mime;
125125
pub mod proxies;
126+
pub mod server;
126127

127128
mod body;
128129
mod error;

src/server/allow.rs

Lines changed: 206 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,206 @@
1+
use crate::headers::{HeaderName, HeaderValue, Headers, ToHeaderValues, ALLOW};
2+
use crate::Method;
3+
4+
use std::collections::{hash_set, HashSet};
5+
use std::fmt::{self, Debug, Write};
6+
use std::iter::Iterator;
7+
use std::option;
8+
use std::str::FromStr;
9+
10+
/// List the set of methods supported by a resource.
11+
///
12+
/// # Specifications
13+
///
14+
/// - [RFC 7231, section 7.4.1: Allow](https://tools.ietf.org/html/rfc7231#section-7.4.1)
15+
///
16+
/// # Examples
17+
///
18+
/// ```
19+
/// # fn main() -> http_types::Result<()> {
20+
/// #
21+
/// use http_types::{Method, Response};
22+
/// use http_types::server::Allow;
23+
///
24+
/// let mut allow = Allow::new();
25+
/// allow.insert(Method::Put);
26+
/// allow.insert(Method::Post);
27+
///
28+
/// let mut res = Response::new(200);
29+
/// allow.apply(&mut res);
30+
///
31+
/// let allow = Allow::from_headers(res)?.unwrap();
32+
/// assert!(allow.contains(Method::Put));
33+
/// assert!(allow.contains(Method::Post));
34+
/// #
35+
/// # Ok(()) }
36+
/// ```
37+
pub struct Allow {
38+
entries: HashSet<Method>,
39+
}
40+
41+
impl Allow {
42+
/// Create a new instance of `CacheControl`.
43+
pub fn new() -> Self {
44+
Self {
45+
entries: HashSet::new(),
46+
}
47+
}
48+
49+
/// Create a new instance from headers.
50+
pub fn from_headers(headers: impl AsRef<Headers>) -> crate::Result<Option<Self>> {
51+
let mut entries = HashSet::new();
52+
let headers = match headers.as_ref().get(ALLOW) {
53+
Some(headers) => headers,
54+
None => return Ok(None),
55+
};
56+
57+
for value in headers {
58+
for part in value.as_str().trim().split(',') {
59+
let method = Method::from_str(part.trim())?;
60+
entries.insert(method);
61+
}
62+
}
63+
64+
Ok(Some(Self { entries }))
65+
}
66+
67+
/// Sets the `Server-Timing` header.
68+
pub fn apply(&self, mut headers: impl AsMut<Headers>) {
69+
headers.as_mut().insert(ALLOW, self.value());
70+
}
71+
72+
/// Get the `HeaderName`.
73+
pub fn name(&self) -> HeaderName {
74+
ALLOW
75+
}
76+
77+
/// Get the `HeaderValue`.
78+
pub fn value(&self) -> HeaderValue {
79+
let mut output = String::new();
80+
for (n, method) in self.entries.iter().enumerate() {
81+
match n {
82+
0 => write!(output, "{}", method).unwrap(),
83+
_ => write!(output, ", {}", method).unwrap(),
84+
};
85+
}
86+
87+
// SAFETY: the internal string is validated to be ASCII.
88+
unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
89+
}
90+
91+
/// Push a method into the set of methods.
92+
pub fn insert(&mut self, method: Method) {
93+
self.entries.insert(method);
94+
}
95+
96+
/// An iterator visiting all server entries.
97+
pub fn iter(&self) -> Iter<'_> {
98+
Iter {
99+
inner: self.entries.iter(),
100+
}
101+
}
102+
103+
/// Returns `true` if the header contains the `Method`.
104+
pub fn contains(&self, method: Method) -> bool {
105+
self.entries.contains(&method)
106+
}
107+
}
108+
109+
impl IntoIterator for Allow {
110+
type Item = Method;
111+
type IntoIter = IntoIter;
112+
113+
#[inline]
114+
fn into_iter(self) -> Self::IntoIter {
115+
IntoIter {
116+
inner: self.entries.into_iter(),
117+
}
118+
}
119+
}
120+
121+
impl<'a> IntoIterator for &'a Allow {
122+
type Item = &'a Method;
123+
type IntoIter = Iter<'a>;
124+
125+
#[inline]
126+
fn into_iter(self) -> Self::IntoIter {
127+
self.iter()
128+
}
129+
}
130+
131+
/// A borrowing iterator over entries in `CacheControl`.
132+
#[derive(Debug)]
133+
pub struct IntoIter {
134+
inner: hash_set::IntoIter<Method>,
135+
}
136+
137+
impl Iterator for IntoIter {
138+
type Item = Method;
139+
140+
fn next(&mut self) -> Option<Self::Item> {
141+
self.inner.next()
142+
}
143+
144+
#[inline]
145+
fn size_hint(&self) -> (usize, Option<usize>) {
146+
self.inner.size_hint()
147+
}
148+
}
149+
150+
/// A lending iterator over entries in `CacheControl`.
151+
#[derive(Debug)]
152+
pub struct Iter<'a> {
153+
inner: hash_set::Iter<'a, Method>,
154+
}
155+
156+
impl<'a> Iterator for Iter<'a> {
157+
type Item = &'a Method;
158+
159+
fn next(&mut self) -> Option<Self::Item> {
160+
self.inner.next()
161+
}
162+
163+
#[inline]
164+
fn size_hint(&self) -> (usize, Option<usize>) {
165+
self.inner.size_hint()
166+
}
167+
}
168+
169+
impl ToHeaderValues for Allow {
170+
type Iter = option::IntoIter<HeaderValue>;
171+
fn to_header_values(&self) -> crate::Result<Self::Iter> {
172+
// A HeaderValue will always convert into itself.
173+
Ok(self.value().to_header_values().unwrap())
174+
}
175+
}
176+
177+
impl Debug for Allow {
178+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
179+
let mut list = f.debug_list();
180+
for method in &self.entries {
181+
list.entry(method);
182+
}
183+
list.finish()
184+
}
185+
}
186+
187+
#[cfg(test)]
188+
mod test {
189+
use super::*;
190+
use crate::headers::Headers;
191+
192+
#[test]
193+
fn smoke() -> crate::Result<()> {
194+
let mut allow = Allow::new();
195+
allow.insert(Method::Put);
196+
allow.insert(Method::Post);
197+
198+
let mut headers = Headers::new();
199+
allow.apply(&mut headers);
200+
201+
let allow = Allow::from_headers(headers)?.unwrap();
202+
assert!(allow.contains(Method::Put));
203+
assert!(allow.contains(Method::Post));
204+
Ok(())
205+
}
206+
}

src/server/mod.rs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
//! HTTP Server Context headers.
2+
3+
mod allow;
4+
5+
pub use allow::Allow;

0 commit comments

Comments
 (0)