webfinger_rs/
http.rs

1use std::str::FromStr;
2
3use http::{
4    uri::{InvalidUri, PathAndQuery, Scheme},
5    Uri,
6};
7use percent_encoding::{utf8_percent_encode, AsciiSet};
8
9use crate::{WebFingerRequest, WebFingerResponse, WELL_KNOWN_PATH};
10
11/// The set of values to percent encode
12///
13/// Notably, this set does not include the `@`, `:`, `?`, and `/` characters which are allowed by
14/// RFC 3986 in the query component.
15///
16/// See the following RFCs for more information:
17/// - <https://www.rfc-editor.org/rfc/rfc7033#section-4.1>
18/// - <https://www.rfc-editor.org/rfc/rfc3986#section-2.1>
19/// - <https://www.rfc-editor.org/rfc/rfc3986#section-3.4>
20/// - <https://www.rfc-editor.org/rfc/rfc3986#appendix-A>
21///
22/// Note: this may be implemented in the `percent-encoding` crate soon in
23/// <https://github.com/servo/rust-url/pull/971>
24const QUERY: AsciiSet = percent_encoding::CONTROLS
25    // RFC 3986
26    .add(b' ')
27    .add(b'"')
28    .add(b'#')
29    .add(b'<')
30    .add(b'>')
31    .add(b'[')
32    .add(b'\\')
33    .add(b']')
34    .add(b'^')
35    .add(b'`')
36    .add(b'{')
37    .add(b'|')
38    .add(b'}')
39    // RFC 7033
40    .add(b'=')
41    .add(b'&');
42
43impl TryFrom<&WebFingerRequest> for PathAndQuery {
44    type Error = InvalidUri;
45
46    fn try_from(query: &WebFingerRequest) -> Result<PathAndQuery, InvalidUri> {
47        let resource = query.resource.to_string();
48        let resource = utf8_percent_encode(&resource, &QUERY).to_string();
49        let mut path = WELL_KNOWN_PATH.to_owned();
50        path.push_str("?resource=");
51        path.push_str(&resource);
52        for rel in &query.rels {
53            let rel = utf8_percent_encode(rel, &QUERY).to_string();
54            path.push_str("&rel=");
55            path.push_str(&rel);
56        }
57        PathAndQuery::from_str(&path)
58    }
59}
60
61impl TryFrom<&WebFingerRequest> for Uri {
62    type Error = http::Error;
63
64    fn try_from(query: &WebFingerRequest) -> Result<Uri, http::Error> {
65        let path_and_query = PathAndQuery::try_from(query)?;
66
67        // HTTPS is mandatory
68        // <https://www.rfc-editor.org/rfc/rfc7033.html#section-4>
69        // <https://www.rfc-editor.org/rfc/rfc7033.html#section-9.1>
70        const SCHEME: Scheme = Scheme::HTTPS;
71
72        Uri::builder()
73            .scheme(SCHEME)
74            .authority(query.host.clone())
75            .path_and_query(path_and_query)
76            .build()
77    }
78}
79
80impl TryFrom<&WebFingerResponse> for http::Response<()> {
81    type Error = http::Error;
82    fn try_from(_: &WebFingerResponse) -> Result<http::Response<()>, http::Error> {
83        http::Response::builder()
84            .header("Content-Type", "application/jrd+json")
85            .body(())
86    }
87}