---
title: "Hyper Rust: HTTP, Axum e Infraestrutura Web em 2026"
url: "https://rustlang.com.br/ecossistema/hyper/"
markdown_url: "https://rustlang.com.br/ecossistema/hyper.MD"
description: "Guia completo de Hyper em Rust: HTTP/1, HTTP/2, servidores, clientes, proxies, integração com Axum, Tower, Reqwest, Tonic e carreira backend."
date: ""
author: ""
---

# Hyper Rust: HTTP, Axum e Infraestrutura Web em 2026

Guia completo de Hyper em Rust: HTTP/1, HTTP/2, servidores, clientes, proxies, integração com Axum, Tower, Reqwest, Tonic e carreira backend.


## Introdução

O **Hyper** é a biblioteca HTTP de baixo nível mais importante do ecossistema Rust. Ele implementa o protocolo HTTP/1.1 e HTTP/2 de forma correta, segura e extremamente performática. Praticamente todo o ecossistema web do Rust é construído sobre o Hyper: o Axum usa Hyper como servidor, o Reqwest usa Hyper como cliente, e o Tonic usa Hyper para transportar gRPC.

Diferente de frameworks de alto nível como Axum ou Actix Web, o Hyper expõe os detalhes do protocolo HTTP, dando a você controle total sobre como as requisições e respostas são processadas. Isso o torna ideal para construir frameworks customizados, proxies, load balancers e qualquer componente de infraestrutura HTTP.

## Hyper Rust em 2026: onde ele encaixa

Quem pesquisa **Hyper Rust** geralmente não está procurando mais um framework web. A dúvida costuma ser mais profunda: como o HTTP realmente entra em um serviço Rust, por que [Axum](/ecossistema/axum/) depende de Hyper, quando [Reqwest](/ecossistema/reqwest/) deixa de ser suficiente como cliente e em que momento vale descer uma camada para controlar conexão, corpo, streaming, upgrade, proxy ou HTTP/2. Hyper é essa camada de infraestrutura.

Na prática, Hyper fica no centro da pilha backend moderna. [Tokio](/ecossistema/tokio/) executa as tarefas assíncronas, Hyper fala HTTP, [Tower](/ecossistema/tower/) compõe `Service` e `Layer`, Axum organiza rotas, Reqwest entrega ergonomia para clientes HTTP e [Tonic](/ecossistema/tonic/) usa HTTP/2 para gRPC. Entender essa relação evita decisões ruins, como escrever um servidor Hyper cru para uma API CRUD simples ou tentar resolver um proxy reverso complexo apenas com abstrações de alto nível.

Para aplicações comuns, comece por Axum. Para consumo de APIs externas, comece por Reqwest. Hyper entra quando o produto exige algo mais próximo do protocolo: gateway interno, proxy, load balancer, servidor embutido em uma ferramenta, terminador HTTP customizado, streaming eficiente, túnel, integração com runtime próprio ou biblioteca que precisa ser reutilizada por frameworks diferentes.

Do ponto de vista de carreira, Hyper diferencia devs que querem atuar em [vagas Rust](/vagas/) de backend avançado, plataforma, infraestrutura, edge, observabilidade, segurança e developer tools. Muitas [empresas que usam Rust](/empresas/) não anunciam "Hyper" no título, mas descrevem sistemas onde entender timeouts, conexões persistentes, backpressure, HTTP/2, graceful shutdown e middleware operacional é mais valioso do que apenas saber criar uma rota. Combine este guia com a trilha de [carreira backend Rust](/carreira/nicho-web-backend/) e com o artigo sobre [serviços resilientes com Tower, Axum e Tokio](/blog/rust-servicos-resilientes-tower-axum-2026/) para transformar conhecimento de protocolo em projeto demonstrável.

### Por que entender o Hyper?

- **Fundação do ecossistema**: Axum, Reqwest e Tonic são construídos sobre ele
- **Performance extrema**: um dos servidores HTTP mais rápidos em qualquer linguagem
- **Controle total**: acesso a cada detalhe do protocolo HTTP
- **HTTP/2 nativo**: suporte completo a HTTP/2, incluindo multiplexação
- **Streaming**: processamento de corpos de requisição/resposta como streams
- **Modular**: pode ser usado como servidor, cliente, ou ambos

## Instalação

Adicione o Hyper ao seu `Cargo.toml`:

