diff --git a/CHANGES.md b/CHANGES.md
deleted file mode 100644
index d548766a4..000000000
--- a/CHANGES.md
+++ /dev/null
@@ -1,29 +0,0 @@
-## From 2.2 to 2.3
-
-* New `isFilterInsecureCipherSuites` config to disable unsecure and weak ciphers filtering performed internally in Netty.
-
-## From 2.1 to 2.2
-
-* New [Typesafe config](https://github.com/lightbend/config) extra module
-* new `enableWebSocketCompression` config to enable per-message and per-frame WebSocket compression extension
-
-## From 2.0 to 2.1
-
-* AHC 2.1 targets Netty 4.1.
-* `org.asynchttpclient.HttpResponseHeaders` was [dropped](https://github.com/AsyncHttpClient/async-http-client/commit/f4786f3ac7699f8f8664e7c7db0b7097585a0786) in favor
- of `io.netty.handler.codec.http.HttpHeaders`.
-* `org.asynchttpclient.cookie.Cookie` was [dropped](https://github.com/AsyncHttpClient/async-http-client/commit/a6d659ea0cc11fa5131304d8a04a7ba89c7a66af) in favor
- of `io.netty.handler.codec.http.cookie.Cookie` as AHC's cookie parsers were contributed to Netty.
-* AHC now has a RFC6265 `CookieStore` that is enabled by default. Implementation can be changed in `AsyncHttpClientConfig`.
-* `AsyncHttpClient` now exposes stats with `getClientStats`.
-* `AsyncHandlerExtensions` was [dropped](https://github.com/AsyncHttpClient/async-http-client/commit/1972c9b9984d6d9f9faca6edd4f2159013205aea) in favor of default methods
- in `AsyncHandler`.
-* `WebSocket` and `WebSocketListener` methods were renamed to mention frames
-* `AsyncHttpClientConfig` various changes:
- * new `getCookieStore` now lets you configure a CookieStore (enabled by default)
- * new `isAggregateWebSocketFrameFragments` now lets you disable WebSocket fragmented frames aggregation
- * new `isUseLaxCookieEncoder` lets you loosen cookie chars validation
- * `isAcceptAnyCertificate` was dropped, as it didn't do what its name stated
- * new `isUseInsecureTrustManager` lets you use a permissive TrustManager, that would typically let you accept self-signed certificates
- * new `isDisableHttpsEndpointIdentificationAlgorithm` disables setting `HTTPS` algorithm on the SSLEngines, typically disables SNI and HTTPS hostname verification
- * new `isAggregateWebSocketFrameFragments` lets you disable fragmented WebSocket frames aggregation
diff --git a/README.md b/README.md
index ed3ef6765..303200eb5 100644
--- a/README.md
+++ b/README.md
@@ -1,263 +1,414 @@
-# Async Http Client
+# Async Http Client
+
[](https://github.com/AsyncHttpClient/async-http-client/actions/workflows/builds.yml)
-
+[](https://central.sonatype.com/artifact/org.asynchttpclient/async-http-client)
+[](https://www.apache.org/licenses/LICENSE-2.0)
+
+AsyncHttpClient (AHC) is a high-performance, asynchronous HTTP client for Java
+built on top of [Netty](https://github.com/netty/netty).
+It supports HTTP/1.1, HTTP/2, and WebSocket protocols.
+
+## Table of Contents
+
+- [Features](#features)
+- [Requirements](#requirements)
+- [Installation](#installation)
+- [Quick Start](#quick-start)
+- [Configuration](#configuration)
+- [HTTP Requests](#http-requests)
+- [Handling Responses](#handling-responses)
+- [HTTP/2](#http2)
+- [WebSocket](#websocket)
+- [Authentication](#authentication)
+- [Proxy Support](#proxy-support)
+- [Community](#community)
+- [License](#license)
+
+## Features
+
+- **HTTP/2 with multiplexing** — enabled by default over TLS via ALPN,
+ with connection multiplexing and GOAWAY handling
+- **HTTP/1.1 and HTTP/1.0** — connection pooling and keep-alive
+- **WebSocket** — text, binary, and ping/pong frame support
+- **Asynchronous API** — non-blocking I/O with `ListenableFuture`
+ and `CompletableFuture`
+- **Compression** — automatic gzip, deflate, Brotli, and Zstd decompression
+- **Authentication** — Basic, Digest, NTLM, and SPNEGO/Kerberos
+- **Proxy** — HTTP, SOCKS4, and SOCKS5 with CONNECT tunneling
+- **Native transports** — optional Epoll, KQueue, and io_uring
+- **Request/response filters** — intercept and transform at each stage
+- **Cookie management** — RFC 6265-compliant cookie store
+- **Multipart uploads** — file, byte array, input stream, and string parts
+- **Resumable downloads** — built-in `ResumableIOExceptionFilter`
+
+## Requirements
+
+Java 11+
-Follow [@AsyncHttpClient](https://twitter.com/AsyncHttpClient) on Twitter.
+## Installation
-The AsyncHttpClient (AHC) library allows Java applications to easily execute HTTP requests and asynchronously process HTTP responses.
-The library also supports the WebSocket Protocol.
+**Maven:**
-It's built on top of [Netty](https://github.com/netty/netty). It's compiled with Java 11.
+```xml
+
+ org.asynchttpclient
+ async-http-client
+ 3.0.7
+
+```
-## Installation
+**Gradle:**
+
+```groovy
+implementation 'org.asynchttpclient:async-http-client:3.0.7'
+```
+
+
+Optional: Native Transport
-Binaries are deployed on Maven Central.
-Add a dependency on the main AsyncHttpClient artifact:
+For lower-latency I/O on Linux, add a native transport dependency:
-Maven:
```xml
-
-
- org.asynchttpclient
- async-http-client
- 3.0.7
-
-
+
+
+ io.netty
+ netty-transport-native-epoll
+ linux-x86_64
+
+
+
+
+ io.netty
+ netty-transport-native-io_uring
+ linux-x86_64
+
```
-Gradle:
-```groovy
-dependencies {
- implementation 'org.asynchttpclient:async-http-client:3.0.7'
-}
+Then enable in config:
+
+```java
+AsyncHttpClient client = asyncHttpClient(config().setUseNativeTransport(true));
+```
+
+
+
+
+Optional: Brotli / Zstd Compression
+
+```xml
+
+ com.aayushatharva.brotli4j
+ brotli4j
+ 1.18.0
+
+
+
+ com.github.luben
+ zstd-jni
+ 1.5.7-2
+
```
-### Dsl
+
-Import the Dsl helpers to use convenient methods to bootstrap components:
+## Quick Start
+
+Import the DSL helpers:
```java
import static org.asynchttpclient.Dsl.*;
```
-### Client
+Create a client, execute a request, and read the response:
```java
-import static org.asynchttpclient.Dsl.*;
+try (AsyncHttpClient client = asyncHttpClient()) {
+ // Asynchronous
+ client.prepareGet("https://www.example.com/")
+ .execute()
+ .toCompletableFuture()
+ .thenApply(Response::getResponseBody)
+ .thenAccept(System.out::println)
+ .join();
-AsyncHttpClient asyncHttpClient=asyncHttpClient();
+ // Synchronous (blocking)
+ Response response = client.prepareGet("https://www.example.com/")
+ .execute()
+ .get();
+}
```
-AsyncHttpClient instances must be closed (call the `close` method) once you're done with them, typically when shutting down your application.
-If you don't, you'll experience threads hanging and resource leaks.
-
-AsyncHttpClient instances are intended to be global resources that share the same lifecycle as the application.
-Typically, AHC will usually underperform if you create a new client for each request, as it will create new threads and connection pools for each.
-It's possible to create shared resources (EventLoop and Timer) beforehand and pass them to multiple client instances in the config. You'll then be responsible for closing
-those shared resources.
+> **Note:** `AsyncHttpClient` instances are long-lived, shared resources.
+> Always close them when done. Creating a new client per request will degrade
+> performance due to repeated thread pool and connection pool creation.
## Configuration
-Finally, you can also configure the AsyncHttpClient instance via its AsyncHttpClientConfig object:
+Use `config()` to build an `AsyncHttpClientConfig`:
```java
-import static org.asynchttpclient.Dsl.*;
-
-AsyncHttpClient c=asyncHttpClient(config().setProxyServer(proxyServer("127.0.0.1",38080)));
+AsyncHttpClient client = asyncHttpClient(config()
+ .setConnectTimeout(Duration.ofSeconds(5))
+ .setRequestTimeout(Duration.ofSeconds(30))
+ .setMaxConnections(500)
+ .setMaxConnectionsPerHost(100)
+ .setFollowRedirect(true)
+ .setMaxRedirects(5)
+ .setCompressionEnforced(true));
```
-## HTTP
+## HTTP Requests
### Sending Requests
-### Basics
-
-AHC provides 2 APIs for defining requests: bound and unbound.
-`AsyncHttpClient` and Dsl` provide methods for standard HTTP methods (POST, PUT, etc) but you can also pass a custom one.
+**Bound** — build directly from the client:
```java
-import org.asynchttpclient.*;
+Response response = client
+ .prepareGet("https://api.example.com/users")
+ .addHeader("Accept", "application/json")
+ .addQueryParam("page", "1")
+ .execute()
+ .get();
+```
-// bound
-Future whenResponse=asyncHttpClient.prepareGet("http://www.example.com/").execute();
+**Unbound** — build standalone via DSL, then execute:
+
+```java
+Request request = get("https://api.example.com/users")
+ .addHeader("Accept", "application/json")
+ .addQueryParam("page", "1")
+ .build();
-// unbound
- Request request=get("http://www.example.com/").build();
- Future whenResponse=asyncHttpClient.executeRequest(request);
+Response response = client.executeRequest(request).get();
```
-#### Setting Request Body
+Methods: `GET`, `POST`, `PUT`, `DELETE`, `PATCH`, `HEAD`, `OPTIONS`, `TRACE`.
-Use the `setBody` method to add a body to the request.
+### Request Bodies
+
+Use `setBody` to attach a body. Supported types:
+
+| Type | Description |
+|---|---|
+| `String` | Text content |
+| `byte[]` | Raw bytes |
+| `ByteBuffer` | NIO buffer |
+| `InputStream` | Streaming input |
+| `File` | File content |
+| `Publisher` | Reactive stream |
+| `BodyGenerator` | Custom body generation |
+
+```java
+Response response = client
+ .preparePost("https://api.example.com/data")
+ .setHeader("Content-Type", "application/json")
+ .setBody("{\"name\": \"value\"}")
+ .execute()
+ .get();
+```
-This body can be of type:
+For streaming bodies, see `FeedableBodyGenerator` which lets you push chunks
+asynchronously.
-* `java.io.File`
-* `byte[]`
-* `List`
-* `String`
-* `java.nio.ByteBuffer`
-* `java.io.InputStream`
-* `Publisher`
-* `org.asynchttpclient.request.body.generator.BodyGenerator`
+### Multipart Uploads
-`BodyGenerator` is a generic abstraction that let you create request bodies on the fly.
-Have a look at `FeedableBodyGenerator` if you're looking for a way to pass requests chunks on the fly.
+```java
+Response response = client
+ .preparePost("https://api.example.com/upload")
+ .addBodyPart(new FilePart("file", new File("report.pdf"), "application/pdf"))
+ .addBodyPart(new StringPart("description", "Monthly report"))
+ .execute()
+ .get();
+```
-#### Multipart
+Part types: `FilePart`, `ByteArrayPart`, `InputStreamPart`, `StringPart`.
-Use the `addBodyPart` method to add a multipart part to the request.
+## Handling Responses
-This part can be of type:
+### Blocking
-* `ByteArrayPart`
-* `FilePart`
-* `InputStreamPart`
-* `StringPart`
+```java
+Response response = client.prepareGet("https://www.example.com/").execute().get();
+```
-### Dealing with Responses
+> Useful for debugging, but defeats the purpose of an async client in production.
-#### Blocking on the Future
+### ListenableFuture
-`execute` methods return a `java.util.concurrent.Future`. You can simply block the calling thread to get the response.
+`execute()` returns a `ListenableFuture` that supports completion listeners:
```java
-Future whenResponse=asyncHttpClient.prepareGet("http://www.example.com/").execute();
- Response response=whenResponse.get();
+ListenableFuture future = client
+ .prepareGet("https://www.example.com/")
+ .execute();
+
+future.addListener(() -> {
+ Response response = future.get();
+ System.out.println(response.getStatusCode());
+}, executor);
```
-This is useful for debugging but you'll most likely hurt performance or create bugs when running such code on production.
-The point of using a non blocking client is to *NOT BLOCK* the calling thread!
+> If `executor` is `null`, the callback runs on the Netty I/O thread.
+> **Never block** inside I/O thread callbacks.
+
+### CompletableFuture
+
+```java
+client.prepareGet("https://www.example.com/")
+ .execute()
+ .toCompletableFuture()
+ .thenApply(Response::getResponseBody)
+ .thenAccept(System.out::println)
+ .join();
+```
-### Setting callbacks on the ListenableFuture
+### AsyncCompletionHandler
-`execute` methods actually return a `org.asynchttpclient.ListenableFuture` similar to Guava's.
-You can configure listeners to be notified of the Future's completion.
+For most async use cases, extend `AsyncCompletionHandler` — it buffers the
+full response and gives you a single `onCompleted(Response)` callback:
```java
- ListenableFuture whenResponse = ???;
- Runnable callback = () - > {
- try {
- Response response = whenResponse.get();
- System.out.println(response);
- } catch (InterruptedException | ExecutionException e) {
- e.printStackTrace();
- }
- };
+client.prepareGet("https://www.example.com/")
+ .execute(new AsyncCompletionHandler() {
+ @Override
+ public String onCompleted(Response response) {
+ return response.getResponseBody();
+ }
+ });
+```
+
+### AsyncHandler
- java.util.concurrent.Executor executor = ???;
- whenResponse.addListener(() - > ??? , executor);
+For fine-grained control, implement `AsyncHandler` directly. This lets you
+inspect status, headers, and body chunks as they arrive and abort early:
+
+```java
+Future future = client
+ .prepareGet("https://www.example.com/")
+ .execute(new AsyncHandler<>() {
+ private int status;
+
+ @Override
+ public State onStatusReceived(HttpResponseStatus s) {
+ status = s.getStatusCode();
+ return State.CONTINUE;
+ }
+
+ @Override
+ public State onHeadersReceived(HttpHeaders headers) {
+ return State.CONTINUE;
+ }
+
+ @Override
+ public State onBodyPartReceived(HttpResponseBodyPart part) {
+ return State.ABORT; // stop early — we only needed the status
+ }
+
+ @Override
+ public Integer onCompleted() {
+ return status;
+ }
+
+ @Override
+ public void onThrowable(Throwable t) {
+ t.printStackTrace();
+ }
+ });
```
-If the `executor` parameter is null, callback will be executed in the IO thread.
-You *MUST NEVER PERFORM BLOCKING* operations in there, typically sending another request and block on a future.
+## HTTP/2
-#### Using custom AsyncHandlers
+HTTP/2 is **enabled by default** for HTTPS connections via ALPN negotiation.
+The client uses HTTP/2 when the server supports it and falls back to HTTP/1.1
+otherwise. No additional configuration is required.
-`execute` methods can take an `org.asynchttpclient.AsyncHandler` to be notified on the different events, such as receiving the status, the headers and body chunks.
-When you don't specify one, AHC will use a `org.asynchttpclient.AsyncCompletionHandler`;
+- **Connection multiplexing** — concurrent streams over a single TCP connection
+- **GOAWAY handling** — graceful connection draining on server shutdown
+- **PING keepalive** — configurable ping frames to keep connections alive
-`AsyncHandler` methods can let you abort processing early (return `AsyncHandler.State.ABORT`) and can let you return a computation result from `onCompleted` that will be used
-as the Future's result.
-See `AsyncCompletionHandler` implementation as an example.
+### HTTP/2 Configuration
-The below sample just capture the response status and skips processing the response body chunks.
+```java
+AsyncHttpClient client = asyncHttpClient(config()
+ .setHttp2MaxConcurrentStreams(100)
+ .setHttp2InitialWindowSize(65_535)
+ .setHttp2MaxFrameSize(16_384)
+ .setHttp2MaxHeaderListSize(8_192)
+ .setHttp2PingInterval(Duration.ofSeconds(30)) // keepalive pings
+ .setHttp2CleartextEnabled(true)); // h2c prior knowledge
+```
-Note that returning `ABORT` closes the underlying connection.
+To force HTTP/1.1, disable HTTP/2:
```java
-import static org.asynchttpclient.Dsl.*;
+AsyncHttpClient client = asyncHttpClient(config().setHttp2Enabled(false));
+```
-import org.asynchttpclient.*;
-import io.netty.handler.codec.http.HttpHeaders;
+## WebSocket
-Future whenStatusCode = asyncHttpClient.prepareGet("http://www.example.com/")
- .execute(new AsyncHandler () {
- private Integer status;
-
- @Override
- public State onStatusReceived(HttpResponseStatus responseStatus) throws Exception {
- status = responseStatus.getStatusCode();
- return State.ABORT;
- }
-
- @Override
- public State onHeadersReceived(HttpHeaders headers) throws Exception {
- return State.ABORT;
- }
-
+```java
+WebSocket ws = client
+ .prepareGet("wss://echo.example.com/")
+ .execute(new WebSocketUpgradeHandler.Builder()
+ .addWebSocketListener(new WebSocketListener() {
@Override
- public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
- return State.ABORT;
+ public void onOpen(WebSocket ws) {
+ ws.sendTextFrame("Hello!");
}
-
+
@Override
- public Integer onCompleted() throws Exception{
- return status;
+ public void onTextFrame(String payload, boolean finalFragment, int rsv) {
+ System.out.println(payload);
}
-
+
@Override
- public void onThrowable(Throwable t) {
- t.printStackTrace();
- }
- });
+ public void onClose(WebSocket ws, int code, String reason) {}
- Integer statusCode = whenStatusCode.get();
+ @Override
+ public void onError(Throwable t) { t.printStackTrace(); }
+ })
+ .build())
+ .get();
```
-#### Using Continuations
-
-`ListenableFuture` has a `toCompletableFuture` method that returns a `CompletableFuture`.
-Beware that canceling this `CompletableFuture` won't properly cancel the ongoing request.
-There's a very good chance we'll return a `CompletionStage` instead in the next release.
+## Authentication
```java
-CompletableFuture whenResponse=asyncHttpClient
- .prepareGet("http://www.example.com/")
- .execute()
- .toCompletableFuture()
- .exceptionally(t->{ /* Something wrong happened... */ })
- .thenApply(response->{ /* Do something with the Response */ return resp;});
- whenResponse.join(); // wait for completion
+// Client-wide Basic auth
+AsyncHttpClient client = asyncHttpClient(config()
+ .setRealm(basicAuthRealm("user", "password")));
+
+// Per-request Digest auth
+Response response = client
+ .prepareGet("https://api.example.com/protected")
+ .setRealm(digestAuthRealm("user", "password").build())
+ .execute()
+ .get();
```
-You may get the complete maven project for this simple demo
-from [org.asynchttpclient.example](https://github.com/AsyncHttpClient/async-http-client/tree/master/example/src/main/java/org/asynchttpclient/example)
+Supported schemes: **Basic**, **Digest**, **NTLM**, **SPNEGO/Kerberos**.
-## WebSocket
-
-Async Http Client also supports WebSocket.
-You need to pass a `WebSocketUpgradeHandler` where you would register a `WebSocketListener`.
+## Proxy Support
```java
-WebSocket websocket = c.prepareGet("ws://demos.kaazing.com/echo")
- .execute(new WebSocketUpgradeHandler.Builder().addWebSocketListener(
- new WebSocketListener() {
-
- @Override
- public void onOpen(WebSocket websocket) {
- websocket.sendTextFrame("...").sendTextFrame("...");
- }
-
- @Override
- public void onClose(WebSocket websocket) {
- // ...
- }
-
- @Override
- public void onTextFrame(String payload, boolean finalFragment, int rsv) {
- System.out.println(payload);
- }
-
- @Override
- public void onError(Throwable t) {
- t.printStackTrace();
- }
- }).build()).get();
+// HTTP proxy
+AsyncHttpClient client = asyncHttpClient(config()
+ .setProxyServer(proxyServer("proxy.example.com", 8080)));
+
+// Authenticated proxy
+AsyncHttpClient client = asyncHttpClient(config()
+ .setProxyServer(proxyServer("proxy.example.com", 8080)
+ .setRealm(basicAuthRealm("proxyUser", "proxyPassword"))));
```
-## User Group
+SOCKS4 and SOCKS5 proxies are also supported.
+
+## Community
+
+- [GitHub Discussions](https://github.com/AsyncHttpClient/async-http-client/discussions) — questions, ideas, and general discussion
+- [Issue Tracker](https://github.com/AsyncHttpClient/async-http-client/issues) — bug reports and feature requests
-Keep up to date on the library development by joining the Asynchronous HTTP Client discussion group
+## License
-[GitHub Discussions](https://github.com/AsyncHttpClient/async-http-client/discussions)
+[Apache License 2.0](LICENSE.txt)
diff --git a/pom.xml b/pom.xml
index a089bd872..16a0e1c55 100644
--- a/pom.xml
+++ b/pom.xml
@@ -66,7 +66,7 @@
scm:git:git@github.com:AsyncHttpClient/async-http-client.git
scm:git:git@github.com:AsyncHttpClient/async-http-client.git
- https://github.com/AsyncHttpClient/async-http-client/tree/master
+ https://github.com/AsyncHttpClient/async-http-client/tree/main
HEAD
@@ -89,10 +89,7 @@
asynchttpclient
- https://groups.google.com/group/asynchttpclient/topics
- https://groups.google.com/group/asynchttpclient/subscribe
- https://groups.google.com/group/asynchttpclient/subscribe
- asynchttpclient@googlegroups.com
+ https://github.com/AsyncHttpClient/async-http-client/discussions