pub struct WebFingerRequest {
pub resource: Resource,
pub host: String,
pub rels: Vec<Rel>,
}Expand description
A WebFinger request.
This represents the request portion of a WebFinger query that can be executed against a WebFinger server.
Request stores three pieces of information that map directly to the outgoing request URL:
resourcebecomes theresource=query parameter.hostbecomes the HTTPS authority for the request URL.- Each value in
relsbecomes anotherrel=query parameter, in insertion order.
In other words, this request:
use webfinger_rs::WebFingerRequest;
let request = WebFingerRequest::builder("acct:carol@example.com")?
.host("example.com")
.rel("http://webfinger.net/rel/profile-page")
.rel("http://webfinger.net/rel/avatar")
.build();maps to this URL shape:
https://example.com/.well-known/webfinger?resource=acct%3Acarol%40example.com&rel=http%3A%2F%2Fwebfinger.net%2Frel%2Fprofile-page&rel=http%3A%2F%2Fwebfinger.net%2Frel%2Favatarhost is required when you want to turn the request into an outgoing HTTP request, because the
WebFinger endpoint is always built as https://{host}/.well-known/webfinger?.... For acct:
resources, set host to the domain that serves WebFinger for that account. In the common case,
that is the same domain that appears after @ in the acct: URI.
resource must be an absolute URI, not a relative reference. acct: resources should include
the full account URI, such as acct:carol@example.com, not just carol@example.com or
@carol@example.com. Hierarchical URIs such as https://example.com/users/carol are also
accepted.
Repeated relation filters are encoded as repeated rel query parameters rather than as a
comma-separated list.
See: RFC 7033 section 4.1 for query-construction rules and parameter encoding.
See: RFC 7565 section 3 for the
acct: URI syntax used by account resources.
§Examples
use webfinger_rs::WebFingerRequest;
let request = WebFingerRequest::builder("acct:carol@example.com")?
.host("example.com")
.rel("http://webfinger.net/rel/profile-page")
.build();To execute the query, enable the reqwest feature and call Request::execute_reqwest.
let response = request.execute_reqwest().await?;Request can be used as an Axum extractor as it implements axum::extract::FromRequestParts.
use webfinger_rs::{WebFingerRequest, WebFingerResponse};
async fn handler(request: WebFingerRequest) -> WebFingerResponse {
// ... handle the request ...
}Fields§
§resource: ResourceQuery target.
This is the absolute URI of the resource to query. It will be stored in the resource
query parameter.
For account lookups, use the full acct: URI, for example acct:carol@example.com.
Relative references such as carol, /relative, ../x, and empty values are rejected by
builders and first-party server extractors.
See: RFC 7565 section 3.
host: StringThe host to query.
This becomes the HTTPS authority of the final request URL. When converting this request to
an outgoing http::Uri, the crate builds
https://{host}/.well-known/webfinger?....
Set this explicitly before executing the request or converting it into an outgoing URL.
For acct: resources, this is usually the domain part of the account identifier.
See: RFC 7033 section 4.1.
TODO: this might be better as an Option<Uri> or Option<Host> or something similar. When
the resource has a host part, it should be used unless this field is set.
rels: Vec<Rel>Link relation types
This is a list of link relation types to query for. Each link relation type will be stored
in its own rel query parameter.
See: RFC 7033 section 4.1.
Implementations§
Source§impl WebFingerRequest
impl WebFingerRequest
Sourcepub async fn execute_reqwest(&self) -> Result<WebFingerResponse, Error>
Available on crate feature reqwest only.
pub async fn execute_reqwest(&self) -> Result<WebFingerResponse, Error>
reqwest only.Executes the WebFinger request with a fresh reqwest::Client.
This is the shortest path from a WebFingerRequest to a parsed WebFingerResponse.
The method:
- Converts the WebFinger query into a
GETreqwest::Request. - Creates a new
reqwest::Clientthat only sends HTTPS requests, including redirects. - Sends the request with that client.
- Rejects non-success HTTP statuses with
reqwest::Response::error_for_status. - Deserializes the response body as JSON into
WebFingerResponse.
Use this when the first-party WebFinger client configuration is sufficient. This path
follows RFC 7033’s HTTPS-only transport requirements by rejecting redirects to non-HTTPS
targets. If you need shared connection pooling, custom headers, middleware, proxies,
timeouts, or TLS settings, prefer Self::execute_reqwest_with_client instead.
Errors are returned as crate::Error:
- Request-construction failures surface as
crate::Error::Httporcrate::Error::InvalidUri. - Reqwest client-construction failures, transport failures, non-success HTTP statuses, and
JSON decoding failures surface as
crate::Error::Reqwest.
§Examples
use webfinger_rs::WebFingerRequest;
let request = WebFingerRequest::builder("acct:carol@example.com")?
.host("example.com")
.rel("http://webfinger.net/rel/profile-page")
.build();
let response = request.execute_reqwest().await?;
println!("{response:#?}");Sourcepub async fn execute_reqwest_with_client(
&self,
client: &Client,
) -> Result<WebFingerResponse, Error>
Available on crate feature reqwest only.
pub async fn execute_reqwest_with_client( &self, client: &Client, ) -> Result<WebFingerResponse, Error>
reqwest only.Executes the WebFinger request with a caller-provided reqwest::Client.
This follows the same conversion, status handling, and JSON decoding path as
Self::execute_reqwest, but reuses the client you provide instead of constructing a new
WebFinger-specific one for each call.
RFC 7033 requires clients to query WebFinger resources using HTTPS only and allows redirects only to HTTPS URIs. Caller-provided clients are used as-is, so configure them to reject non-HTTPS requests and redirect targets when you need RFC-compliant WebFinger execution.
Use this when your application already owns a configured client, for example to:
- reuse connection pools across multiple requests;
- set default headers, user agents, or auth;
- configure timeouts, proxies, redirects, or TLS behavior; or
- integrate with Reqwest middleware or client-wide instrumentation.
Non-success HTTP statuses and JSON decoding failures still surface as
crate::Error::Reqwest, because they originate from Reqwest’s response handling.
§Examples
use std::time::Duration;
use reqwest::Client;
use webfinger_rs::WebFingerRequest;
let client = Client::builder()
.timeout(Duration::from_secs(10))
.user_agent("webfinger-rs docs example")
.https_only(true)
.build()?;
let request = WebFingerRequest::builder("acct:carol@example.com")?
.host("example.com")
.build();
let response = request.execute_reqwest_with_client(&client).await?;
println!("{response:#?}");Sourcepub fn try_into_reqwest(&self) -> Result<Request, Error>
Available on crate feature reqwest only.
pub fn try_into_reqwest(&self) -> Result<Request, Error>
reqwest only.Converts this WebFinger query into a reqwest::Request without executing it.
This is useful when you want to inspect or modify the outgoing request before sending it, or when another part of your application is responsible for execution.
The resulting request is an HTTPS GET to the WebFinger well-known endpoint with the
current resource, host, and rel values encoded into the URL.
This only performs request construction. It does not send anything over the network.
§Examples
use webfinger_rs::WebFingerRequest;
let request = WebFingerRequest::builder("acct:carol@example.com")?
.host("example.com")
.rel("http://webfinger.net/rel/profile-page")
.build();
let reqwest_request = request.try_into_reqwest()?;
assert_eq!(reqwest_request.method(), reqwest::Method::GET);
assert_eq!(
reqwest_request.url().as_str(),
"https://example.com/.well-known/webfinger?resource=acct%3Acarol%40example.com&rel=http%3A%2F%2Fwebfinger.net%2Frel%2Fprofile-page"
);Examples found in repository?
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
11
12 let request = WebFingerRequest::builder(SUBJECT)?
13 .host(HOST)
14 .rel(PROFILE_PAGE_REL)
15 .rel(AVATAR_REL)
16 .build();
17
18 let reqwest_request = request.try_into_reqwest()?;
19 eprintln!("GET {}", reqwest_request.url());
20
21 let client = reqwest::Client::builder()
22 .danger_accept_invalid_certs(true)
23 .https_only(true)
24 .build()?;
25 let response = client.execute(reqwest_request).await?;
26 let response = WebFingerResponse::try_from_reqwest(response).await?;
27
28 println!("Subject: {}", response.subject);
29 for rel in [PROFILE_PAGE_REL, AVATAR_REL] {
30 if let Some(href) = response
31 .links
32 .iter()
33 .find(|link| link.rel.as_ref() == rel)
34 .and_then(|link| link.href.as_ref().map(|href| href.as_ref()))
35 {
36 println!("{rel}: {href}");
37 }
38 }
39 println!("{}", serde_json::to_string_pretty(&response)?);
40 Ok(())
41}Source§impl Request
impl Request
Sourcepub fn new(resource: Resource) -> Self
pub fn new(resource: Resource) -> Self
Creates a new WebFinger request.
This low-level constructor accepts an already validated Resource. Use
Request::builder or str::parse::<Resource>() when accepting untrusted resource text.
Sourcepub fn builder<U>(uri: U) -> Result<Builder, Error>
pub fn builder<U>(uri: U) -> Result<Builder, Error>
Creates a new Builder for a WebFinger request.
Examples found in repository?
9async fn main() -> Result<(), Box<dyn std::error::Error>> {
10 let _ = rustls::crypto::aws_lc_rs::default_provider().install_default();
11
12 let request = WebFingerRequest::builder(SUBJECT)?
13 .host(HOST)
14 .rel(PROFILE_PAGE_REL)
15 .rel(AVATAR_REL)
16 .build();
17
18 let reqwest_request = request.try_into_reqwest()?;
19 eprintln!("GET {}", reqwest_request.url());
20
21 let client = reqwest::Client::builder()
22 .danger_accept_invalid_certs(true)
23 .https_only(true)
24 .build()?;
25 let response = client.execute(reqwest_request).await?;
26 let response = WebFingerResponse::try_from_reqwest(response).await?;
27
28 println!("Subject: {}", response.subject);
29 for rel in [PROFILE_PAGE_REL, AVATAR_REL] {
30 if let Some(href) = response
31 .links
32 .iter()
33 .find(|link| link.rel.as_ref() == rel)
34 .and_then(|link| link.href.as_ref().map(|href| href.as_ref()))
35 {
36 println!("{rel}: {href}");
37 }
38 }
39 println!("{}", serde_json::to_string_pretty(&response)?);
40 Ok(())
41}More examples
23async fn main() -> Result<()> {
24 color_eyre::install()?;
25 tracing_subscriber::fmt()
26 .with_max_level(LevelFilter::DEBUG)
27 .init();
28
29 let addr = SocketAddr::new(Ipv4Addr::LOCALHOST.into(), 3000);
30 let router = Router::new()
31 .route(WELL_KNOWN_PATH, get(webfinger))
32 .route_layer(TraceLayer::new_for_http())
33 .into_make_service();
34 let config = tls_config().await?;
35 let unfiltered_request = WebFingerRequest::builder(SUBJECT)?.host(HOST).build();
36 let profile_request = WebFingerRequest::builder(SUBJECT)?
37 .host(HOST)
38 .rel(PROFILE_PAGE_REL)
39 .build();
40 let avatar_request = WebFingerRequest::builder(SUBJECT)?
41 .host(HOST)
42 .rel(AVATAR_REL)
43 .build();
44
45 info!("Listening at https://{addr}{WELL_KNOWN_PATH}");
46 info!(
47 "Unfiltered query: {}",
48 http::Uri::try_from(&unfiltered_request)?
49 );
50 info!(
51 "Profile-page query: {}",
52 http::Uri::try_from(&profile_request)?
53 );
54 info!("Avatar query: {}", http::Uri::try_from(&avatar_request)?);
55 axum_server::bind_rustls(addr, config).serve(router).await?;
56
57 Ok(())
58}Trait Implementations§
Source§impl<'de> Deserialize<'de> for Request
impl<'de> Deserialize<'de> for Request
Source§fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>where
__D: Deserializer<'de>,
impl Eq for Request
Source§impl FromRequest for WebFingerRequest
Available on crate feature actix only.
impl FromRequest for WebFingerRequest
actix only.Source§type Error = Error
type Error = Error
Extracts a WebFingerRequest from an Actix request.
The extractor reads:
- the host from the request URI authority or the HTTP
Hostheader; - the decoded
resourcequery parameter; and - every repeated decoded
relquery parameter.
Query parsing percent-decodes parameters while preserving RFC 3986 query semantics.
§Errors
- If the request has zero or more than one
resourcequery parameter, extraction returns a bad request. - If the request has no URI authority and no
Hostheader, extraction returnsErrorBadRequest("missing host"). - If the query contains malformed percent encoding, extraction returns a bad request.
- If
resourceis present but cannot be parsed as a URI, extraction returnsErrorBadRequest("invalid resource: ...").
See also the crate::actix module docs and the Actix example.
§Example
use actix_web::{get, App};
use webfinger_rs::{WebFingerRequest, WebFingerResponse};
#[get("/.well-known/webfinger")]
async fn webfinger(request: WebFingerRequest) -> WebFingerResponse {
WebFingerResponse::new(request.resource.to_string())
}
let app = App::new().service(webfinger);Source§type Future = Ready<Result<Request, <Request as FromRequest>::Error>>
type Future = Ready<Result<Request, <Request as FromRequest>::Error>>
Self. Read moreSource§fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future
fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future
Self from request parts asynchronously.Source§impl<S: Send + Sync> FromRequestParts<S> for WebFingerRequest
Available on crate feature axum only.
impl<S: Send + Sync> FromRequestParts<S> for WebFingerRequest
axum only.Source§async fn from_request_parts(
parts: &mut Parts,
_state: &S,
) -> Result<Self, Self::Rejection>
async fn from_request_parts( parts: &mut Parts, _state: &S, ) -> Result<Self, Self::Rejection>
Extracts a WebFingerRequest from Axum request parts.
The extractor expects a request routed to crate::WELL_KNOWN_PATH with:
- a
resourcequery parameter containing the target resource URI; and - zero or more repeated
relquery parameters.
Host resolution follows this order:
- Use the authority from
parts.uriwhen the request URI is absolute. - Otherwise, fall back to the HTTP
Hostheader.
The extracted host, parsed resource, and collected relations are used to construct the
resulting WebFingerRequest.
§Errors
- If the request has neither a URI authority nor a
Hostheader, extraction fails withRejection::MissingHost. - If the query string is missing
resource, contains more than oneresource, or contains malformed percent encoding, extraction fails withRejection::InvalidQueryString. - If
resourceis present but cannot be parsed as a URI, extraction fails withRejection::InvalidResource.
See also the crate::axum module docs and the Axum example.
§Example
use axum::{Router, routing::get};
use webfinger_rs::{WELL_KNOWN_PATH, WebFingerRequest, WebFingerResponse};
async fn webfinger(request: WebFingerRequest) -> WebFingerResponse {
WebFingerResponse::new(request.resource.to_string())
}
let app = Router::<()>::new().route(WELL_KNOWN_PATH, get(webfinger));Source§impl Ord for Request
impl Ord for Request
1.21.0 (const: unstable) · Source§fn max(self, other: Self) -> Selfwhere
Self: Sized,
fn max(self, other: Self) -> Selfwhere
Self: Sized,
Source§impl PartialOrd for Request
impl PartialOrd for Request
impl StructuralPartialEq for Request
Source§impl TryFrom<&Request> for PathAndQuery
impl TryFrom<&Request> for PathAndQuery
Source§type Error = InvalidUri
type Error = InvalidUri
Source§fn try_from(query: &WebFingerRequest) -> Result<PathAndQuery, InvalidUri>
fn try_from(query: &WebFingerRequest) -> Result<PathAndQuery, InvalidUri>
Auto Trait Implementations§
impl Freeze for Request
impl RefUnwindSafe for Request
impl Send for Request
impl Sync for Request
impl Unpin for Request
impl UnsafeUnpin for Request
impl UnwindSafe for Request
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> CloneToUninit for Twhere
T: Clone,
impl<T> CloneToUninit for Twhere
T: Clone,
Source§impl<Q, K> Comparable<K> for Q
impl<Q, K> Comparable<K> for Q
impl<T> DeserializeOwned for Twhere
T: for<'de> Deserialize<'de>,
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§impl<Q, K> Equivalent<K> for Q
impl<Q, K> Equivalent<K> for Q
Source§fn equivalent(&self, key: &K) -> bool
fn equivalent(&self, key: &K) -> bool
key and return true if they are equal.