Add `other::Referer` header

This commit is contained in:
Yoshua Wuyts 2020-11-05 23:53:09 +01:00 committed by Yoshua Wuyts
parent ef5d1c84c0
commit b0e460de6d
2 changed files with 164 additions and 0 deletions

View File

@ -2,8 +2,10 @@
mod date;
mod expect;
mod referer;
mod source_map;
pub use date::Date;
pub use expect::Expect;
pub use referer::Referer;
pub use source_map::SourceMap;

162
src/other/referer.rs Normal file
View File

@ -0,0 +1,162 @@
use crate::headers::{HeaderName, HeaderValue, Headers, REFERER};
use crate::{bail_status as bail, Status, Url};
use std::convert::TryInto;
/// Contains the address of the page making the request.
///
/// __Important__: Although this header has many innocent uses it can have
/// undesirable consequences for user security and privacy.
///
/// [MDN Documentation](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referer)
///
/// # Specifications
///
/// - [RFC 7231, section 5.5.2: Referer](https://tools.ietf.org/html/rfc7231#section-5.5.2)
///
/// # Examples
///
/// ```
/// # fn main() -> http_types::Result<()> {
/// #
/// use http_types::{Response, Url};
/// use http_types::other::Referer;
///
/// let referer = Referer::new(Url::parse("https://example.net/")?);
///
/// let mut res = Response::new(200);
/// referer.apply(&mut res);
///
/// let base_url = Url::parse("https://example.net/")?;
/// let referer = Referer::from_headers(base_url, res)?.unwrap();
/// assert_eq!(referer.location(), &Url::parse("https://example.net/")?);
/// #
/// # Ok(()) }
/// ```
#[derive(Debug)]
pub struct Referer {
location: Url,
}
impl Referer {
/// Create a new instance of `Referer` header.
pub fn new(location: Url) -> Self {
Self { location }
}
/// Create a new instance from headers.
pub fn from_headers<U>(base_url: U, headers: impl AsRef<Headers>) -> crate::Result<Option<Self>>
where
U: TryInto<Url>,
U::Error: std::fmt::Debug,
{
let headers = match headers.as_ref().get(REFERER) {
Some(headers) => headers,
None => return Ok(None),
};
// If we successfully parsed the header then there's always at least one
// entry. We want the last entry.
let header_value = headers.iter().last().unwrap();
let url = match Url::parse(header_value.as_str()) {
Ok(url) => url,
Err(_) => match base_url.try_into() {
Ok(base_url) => base_url.join(header_value.as_str().trim()).status(400)?,
Err(_) => bail!(400, "Invalid base url provided"),
},
};
Ok(Some(Self { location: url }))
}
/// Sets the header.
pub fn apply(&self, mut headers: impl AsMut<Headers>) {
headers.as_mut().insert(self.name(), self.value());
}
/// Get the `HeaderName`.
pub fn name(&self) -> HeaderName {
REFERER
}
/// Get the `HeaderValue`.
pub fn value(&self) -> HeaderValue {
let output = self.location.to_string();
// SAFETY: the internal string is validated to be ASCII.
unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
}
/// Get the url.
pub fn location(&self) -> &Url {
&self.location
}
/// Set the url.
pub fn set_location<U>(&mut self, location: U)
where
U: TryInto<Url>,
U::Error: std::fmt::Debug,
{
self.location = location
.try_into()
.expect("Could not convert into valid URL")
}
}
#[cfg(test)]
mod test {
use super::*;
use crate::headers::Headers;
#[test]
fn smoke() -> crate::Result<()> {
let referer = Referer::new(Url::parse("https://example.net/test.json")?);
let mut headers = Headers::new();
referer.apply(&mut headers);
let base_url = Url::parse("https://example.net/")?;
let referer = Referer::from_headers(base_url, headers)?.unwrap();
assert_eq!(
referer.location(),
&Url::parse("https://example.net/test.json")?
);
Ok(())
}
#[test]
fn bad_request_on_parse_error() -> crate::Result<()> {
let mut headers = Headers::new();
headers.insert(REFERER, "htt://<nori ate the tag. yum.>");
let err =
Referer::from_headers(Url::parse("https://example.net").unwrap(), headers).unwrap_err();
assert_eq!(err.status(), 400);
Ok(())
}
#[test]
fn fallback_works() -> crate::Result<()> {
let mut headers = Headers::new();
headers.insert(REFERER, "/test.json");
let base_url = Url::parse("https://fallback.net/")?;
let referer = Referer::from_headers(base_url, headers)?.unwrap();
assert_eq!(
referer.location(),
&Url::parse("https://fallback.net/test.json")?
);
let mut headers = Headers::new();
headers.insert(REFERER, "https://example.com/test.json");
let base_url = Url::parse("https://fallback.net/")?;
let referer = Referer::from_headers(base_url, headers)?.unwrap();
assert_eq!(
referer.location(),
&Url::parse("https://example.com/test.json")?
);
Ok(())
}
}