# Fetch a page from blocking and async code

**For:** Rust developers who need to make HTTP requests.

You fetch a web page with fibre_http, first from plain blocking code and then from async code. Then you send a request from each kind of code through the same client and see that they share one connection.

Before you start: you need Rust with Cargo. fibre_http comes from crates.io as `fibre_http = "0"`.

## Set up the project

Start a new binary crate.

```sh
cargo new fetch_pages
cd fetch_pages
```

Put this in the `[dependencies]` section of `Cargo.toml`:

```toml title="Cargo.toml"
[dependencies]
fibre_http = "0"
tokio = { version = "1", features = ["full"] }
```

You only need tokio for the async part. The blocking client works without any runtime.

## Fetch a page from blocking code

`SyncClient` is the blocking client. `get` builds a request and `send` sends it and waits for the response. Replace `src/main.rs` with this:

```rust title="src/main.rs"
use fibre_http::SyncClient;

fn main() -> Result<(), fibre_http::Error> {
    let client = SyncClient::new();
    let response = client.get("https://example.com/").send()?;
    println!("status: {}", response.status());
    println!("content type: {:?}", response.headers().get("content-type"));

    let page = response.text()?;
    let title = page.split("<title>").nth(1).and_then(|rest| rest.split("</title>").next());
    println!("title: {title:?}");
    Ok(())
}
```

Run it:

```shell-session
$ cargo run
status: 200 OK
content type: Some("text/html; charset=utf-8")
title: Some("Example Domain")
```

The status and headers are there as soon as `send` returns. `text` reads the rest of the body and turns it into a `String`.

## Fetch the same page from async code

`AsyncClient` is the async client. It has the same methods. You add `.await` after the ones that wait on the network.

Async programs often have some blocking work too, such as a library that only has a blocking API. `to_sync()` turns an async client into a blocking one for that code. The two share their configuration and their connection pool.

To see the sharing, build the client on a `Pool` you keep a handle to. Replace `src/main.rs` with this:

```rust title="src/main.rs"
use fibre_http::{AsyncClient, Pool};

fn title(page: &str) -> Option<&str> {
    page.split("<title>").nth(1).and_then(|rest| rest.split("</title>").next())
}

#[tokio::main]
async fn main() -> Result<(), fibre_http::Error> {
    let pool = Pool::builder().build();
    let client = AsyncClient::builder().pool(&pool).build_async()?;

    let page = client.get("https://example.com/").send().await?.text().await?;
    println!("async: {:?}", title(&page));

    let blocking = client.clone().to_sync();
    let page = tokio::task::spawn_blocking(move || blocking.get("https://example.com/").send()?.text())
        .await
        .expect("blocking task panicked")?;
    println!("blocking: {:?}", title(&page));

    println!("connections opened: {}", pool.connections_opened());
    Ok(())
}
```

Run it:

```shell-session
$ cargo run
async: Some("Example Domain")
blocking: Some("Example Domain")
connections opened: 1
```

The blocking request runs on tokio's blocking thread pool through `spawn_blocking`, so it never holds up an async task. It reuses the connection the async request opened, which is why the pool counts one connection for two requests.

`to_sync()` and `to_async()` cost nothing, so you can switch wherever it is convenient.

## Where to go next

The usage guide covers headers, query strings, request bodies, HTTP/2, TLS options and proxies: [https://github.com/excsn/fibre/blob/main/http/README.USAGE.md](https://github.com/excsn/fibre/blob/main/http/README.USAGE.md)

The full API is on docs.rs at [https://docs.rs/fibre_http](https://docs.rs/fibre_http).
