[GH-ISSUE #1532] [Client] Doc improvement for async usage #692

Closed
opened 2026-03-15 23:50:24 +03:00 by kerem · 1 comment
Owner

Originally created by @ErwanDL on GitHub (Aug 6, 2021).
Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/1532

Hello, and thanks for the work on this project.

I think the documentation for the client is somewhat lacking in examples, notably concerning usage of the AsyncClient. The only example I found in the docs is this one, that only tackles the UDP case, and does not use async/await syntax although I believe most users opt for the AsyncClient specifically because they want to use async/await.

Here is an adapated version of this example, using a TCP connection and async/await syntax (I am not 100% sure of the TCP client construction process, I tried to take inspiration from what I saw in the integration tests):

use std::net::Ipv4Addr;
use std::str::FromStr;
use tokio::net::TcpStream as TokioTcpStream;
use trust_dns_client::client::{AsyncClient, ClientHandle};
use trust_dns_client::proto::iocompat::AsyncIoTokioAsStd;
use trust_dns_client::rr::{DNSClass, Name, RData, RecordType};
use trust_dns_client::tcp::TcpClientStream;

#[tokio::main]
async fn main() {
    // Establishing a TCP connection
    let (stream, sender) =
        TcpClientStream::<AsyncIoTokioAsStd<TokioTcpStream>>::new(([8, 8, 8, 8], 53).into());

    // Create a new client, the bg is a background future which handles
    //   the multiplexing of the DNS requests to the server.
    //   the client is a handle to an unbounded queue for sending requests via the
    //   background. The background must be scheduled to run before the client can
    //   send any dns requests
    let client = AsyncClient::new(stream, sender, None);

    // await the connection to be established
    let (mut client, bg) = client.await.expect("connection failed");

    // make sure to run the background task
    tokio::spawn(bg);

    // Create a query future
    let query = client.query(
        Name::from_str("www.example.com.").unwrap(),
        DNSClass::IN,
        RecordType::A,
    );

    // wait for its response
    let response = query.await.unwrap();

    // validate it's what we expected
    if let RData::A(addr) = response.answers()[0].rdata() {
        assert_eq!(*addr, Ipv4Addr::new(93, 184, 216, 34));
    }
}

Do you think it would be a good idea to add this example to the docs (I can submit a PR for this) ?

On another note, I am quite confused by the AsyncClient vs AsyncClientConnect distinction. Are there reasons not to define AsyncClient::new and AsyncClient::connect as async fn that return an AsyncClient once they have been awaited ? This could remove the need for this intermediary AsyncConnect object.

Originally created by @ErwanDL on GitHub (Aug 6, 2021). Original GitHub issue: https://github.com/hickory-dns/hickory-dns/issues/1532 Hello, and thanks for the work on this project. I think the documentation for the client is somewhat lacking in examples, notably concerning usage of the `AsyncClient`. The only example I found in the docs is [this one](https://docs.rs/trust-dns-client/0.20.3/trust_dns_client/#async-usage), that only tackles the UDP case, and does not use `async/await` syntax although I believe most users opt for the `AsyncClient` specifically because they want to use `async/await`. Here is an adapated version of this example, using a TCP connection and `async/await` syntax (I am not 100% sure of the TCP client construction process, I tried to take inspiration from what I saw in the integration tests): ```rust use std::net::Ipv4Addr; use std::str::FromStr; use tokio::net::TcpStream as TokioTcpStream; use trust_dns_client::client::{AsyncClient, ClientHandle}; use trust_dns_client::proto::iocompat::AsyncIoTokioAsStd; use trust_dns_client::rr::{DNSClass, Name, RData, RecordType}; use trust_dns_client::tcp::TcpClientStream; #[tokio::main] async fn main() { // Establishing a TCP connection let (stream, sender) = TcpClientStream::<AsyncIoTokioAsStd<TokioTcpStream>>::new(([8, 8, 8, 8], 53).into()); // Create a new client, the bg is a background future which handles // the multiplexing of the DNS requests to the server. // the client is a handle to an unbounded queue for sending requests via the // background. The background must be scheduled to run before the client can // send any dns requests let client = AsyncClient::new(stream, sender, None); // await the connection to be established let (mut client, bg) = client.await.expect("connection failed"); // make sure to run the background task tokio::spawn(bg); // Create a query future let query = client.query( Name::from_str("www.example.com.").unwrap(), DNSClass::IN, RecordType::A, ); // wait for its response let response = query.await.unwrap(); // validate it's what we expected if let RData::A(addr) = response.answers()[0].rdata() { assert_eq!(*addr, Ipv4Addr::new(93, 184, 216, 34)); } } ``` Do you think it would be a good idea to add this example to the docs (I can submit a PR for this) ? On another note, I am quite confused by the `AsyncClient` vs `AsyncClientConnect` distinction. Are there reasons not to define `AsyncClient::new` and `AsyncClient::connect` as `async fn` that return an `AsyncClient` once they have been `await`ed ? This could remove the need for this intermediary `AsyncConnect` object.
kerem 2026-03-15 23:50:24 +03:00
  • closed this issue
  • added the
    docs
    label
Author
Owner

@bluejekyll commented on GitHub (Aug 8, 2021):

Thank you for putting this together! Would you like to add this to the client root documentation?

On another note, I am quite confused by the AsyncClient vs AsyncClientConnect distinction. Are there reasons not to define AsyncClient::new and AsyncClient::connect as async fn that return an AsyncClient once they have been awaited ? This could remove the need for this intermediary AsyncConnect object.

I think those functions can be simplified. The only reason I can think that didn't happen is because AsyncClient is older than async/await in Rust. Most likely it can be simplified to be async fn instead of Future implementations. If that's something you'd be interested in working on, it would be appreciated...

<!-- gh-comment-id:894864002 --> @bluejekyll commented on GitHub (Aug 8, 2021): Thank you for putting this together! Would you like to add this to the client root documentation? > On another note, I am quite confused by the AsyncClient vs AsyncClientConnect distinction. Are there reasons not to define AsyncClient::new and AsyncClient::connect as async fn that return an AsyncClient once they have been awaited ? This could remove the need for this intermediary AsyncConnect object. I think those functions can be simplified. The only reason I can think that didn't happen is because AsyncClient is older than async/await in Rust. Most likely it can be simplified to be `async fn` instead of Future implementations. If that's something you'd be interested in working on, it would be appreciated...
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
starred/hickory-dns#692
No description provided.