```toml
[dependencies]
hyper = { version = "1", features = ["full"] }
hyper-util = { version = "0.1", features = ["full"] }
http-body-util = "0.1"
tokio = { version = "1", features = ["full"] }
bytes = "1"
```

As features do Hyper são granulares, permitindo incluir apenas o que você precisa:

```toml
[dependencies]
# Apenas servidor HTTP/1
hyper = { version = "1", features = ["http1", "server"] }

# Apenas cliente HTTP/2
hyper = { version = "1", features = ["http2", "client"] }

# Tudo incluído
hyper = { version = "1", features = ["full"] }
```

O pacote `hyper-util` fornece utilitários adicionais como `TokioIo` para integrar com Tokio e `TokioExecutor` para execução de tasks.

## Uso Básico

### Servidor HTTP simples

O exemplo mais básico de um servidor HTTP com Hyper:

```rust
use std::convert::Infallible;
use std::net::SocketAddr;

use http_body_util::Full;
use hyper::body::Bytes;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response, StatusCode};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;

// Handler que processa cada requisição
async fn handler(
    req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
    let resposta = match (req.method(), req.uri().path()) {
        (&hyper::Method::GET, "/") => {
            Response::new(Full::new(Bytes::from("Olá, mundo!")))
        }
        (&hyper::Method::GET, "/saude") => {
            Response::new(Full::new(Bytes::from(r#"{"status": "ok"}"#)))
        }
        _ => {
            let mut resp = Response::new(Full::new(Bytes::from("Não encontrado")));
            *resp.status_mut() = StatusCode::NOT_FOUND;
            resp
        }
    };

    Ok(resposta)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = TcpListener::bind(addr).await?;

    println!("Servidor rodando em http://{}", addr);

    loop {
        let (stream, _) = listener.accept().await?;
        let io = TokioIo::new(stream);

        tokio::task::spawn(async move {
            if let Err(err) = http1::Builder::new()
                .serve_connection(io, service_fn(handler))
                .await
            {
                eprintln!("Erro ao servir conexão: {:?}", err);
            }
        });
    }
}
```

### Servidor com estado compartilhado

```rust
use std::convert::Infallible;
use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicU64, Ordering};

use http_body_util::Full;
use hyper::body::Bytes;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;

// Estado compartilhado entre todas as conexões
struct AppState {
    contador_requisicoes: AtomicU64,
    nome_app: String,
}

async fn handler(
    state: Arc<AppState>,
    _req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
    let contagem = state.contador_requisicoes.fetch_add(1, Ordering::Relaxed) + 1;

    let corpo = format!(
        "App: {} | Requisição #{}\n",
        state.nome_app, contagem
    );

    Ok(Response::new(Full::new(Bytes::from(corpo))))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let state = Arc::new(AppState {
        contador_requisicoes: AtomicU64::new(0),
        nome_app: "Meu Servidor Hyper".to_string(),
    });

    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = TcpListener::bind(addr).await?;
    println!("Servidor rodando em http://{}", addr);

    loop {
        let (stream, _) = listener.accept().await?;
        let io = TokioIo::new(stream);
        let state = state.clone();

        tokio::task::spawn(async move {
            let service = service_fn(move |req| {
                let state = state.clone();
                async move { handler(state, req).await }
            });

            if let Err(err) = http1::Builder::new()
                .serve_connection(io, service)
                .await
            {
                eprintln!("Erro: {:?}", err);
            }
        });
    }
}
```

### Roteamento manual

