Ir para o conteúdo

title: Recursos Avançados do Cliente HTTP ESP32 - Autenticação, Callbacks e Retries description: Padrões avançados de cliente HTTP no ESP32: Bearer JWT e Basic Auth, callbacks de requisição, cabeçalhos customizados, Keep-Alive e retries. keywords: autenticacao HTTP ESP32, Bearer token ESP32, cabecalhos HTTP ESP32, Keep-Alive ESP32, callbacks de requisicao ESP32 tags: - guide - advanced


Uso Avançado

Helpers de Autenticação

O ESP32HTTPClient fornece métodos auxiliares dedicados para os esquemas de autenticação mais comuns. Assim como o setHeader(), esses helpers configuram cabeçalhos persistentes enviados com todas as requisições subsequentes.

Bearer Token (JWT / OAuth)

client.bearer("eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...");
client.get("/api/profile").getBody("username", username, sizeof(username));

HTTP Basic Auth

Passe seu usuário e senha; a biblioteca formata e codifica as credenciais em Base64 automaticamente:

client.basic("admin", "secret123");
client.get("/admin/metrics").getBody("uptime", &uptime);

Chave de API (API Key)

Passe o nome do cabeçalho e sua chave de API:

client.apiKey("x-api-key", "minha-chave-api-secreta");
client.get("/v1/sensors").getBody("temperature", &temp);

Cabeçalhos HTTP Personalizados

Use setHeader() para registrar qualquer cabeçalho persistente personalizado que será enviado com todas as requisições subsequentes dessa instância do cliente:

// Cabeçalhos personalizados
client.setHeader("X-Device-ID", "ESP32-001");
client.setHeader("X-Custom-Header", "custom-value");

// Cabeçalhos personalizados são enviados em todas as requisições subsequentes
client.get("/protected/resource").getBody("data", &minhaVar);

Persistência de cabeçalhos

Cabeçalhos registrados com setHeader(), bearer(), basic() ou apiKey() persistem durante todo o ciclo de vida do objeto cliente. Eles são enviados a cada requisição. Para alterar um cabeçalho ou token, chame o método novamente com o novo valor.

Lendo cabeçalhos da resposta

setHeader() define cabeçalhos para envio na requisição. Se você precisa ler cabeçalhos retornados pelo servidor na resposta (como token, Content-Length, Date, etc.), utilize o método .getHeader() do RestRequest. Veja como coletá-los no guia Lendo Cabeçalhos de Resposta ou na referência de RestRequest::getHeader.


Content-Type Personalizado

Por padrão, o cabeçalho Content-Type é definido como application/json para todas as requisições com corpo. Substitua-o com setContentType():

client.setContentType("application/x-www-form-urlencoded");
client.post("/form").body("field", "value");

Gerenciamento de Conexão Keep-Alive

Por padrão, o ESP32HTTPClient habilita o HTTP Keep-Alive, reutilizando a conexão TCP/TLS subjacente entre as requisições. Este é o principal motivo pelo qual a biblioteca é ~12x mais rápida do que a abordagem padrão, já que os dispendiosos handshakes TLS acontecem apenas uma vez.

Quando chamar end()

Após uma sequência de requisições, se o seu sketch entrar em um longo período de inatividade ou se você quiser liberar os buffers de memória TLS (~45KB para uma conexão ativa), chame end():

// Fazer várias requisições
client.get("/data1").getBody("val", &v1);
client.get("/data2").getBody("val", &v2);
client.get("/data3").getBody("val", &v3);

// Concluído por enquanto — liberar memória TLS
client.end();

delay(60000); // aguardar 60 segundos

// A próxima requisição restabelecerá a conexão automaticamente
client.get("/data4").getBody("val", &v4);

Você não precisa chamar end() entre as requisições

O Keep-Alive é automático. Chame end() apenas quando quiser liberar explicitamente a memória da conexão após um período de inatividade.


Alterando a URL do Servidor em Tempo de Execução

Você pode alterar a URL base ou porta de destino sem precisar recriar a instância do ESP32HTTPClient:

ESP32HTTPClient client("https://api.v1.exemplo.com");

// Alterna para o endpoint v2 ou servidor de homologação local
client.setBaseUrl("https://api.v2.exemplo.com", 443);

// Ou altera a porta independentemente
client.setUrl("http://192.168.1.100");
client.setPort(8080);

Configuração de Timeout

