mirror of https://github.com/http-rs/http-types
Add `other::Referer` header
This commit is contained in:
parent
ef5d1c84c0
commit
b0e460de6d
|
@ -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;
|
||||
|
|
|
@ -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(())
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue