webfinger_rs/
http.rs

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