Por padrão, o cliente utiliza um tempo limite (timeout) de 60000 ms (1 minuto / 60 segundos). Você pode definir o padrão global ou sobrescrevê-lo em requisições individuais:

Timeout Global

// Define timeout padrão de 10 segundos para requisições futuras
client.setTimeout(10000);

Timeout por Requisição

// Requisição rápida com timeout de 1.5s
client.get("/quick-ping")
      .timeout(1500)
      .getBody("ok", &isOk);

Tentativas Automáticas (Retries) & Recuperação de Rede

O ESP32HTTPClient gerencia automaticamente quedas de rede e conexões Keep-Alive inativas repetindo as tentativas com falha até um limite configurado. O padrão é 1 tentativa extra (retry).

Max Retry Global

// Permite até 3 tentativas adicionais em caso de falha de conexão
client.setMaxRetry(3);

Retry por Requisição

// Desativa tentativas automáticas para operações não-idempotentes
client.post("/payment/charge")
      .retry(0)
      .body("amount", 100);

Callbacks (onSuccess, onError, onResponse)

Callbacks permitem associar funções de tratamento de forma limpa às requisições ou a nível de cliente.

Callbacks na Requisição

client.get("/sensors/temp")
      .onSuccess([](int code) {
          Serial.printf("Sucesso: HTTP %d\n", code);
      })
      .onError([](int code, const char* message) {
          Serial.printf("Falha na requisição (%d): %s\n", code, message);
      })
      .onResponse([](int code) {
          Serial.printf("Concluído com código %d\n", code);
      })
      .getBody("temperature", &temp);

Callbacks no Cliente

Callbacks registrados no cliente são acionados em todas as requisições executadas por aquela instância:

client.onError([](int code, const char* message) {
    Serial.printf("[Tratamento Global de Erro] Código %d: %s\n", code, message);
});

Tratamento e Inspeção de Erros

Você pode inspecionar o resultado de qualquer requisição usando os métodos auxiliares integrados:

client.get("/users/1").getBody("name", name, sizeof(name));

if (client.isSuccess()) {
    Serial.println("Usuário carregado com sucesso");
} else if (client.hasError()) {
    int code = client.getStatusCode();
    String errorMsg = client.getErrorMessage();
    Serial.printf("Falha com código %d: %s\n", code, errorMsg.c_str());
}

Tabela de Referência de Códigos de Erro

Erros de Rede / Cliente (code < 0)

Código Constante Descrição
-1 HTTPC_ERROR_CONNECTION_REFUSED O host de destino recusou a conexão TCP.
-2 HTTPC_ERROR_SEND_HEADER_FAILED Falha ao enviar cabeçalhos HTTP pelo socket.
-3 HTTPC_ERROR_SEND_PAYLOAD_FAILED Falha ao transmitir o corpo da requisição.
-4 HTTPC_ERROR_NOT_CONNECTED Cliente não está conectado à rede/socket.
-5 HTTPC_ERROR_CONNECTION_LOST A conexão TCP foi interrompida inesperadamente.
-6 HTTPC_ERROR_NO_STREAM Nenhum stream de resposta disponível.
-7 HTTPC_ERROR_NO_HTTP_SERVER Servidor não respondeu com HTTP válido.
-8 HTTPC_ERROR_TOO_LESS_RAM Memória RAM (Heap) insuficiente no ESP32.
-9 HTTPC_ERROR_ENCODING Erro de codificação ou decodificação de transferência.
-10 HTTPC_ERROR_STREAM_WRITE Falha na operação de escrita no stream.
-11 HTTPC_ERROR_READ_TIMEOUT Tempo limite esgotado aguardando resposta do servidor.
-12 HTTPC_ERROR_TLS_NOT_VALIDATED Requisição HTTPS recusada pelo modo fail-closed (requireValidatedTls) por ausência de Root CA ou CA bundle.

Códigos de Status HTTP (code > 0)

Código Status Descrição
200 OK Requisição concluída com sucesso.
201 Created Recurso criado com sucesso no servidor.
202 Accepted Requisição aceita para processamento.
204 No Content Sucesso, servidor não retornou conteúdo.
400 Bad Request Requisição malformada ou parâmetros inválidos.
401 Unauthorized Credenciais de autenticação ausentes ou inválidas.
403 Forbidden Autenticado, mas sem permissão de acesso.
404 Not Found Endpoint solicitado não existe no servidor.
408 Request Timeout Servidor expirou o tempo de espera.
429 Too Many Requests Limite de taxa de requisições excedido.
500 Internal Server Error Erro interno genérico no servidor.
502 Bad Gateway Resposta inválida do servidor upstream.
503 Service Unavailable Servidor sobrecarregado ou em manutenção.
504 Gateway Timeout Gateway upstream expirou aguardando resposta.

