http_types_rs/other/
referer.rs1use crate::headers::{Header, 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 location(&self) -> &Url {
75 &self.location
76 }
77
78 pub fn set_location<U>(&mut self, location: U) -> Result<(), U::Error>
80 where
81 U: TryInto<Url>,
82 U::Error: std::fmt::Debug,
83 {
84 self.location = location.try_into()?;
85 Ok(())
86 }
87}
88
89impl Header for Referer {
90 fn header_name(&self) -> HeaderName {
91 REFERER
92 }
93
94 fn header_value(&self) -> HeaderValue {
95 let output = self.location.to_string();
96
97 unsafe { HeaderValue::from_bytes_unchecked(output.into()) }
99 }
100}
101
102#[cfg(test)]
103mod test {
104 use super::*;
105 use crate::headers::Headers;
106
107 #[test]
108 fn smoke() -> crate::Result<()> {
109 let referer = Referer::new(Url::parse("https://example.net/test.json")?);
110
111 let mut headers = Headers::new();
112 referer.apply_header(&mut headers);
113
114 let base_url = Url::parse("https://example.net/")?;
115 let referer = Referer::from_headers(base_url, headers)?.unwrap();
116 assert_eq!(referer.location(), &Url::parse("https://example.net/test.json")?);
117 Ok(())
118 }
119
120 #[test]
121 fn bad_request_on_parse_error() {
122 let mut headers = Headers::new();
123 headers.insert(REFERER, "htt://<nori ate the tag. yum.>").unwrap();
124 let err = Referer::from_headers(Url::parse("https://example.net").unwrap(), headers).unwrap_err();
125 assert_eq!(err.status(), 500);
126 }
127
128 #[test]
129 fn fallback_works() -> crate::Result<()> {
130 let mut headers = Headers::new();
131 headers.insert(REFERER, "/test.json").unwrap();
132
133 let base_url = Url::parse("https://fallback.net/")?;
134 let referer = Referer::from_headers(base_url, headers)?.unwrap();
135 assert_eq!(referer.location(), &Url::parse("https://fallback.net/test.json")?);
136
137 let mut headers = Headers::new();
138 headers.insert(REFERER, "https://example.com/test.json").unwrap();
139
140 let base_url = Url::parse("https://fallback.net/")?;
141 let referer = Referer::from_headers(base_url, headers)?.unwrap();
142 assert_eq!(referer.location(), &Url::parse("https://example.com/test.json")?);
143 Ok(())
144 }
145}