webfinger_rs/
http.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
use std::str::FromStr;

use ::http::{
    uri::{InvalidUri, PathAndQuery, Scheme},
    Uri,
};
use percent_encoding::{utf8_percent_encode, AsciiSet};

use crate::{Request, Response};

const WELL_KNOWN_PATH: &str = "/.well-known/webfinger";
#[allow(unused)]
const JRD_CONTENT_TYPE: &str = "application/jrd+json";

/// The set of values to percent encode
///
/// Notably, this set does not include the `@`, `:`, `?`, and `/` characters which are allowed by
/// RFC 3986 in the query component.
///
/// See the following RFCs for more information:
/// - <https://www.rfc-editor.org/rfc/rfc7033#section-4.1>
/// - <https://www.rfc-editor.org/rfc/rfc3986#section-2.1>
/// - <https://www.rfc-editor.org/rfc/rfc3986#section-3.4>
/// - <https://www.rfc-editor.org/rfc/rfc3986#appendix-A>
///
/// Note: this may be implemented in the `percent-encoding` crate soon in
/// <https://github.com/servo/rust-url/pull/971>
const QUERY: AsciiSet = percent_encoding::CONTROLS
    // RFC 3986
    .add(b' ')
    .add(b'"')
    .add(b'#')
    .add(b'<')
    .add(b'>')
    .add(b'[')
    .add(b'\\')
    .add(b']')
    .add(b'^')
    .add(b'`')
    .add(b'{')
    .add(b'|')
    .add(b'}')
    // RFC 7033
    .add(b'=')
    .add(b'&');

impl TryFrom<&Request> for PathAndQuery {
    type Error = InvalidUri;

    fn try_from(query: &Request) -> Result<PathAndQuery, InvalidUri> {
        let resource = query.resource.to_string();
        let resource = utf8_percent_encode(&resource, &QUERY).to_string();
        let mut path = WELL_KNOWN_PATH.to_owned();
        path.push_str("?resource=");
        path.push_str(&resource);
        for rel in &query.link_relation_types {
            let rel = utf8_percent_encode(rel, &QUERY).to_string();
            path.push_str("&rel=");
            path.push_str(&rel);
        }
        PathAndQuery::from_str(&path)
    }
}

impl TryFrom<&Request> for Uri {
    type Error = http::Error;

    fn try_from(query: &Request) -> Result<Uri, http::Error> {
        let path_and_query = PathAndQuery::try_from(query)?;

        // HTTPS is mandatory
        // <https://www.rfc-editor.org/rfc/rfc7033.html#section-4>
        // <https://www.rfc-editor.org/rfc/rfc7033.html#section-9.1>
        const SCHEME: Scheme = Scheme::HTTPS;

        Uri::builder()
            .scheme(SCHEME)
            .authority(query.host.clone())
            .path_and_query(path_and_query)
            .build()
    }
}

impl TryFrom<&Response> for http::Response<()> {
    type Error = http::Error;
    fn try_from(_: &Response) -> Result<http::Response<()>, http::Error> {
        http::Response::builder()
            .header("Content-Type", "application/jrd+json")
            .body(())
    }
}