# Set timeouts and handle errors

**For:** Rust developers calling servers that can be slow or answer with an error.

You give a fibre_http client a timeout, turn error statuses like 404 into errors you can match on and watch a request to a server that never answers give up on time.

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 timeouts
cd timeouts
```

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

```toml title="Cargo.toml"
[dependencies]
fibre_http = "0"
```

This tutorial uses the blocking client, so it needs no async runtime.

## The program

Replace `src/main.rs` with this:

```rust title="src/main.rs"
use std::net::TcpListener;
use std::time::{Duration, Instant};

use fibre_http::{Error, SyncClient};

fn fetch(client: &SyncClient, url: &str) -> Result<String, Error> {
    let response = client.get(url).send()?.error_for_status()?;
    response.text()
}

fn main() -> Result<(), Error> {
    let client = SyncClient::builder().timeout(Duration::from_secs(10)).build_sync()?;

    match fetch(&client, "https://example.com/") {
        Ok(page) => println!("example.com: {} bytes", page.len()),
        Err(e) => println!("example.com failed: {e}"),
    }

    match fetch(&client, "https://example.com/no-such-page") {
        Ok(page) => println!("no-such-page: {} bytes", page.len()),
        Err(Error::Status(code)) => println!("no-such-page: the server answered {code}"),
        Err(e) => println!("no-such-page failed: {e}"),
    }

    let silent = TcpListener::bind("127.0.0.1:0").expect("bind a local port");
    let url = format!("http://{}/", silent.local_addr().expect("local address"));

    let started = Instant::now();
    let result = client.get(&url).timeout(Duration::from_millis(500)).send();
    match result {
        Ok(response) => println!("silent server: {}", response.status()),
        Err(Error::Timeout) => println!("silent server: gave up after {} ms", started.elapsed().as_millis()),
        Err(e) => println!("silent server failed: {e}"),
    }
    Ok(())
}
```

Run it:

```shell-session
$ cargo run
example.com: 577 bytes
no-such-page: the server answered 404
silent server: gave up after 500 ms
```

## What each part does

`timeout(Duration::from_secs(10))` on the builder gives every request from this client a 10 second limit. The limit covers the whole request: getting a connection, sending, waiting for the answer and reading the body.

A 404 is still a response, so `send` succeeds. `error_for_status()` turns any 4xx or 5xx status into `Error::Status`, which carries the code. That lets one `match` handle network failures and error statuses together.

The third request goes to a local port that accepts connections and never replies. `timeout` on the request replaces the client's 10 seconds with 500 ms for this one call. When the time runs out, `send` returns `Error::Timeout`.

`Error` has other cases worth matching on, such as `Error::Tls` for a certificate problem and `Error::Refused` when a server turns a request away before handling it. The usage guide lists them.

## Where to go next

The usage guide covers redirects, retries, proxies and the rest of the error cases: [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).