```rust
use std::convert::Infallible;
use http_body_util::{BodyExt, Full};
use hyper::body::Bytes;
use hyper::{Method, Request, Response, StatusCode};

async fn roteador(
    req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
    match (req.method(), req.uri().path()) {
        // GET /
        (&Method::GET, "/") => ok_json(r#"{"mensagem": "Bem-vindo à API"}"#),

        // GET /usuarios
        (&Method::GET, "/usuarios") => {
            ok_json(r#"[{"id": 1, "nome": "Maria"}, {"id": 2, "nome": "João"}]"#)
        }

        // POST /usuarios
        (&Method::POST, "/usuarios") => {
            // Ler o corpo da requisição
            let corpo = req.collect().await
                .map(|c| c.to_bytes())
                .unwrap_or_default();

            let texto = String::from_utf8_lossy(&corpo);
            let resposta = format!(r#"{{"criado": true, "dados": {}}}"#, texto);

            let mut resp = Response::new(Full::new(Bytes::from(resposta)));
            *resp.status_mut() = StatusCode::CREATED;
            resp.headers_mut().insert(
                hyper::header::CONTENT_TYPE,
                "application/json".parse().unwrap(),
            );
            Ok(resp)
        }

        // GET /usuarios/:id (roteamento simples por prefixo)
        (&Method::GET, path) if path.starts_with("/usuarios/") => {
            let id = &path["/usuarios/".len()..];
            ok_json(&format!(r#"{{"id": {}, "nome": "Usuário {}"}}"#, id, id))
        }

        // 404 para todas as outras rotas
        _ => {
            let mut resp = Response::new(Full::new(Bytes::from(
                r#"{"erro": "Rota não encontrada"}"#,
            )));
            *resp.status_mut() = StatusCode::NOT_FOUND;
            Ok(resp)
        }
    }
}

fn ok_json(corpo: &str) -> Result<Response<Full<Bytes>>, Infallible> {
    let mut resp = Response::new(Full::new(Bytes::from(corpo.to_string())));
    resp.headers_mut().insert(
        hyper::header::CONTENT_TYPE,
        "application/json".parse().unwrap(),
    );
    Ok(resp)
}
```

## Recursos Avançados

### Servidor HTTP/2

```rust
use std::net::SocketAddr;

use http_body_util::Full;
use hyper::body::Bytes;
use hyper::server::conn::http2;
use hyper::service::service_fn;
use hyper::{Request, Response};
use hyper_util::rt::{TokioExecutor, TokioIo};
use tokio::net::TcpListener;

async fn handler(
    _req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, std::convert::Infallible> {
    Ok(Response::new(Full::new(Bytes::from("HTTP/2 funcionando!"))))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = TcpListener::bind(addr).await?;

    println!("Servidor HTTP/2 em http://{}", addr);

    loop {
        let (stream, _) = listener.accept().await?;
        let io = TokioIo::new(stream);

        tokio::task::spawn(async move {
            if let Err(err) = http2::Builder::new(TokioExecutor::new())
                .serve_connection(io, service_fn(handler))
                .await
            {
                eprintln!("Erro HTTP/2: {:?}", err);
            }
        });
    }
}
```

### Servidor que suporta HTTP/1 e HTTP/2 simultaneamente

```rust
use std::net::SocketAddr;

use http_body_util::Full;
use hyper::body::Bytes;
use hyper::service::service_fn;
use hyper::{Request, Response};
use hyper_util::rt::{TokioExecutor, TokioIo};
use hyper_util::server::conn::auto;
use tokio::net::TcpListener;

async fn handler(
    req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, std::convert::Infallible> {
    let versao = format!("{:?}", req.version());
    let corpo = format!("Versão HTTP: {}", versao);
    Ok(Response::new(Full::new(Bytes::from(corpo))))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = TcpListener::bind(addr).await?;

    println!("Servidor HTTP/1+2 em http://{}", addr);

    loop {
        let (stream, _) = listener.accept().await?;
        let io = TokioIo::new(stream);

        tokio::task::spawn(async move {
            if let Err(err) = auto::Builder::new(TokioExecutor::new())
                .serve_connection(io, service_fn(handler))
                .await
            {
                eprintln!("Erro: {:?}", err);
            }
        });
    }
}
```

### Cliente HTTP com Hyper

```rust
use http_body_util::{BodyExt, Empty};
use hyper::body::Bytes;
use hyper::Request;
use hyper_util::client::legacy::Client;
use hyper_util::rt::TokioExecutor;

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let client = Client::builder(TokioExecutor::new())
        .build_http();

    // Construir requisição manualmente
    let req = Request::builder()
        .method("GET")
        .uri("http://httpbin.org/get")
        .header("User-Agent", "hyper-client/1.0")
        .body(Empty::<Bytes>::new())?;

    let resp = client.request(req).await?;

    println!("Status: {}", resp.status());
    println!("Headers: {:#?}", resp.headers());

    // Ler o corpo completo
    let corpo = resp.into_body().collect().await?.to_bytes();
    println!("Corpo: {}", String::from_utf8_lossy(&corpo));

    Ok(())
}
```

### Streaming de corpo de resposta

