Skip to main content

WebFingerRequest

Struct WebFingerRequest 

Source
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:

  • resource becomes the resource= query parameter.
  • host becomes the HTTPS authority for the request URL.
  • Each value in rels becomes another rel= 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%2Favatar

host 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: Resource

Query 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: String

The 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

Source

pub async fn execute_reqwest(&self) -> Result<WebFingerResponse, Error>

Available on crate feature 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:

  1. Converts the WebFinger query into a GET reqwest::Request.
  2. Creates a new reqwest::Client that only sends HTTPS requests, including redirects.
  3. Sends the request with that client.
  4. Rejects non-success HTTP statuses with reqwest::Response::error_for_status.
  5. 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:

§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:#?}");
Source

pub async fn execute_reqwest_with_client( &self, client: &Client, ) -> Result<WebFingerResponse, Error>

Available on crate feature 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:#?}");
Source

pub fn try_into_reqwest(&self) -> Result<Request, Error>

Available on crate feature 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?
examples/client.rs (line 18)
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

Source

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.

Source

pub fn builder<U>(uri: U) -> Result<Builder, Error>

Creates a new Builder for a WebFinger request.

Examples found in repository?
examples/client.rs (line 12)
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
Hide additional examples
examples/axum.rs (line 35)
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 Clone for Request

Source§

fn clone(&self) -> Request

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Request

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl<'de> Deserialize<'de> for Request

Source§

fn deserialize<__D>(__deserializer: __D) -> Result<Self, __D::Error>
where __D: Deserializer<'de>,

Deserialize this value from the given Serde deserializer. Read more
Source§

impl Eq for Request

Source§

impl FromRequest for WebFingerRequest

Available on crate feature actix only.
Source§

type Error = Error

Extracts a WebFingerRequest from an Actix request.

The extractor reads:

  • the host from the request URI authority or the HTTP Host header;
  • the decoded resource query parameter; and
  • every repeated decoded rel query parameter.

Query parsing percent-decodes parameters while preserving RFC 3986 query semantics.