Timestamps Unix Grandes (long)

Para timestamps Unix e outros inteiros grandes (maiores que 2^31 - 1), use uma vinculação do tipo long:

long unixTimestamp = 0;

client.get("/api/v1/time/current/unix")
      .getBody("unix_timestamp", &unixTimestamp);

Serial.printf("Tempo Unix: %ld\n", unixTimestamp);

Mapeamento Struct <-> JSON

O ESP32-HTTP-Client oferece suporte a conversão bidirecional entre structs em C++ e dados JSON diretamente do stream de rede, sem alocação dinâmica de documentos intermediários, sem sobrecarga de reflexão e sem dependências externas.

Declarando Structs Mapeáveis

Use a macro REST_JSON_MAP dentro da sua struct para registrar os campos JSON:

struct User {
    int id = 0;
    char name[32] = {0};
    float score = 0.0f;
    bool active = false;
    String email = "";

    REST_JSON_MAP(
        REST_FIELD(id),
        REST_FIELD(name),
        REST_FIELD(score),
        REST_FIELD(active),
        REST_FIELD(email)
    )
};

Caso os nomes das chaves JSON sejam diferentes dos membros C++, utilize REST_FIELD_NAMED:

struct Profile {
    int userId = 0;
    char fullName[32] = {0};

    REST_JSON_MAP(
        REST_FIELD_NAMED("user_id", userId),
        REST_FIELD_NAMED("full_name", fullName)
    )
};

Para structs de bibliotecas externas de terceiros onde você não pode alterar a definição da struct, use REST_JSON_MAP_EXT:

struct ExternalDevice {
    int deviceId;
    String status;
};

REST_JSON_MAP_EXT(ExternalDevice,
    REST_FIELD_EXT(deviceId),
    REST_FIELD_EXT(status)
)

Enviando Structs no Corpo da Requisição

Passe a struct diretamente para .body():

User user;
user.id = 15;
strncpy(user.name, "Pedro", sizeof(user.name));
user.score = 9.8f;
user.active = true;
user.email = "[email protected]";

// Serializado automaticamente como JSON: {"id":15,"name":"Pedro","score":9.8,"active":true,"email":"[email protected]"}
client.post("/users").body(user);

Recebendo e Preenchendo Structs na Resposta

Preencha a struct diretamente a partir da resposta HTTP:

User user;
// Preenche a struct diretamente a partir da resposta JSON raiz
client.get("/users/15").getBody(&user);

Serial.printf("Usuário: ID=%d, Nome=%s, Ativo=%d\n", user.id, user.name, user.active);

Você também pode extrair objetos aninhados diretamente para a struct:

User nestedUser;
// Mapeia os campos para "data.user.id", "data.user.name", etc.
client.get("/profile").getBody("data.user", &nestedUser);

Tratamento de Campos Nulos e Ausentes

  • Se o JSON da resposta não contiver uma propriedade definida na struct, o valor padrão original da struct é mantido intacto.
  • Se o JSON contiver propriedades extras, elas são ignoradas automaticamente.
  • Se uma propriedade no JSON for null, valores numéricos são definidos como 0, booleanos como false e strings como "".

Serialização e Desserialização Independente (RestJson / toJson / fromJson)

Você também pode converter structs para JSON e preencher structs a partir de strings JSON fora de requisições HTTP:

User user;
user.id = 42;
strncpy(user.name, "Ana", sizeof(user.name));

// Struct -> String JSON
String jsonStr = ESP32HTTPClient::toJson(user);
// Ou: String jsonStr = RestJson::toJson(user);

// String JSON -> Struct
User parsedUser;
ESP32HTTPClient::fromJson(jsonStr, &parsedUser);
// Ou: RestJson::fromJson(jsonStr, &parsedUser);

Segurança e Certificados TLS / HTTPS

O ESP32HTTPClient inclui suporte integrado a conexões criptografadas HTTPS com validação de certificados X.509, autenticação mútua com certificados de cliente (mTLS), bundles de certificados, modo "falhar fechado" (fail-closed) e diagnóstico detalhado de erros do mbedTLS.