```rust
use std::convert::Infallible;

use bytes::Bytes;
use http_body_util::StreamBody;
use hyper::body::Frame;
use hyper::{Request, Response};
use tokio_stream::StreamExt;

async fn streaming_handler(
    _req: Request<hyper::body::Incoming>,
) -> Result<Response<StreamBody<impl tokio_stream::Stream<Item = Result<Frame<Bytes>, Infallible>>>>, Infallible> {
    // Criar um stream que envia dados gradualmente
    let stream = tokio_stream::iter(0..10).map(|i| {
        let chunk = format!("Chunk {}: dados do servidor\n", i);
        Ok(Frame::data(Bytes::from(chunk)))
    });

    let body = StreamBody::new(stream);
    Ok(Response::new(body))
}
```

### Middleware manual (wrapping services)

```rust
use std::convert::Infallible;
use std::time::Instant;

use http_body_util::Full;
use hyper::body::Bytes;
use hyper::{Request, Response};

// Wrapper que adiciona logging
async fn com_logging(
    req: Request<hyper::body::Incoming>,
    handler: impl Fn(Request<hyper::body::Incoming>) -> std::pin::Pin<Box<dyn std::future::Future<Output = Result<Response<Full<Bytes>>, Infallible>> + Send>>,
) -> Result<Response<Full<Bytes>>, Infallible> {
    let metodo = req.method().clone();
    let caminho = req.uri().path().to_string();
    let inicio = Instant::now();

    let resposta = handler(req).await;

    let duracao = inicio.elapsed();
    if let Ok(ref resp) = resposta {
        println!(
            "{} {} -> {} ({:?})",
            metodo,
            caminho,
            resp.status(),
            duracao
        );
    }

    resposta
}
```

### Graceful shutdown

```rust
use std::net::SocketAddr;

use http_body_util::Full;
use hyper::body::Bytes;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response};
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;
use tokio::signal;

async fn handler(
    _req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, std::convert::Infallible> {
    Ok(Response::new(Full::new(Bytes::from("Servidor ativo!"))))
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = TcpListener::bind(addr).await?;
    println!("Servidor em http://{}", addr);

    // Canal para sinalizar shutdown
    let (tx_shutdown, _) = tokio::sync::broadcast::channel::<()>(1);

    loop {
        tokio::select! {
            resultado = listener.accept() => {
                let (stream, _) = resultado?;
                let io = TokioIo::new(stream);
                let mut rx = tx_shutdown.subscribe();

                tokio::task::spawn(async move {
                    let conn = http1::Builder::new()
                        .serve_connection(io, service_fn(handler));

                    tokio::pin!(conn);

                    tokio::select! {
                        resultado = &mut conn => {
                            if let Err(err) = resultado {
                                eprintln!("Erro: {:?}", err);
                            }
                        }
                        _ = rx.recv() => {
                            println!("Encerrando conexão graciosamente...");
                            conn.as_mut().graceful_shutdown();
                            // Aguardar conclusão das requisições em andamento
                            if let Err(err) = conn.await {
                                eprintln!("Erro no shutdown: {:?}", err);
                            }
                        }
                    }
                });
            }
            _ = signal::ctrl_c() => {
                println!("\nRecebido Ctrl+C, encerrando...");
                let _ = tx_shutdown.send(());
                break;
            }
        }
    }

    println!("Servidor encerrado.");
    Ok(())
}
```

## Boas Práticas

### 1. Use Hyper apenas quando necessário

Na maioria dos casos, frameworks de alto nível como Axum ou Actix Web são mais apropriados. Use Hyper diretamente quando:

- Estiver construindo um framework web
- Precisar de controle total sobre o protocolo HTTP
- Estiver construindo um proxy ou load balancer
- Precisar de performance extrema e quiser eliminar overhead

### 2. Sempre trate erros de conexão

```rust
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper_util::rt::TokioIo;
use tokio::net::TcpListener;

async fn aceitar_conexoes(listener: TcpListener) {
    loop {
        match listener.accept().await {
            Ok((stream, addr)) => {
                println!("Nova conexão de: {}", addr);
                let io = TokioIo::new(stream);

                tokio::task::spawn(async move {
                    let resultado = http1::Builder::new()
                        .serve_connection(io, service_fn(|_req| async {
                            Ok::<_, std::convert::Infallible>(
                                hyper::Response::new(
                                    http_body_util::Full::new(
                                        hyper::body::Bytes::from("OK")
                                    )
                                )
                            )
                        }))
                        .await;

                    if let Err(err) = resultado {
                        // Diferenciar tipos de erro
                        if err.is_incomplete_message() {
                            // Cliente desconectou - normal
                        } else if err.is_parse() {
                            eprintln!("Erro de parse HTTP de {}: {}", addr, err);
                        } else {
                            eprintln!("Erro de conexão de {}: {}", addr, err);
                        }
                    }
                });
            }
            Err(e) => {
                eprintln!("Erro ao aceitar conexão: {}", e);
                tokio::time::sleep(std::time::Duration::from_millis(100)).await;
            }
        }
    }
}
```

