http_types/other/
referer.rs1use crate::headers::{HeaderName, HeaderValue, Headers, REFERER};
2use crate::{bail_status as bail, Status, Url};
3
4use std::convert::TryInto;
5
6#[derive(Debug)]
37pub struct Referer {
38 location: Url,
39}
40
41impl Referer {
42 pub fn new(location: Url) -> Self {
44 Self { location }
45 }
46
47 pub fn from_headers<U>(base_url: U, headers: impl AsRef<Headers>) -> crate::Result<Option<Self>>
49 where
50 U: TryInto<Url>,
51 U::Error: std::fmt::Debug,
52 {
53 let headers = match headers.as_ref().get(REFERER) {
54 Some(headers) => headers,
55 None => return Ok(None),
56 };
57
58 let header_value = headers.iter().last().unwrap();
61
62 let url = match Url::parse(header_value.as_str()) {
63 Ok(url) => url,
64 Err(_) => match base_url.try_into() {
65 Ok(base_url) => base_url.join(header_value.as_str().trim()).status(500)?,
66 Err(_) => bail!(500, "Invalid base url provided"),
67 },
68 };
69
70 Ok(Some(Self { location: url }))
71 }
72
73 pub fn apply(&self, mut headers: impl AsMut<Headers>) {
75 headers.as_mut().insert(self.name(), self.value());
76 }
77
78 pub fn name(&self) -> HeaderName {
80 REFERER
81 }
82
83 pub fn value(&self) -> HeaderValue {
85 let output = self.location.to_string();
86
87 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
89 }
90
91 pub fn location(&self) -> &Url {
93 &self.location
94 }
95
96 pub fn set_location<U>(&mut self, location: U) -> Result<(), U::Error>
98 where
99 U: TryInto<Url>,
100 U::Error: std::fmt::Debug,
101 {
102 self.location = location.try_into()?;
103 Ok(())
104 }
105}
106
107#[cfg(test)]
108mod test {
109 use super::*;
110 use crate::headers::Headers;
111
112 #[test]
113 fn smoke() -> crate::Result<()> {
114 let referer = Referer::new(Url::parse("https://example.net/test.json")?);
115
116 let mut headers = Headers::new();
117 referer.apply(&mut headers);
118
119 let base_url = Url::parse("https://example.net/")?;
120 let referer = Referer::from_headers(base_url, headers)?.unwrap();
121 assert_eq!(
122 referer.location(),
123 &Url::parse("https://example.net/test.json")?
124 );
125 Ok(())
126 }
127
128 #[test]
129 fn bad_request_on_parse_error() {
130 let mut headers = Headers::new();
131 headers.insert(REFERER, "htt://<nori ate the tag. yum.>");
132 let err =
133 Referer::from_headers(Url::parse("https://example.net").unwrap(), headers).unwrap_err();
134 assert_eq!(err.status(), 500);
135 }
136
137 #[test]
138 fn fallback_works() -> crate::Result<()> {
139 let mut headers = Headers::new();
140 headers.insert(REFERER, "/test.json");
141
142 let base_url = Url::parse("https://fallback.net/")?;
143 let referer = Referer::from_headers(base_url, headers)?.unwrap();
144 assert_eq!(
145 referer.location(),
146 &Url::parse("https://fallback.net/test.json")?
147 );
148
149 let mut headers = Headers::new();
150 headers.insert(REFERER, "https://example.com/test.json");
151
152 let base_url = Url::parse("https://fallback.net/")?;
153 let referer = Referer::from_headers(base_url, headers)?.unwrap();
154 assert_eq!(
155 referer.location(),
156 &Url::parse("https://example.com/test.json")?
157 );
158 Ok(())
159 }
160}