§Errors
  • If the request has zero or more than one resource query parameter, extraction returns a bad request.
  • If the request has no URI authority and no Host header, extraction returns ErrorBadRequest("missing host").
  • If the query contains malformed percent encoding, extraction returns a bad request.
  • If resource is present but cannot be parsed as a URI, extraction returns ErrorBadRequest("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>>

Future that resolves to a Self. Read more
Source§

fn from_request(req: &HttpRequest, _: &mut Payload) -> Self::Future

Create a Self from request parts asynchronously.
Source§

fn extract(req: &HttpRequest) -> Self::Future

Create a Self from request head asynchronously. Read more
Source§

impl<S: Send + Sync> FromRequestParts<S> for WebFingerRequest

Available on crate feature axum only.
Source§

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 resource query parameter containing the target resource URI; and
  • zero or more repeated rel query parameters.

Host resolution follows this order:

  1. Use the authority from parts.uri when the request URI is absolute.
  2. Otherwise, fall back to the HTTP Host header.

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 Host header, extraction fails with Rejection::MissingHost.
  • If the query string is missing resource, contains more than one resource, or contains malformed percent encoding, extraction fails with Rejection::InvalidQueryString.
  • If resource is present but cannot be parsed as a URI, extraction fails with Rejection::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§

type Rejection = Rejection

If the extractor fails it’ll use this “rejection” type. A rejection is a kind of error that can be converted into a response.
Source§

impl Hash for Request

Source§

fn hash<__H: Hasher>(&self, state: &mut __H)

Feeds this value into the given Hasher. Read more
1.3.0 · Source§

fn hash_slice<H>(data: &[Self], state: &mut H)
where H: Hasher, Self: Sized,

Feeds a slice of this type into the given Hasher. Read more
Source§

impl Ord for Request

Source§

fn cmp(&self, other: &Request) -> Ordering

This method returns an Ordering between self and other. Read more
1.21.0 (const: unstable) · Source§

fn max(self, other: Self) -> Self
where Self: Sized,

Compares and returns the maximum of two values. Read more
1.21.0 (const: unstable) · Source§

fn min(self, other: Self) -> Self
where Self: Sized,

Compares and returns the minimum of two values. Read more
1.50.0 (const: unstable) · Source§

fn clamp(self, min: Self, max: Self) -> Self
where Self: Sized,

Restrict a value to a certain interval. Read more
Source§

impl PartialEq for Request

Source§

fn eq(&self, other: &Request) -> bool

Tests for self and other values to be equal, and is used by ==.
1.0.0 (const: unstable) · Source§

fn ne(&self, other: &Rhs) -> bool

Tests for !=. The default implementation is almost always sufficient, and should not be overridden without very good reason.
Source§

impl PartialOrd for Request

Source§

fn partial_cmp(&self, other: &Request) -> Option<Ordering>

This method returns an ordering between self and other values if one exists. Read more
1.0.0 (const: unstable) · Source§

fn lt(&self, other: &Rhs) -> bool

Tests less than (for self and other) and is used by the < operator. Read more
1.0.0 (const: unstable) · Source§

fn le(&self, other: &Rhs) -> bool

Tests less than or equal to (for self and other) and is used by the <= operator. Read more
1.0.0 (const: unstable) · Source§

fn gt(&self, other: &Rhs) -> bool

Tests greater than (for self and other) and is used by the > operator. Read more
1.0.0 (const: unstable) · Source§

fn ge(&self, other: &Rhs) -> bool

Tests greater than or equal to (for self and other) and is used by the >= operator. Read more
Source§

impl Serialize for Request

Source§

fn serialize<__S>(&self, __serializer: __S) -> Result<__S::Ok, __S::Error>
where __S: Serializer,

Serialize this value into the given Serde serializer. Read more
Source§

impl StructuralPartialEq for Request

Source§

impl TryFrom<&Request> for PathAndQuery

Source§

type Error = InvalidUri

The type returned in the event of a conversion error.
Source§

fn try_from(query: &WebFingerRequest) -> Result<PathAndQuery, InvalidUri>

Performs the conversion.
Source§

impl TryFrom<&Request> for Uri

Source§

type Error = Error

The type returned in the event of a conversion error.
Source§

fn try_from(query: &WebFingerRequest) -> Result<Uri, Error>

Performs the conversion.
Source§

impl TryFrom<&Request> for Request<EmptyBody>

Available on crate feature reqwest only.
Source§

type Error = Error

The type returned in the event of a conversion error.
Source§

fn try_from(query: &WebFingerRequest) -> Result<Request<EmptyBody>, Error>

Performs the conversion.
Source§

impl TryFrom<&Request> for Request

Available on crate feature reqwest only.
Source§

type Error = Error

The type returned in the event of a conversion error.
Source§

fn try_from(query: &WebFingerRequest) -> Result<Request, Error>

Performs the conversion.

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<Q, K> Comparable<K> for Q
where Q: Ord + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn compare(&self, key: &K) -> Ordering

Compare self to key and return their ordering.
Source§

impl<T> DeserializeOwned for T
where T: for<'de> Deserialize<'de>,

Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Checks if this value is equivalent to the given key. Read more
Source§

impl<Q, K> Equivalent<K> for Q
where Q: Eq + ?Sized, K: Borrow<Q> + ?Sized,

Source§

fn equivalent(&self, key: &K) -> bool

Compare self to key and return true if they are equal.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> FromRef<T> for T
where T: Clone,

Source§

fn from_ref(input: &T) -> T

Converts to this type from a reference to the input type.
Source§

impl<S, T> FromRequest<S, ViaParts> for T
where S: Send + Sync, T: FromRequestParts<S>,

Source§

type Rejection = <T as FromRequestParts<S>>::Rejection

If the extractor fails it’ll use this “rejection” type. A rejection is a kind of error that can be converted into a response.
Source§

fn from_request( req: Request<Body>, state: &S, ) -> impl Future<Output = Result<T, <T as FromRequest<S, ViaParts>>::Rejection>>

Perform the extraction.
Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Sized + Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into<'async_trait>( self, ) -> Pin<Box<dyn Future<Output = Result<U, <U as TryFrom<T>>::Error>> + 'async_trait>>
where T: 'async_trait,

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more