### 3. Configure limites de conexão

```rust
use hyper::server::conn::http1;

fn configurar_http1() -> http1::Builder {
    let mut builder = http1::Builder::new();

    // Limitar tamanho do cabeçalho (padrão: ~400KB)
    builder.max_buf_size(8 * 1024); // 8KB

    // Habilitar keep-alive
    builder.keep_alive(true);

    // Habilitar pipeline HTTP/1.1
    builder.pipeline_flush(true);

    builder
}
```

### 4. Prefira tipos concretos a trait objects

```rust
use http_body_util::Full;
use hyper::body::Bytes;
use hyper::Response;

// Bom: tipo concreto, sem alocação dinâmica
fn resposta_rapida() -> Response<Full<Bytes>> {
    Response::new(Full::new(Bytes::from("Rápido!")))
}

// Evitar em hot paths: BoxBody usa alocação dinâmica
// use http_body_util::combinators::BoxBody;
// fn resposta_lenta() -> Response<BoxBody<Bytes, hyper::Error>> { ... }
```

### 5. Use tower::Service para composição

```rust
// O Hyper se integra com Tower para middleware composável
// Veja a página sobre Tower para detalhes

use tower::ServiceBuilder;
use tower::timeout::TimeoutLayer;
use std::time::Duration;

// Exemplo conceitual de composição com Tower
fn criar_service() {
    let _service = ServiceBuilder::new()
        .layer(TimeoutLayer::new(Duration::from_secs(30)))
        // .layer(RateLimitLayer::new(100, Duration::from_secs(1)))
        // .service(meu_handler)
        ;
}
```

## Exemplos Práticos

### Servidor HTTP completo com roteamento e JSON