Como Obter a CA Raiz Correta (openssl s_client)

Não fixe o certificado folha (leaf certificate) do servidor no firmware — certificados de servidor expiram a cada 60 a 90 dias (ex.: Let's Encrypt), o que faria seus dispositivos pararem de conectar assim que o servidor renovasse o certificado. Em vez disso, fixe sempre a CA Raiz (Root CA) no topo da cadeia de certificação.

Execute o comando openssl s_client no seu computador para inspecionar a cadeia completa de certificados apresentada pelo servidor:

openssl s_client -showcerts -servername api.exemplo.com -connect api.exemplo.com:443 </dev/null

Observe o último certificado listado na saída (ou verifique o campo i: / Issuer do certificado intermediário). Copie o bloco PEM da CA Raiz (-----BEGIN CERTIFICATE----- ... -----END CERTIFICATE-----), ou baixe o PEM oficial no repositório da Autoridade Certificadora correspondente (ex.: ISRG Root X1 para Let's Encrypt, DigiCert Global Root G2 ou Amazon Root CA 1).

Quando Usar Raiz Única (setCACert) vs. Bundle de Certificados (setCACertBundle)

Abordagem API Custo de Flash Quando Utilizar
CA Raiz Única client.setCACert(root_ca_pem) ~1,5 – 2 KB por PEM Recomendado para dispositivos IoT com backend fixo. Ideal quando o ESP32 se comunica com uma API própria ou provedor cloud específico cuja PKI você conhece ou controla.
Bundle de CAs client.setCACertBundle(bundle, size) ~25 – 65 KB Recomendado para múltiplos domínios ou URLs configuráveis. Ideal quando o dispositivo acessa webhooks arbitrários, APIs de terceiros ou CDNs (como Cloudflare e CloudFront) que podem alternar automaticamente entre diferentes Autoridades Certificadoras (Let's Encrypt, Google Trust Services, DigiCert).

Expiração de Certificados e CAs Raízes (Atualização de Firmware / OTA)

  • Certificados do Servidor (Folha): São renovados com frequência (60–90 dias). Como o ESP32 valida a assinatura usando a CA Raiz, renovações normais do certificado do servidor não exigem atualização de firmware.
  • Certificados de CA Raiz: CAs raízes possuem validade longa (tipicamente 5 a 20 anos), mas também expiram, podem ser revogadas ou o provedor de nuvem pode migrar para uma nova hierarquia de CA (como ocorreu na transição do DST Root CA X3 para ISRG Root X1, ou do Baltimore CyberTrust Root para DigiCert Global Root G2).
  • Estratégia para Produção: Nunca envie dispositivos conectados via HTTPS para o campo sem um mecanismo de atualização de firmware OTA (Over-The-Air) — ou armazene os certificados CA em uma partição atualizável (como LittleFS ou NVS) — garantindo que você possa atualizar as âncoras de confiança antes que expirem.

Tempo de Vida dos Buffers (Semântica de Ponteiros)

Certificados e chaves privadas são armazenados por ponteiro no ESP32HTTPClient. Nenhuma cópia dinâmica é criada na heap. Portanto, garanta que os buffers permaneçam válidos na memória enquanto o cliente existir:

// Armazenado como constante estática global ou em PROGMEM
static const char* isrg_root_ca =
    "-----BEGIN CERTIFICATE-----\n"
    "MIIFazCCA1OgAwIBAgIRAIIQz7urnIjMvluidIT5FsAwDQYJKoZIhvcNAQELBQAw\n"
    "...\n"
    "-----END CERTIFICATE-----\n";

void setup() {
    client.setCACert(isrg_root_ca);
}

Modo "Falhar Fechado" (requireValidatedTls) e Avisos de Segurança

Por padrão, se uma requisição https:// for iniciada sem configurar setCACert() ou setCACertBundle(), ou quando setInsecure() for chamado, o ESP32HTTPClient emite um log de aviso (ESP32HTTPCLIENT_LOG_W / log_w) alertando que a conexão TLS não possui validação de certificado configurada.

Para ativar o modo falhar fechado (fail-closed) em produção — recusando conexões HTTPS caso alguém esqueça de configurar a CA/bundle ou deixe setInsecure() ativo acidentalmente — habilite requireValidatedTls(true):

client.requireValidatedTls(true);
client.setCACert(isrg_root_ca); // Se omitido, requisições HTTPS falham imediatamente com -12

Você também pode exigir TLS validado globalmente em tempo de compilação para todas as instâncias de ESP32HTTPClient via platformio.ini:

build_flags =
    -DESP32HTTPCLIENT_REQUIRE_VALIDATED_TLS=1

Quando o modo fail-closed bloqueia uma conexão HTTPS sem validação, nenhum socket de rede é aberto e a requisição retorna imediatamente o código HTTPC_ERROR_TLS_NOT_VALIDATED (-12).

Sincronização Obrigatória de Relógio via NTP

Cadeias de certificados TLS validam datas de emissão e expiração (Not Before e Not After). Como a placa ESP32 não possui RTC alimentado por bateria, seu relógio inicia no Unix epoch 0 (1 de janeiro de 1970) após a inicialização. Sem sincronização de relógio, o stack mbedTLS rejeitará certificados modernos por data inválida (MBEDTLS_ERR_X509_CERT_VERIFY_FAILED, -0x2700).

Sempre sincronize a data/hora logo após a conexão WiFi:

configTime(0, 0, "pool.ntp.org", "time.nist.gov");
time_t now = time(nullptr);
while (now < 1672531200) { // 1 de janeiro de 2023
    delay(500);
    now = time(nullptr);
}

Diagnóstico Prático de Erros TLS (getTlsErrorHint)

Quando o handshake TLS falha, receber apenas "Connection Refused" (-1) dificulta descobrir a causa real. O ESP32HTTPClient enriquece automaticamente client.getErrorMessage() com o código hexadecimal do mbedTLS e uma dica prática de solução, além de disponibilizar client.getTlsErrorHint() (e o método estático ESP32HTTPClient::tlsErrorToHint(tlsError, currentEpoch)):

Causa Real Comum Código(s) mbedTLS Dica de Diagnóstico Retornada
Relógio sem NTP (antes de 2023) -0x2700 (com relógio < 2023) A hora do sistema está antes de 2023 (não sincronizada via NTP); chame configTime(0, 0, "pool.ntp.org") antes de iniciar requisições HTTPS.
CA errada / Certificado expirado / Hostname divergente -0x2700 (X509_CERT_VERIFY_FAILED) Falha na verificação X.509: confira se a Root CA corresponde à cadeia do servidor (openssl s_client -showcerts), se o certificado não expirou e se o hostname na URL coincide com o CN/SAN.
Formato PEM / DER inválido -0x2180 .. -0x3E80 Formato de certificado/chave inválido: verifique os delimitadores -----BEGIN/END CERTIFICATE-----, quebras de linha \n e se o ponteiro permanece válido na memória.
Memória RAM (Heap) insuficiente -0x7F00, -0x2880, -0x0010 Falta de memória heap durante o handshake TLS (~45 KB livres necessários). Chame client.end() em clientes ociosos.
Alerta Fatal / mTLS exigido -0x7780 (SSL_FATAL_ALERT_MESSAGE) O servidor rejeitou o handshake com alerta fatal (verifique se o servidor exige certificado de cliente mTLS via setClientCert(), SNI ou cifra compatível).
SNI / Certificado ausente -0x7280, -0x7880, -0x7100, -0x7680 Falha na negociação do handshake ou nenhum certificado correspondeu ao hostname solicitado (SNI).
client.get("/todos/1");
if (client.hasError()) {
    // Exibe o erro completo + código mbedTLS + dica prática de diagnóstico
    Serial.printf("Erro (%d): %s\n",
                  client.getStatusCode(), client.getErrorMessage().c_str());

    if (client.getTlsError() != 0) {
        Serial.printf("Código TLS: -0x%04X | Dica: %s\n",
                      -client.getTlsError(), client.getTlsErrorHint().c_str());
    }
}

Modo Inseguro (Apenas para Desenvolvimento)

Para desenvolvimento em rede local ou testes com certificados autoassinados, a validação de certificados pode ser desativada explicitamente (desde que requireValidatedTls(true) não esteja ativo):

client.setInsecure();

Risco de Segurança

setInsecure() emite um log de aviso e desativa totalmente a validação do certificado e do hostname, tornando as conexões vulneráveis a ataques Man-in-the-Middle (MITM). Nunca utilize setInsecure() em ambientes de produção.