O nosso ponto de vista: somos a Geonode e comercializamos proxies, e a configuração do proxy do OkHttp é realmente invulgar — a autenticação do proxy é feita através de um proxyAuthenticator, e não de um cabeçalho, e se isso for feito incorretamente, gera um erro 407 que parece ser um problema de credenciais, quando na verdade é um problema de configuração. Essa secção encontra-se perto do final. Tudo o que vem antes disso funciona sem qualquer proxy e, se estiver a aprender a biblioteca, comece por testá-la com a sua própria ligação.
Onde se encontra agora o OkHttp
Vale a pena referir logo de início, porque os links antigos já não funcionam.
O repositório do OkHttp foi transferido. github.com/square/okhttp
redireciona agora para github.com/lysine-dev/okhttp
, e o site de documentação que se encontrava em square.github.io/okhttp
apresenta um erro 404 — o site atual está em lysine.dev/okhttp
. O projeto é mantido ativamente e está licenciado sob a licença Apache-2.0, conforme verificado em setembro de 2026.
As coordenadas do Maven não se alteraram. Continua publicado como com.squareup.okhttp3:okhttp
:
implementation("com.squareup.okhttp3:okhttp:5.5.0")
Existe também uma lista de materiais para manter os artefactos relacionados atualizados:
implementation(platform("com.squareup.okhttp3:okhttp-bom:5.5.0"))
Requisitos: Android 5.0+ (nível de API 21+) e Java 8+. O OkHttp depende do Okio para E/S e da biblioteca padrão do Kotlin — ambas descritas pelo projeto como «pequenas bibliotecas com forte compatibilidade retroativa». No Android, utiliza o AndroidX Startup e, se desativar o inicializador no manifesto, a sua aplicação deve chamar OkHttp.initialize(applicationContext)
em Application.onCreate
.
O antigo ramo 3.12.x
suporta Android 2.3+ e Java 7, e o projeto é claro ao afirmar que essas plataformas «não suportam TLS 1.2 e não devem ser utilizadas».
A sua primeira solicitação
OkHttpClient client = new OkHttpClient();
String run(String url) throws IOException {
Request request = new Request.Builder()
.url(url)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
Há três aspetos importantes nesse exemplo de sete linhas.
**try
-with-resources não é opcional.** Response
implementa Closeable
, e uma resposta não encerrada mantém a sua ligação. Se houver fugas suficientes, o conjunto de ligações esgota-se, altura em que as solicitações ficam pendentes em vez de falharem — um sintoma que se assemelha a um problema de rede, mas que não o é.
**body().string()
pode ser chamado uma vez.** Consome o fluxo. Chamá-lo duas vezes provoca um erro, e chamá-lo depois de a resposta ter sido fechada também provoca um erro. Se precisar do corpo mais do que uma vez, guarde a cadeia de caracteres.
**execute()
é síncrono.** Para trabalho assíncrono, enqueue()
aceita um Callback
e é executado no conjunto de threads do despachante do OkHttp.
Um POST tem a mesma estrutura com um corpo:
public static final MediaType JSON = MediaType.get("application/json");
String post(String url, String json) throws IOException {
RequestBody body = RequestBody.create(json, JSON);
Request request = new Request.Builder()
.url(url)
.post(body)
.build();
try (Response response = client.newCall(request).execute()) {
return response.body().string();
}
}
Partilhar o Cliente
A decisão de design em que as pessoas mais frequentemente erram.
OkHttpClient
mantém um conjunto de ligações e um conjunto de threads. Criar um por cada pedido desperdiça todas as ligações que poderias ter reutilizado e cria threads que depois abandonas. Funciona, é lento e, sob carga, esgota os recursos.
Cria um único cliente para a tua aplicação e partilha-o. É seguro para threads por definição.
Quando precisar de configurações diferentes para uma parte do seu código, não crie um segundo cliente do zero — clone o já existente para que os conjuntos sejam partilhados:
OkHttpClient shortTimeout = client.newBuilder()
.readTimeout(5, TimeUnit.SECONDS)
.build();
newBuilder()
produz um cliente que partilha o conjunto de ligações e o distribuidor com o seu pai, que é exatamente o que pretende.
Para o encerramento, especialmente em processos de curta duração, liberte os recursos explicitamente:
client.dispatcher().executorService().shutdown();
client.connectionPool().evictAll();
Sem isto, uma JVM pode ficar bloqueada durante o período de manutenção da ligação do conjunto antes de terminar, o que é algo complicado de depurar numa ferramenta CLI.
O que o OkHttp acrescenta à sua solicitação
A biblioteca reescreve as solicitações, e saber o que ela acrescenta evita uma série de confusões.
A documentação é explícita: «O OkHttp pode adicionar cabeçalhos que não constam na solicitação original, incluindo Content-Length, Transfer-Encoding, User-Agent, Host, Connection e Content-Type. Adicionará um cabeçalho Accept-Encoding para compressão transparente da resposta, a menos que esse cabeçalho já esteja presente. Se houver cookies, o OkHttp adicionará um cabeçalho Cookie com os mesmos.»
Duas consequências.
A compressão transparente é automática e revertida automaticamente. O OkHttp solicita a compressão, descomprime a resposta e, em seguida, «eliminará os cabeçalhos de resposta correspondentes Content-Encoding e Content-Length, uma vez que estes não se aplicam ao corpo da resposta descomprimida». Portanto, a ausência de Content-Length numa resposta é normal e não um erro — a menos que defina Accept-Encoding manualmente, caso em que a descompressão fica a seu cargo.
Os pedidos condicionais ocorrem automaticamente quando o armazenamento em cache está ativado. O OkHttp adiciona If-Modified-Since e If-None-Match para revalidar entradas de cache desatualizadas.
Por predefinição, também segue redirecionamentos e, num desafio de autorização, «solicitará ao Authenticator (se estiver configurado) que satisfaça o desafio», tentando novamente com as credenciais fornecidas.
A biblioteca descreve-se como «baseada em princípios e evita ser excessivamente configurável, especialmente quando essa configuração se destina a contornar um servidor com erros, testar cenários inválidos ou que contradigam a RFC relevante». Enumera as suas próprias limitações com honestidade — «não permite GET com corpo» e a cache «não é uma interface com implementações alternativas». Se precisar de enviar pedidos deliberadamente inválidos, esta não é a biblioteca adequada para o efeito, e isso é uma escolha de conceção e não uma omissão.
Interceptores
O principal ponto de extensão e a característica que irá definir a forma como utiliza a biblioteca.
class LoggingInterceptor implements Interceptor {
@Override public Response intercept(Interceptor.Chain chain) throws IOException {
Request request = chain.request();
long t1 = System.nanoTime();
logger.info(String.format("Sending request %s on %s%n%s",
request.url(), chain.connection(), request.headers()));
Response response = chain.proceed(request);
long t2 = System.nanoTime();
logger.info(String.format("Received response for %s in %.1fms%n%s",
response.request().url(), (t2 - t1) / 1e6d, response.headers()));
return response;
}
}
A documentação salienta que «uma chamada a chain.proceed(request) é uma parte essencial da implementação de cada interceptor. Este método, aparentemente simples, é onde todo o trabalho HTTP ocorre.» E um aviso que vale a pena ter em conta: «se chain.proceed(request) for chamado mais do que uma vez, os corpos das respostas anteriores têm de ser fechados.»
Existem dois tipos, e é importante escolher corretamente. Registe-se com addInterceptor() ou addNetworkInterceptor(). A documentação explica a diferença com precisão.
Interceptores de aplicação:
- «Não é preciso preocupar-se com respostas intermédias, como redirecionamentos e novas tentativas.»
- «São sempre invocados uma vez, mesmo que a resposta HTTP seja servida a partir da cache.»
- «Respeitam a intenção original da aplicação. Não se preocupam com cabeçalhos injetados pelo OkHttp, como
If-None-Match.» - «Podem interromper o fluxo e não chamar
Chain.proceed().» - «Podem tentar novamente e efetuar várias chamadas a
Chain.proceed().» - «Pode ajustar os tempos limite das chamadas utilizando
withConnectTimeout,withReadTimeout,withWriteTimeout.»
Interceptores de rede:
- «Capaz de operar em respostas intermédias, como redirecionamentos e novas tentativas.»
- «Não é invocado para respostas em cache que contornam a rede.»
- «Observe os dados tal como serão transmitidos pela rede.»
- «Acesso ao
Connectionque transporta o pedido.»
A regra prática: utilize um interceptor de aplicação para tudo o que diga respeito à intenção do seu pedido — adicionar um cabeçalho de autorização, um agente de utilizador, registo ao nível da aplicação. Utilize um interceptor de rede para tudo o que diga respeito ao que realmente atravessa a rede — inspecionar corpos comprimidos, verificar cada salto de redirecionamento, examinar a ligação.
O erro mais comum é registar um interceptor de autenticação como um interceptor de rede, o que faz com que este seja acionado uma vez por cada salto de redirecionamento e possa revelar as suas credenciais a um anfitrião que não pretendia.
Tempos limite
O OkHttp tem valores predefinidos razoáveis e quatro configurações distintas; saber qual delas foi acionada indica onde está o problema.
OkHttpClient client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.callTimeout(60, TimeUnit.SECONDS)
.build();
**connectTimeout
** abrange o estabelecimento da ligação TCP e TLS.
**readTimeout
** aplica-se entre leituras individuais, não à resposta na totalidade — por isso, um download lento, mas que avança, nunca o aciona.
**writeTimeout
** faz o mesmo para os uploads.
**callTimeout
** limita toda a chamada, incluindo redirecionamentos, novas tentativas e transferência do corpo da mensagem. Por predefinição, é zero, o que significa que não há limite global.
É este último que deve ser definido. Sem ele, uma chamada que continua a receber dados aos poucos pode decorrer indefinidamente, porque o tempo limite de leitura é reiniciado a cada byte recebido. callTimeout
é o limite máximo que faz com que uma tarefa termine, e é o equivalente a --max-time
no curl — uma distinção que abordámos em definir um tempo limite com o curl.
As substituições por chamada estão disponíveis através de ``withReadTimeout`
` e métodos semelhantes de um interceptor de aplicação, sendo esta a forma de conceder mais margem a um ponto final lento sem flexibilizar os valores predefinidos para tudo.
Proxies
É aqui que o OkHttp difere da maioria dos clientes e onde as pessoas perdem tempo.
Proxy proxy = new Proxy(Proxy.Type.HTTP,
new InetSocketAddress("proxy.example.com", 9000));
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.build();
Para SOCKS, utilize Proxy.Type.SOCKS com o mesmo formato.
A autenticação é a parte que surpreende as pessoas. Não é necessário definir um cabeçalho Proxy-Authorization. Deve fornecer um proxyAuthenticator, que o OkHttp invoca quando o proxy emite um desafio 407:
Authenticator proxyAuth = (route, response) -> {
if (response.request().header("Proxy-Authorization") != null) {
return null; // already tried these credentials; give up
}
String credential = Credentials.basic("user", "pass");
return response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build();
};
OkHttpClient client = new OkHttpClient.Builder()
.proxy(proxy)
.proxyAuthenticator(proxyAuth)
.build();
O retorno nulo é essencial. Sem ele, uma palavra-passe errada gera um ciclo infinito de tentativas em vez de uma falha — o OkHttp consulta o autenticador, recebe a mesma credencial incorreta, recebe outro 407 e volta a perguntar. Verificar se já enviou um cabeçalho Proxy-Authorization é a forma de quebrar o ciclo.
Três notas adicionais.
Um 407 não é um 401. O proxy recusou o acesso e nunca chegou ao destino. Um 401 significa que o proxy funcionou e que o destino solicita credenciais, o que é uma situação de authenticator() completamente diferente.
proxySelector() permite-lhe escolher um proxy por pedido, em vez de por cliente, o que é a forma de encaminhar diferentes hosts de forma distinta sem ter de criar vários clientes.
Verifique se a alteração surtiu efeito. Solicite um serviço que devolva o seu endereço, com e sem o proxy configurado. Uma configuração incorreta neste caso passa despercebida — os pedidos são bem-sucedidos e seguem diretamente — e confirmar o endereço de saída é a única forma de ter a certeza. Esse padrão de «sucesso silencioso» é aquele sobre o qual escrevemos em por que é importante testar proxies.
Tratar as respostas de forma adequada
Alguns padrões que fazem a diferença entre código que funciona e código que funciona sob carga.
**Verifique se há um erro de rede (isSuccessful()
), e não apenas a ausência de uma exceção.** O OkHttp lança um erro de rede (IOException
) em caso de falhas de rede, e não em caso de códigos de erro HTTP. Um 404 ou um 500 surge como um erro de rede comum (Response
):
try (Response response = client.newCall(request).execute()) {
if (!response.isSuccessful()) {
String body = response.body() != null ? response.body().string() : "";
throw new IOException("HTTP " + response.code() + " from " + request.url()
+ ": " + body.substring(0, Math.min(200, body.length())));
}
return response.body().string();
}
Incluir a primeira parte do corpo do erro transforma um código de estado opaco numa mensagem que identifica o problema — a maioria das APIs explica-se no corpo de um 400.
Não armazene respostas grandes numa String. body().string()
lê tudo para a memória. Para um download grande, utilize streaming:
try (Response response = client.newCall(request).execute();
BufferedSource source = response.body().source();
BufferedSink sink = Okio.buffer(Okio.sink(new File("out.bin")))) {
sink.writeAll(source);
}
As chamadas assíncronas continuam a necessitar que o corpo seja fechado, e o callback é executado numa thread em segundo plano:
client.newCall(request).enqueue(new Callback() {
@Override public void onFailure(Call call, IOException e) {
logger.warn("request failed", e);
}
@Override public void onResponse(Call call, Response response) throws IOException {
try (ResponseBody body = response.body()) {
handle(body.string());
}
}
});
Note-se que onFailure
é disparado apenas em caso de problemas de rede. Um erro HTTP 500 surge em onResponse
, o que surpreende quem espera que a nomenclatura signifique exatamente o que parece.
As tentativas de repetição requerem cuidado. O OkHttp repete automaticamente algumas falhas ao nível da ligação, controladas por retryOnConnectionFailure()
, que está ativado por predefinição. Não repete tentativas em caso de códigos de erro HTTP, e nem deve fazê-lo — repetir uma chamada POST que possa ter sido bem-sucedida pode duplicar uma gravação. Quando adicionar a sua própria lógica de repetição de tentativas, faça-o num interceptor da aplicação, limite o número de tentativas, deixe um intervalo entre elas e restrinja-a a métodos idempotentes, a menos que a API ofereça uma chave de idempotência.
O que mais lhe oferece
Resumidamente, uma vez que estas são as razões para o escolher.
HTTP/2, em que «o suporte permite que todos os pedidos para o mesmo anfitrião partilhem um socket». Pool de ligações, que «reduz a latência das solicitações (caso o HTTP/2 não esteja disponível)». GZIP transparente. Armazenamento em cache de respostas, que «evita completamente a rede para solicitações repetidas».
Resiliência. «Recupera silenciosamente de problemas comuns de ligação» e, quando um serviço tem vários endereços, «tenta endereços alternativos se a primeira ligação falhar» — o que, segundo o projeto, «é necessário para IPv4+IPv6 e serviços alojados em centros de dados redundantes».
TLS moderno, incluindo TLS 1.3, ALPN e fixação de certificados, utilizando a implementação da plataforma. Na JVM, também suporta o Conscrypt, que integra o BoringSSL com o Java, utilizado automaticamente se for o primeiro fornecedor de segurança.
Conformidade com normas. O projeto enumera as especificações que segue: RFC 9110 para semântica HTTP, RFC 9111 para armazenamento em cache, RFC 9112 para HTTP/1.1, RFC 9113 para HTTP/2, RFC 6455 para WebSockets e a especificação WHATWG para eventos enviados pelo servidor. Quando uma especificação é ambígua, «segue os agentes de utilizador modernos, tais como navegadores populares ou bibliotecas HTTP comuns».
Perguntas frequentes
O OkHttp ainda é mantido?
Sim. O repositório mudou de square/okhttp para lysine-dev/okhttp e o site da documentação está agora em lysine.dev/okhttp, mas o projeto continua a ser desenvolvido ativamente e está licenciado sob a licença Apache-2.0. As coordenadas do Maven continuam a ser com.squareup.okhttp3:okhttp.
Devo criar um novo OkHttpClient para cada pedido?
Não. O cliente mantém um conjunto de ligações e um conjunto de threads, e é seguro para threads por definição. Crie um para a sua aplicação e partilhe-o. Quando precisar de configurações diferentes, utilize newBuilder() no cliente existente para que os conjuntos sejam partilhados.
Por que tenho de fechar a resposta?
Porque Response mantém uma ligação até ser fechada, e as respostas que ficam em aberto esgotam o conjunto de ligações. O sintoma é que as solicitações ficam pendentes em vez de falharem, o que é difícil de diagnosticar. Utilize sempre try-with-resources.
Qual é a diferença entre um interceptor de aplicação e um interceptor de rede?
Um interceptor de aplicação vê o seu pedido original e é invocado exatamente uma vez, mesmo para respostas em cache, podendo interromper o processo ou tentar novamente. Um interceptor de rede vê cada troca de rede individual, incluindo redirecionamentos e novas tentativas, tem acesso à ligação e é totalmente ignorado no caso de respostas em cache.
Como configuro um proxy no OkHttp?
Passe um objeto java.net.Proxy para OkHttpClient.Builder.proxy(). Para autenticação, defina um proxyAuthenticator em vez de um cabeçalho — e devolva null quando um cabeçalho Proxy-Authorization já estiver presente, caso contrário, credenciais incorretas podem provocar um ciclo infinito de tentativas.
Que tempos de espera devo definir?
O «connectTimeout» (tempo de espera de leitura) em cerca de 10 segundos, o «readTimeout» (tempo de espera de resposta) e o «writeTimeout» (tempo de espera de conexão) em cerca de 30, e — mais importante ainda — o «callTimeout» (tempo de espera de chamada), cujo valor predefinido é zero e que é a única configuração que limita toda a chamada. Sem ele, uma resposta que chega lentamente nunca atinge o tempo de espera, porque o tempo de espera de leitura é reiniciado a cada byte.
O OkHttp lida com GZIP automaticamente?
Sim, se não definir manualmente Accept-Encoding. Ele solicita a compressão, descomprime a resposta e descarta Content-Encoding e Content-Length, uma vez que estes já não descrevem o corpo descomprimido. Se definir o cabeçalho manualmente, fica também responsável pela descompressão.
Que versão do Java é necessária para o OkHttp?
Java 8 ou posterior e Android 5.0 (nível de API 21) ou posterior. Depende do Okio e da biblioteca padrão do Kotlin. O antigo ramo 3.12.x suporta Java 7 e Android 2.3, mas não suporta TLS 1.2 e não deve ser utilizado.
Conclusão
O OkHttp é uma API compacta baseada numa implementação bem concebida, e há dois hábitos que abrangem a maior parte da sua utilização correta: partilhar um único cliente em toda a aplicação e encerrar todas as respostas com ``try-with-resources. Ambas as falhas ocorrem de forma silenciosa e acabam por se manifestar como pedidos que ficam bloqueados, em vez de erros que se possam ler.
É nos interceptores que irá dedicar a maior parte do seu tempo, e vale a pena aprender bem a distinção entre «aplicação» e «rede», em vez de aprender por tentativa e erro. Os interceptores de aplicação detetam a sua intenção e são executados uma única vez; os interceptores de rede detetam cada salto e são ignorados no caso de respostas em cache. Registar um interceptor de autorização na camada de rede é o erro clássico, e este é acionado em cada redirecionamento.
Defina um callTimeout. O valor predefinido é zero; é a única configuração que delimita uma chamada na íntegra, e a sua ausência é a razão pela qual uma tarefa que deveria demorar segundos, ocasionalmente, continua a executar-se até que outra coisa a interrompa.
E se estiver a seguir documentação mais antiga, verifique os links. O projeto mudou-se para lysine.dev/okhttp e o site hospedado pela Square já não existe — embora as coordenadas do artefacto permaneçam inalteradas, pelo que o seu ficheiro de compilação não necessita de quaisquer alterações.