```rust
use std::collections::HashMap;
use std::convert::Infallible;
use std::net::SocketAddr;
use std::sync::{Arc, Mutex};

use http_body_util::{BodyExt, Full};
use hyper::body::Bytes;
use hyper::header::CONTENT_TYPE;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Method, Request, Response, StatusCode};
use hyper_util::rt::TokioIo;
use serde::{Deserialize, Serialize};
use tokio::net::TcpListener;

#[derive(Clone, Debug, Serialize, Deserialize)]
struct Nota {
    id: u64,
    titulo: String,
    conteudo: String,
}

struct AppState {
    notas: Mutex<HashMap<u64, Nota>>,
    proximo_id: Mutex<u64>,
}

impl AppState {
    fn new() -> Self {
        Self {
            notas: Mutex::new(HashMap::new()),
            proximo_id: Mutex::new(1),
        }
    }
}

fn json_response(status: StatusCode, corpo: &str) -> Response<Full<Bytes>> {
    let mut resp = Response::new(Full::new(Bytes::from(corpo.to_string())));
    *resp.status_mut() = status;
    resp.headers_mut().insert(CONTENT_TYPE, "application/json".parse().unwrap());
    resp
}

async fn roteador(
    state: Arc<AppState>,
    req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
    let metodo = req.method().clone();
    let caminho = req.uri().path().to_string();

    let resposta = match (metodo, caminho.as_str()) {
        // Listar todas as notas
        (Method::GET, "/notas") => {
            let notas = state.notas.lock().unwrap();
            let lista: Vec<&Nota> = notas.values().collect();
            let json = serde_json::to_string(&lista).unwrap();
            json_response(StatusCode::OK, &json)
        }

        // Criar nota
        (Method::POST, "/notas") => {
            let corpo = req.collect().await.unwrap().to_bytes();
            match serde_json::from_slice::<serde_json::Value>(&corpo) {
                Ok(dados) => {
                    let mut proximo_id = state.proximo_id.lock().unwrap();
                    let id = *proximo_id;
                    *proximo_id += 1;

                    let nota = Nota {
                        id,
                        titulo: dados["titulo"]
                            .as_str()
                            .unwrap_or("Sem título")
                            .to_string(),
                        conteudo: dados["conteudo"]
                            .as_str()
                            .unwrap_or("")
                            .to_string(),
                    };

                    let json = serde_json::to_string(&nota).unwrap();
                    state.notas.lock().unwrap().insert(id, nota);
                    json_response(StatusCode::CREATED, &json)
                }
                Err(_) => json_response(
                    StatusCode::BAD_REQUEST,
                    r#"{"erro": "JSON inválido"}"#,
                ),
            }
        }

        // Obter nota por ID
        (Method::GET, path) if path.starts_with("/notas/") => {
            let id_str = &path["/notas/".len()..];
            match id_str.parse::<u64>() {
                Ok(id) => {
                    let notas = state.notas.lock().unwrap();
                    match notas.get(&id) {
                        Some(nota) => {
                            let json = serde_json::to_string(nota).unwrap();
                            json_response(StatusCode::OK, &json)
                        }
                        None => json_response(
                            StatusCode::NOT_FOUND,
                            r#"{"erro": "Nota não encontrada"}"#,
                        ),
                    }
                }
                Err(_) => json_response(
                    StatusCode::BAD_REQUEST,
                    r#"{"erro": "ID inválido"}"#,
                ),
            }
        }

        // Deletar nota
        (Method::DELETE, path) if path.starts_with("/notas/") => {
            let id_str = &path["/notas/".len()..];
            match id_str.parse::<u64>() {
                Ok(id) => {
                    let mut notas = state.notas.lock().unwrap();
                    if notas.remove(&id).is_some() {
                        json_response(StatusCode::OK, r#"{"removida": true}"#)
                    } else {
                        json_response(
                            StatusCode::NOT_FOUND,
                            r#"{"erro": "Nota não encontrada"}"#,
                        )
                    }
                }
                Err(_) => json_response(
                    StatusCode::BAD_REQUEST,
                    r#"{"erro": "ID inválido"}"#,
                ),
            }
        }

        // Rota padrão
        _ => json_response(
            StatusCode::NOT_FOUND,
            r#"{"erro": "Rota não encontrada"}"#,
        ),
    };

    Ok(resposta)
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let state = Arc::new(AppState::new());
    let addr = SocketAddr::from(([127, 0, 0, 1], 3000));
    let listener = TcpListener::bind(addr).await?;

    println!("API de Notas rodando em http://{}", addr);
    println!("Rotas:");
    println!("  GET    /notas      - Listar notas");
    println!("  POST   /notas      - Criar nota");
    println!("  GET    /notas/:id  - Obter nota");
    println!("  DELETE /notas/:id  - Deletar nota");

    loop {
        let (stream, _) = listener.accept().await?;
        let io = TokioIo::new(stream);
        let state = state.clone();

        tokio::task::spawn(async move {
            let service = service_fn(move |req| {
                let state = state.clone();
                async move { roteador(state, req).await }
            });

            if let Err(err) = http1::Builder::new()
                .serve_connection(io, service)
                .await
            {
                eprintln!("Erro de conexão: {:?}", err);
            }
        });
    }
}
```

### Proxy reverso simples

```rust
use std::convert::Infallible;
use std::net::SocketAddr;

use http_body_util::{BodyExt, Full};
use hyper::body::Bytes;
use hyper::server::conn::http1;
use hyper::service::service_fn;
use hyper::{Request, Response, StatusCode};
use hyper_util::client::legacy::Client;
use hyper_util::rt::{TokioExecutor, TokioIo};
use tokio::net::TcpListener;

async fn proxy(
    client: Client<hyper_util::client::legacy::connect::HttpConnector, Full<Bytes>>,
    destino: String,
    req: Request<hyper::body::Incoming>,
) -> Result<Response<Full<Bytes>>, Infallible> {
    let caminho = req.uri().path().to_string();
    let query = req.uri().query().map(|q| format!("?{}", q)).unwrap_or_default();
    let uri_destino = format!("{}{}{}", destino, caminho, query);

    println!("Proxy: {} -> {}", req.uri(), uri_destino);

    // Construir nova requisição para o destino
    let proxy_req = Request::builder()
        .method(req.method())
        .uri(&uri_destino)
        .body(Full::new(Bytes::new()))
        .unwrap();

    match client.request(proxy_req).await {
        Ok(resp) => {
            let status = resp.status();
            let corpo = resp.into_body().collect().await
                .map(|c| c.to_bytes())
                .unwrap_or_default();

            let mut resposta = Response::new(Full::new(corpo));
            *resposta.status_mut() = status;
            Ok(resposta)
        }
        Err(err) => {
            eprintln!("Erro no proxy: {}", err);
            let mut resp = Response::new(Full::new(Bytes::from("Erro no proxy")));
            *resp.status_mut() = StatusCode::BAD_GATEWAY;
            Ok(resp)
        }
    }
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
    let destino = "http://httpbin.org".to_string();
    let client = Client::builder(TokioExecutor::new()).build_http();

    let addr = SocketAddr::from(([127, 0, 0, 1], 8080));
    let listener = TcpListener::bind(addr).await?;
    println!("Proxy rodando em http://{} -> {}", addr, destino);

    loop {
        let (stream, _) = listener.accept().await?;
        let io = TokioIo::new(stream);
        let client = client.clone();
        let destino = destino.clone();

        tokio::task::spawn(async move {
            let service = service_fn(move |req| {
                let client = client.clone();
                let destino = destino.clone();
                async move { proxy(client, destino, req).await }
            });

            if let Err(err) = http1::Builder::new()
                .serve_connection(io, service)
                .await
            {
                eprintln!("Erro: {:?}", err);
            }
        });
    }
}
```

## Comparação com Alternativas

| Característica | Hyper | Axum | Actix Web | Warp |
|---|---|---|---|---|
| **Nível** | Baixo nível | Alto nível | Alto nível | Alto nível |
| **Roteamento** | Manual | Embutido | Embutido | Embutido |
| **Middleware** | Manual/Tower | Tower | Próprio | Filters |
| **HTTP/2** | Nativo | Via Hyper | Nativo | Via Hyper |
| **Ergonomia** | Baixa | Alta | Alta | Média |
| **Performance** | Máxima | Excelente | Excelente | Excelente |
| **Casos de uso** | Infra/frameworks | APIs/web apps | APIs/web apps | APIs/web apps |

- **Hyper** vs **Axum**: Axum é construído sobre Hyper e adiciona roteamento, extratores e middleware. Use Axum para aplicações web e Hyper para componentes de infraestrutura.
- **Hyper** vs **Actix Web**: Actix Web usa seu próprio runtime e modelo de atores. Hyper é mais flexível e integra melhor com o ecossistema Tokio.
- **Hyper** vs **Warp**: Warp também usa Hyper internamente, mas com uma API baseada em filtros composáveis. Warp tem menos manutenção ativa que Axum.

## Conclusão

O Hyper é a fundação do ecossistema HTTP em Rust. Mesmo que você nunca o use diretamente, entender como ele funciona vai ajudá-lo a debugar problemas, otimizar performance e tomar decisões arquiteturais melhores ao usar frameworks como Axum ou Reqwest.

Para aplicações web típicas, prefira usar [Axum](/ecossistema/axum/) (que é construído sobre Hyper). Reserve o uso direto do Hyper para cenários que exigem controle total do protocolo HTTP, como proxies, load balancers e frameworks customizados. Se o objetivo é mostrar maturidade profissional, conecte Hyper com [Tower](/ecossistema/tower/), [Tracing](/ecossistema/tracing/), [Tokio](/ecossistema/tokio/) e práticas de produção em vez de publicar apenas um exemplo mínimo de servidor.

Para estudar com foco em mercado, monte um projeto pequeno de gateway HTTP: aceite uma requisição, valide headers, aplique timeout, encaminhe para um backend de teste, registre spans com Tracing e exponha métricas simples. Esse tipo de portfólio conversa com [vagas Rust](/vagas/) de plataforma, infraestrutura, backend e developer tools, e ajuda a explicar para [empresas que usam Rust](/empresas/) por que você entende a fronteira entre framework, protocolo e operação.

### Próximos passos

- Aprenda [Axum](/ecossistema/axum/) para construir APIs web de forma ergonômica sobre o Hyper
- Explore [Tower](/ecossistema/tower/) para adicionar middleware composável ao Hyper
- Estude [Reqwest](/ecossistema/reqwest/) para o lado do cliente HTTP, também construído sobre Hyper
- Veja [Tonic](/ecossistema/tonic/) para gRPC, que também é construído sobre Hyper
- Use a trilha de [backend Rust](/carreira/nicho-web-backend/) para transformar Hyper em projeto de carreira
