New Upstream Snapshot - async-http-client

Ready changes

Summary

Merged new upstream version: 2.12.3+git20221122.1.57326c7 (was: 2.12.3).

Resulting package

Built on 2023-01-04T21:16 (took 5m28s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-snapshots libasync-http-client-java

Lintian Result

Diff

diff --git a/.github/workflows/maven.yml b/.github/workflows/maven.yml
deleted file mode 100644
index 5d16905..0000000
--- a/.github/workflows/maven.yml
+++ /dev/null
@@ -1,20 +0,0 @@
-# This workflow is designed to build PRs for AHC. Note that it does not actually publish AHC, just builds and test it.
-# Docs: https://help.github.com/actions/language-and-framework-guides/building-and-testing-java-with-maven
-
-name: Test PR
-
-on:
-  pull_request:
-    branches: [ master ]
-
-jobs:
-  build:
-    runs-on: ubuntu-20.04
-    steps:
-    - uses: actions/checkout@v2
-    - name: Set up JDK 1.8
-      uses: actions/setup-java@v1
-      with:
-        java-version: 1.8
-    - name: Build and test with Maven
-      run: mvn test -Ptest-output
diff --git a/.gitignore b/.gitignore
deleted file mode 100644
index d424b25..0000000
--- a/.gitignore
+++ /dev/null
@@ -1,21 +0,0 @@
-*.class
-*~
-.*.swp
-.*.swo
-.loadpath
-.buildpath
-.classpath
-.project
-.settings
-.idea
-*.iml
-*.ipr
-*.iws
-nbproject
-.DS_Store
-target
-test-output
-MANIFEST.MF
-work
-atlassian-ide-plugin.xml
-/bom/.flattened-pom.xml
diff --git a/README.md b/README.md
index 9e1cd43..1533f60 100644
--- a/README.md
+++ b/README.md
@@ -7,13 +7,9 @@ The library also supports the WebSocket Protocol.
 
 It's built on top of [Netty](https://github.com/netty/netty). It's currently compiled on Java 8 but runs on Java 9 too.
 
-## New Roadmap RFCs!
+## New Maintainer!
 
-Well, not really RFCs, but as [I](https://github.com/TomGranot) am ramping up to release a new version, I would appreciate the comments from the community. Please add an issue and [label it RFC](https://github.com/AsyncHttpClient/async-http-client/labels/RFC) and I'll take a look! 
-
-## This Repository is Actively Maintained
-
-[@TomGranot](https://github.com/TomGranot) is the current maintainer of this repository. You should feel free to reach out to him in an issue here or on [Twitter](https://twitter.com/TomGranot) for anything regarding this repository.
+[Aayush (hyperxpro)](https://twitter.com/HyperXPro) has gracefully decided to take over maintainership from [Tom](https://github.com/TomGranot), and is available for your questions. Please mention him in your issues and PRs from now on!
 
 ## Installation
 
@@ -22,6 +18,7 @@ Binaries are deployed on Maven Central.
 Import the AsyncHttpClient Bill of Materials (BOM) to add dependency management for AsyncHttpClient artifacts to your project:
 
 ```xml
+
 <dependencyManagement>
     <dependencies>
         <dependency>
@@ -35,13 +32,14 @@ Import the AsyncHttpClient Bill of Materials (BOM) to add dependency management
 </dependencyManagement>
 ```
 
-Add a dependency on the main AsyncHttpClient artifact: 
+Add a dependency on the main AsyncHttpClient artifact:
 
 ```xml
+
 <dependencies>
     <dependency>
-    	<groupId>org.asynchttpclient</groupId>
-    	<artifactId>async-http-client</artifactId>
+        <groupId>org.asynchttpclient</groupId>
+        <artifactId>async-http-client</artifactId>
     </dependency>
 </dependencies>
 ```
@@ -75,7 +73,7 @@ import static org.asynchttpclient.Dsl.*;
 ```java
 import static org.asynchttpclient.Dsl.*;
 
-AsyncHttpClient asyncHttpClient = asyncHttpClient();
+AsyncHttpClient asyncHttpClient=asyncHttpClient();
 ```
 
 AsyncHttpClient instances must be closed (call the `close` method) once you're done with them, typically when shutting down your application.
@@ -83,7 +81,8 @@ 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.
+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.
 
 ## Configuration
 
@@ -92,7 +91,7 @@ Finally, you can also configure the AsyncHttpClient instance via its AsyncHttpCl
 ```java
 import static org.asynchttpclient.Dsl.*;
 
-AsyncHttpClient c = asyncHttpClient(config().setProxyServer(proxyServer("127.0.0.1", 38080)));
+AsyncHttpClient c=asyncHttpClient(config().setProxyServer(proxyServer("127.0.0.1",38080)));
 ```
 
 ## HTTP
@@ -108,11 +107,11 @@ AHC provides 2 APIs for defining requests: bound and unbound.
 import org.asynchttpclient.*;
 
 // bound
-Future<Response> whenResponse = asyncHttpClient.prepareGet("http://www.example.com/").execute();
+Future<Response> whenResponse=asyncHttpClient.prepareGet("http://www.example.com/").execute();
 
 // unbound
-Request request = get("http://www.example.com/").build();
-Future<Response> whenResponse = asyncHttpClient.executeRequest(request);
+        Request request=get("http://www.example.com/").build();
+        Future<Response> whenResponse=asyncHttpClient.executeRequest(request);
 ```
 
 #### Setting Request Body
@@ -120,6 +119,7 @@ Future<Response> whenResponse = asyncHttpClient.executeRequest(request);
 Use the `setBody` method to add a body to the request.
 
 This body can be of type:
+
 * `java.io.File`
 * `byte[]`
 * `List<byte[]>`
@@ -130,13 +130,14 @@ This body can be of type:
 * `org.asynchttpclient.request.body.generator.BodyGenerator`
 
 `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. 
+Have a look at `FeedableBodyGenerator` if you're looking for a way to pass requests chunks on the fly.
 
 #### Multipart
 
 Use the `addBodyPart` method to add a multipart part to the request.
 
 This part can be of type:
+
 * `ByteArrayPart`
 * `FilePart`
 * `InputStreamPart`
@@ -149,8 +150,8 @@ This part can be of type:
 `execute` methods return a `java.util.concurrent.Future`. You can simply block the calling thread to get the response.
 
 ```java
-Future<Response> whenResponse = asyncHttpClient.prepareGet("http://www.example.com/").execute();
-Response response = whenResponse.get();
+Future<Response> whenResponse=asyncHttpClient.prepareGet("http://www.example.com/").execute();
+        Response response=whenResponse.get();
 ```
 
 This is useful for debugging but you'll most likely hurt performance or create bugs when running such code on production.
@@ -159,20 +160,20 @@ The point of using a non blocking client is to *NOT BLOCK* the calling thread!
 ### Setting callbacks on the ListenableFuture
 
 `execute` methods actually return a `org.asynchttpclient.ListenableFuture` similar to Guava's.
-You can configure listeners to be notified of the Future's completion. 
+You can configure listeners to be notified of the Future's completion.
 
 ```java
-ListenableFuture<Response> whenResponse = ???;
-Runnable callback = () -> {
-	try  {
-		Response response = whenResponse.get();
-		System.out.println(response);
-	} catch (InterruptedException | ExecutionException e) {
-		e.printStackTrace();
-	}
-};
-java.util.concurrent.Executor executor = ???;
-whenResponse.addListener(() -> ???, executor);
+ListenableFuture<Response> whenResponse=???;
+        Runnable callback=()->{
+        try{
+        Response response=whenResponse.get();
+        System.out.println(response);
+        }catch(InterruptedException|ExecutionException e){
+        e.printStackTrace();
+        }
+        };
+        java.util.concurrent.Executor executor=???;
+        whenResponse.addListener(()->???,executor);
 ```
 
 If the `executor` parameter is null, callback will be executed in the IO thread.
@@ -183,7 +184,8 @@ You *MUST NEVER PERFORM BLOCKING* operations in there, typically sending another
 `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`;
 
-`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.
+`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.
 
 The below sample just capture the response status and skips processing the response body chunks.
@@ -192,35 +194,36 @@ Note that returning `ABORT` closes the underlying connection.
 
 ```java
 import static org.asynchttpclient.Dsl.*;
+
 import org.asynchttpclient.*;
 import io.netty.handler.codec.http.HttpHeaders;
 
-Future<Integer> whenStatusCode = asyncHttpClient.prepareGet("http://www.example.com/")
-.execute(new AsyncHandler<Integer>() {
-	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;
-	}
-	@Override
-	public State onBodyPartReceived(HttpResponseBodyPart bodyPart) throws Exception {
-		return State.ABORT;
-	}
-	@Override
-	public Integer onCompleted() throws Exception {
-		return status;
-	}
-	@Override
-	public void onThrowable(Throwable t) {
-	}
-});
-
-Integer statusCode = whenStatusCode.get();
+Future<Integer> whenStatusCode=asyncHttpClient.prepareGet("http://www.example.com/")
+        .execute(new AsyncHandler<Integer>(){
+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;
+        }
+@Override
+public State onBodyPartReceived(HttpResponseBodyPart bodyPart)throws Exception{
+        return State.ABORT;
+        }
+@Override
+public Integer onCompleted()throws Exception{
+        return status;
+        }
+@Override
+public void onThrowable(Throwable t){
+        }
+        });
+
+        Integer statusCode=whenStatusCode.get();
 ```
 
 #### Using Continuations
@@ -230,16 +233,17 @@ Beware that canceling this `CompletableFuture` won't properly cancel the ongoing
 There's a very good chance we'll return a `CompletionStage` instead in the next release.
 
 ```java
-CompletableFuture<Response> 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
+CompletableFuture<Response> 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
 ```
 
-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)
+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)
 
 ## WebSocket
 
@@ -247,28 +251,28 @@ Async Http Client also supports WebSocket.
 You need to pass a `WebSocketUpgradeHandler` where you would register a `WebSocketListener`.
 
 ```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) {
-          }
-      }).build()).get();
+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){
+        }
+        }).build()).get();
 ```
 
 ## Reactive Streams
@@ -287,21 +291,22 @@ AsyncHttpClient has build in support for the WebDAV protocol.
 The API can be used the same way normal HTTP request are made:
 
 ```java
-Request mkcolRequest = new RequestBuilder("MKCOL").setUrl("http://host:port/folder1").build();
-Response response = c.executeRequest(mkcolRequest).get();
+Request mkcolRequest=new RequestBuilder("MKCOL").setUrl("http://host:port/folder1").build();
+        Response response=c.executeRequest(mkcolRequest).get();
 ```
+
 or
 
 ```java
-Request propFindRequest = new RequestBuilder("PROPFIND").setUrl("http://host:port").build();
-Response response = c.executeRequest(propFindRequest, new AsyncHandler() {
-  // ...
-}).get();
+Request propFindRequest=new RequestBuilder("PROPFIND").setUrl("http://host:port").build();
+        Response response=c.executeRequest(propFindRequest,new AsyncHandler(){
+        // ...
+        }).get();
 ```
 
 ## More
 
-You can find more information on Jean-François Arcand's blog.  Jean-François is the original author of this library.
+You can find more information on Jean-François Arcand's blog. Jean-François is the original author of this library.
 Code is sometimes not up-to-date but gives a pretty good idea of advanced features.
 
 * http://web.archive.org/web/20111224171448/http://jfarcand.wordpress.com/2011/01/12/going-asynchronous-using-asynchttpclient-for-dummies/
@@ -324,5 +329,5 @@ Here are the few rules we'd like you to respect if you do so:
 * Only edit the code related to the suggested change, so DON'T automatically format the classes you've edited.
 * Use IntelliJ default formatting rules.
 * Regarding licensing:
-  * You must be the original author of the code you suggest.
-  * You must give the copyright to "the AsyncHttpClient Project"
+    * You must be the original author of the code you suggest.
+    * You must give the copyright to "the AsyncHttpClient Project"
diff --git a/bom/pom.xml b/bom/pom.xml
index 867f231..072db56 100644
--- a/bom/pom.xml
+++ b/bom/pom.xml
@@ -5,7 +5,7 @@
     <parent>
         <groupId>org.asynchttpclient</groupId>
         <artifactId>async-http-client-project</artifactId>
-        <version>2.12.3</version>
+        <version>2.12.4-SNAPSHOT</version>
     </parent>
 
     <artifactId>async-http-client-bom</artifactId>
diff --git a/client/pom.xml b/client/pom.xml
index 59b67c1..c9e13ae 100644
--- a/client/pom.xml
+++ b/client/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.asynchttpclient</groupId>
     <artifactId>async-http-client-project</artifactId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>async-http-client</artifactId>
diff --git a/debian/changelog b/debian/changelog
index 2cad40e..c807c48 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+async-http-client (2.12.3+git20221122.1.57326c7-1) UNRELEASED; urgency=low
+
+  * New upstream snapshot.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Wed, 04 Jan 2023 21:13:33 -0000
+
 async-http-client (2.12.3-1) unstable; urgency=medium
 
   * New upstream release
diff --git a/debian/patches/remove_classifier.patch b/debian/patches/remove_classifier.patch
index 53aee23..523094e 100644
--- a/debian/patches/remove_classifier.patch
+++ b/debian/patches/remove_classifier.patch
@@ -4,8 +4,10 @@ Author: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
 
 ---
 
---- a/client/pom.xml
-+++ b/client/pom.xml
+Index: async-http-client.git/client/pom.xml
+===================================================================
+--- async-http-client.git.orig/client/pom.xml
++++ async-http-client.git/client/pom.xml
 @@ -58,7 +58,6 @@
      <dependency>
        <groupId>io.netty</groupId>
@@ -14,8 +16,10 @@ Author: Sudip Mukherjee <sudipm.mukherjee@gmail.com>
      </dependency>
      <dependency>
        <groupId>org.reactivestreams</groupId>
---- a/pom.xml
-+++ b/pom.xml
+Index: async-http-client.git/pom.xml
+===================================================================
+--- async-http-client.git.orig/pom.xml
++++ async-http-client.git/pom.xml
 @@ -321,7 +321,6 @@
        <dependency>
          <groupId>io.netty</groupId>
diff --git a/docs/technical-overview.md b/docs/technical-overview.md
new file mode 100644
index 0000000..2d4e4b1
--- /dev/null
+++ b/docs/technical-overview.md
@@ -0,0 +1,290 @@
+# [WIP] AsyncHttpClient Technical Overview
+
+#### Disclaimer
+
+This document is a work in progress. 
+
+## Motivation
+
+While heavily used (~2.3M downloads across the project in December 2020 alone), AsyncHttpClient (or AHC) does not - at this point in time - have a single guiding document that explains how it works internally. As a maintainer fresh on the scene it was unclear to me ([@TomGranot](https://github.com/TomGranot)) exactly how all the pieces fit together.
+
+As part of the attempt to educate myself, I figured it would be a good idea to write a technical overview of the project.  This document  provides an in-depth walkthtough of the library, allowing new potential contributors to "hop on" the coding train as fast as possible. 
+
+Note that this library *is not small*. I expect that in addition to offering a better understanding as to how each piece *works*, writing this document will also allow me to understand which pieces *do not work* as well as expected, and direct me towards things that need a little bit of love.
+
+PRs are open for anyone who wants to help out. For now - let the fun begin. :)
+
+**Note: I wrote this guide while using AHC 2.12.2**.
+
+## The flow of a request 
+
+### Introduction
+
+AHC is an *Asynchronous* HTTP Client. That means that it needs to have some underlying mechanism of dealing with response data that arrives **asynchronously**. To make that part easier, the creator of the library ([@jfarcand](https://github.com/jfarcand)) built it on top of [Netty](https://netty.io/), which is (by [their own definition](https://netty.io/#content:~:text=Netty%20is%20a%20NIO%20client%20server,as%20TCP%20and%20UDP%20socket%20server.)) "a framework that enables quick and easy development of network applications".  
+
+This article is not a Netty user guide. If you're interested in all Netty has to offer, you should check out the [official user guide](https://netty.io/wiki/user-guide-for-4.x.html). This article is, instead, more of a discussion of using Netty *in the wild* - an overview of what a client library built on top of Netty actually looks like in practice. 
+
+### The code in full
+
+The best way to explore what the client actually does is, of course, by following the path a request takes.
+
+Consider the following bit of code, [taken verbatim from one of the simplest tests](https://github.com/AsyncHttpClient/async-http-client/blob/2b12d0ba819e05153fa265b4da7ca900651fd5b3/client/src/test/java/org/asynchttpclient/BasicHttpTest.java#L81-L91) in the library:
+
+```java
+@Test
+  public void getRootUrl() throws Throwable {
+    withClient().run(client ->
+      withServer(server).run(server -> {
+        String url = server.getHttpUrl();
+        server.enqueueOk();
+
+        Response response = client.executeRequest(get(url), new AsyncCompletionHandlerAdapter()).get(TIMEOUT, SECONDS);
+        assertEquals(response.getUri().toUrl(), url);
+      }));
+  }
+```
+
+Let's take it bit by bit.
+
+First:
+```java
+withClient().run(client ->
+  withServer(server).run(server -> {
+    String url = server.getHttpUrl();
+    server.enqueueOk();
+```
+
+These lines take care of spinning up a server to run the test against, and create an instance of `AsyncHttpClient` called `client`.  If you were to drill deeper into the code, you'd notice that the instantiation of `client` can be simplified to (converted from functional to procedural for the sake of the explanation):
+
+```java
+DefaultAsyncHttpClientConfig config = new DefaultAsyncHttpClientConfig.Builder().build.()setMaxRedirects(0);
+AsyncHttpClient client = new DefaultAsyncHttpClient(config);
+```
+
+Once the server and the client have been created, we can now run our test:
+
+```java
+Response response = client.executeRequest(get(url), new AsyncCompletionHandlerAdapter()).get(TIMEOUT, SECONDS);
+assertEquals(response.getUri().toUrl(), url);
+```
+
+The first line executes a `GET` request to the URL of the server that was previously spun up, while the second line is the assertion part of our test. Once the request is completed, the final `Response` object is returned.
+
+The intersting bits, of course, happen between the lines - and the best way to start the discussion is by considering what happens under the hood when a new client is instantiated.
+
+### Creating a new AsyncHTTPClient - Configuration
+
+AHC was designed to be *heavily configurable*. There are many, many different knobs you can turn and buttons you can press in order to get it to behave _just right_. [`DefaultAsyncHttpClientConfig`](https://github.com/AsyncHttpClient/async-http-client/blob/d4f1e5835b81a5e813033ba2a64a07b020c70007/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClientConfig.java) is a utility class that pulls a set of [hard-coded, sane defaults](https://github.com/AsyncHttpClient/async-http-client/blob/d4f1e5835b81a5e813033ba2a64a07b020c70007/client/src/main/resources/org/asynchttpclient/config/ahc-default.properties) to prevent you from having to deal with these mundane configurations - please check it out if you have the time. 
+
+A keen observer will note that the construction of a `DefaultAsyncHttpClient` is done using the [Builder Pattern](https://dzone.com/articles/design-patterns-the-builder-pattern) - this is useful, since many times you want to change a few parameters in a request (`followRedirect`, `requestTimeout`, etc... ) but still rely on the rest of the default configuration properties. The reason I'm mentioning this here is to prevent a bit of busywork on your end the next time you want to create a client - it's much, much easier to work off of the default client and tweak properties than creating your own set of configuration properties.
+
+ The `setMaxRedicrects(0)` from the initialization code above is an example of doign this in practice. Having no redirects following the `GET` requeset is useful in the context of the test, and so we turn a knob to ensure none do.
+
+### Creating a new AsyncHTTPClient - Client Instantiation
+
+Coming back to our example - once we've decided on a proper configuration, it's time to create a client. Let's look at the constructor of the [`DefaultAsyncHttpClient`](https://github.com/AsyncHttpClient/async-http-client/blob/a44aac86616f4e8ffe6977dfef0f0aa460e79d07/client/src/main/java/org/asynchttpclient/DefaultAsyncHttpClient.java): 
+
+```java
+  public DefaultAsyncHttpClient(AsyncHttpClientConfig config) {
+
+    this.config = config;
+    this.noRequestFilters = config.getRequestFilters().isEmpty();
+    allowStopNettyTimer = config.getNettyTimer() == null;
+    nettyTimer = allowStopNettyTimer ? newNettyTimer(config) : config.getNettyTimer();
+
+    channelManager = new ChannelManager(config, nettyTimer);
+    requestSender = new NettyRequestSender(config, channelManager, nettyTimer, new AsyncHttpClientState(closed));
+    channelManager.configureBootstraps(requestSender);
+
+    CookieStore cookieStore = config.getCookieStore();
+    if (cookieStore != null) {
+      int cookieStoreCount = config.getCookieStore().incrementAndGet();
+      if (
+        allowStopNettyTimer // timer is not shared
+        || cookieStoreCount == 1 // this is the first AHC instance for the shared (user-provided) timer
+      ) {
+        nettyTimer.newTimeout(new CookieEvictionTask(config.expiredCookieEvictionDelay(), cookieStore),
+          config.expiredCookieEvictionDelay(), TimeUnit.MILLISECONDS);
+      }
+    }
+  }
+```
+
+ The constructor actually reveals a lot of the moving parts of AHC, and is worth a proper walkthrough:
+
+#### `RequestFilters`
+
+
+```java
+ this.noRequestFilters = config.getRequestFilters().isEmpty();
+```
+
+`RequestFilters` are a way to perform some form of computation **before sending a request to a server**. You can read more about request filters [here](#request-filters), but a simple example is the [ThrottleRequestFilter](https://github.com/AsyncHttpClient/async-http-client/blob/758dcf214bf0ec08142ba234a3967d98a3dc60ef/client/src/main/java/org/asynchttpclient/filter/ThrottleRequestFilter.java) that throttles requests by waiting for a response to arrive before executing the next request in line. 
+
+Note that there is another set of filters, `ResponseFilters`, that can perform computations **before processing the first byte of the response**. You can read more about them [here](#response-filters).
+
+#### `NettyTimer`
+
+```java
+allowStopNettyTimer = config.getNettyTimer() == null;
+nettyTimer = allowStopNettyTimer ? newNettyTimer(config) : config.getNettyTimer();
+```
+
+`NettyTimer` is actually not a timer, but a *task executor* that waits an arbitrary amount of time before performing the next task. In the case of the code above, it is used for evicting cookies after they expire - but it has many different use cases (request timeouts being a prime example).
+
+#### `ChannelManager`
+
+```java
+channelManager = new ChannelManager(config, nettyTimer);
+```
+
+`ChannelManager` requires a [section of its own](#channelmanager), but the bottom line is that one has to do a lot of boilerplate work with Channels when building an HTTP client using Netty. For any given request there's a variable number of channel operations you would have to take, and there's a lot of value in re-using existing channels in clever ways instead of opening new ones. `ChannelManager` is AHC's way of encapsulating at least some of that functionality (for example, [connection pooling](https://en.wikipedia.org/wiki/Connection_pool#:~:text=In%20software%20engineering%2C%20a%20connection,executing%20commands%20on%20a%20database.)) into a single object, instead of having it spread out all over the place.
+
+There are two similiarly-named constructs in the project, so I'm mentioning them in this
+
+* `ChannelPool`, as it is [implemented in AHC](https://github.com/AsyncHttpClient/async-http-client/blob/758dcf214bf0ec08142ba234a3967d98a3dc60ef/client/src/main/java/org/asynchttpclient/channel/ChannelPool.java#L21), is an **AHC structure** designed to be a "container" of channels - a place you can add and remove channels from as the need arises. Note that the AHC implementation (that might go as far back as 2012) *predates* the [Netty implementation](https://netty.io/news/2015/05/07/4-0-28-Final.html) introduced in 2015 (see this [AHC user guide entry](https://asynchttpclient.github.io/async-http-client/configuring.html#contentBox:~:text=ConnectionsPoo,-%3C) from 2012 in which `ConnectionPool` is referenced as proof). 
+
+  As the [Netty release mentions](https://netty.io/news/2015/05/07/4-0-28-Final.html#main-content:~:text=Many%20of%20our%20users%20needed%20to,used%20Netty%20to%20writing%20a%20client.), connection pooling in the world of Netty-based clients is a valuable feature to have, one that [Jean-Francois](https://github.com/jfarcand) implemented himself instead of waiting for Netty to do so. This might confuse anyone coming to the code a at a later point in time - like me - and I have yet to explore the tradeoffs of stripping away the current implementation and in favor of the upstream one. See [this issue](https://github.com/AsyncHttpClient/async-http-client/issues/1766) for current progress.
+
+* [`ChannelGroup`](https://netty.io/4.0/api/io/netty/channel/group/ChannelGroup.html) (not to be confused with `ChannelPool`) is a **Netty structure** designed to work with Netty `Channel`s *in bulk*, to reduce the need to perform the same operation on multiple channels sequnetially. 
+
+#### `NettyRequestSender`
+
+```java
+requestSender = new NettyRequestSender(config, channelManager, nettyTimer, new AsyncHttpClientState(closed));
+channelManager.configureBootstraps(requestSender);
+```
+
+`NettyRequestSender` does the all the heavy lifting required for sending the HTTP request - creating the required `Request` and `Response` objects, making sure `CONNECT` requests are sent before the relevant requests, dealing with proxy servers (in the case of [HTTPS connections](https://developer.mozilla.org/en-US/docs/Web/HTTP/Methods/CONNECT)), dispatching DNS hostname resolution requests and more. 
+
+ A few extra comments before we move on: 
+
+* When finished with all the work, `NettyRequestSender` will send back a [`ListenableFuture`](https://github.com/AsyncHttpClient/async-http-client/blob/d47c56e7ee80b76a4cffd4770237239cfea0ffd6/client/src/main/java/org/asynchttpclient/ListenableFuture.java#L40). AHC's `ListenableFuture` is an extension of a normal Java `Future` that allows for the addition of "Listeners" - pieces of code that get executed once the computation (the one blocking the `Future` from completing) is finished. It is an example of a *very* common abstraction that exists in many different Java projects - Google's [Guava](https://github.com/google/guava) [has one](https://github.com/google/guava/blob/master/futures/listenablefuture1/src/com/google/common/util/concurrent/ListenableFuture.java), for example, and so does [Spring](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/util/concurrent/ListenableFuture.html)).
+
+* Note the invocation of `configureBootstraps` in the second line here. `Bootstrap`s are a Netty concept that make it easy to set up `Channel`s - we'll talk about them a bit later.
+
+#### `CookieStore`
+
+```java
+CookieStore cookieStore = config.getCookieStore();
+    if (cookieStore != null) {
+      int cookieStoreCount = config.getCookieStore().incrementAndGet();
+      if (
+        allowStopNettyTimer // timer is not shared
+        || cookieStoreCount == 1 // this is the first AHC instance for the shared (user-provided) timer
+      ) {
+        nettyTimer.newTimeout(new CookieEvictionTask(config.expiredCookieEvictionDelay(), cookieStore),
+          config.expiredCookieEvictionDelay(), TimeUnit.MILLISECONDS);
+      }
+    }
+```
+
+`CookieStore` is, well, a container for cookies. In this context, it is used to handle the task of cookie eviction (removing cookies whose expiry date has passed). This is, by the way, an example of one of the *many, many features* AHC supports out of the box that might not be evident upon first observation.
+
+Once the client has been properly configured, it's time to actually execute the request.
+
+### Executing a request - Before execution
+
+Take a look at the execution line from the code above again:
+
+```java
+Response response = client.executeRequest(get(url), new AsyncCompletionHandlerAdapter()).get(TIMEOUT, SECONDS);
+```
+
+Remember that what we have in front of us is an instance of `AsyncHttpClient` called `client` that is configured with an `AsyncHttpClientConfig`, and more specifically an instance of `DefaultAsyncHttpClient` that is configured with `DefaultAsyncHttpClientConfig`. 
+
+The `executeRequest` method is passed two arguments, and returns a `ListenableFuture`. The `Response` created by executing the `get` method on the `ListenableFuture` is the end of the line in our case here, since this test is very simple - there's no response body to parse or any other computations to do in order to assert the test succeeded. The only thing that is required for the correct operation of the code is for the `Response` to come back with the correct URL. 
+
+Let's turn our eyes to the two arguments passed to `executeRequest`, then, since they are the key parts here:
+
+1. `get(url)` is the functional equivalent of `new RequestBuilder("GET").setUrl(url)`. `RequestBuilder` is in charge of scaffolding an instance of [AHC's `Request` object](https://github.com/AsyncHttpClient/async-http-client/blob/c5eff423ebdd0cddd00bc6fcf17682651a151028/client/src/main/java/org/asynchttpclient/Request.java) and providing it with sane defaults - mostly regarding HTTP headers (`RequestBuilder` does for `Request` what `DefaultAsyncHttpClientConfig.Builder()` does for `DefaultAsyncHttpClient`).  
+   1. In our case, the `Request` contains no body (it's a simple `GET`). However, if that request was, for example, a `POST` - it could have a payload that would need to be sent to the server via HTTP as well.  We'll be talking about `Request` in more detail [here](#working-with-request-bodies), including how to work with request bodies.  
+2. To fully understand what `AsyncCompletionHandlerAdapter` is, and why it's such a core piece of everything that goes on in AHC, a bit of Netty background is required. Let's take a sidestep for a moment:
+
+#### Netty `Channel`s and their associated entities ( `ChannelPipeline`s, `ChannelHandler`s and `ChannelAdapter`s)
+
+Recall that AHC is built on [Netty](https://netty.io/) and its networking abstractions. If you want to dive deeper into the framework you **should** read [Netty in Action](https://www.manning.com/books/netty-in-action) (great book, Norman!), but for the sake of our discussion it's enough to settle on clarifying a few basic terms:
+
+1. [`Channel`](https://netty.io/4.1/api/io/netty/channel/Channel.html) is Netty's version of a normal Java [`Socket`](https://docs.oracle.com/javase/8/docs/api/java/net/Socket.html), greatly simplified for easier usage. It encapsulates [all that you can know and do](https://netty.io/4.1/api/io/netty/channel/Channel.html#allclasses_navbar_top:~:text=the%20current%20state%20of%20the%20channel,and%20requests%20associated%20with%20the%20channel.) with a regular `Socket`:
+   
+   1.  **State** - Is the socket currently open? Is it currently closed?
+   2. **I/O Options** - Can we read from it? Can we write to it?
+   3. **Configuration** - What is the receive buffer size? What is the connect timeout?
+   4. `ChannelPipeline` - A reference to this `Channel`'s `ChannelPipeline`.
+   
+2.  Note that operations on a channel, in and of themselves, are **blocking** - that is, any operation that is performed on a channel blocks any other operations from being performed on the channel at any given point in time. This is contrary to the Asynchronous nature Netty purports to support. 
+
+    To solve the issue, Netty adds a `ChannelPipeline` to every new `Channel` that is initialised. A `ChannelPipeline` is nothing but a container for `ChannelHandlers`. 
+3. [`ChannelHandler`](https://netty.io/4.1/api/io/netty/channel/ChannelHandler.html)s encapsulate the application logic of a Netty application. To be more precise, a *chain* of `ChannelHandler`s, each in charge of one or more small pieces of logic that - when taken together - describe the entire data processing that is supposed to take place during the lifetime of the application. 
+
+4. [`ChannelHandlerContext`](https://netty.io/4.0/api/io/netty/channel/ChannelHandlerContext.html) is also worth mentioning here - it's the actual mechanism a `ChannelHandler` uses to talk to the `ChannelPipeline` that encapsulates it. 
+
+5. `ChannelXHandlerAdapter`s are a set of *default* handler implementations - "sugar" that should make the development of application logic easier. `X` can be `Inbound ` (`ChannelInboundHandlerAdapter`), `Oubound` (`ChannelOutboundHandlerAdapter`) or one of many other options Netty provides out of the box. 
+
+#### `ChannelXHandlerAdapter` VS. `AsyncXHandlerAdapter`
+
+This where it's important to note the difference between `ChannelXHandlerAdapter` (i.e. `ChannelInboundHandlerAdapater`) - which is a **Netty construct** and `AsyncXHandlerAdapter` (i.e. `AsyncCompletionHandlerAdapater`) - which is an **AHC construct**. 
+
+Basically, `ChannelXHandlerAdapter` is a Netty construct that provides a default implementation of a `ChannelHandler`, while `AsyncXHandlerAdapter` is an AHC construct that provides a default implementation of an `AsyncHandler`. 
+
+A `ChannelXHandlerAdapter` has methods that are called when *handler-related* and *channel-related* events occur. When the events "fire",  a piece of business logic is carried out in the relevant method, and the operation is then **passed on to the** **next `ChannelHandler` in line.** *The methods return nothing.* 
+
+An `AsyncXHandlerAdapter` works a bit differently. It has methods that are triggered when *some piece of data is available during an asynchronous response processing*. The methods are invoked in a pre-determined order, based on the expected arrival of each piece of data (when the status code arrives, when the headers arrive, etc.).  When these pieces of information become availale, a piece of business logic is carried out in the relevant method, and *a [`STATE`](https://github.com/AsyncHttpClient/async-http-client/blob/f61f88e694850818950195379c5ba7efd1cd82ee/client/src/main/java/org/asynchttpclient/AsyncHandler.java#L242-L253) is returned*. This `STATE` enum instructs the current implementation of the `AsyncHandler` (in our case, `AsyncXHandlerAdapater`) whether it should `CONTINUE` or `ABORT` the current processing.
+
+This is **the core of AHC**: an asynchronous mechanism that encodes - and allows a developer to "hook" into - the various stages of the asynchronous processing of an HTTP response.
+
+###  Executing a request - During execution
+
+TODO
+
+### Executing a request - After execution
+
+TODO
+
+## Working with Netty channels
+
+### ChannelManager
+
+TODO
+
+## Transforming requests and responses
+
+TODO
+
+### Working with Request Bodies
+
+TODO
+
+### Request Filters
+
+TODO
+
+### Working with Response Bodies
+
+TODO
+
+### Response Filters
+
+TODO
+
+### Handlers
+
+TODO
+
+## Resources
+
+### Netty
+
+* https://seeallhearall.blogspot.com/2012/05/netty-tutorial-part-1-introduction-to.html
+
+### AsyncHttpClient
+
+TODO
+
+### HTTP
+
+TODO
+
+## Footnotes
+
+[^1] Some Netty-related definitions borrow heavily from [here](https://seeallhearall.blogspot.com/2012/05/netty-tutorial-part-1-introduction-to.html).
diff --git a/example/pom.xml b/example/pom.xml
index 5643fea..6b96cda 100644
--- a/example/pom.xml
+++ b/example/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.asynchttpclient</groupId>
     <artifactId>async-http-client-project</artifactId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>async-http-client-example</artifactId>
diff --git a/extras/guava/pom.xml b/extras/guava/pom.xml
index 39fd913..12fed4e 100644
--- a/extras/guava/pom.xml
+++ b/extras/guava/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.asynchttpclient</groupId>
     <artifactId>async-http-client-extras-parent</artifactId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>async-http-client-extras-guava</artifactId>
diff --git a/extras/jdeferred/pom.xml b/extras/jdeferred/pom.xml
index d3c7d6a..0d57752 100644
--- a/extras/jdeferred/pom.xml
+++ b/extras/jdeferred/pom.xml
@@ -18,7 +18,7 @@
   <parent>
     <artifactId>async-http-client-extras-parent</artifactId>
     <groupId>org.asynchttpclient</groupId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <artifactId>async-http-client-extras-jdeferred</artifactId>
   <name>Asynchronous Http Client JDeferred Extras</name>
diff --git a/extras/pom.xml b/extras/pom.xml
index 5fccc3b..e0e053f 100644
--- a/extras/pom.xml
+++ b/extras/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.asynchttpclient</groupId>
     <artifactId>async-http-client-project</artifactId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>async-http-client-extras-parent</artifactId>
diff --git a/extras/registry/pom.xml b/extras/registry/pom.xml
index 492ef41..1cd8b24 100644
--- a/extras/registry/pom.xml
+++ b/extras/registry/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.asynchttpclient</groupId>
     <artifactId>async-http-client-extras-parent</artifactId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>async-http-client-extras-registry</artifactId>
diff --git a/extras/retrofit2/pom.xml b/extras/retrofit2/pom.xml
index f95bd3a..f82ceed 100644
--- a/extras/retrofit2/pom.xml
+++ b/extras/retrofit2/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <artifactId>async-http-client-extras-parent</artifactId>
     <groupId>org.asynchttpclient</groupId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
 
   <artifactId>async-http-client-extras-retrofit2</artifactId>
diff --git a/extras/rxjava/pom.xml b/extras/rxjava/pom.xml
index 0668033..8cdf600 100644
--- a/extras/rxjava/pom.xml
+++ b/extras/rxjava/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>async-http-client-extras-parent</artifactId>
     <groupId>org.asynchttpclient</groupId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <artifactId>async-http-client-extras-rxjava</artifactId>
   <name>Asynchronous Http Client RxJava Extras</name>
diff --git a/extras/rxjava2/pom.xml b/extras/rxjava2/pom.xml
index e1c7af8..d0a551c 100644
--- a/extras/rxjava2/pom.xml
+++ b/extras/rxjava2/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>async-http-client-extras-parent</artifactId>
     <groupId>org.asynchttpclient</groupId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <artifactId>async-http-client-extras-rxjava2</artifactId>
   <name>Asynchronous Http Client RxJava2 Extras</name>
diff --git a/extras/simple/pom.xml b/extras/simple/pom.xml
index 92ee873..94e5134 100644
--- a/extras/simple/pom.xml
+++ b/extras/simple/pom.xml
@@ -3,7 +3,7 @@
   <parent>
     <artifactId>async-http-client-extras-parent</artifactId>
     <groupId>org.asynchttpclient</groupId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <artifactId>async-http-client-extras-simple</artifactId>
   <name>Asynchronous Http Simple Client</name>
diff --git a/extras/typesafeconfig/pom.xml b/extras/typesafeconfig/pom.xml
index 437b657..1908275 100644
--- a/extras/typesafeconfig/pom.xml
+++ b/extras/typesafeconfig/pom.xml
@@ -4,7 +4,7 @@
   <parent>
     <artifactId>async-http-client-extras-parent</artifactId>
     <groupId>org.asynchttpclient</groupId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
 
   <artifactId>async-http-client-extras-typesafe-config</artifactId>
diff --git a/netty-utils/pom.xml b/netty-utils/pom.xml
index d2be381..a2e4fdb 100644
--- a/netty-utils/pom.xml
+++ b/netty-utils/pom.xml
@@ -2,7 +2,7 @@
   <parent>
     <groupId>org.asynchttpclient</groupId>
     <artifactId>async-http-client-project</artifactId>
-    <version>2.12.3</version>
+    <version>2.12.4-SNAPSHOT</version>
   </parent>
   <modelVersion>4.0.0</modelVersion>
   <artifactId>async-http-client-netty-utils</artifactId>
diff --git a/pom.xml b/pom.xml
index 0ab1e95..ca2c7ef 100644
--- a/pom.xml
+++ b/pom.xml
@@ -4,7 +4,7 @@
 
   <groupId>org.asynchttpclient</groupId>
   <artifactId>async-http-client-project</artifactId>
-  <version>2.12.3</version>
+  <version>2.12.4-SNAPSHOT</version>
   <packaging>pom</packaging>
 
   <name>Asynchronous Http Client Project</name>
@@ -34,7 +34,7 @@
     <connection>scm:git:git@github.com:AsyncHttpClient/async-http-client.git</connection>
     <developerConnection>scm:git:git@github.com:AsyncHttpClient/async-http-client.git</developerConnection>
     <url>https://github.com/AsyncHttpClient/async-http-client/tree/master</url>
-    <tag>async-http-client-project-2.12.3</tag>
+    <tag>HEAD</tag>
   </scm>
 
   <distributionManagement>

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client-netty-utils/2.12.4-SNAPSHOT/async-http-client-netty-utils-2.12.4-SNAPSHOT.pom
-rw-r--r--  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client-project/2.12.4-SNAPSHOT/async-http-client-project-2.12.4-SNAPSHOT.pom
-rw-r--r--  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client/2.12.4-SNAPSHOT/async-http-client-2.12.4-SNAPSHOT.pom
lrwxrwxrwx  root/root   /usr/share/java/async-http-client-2.12.4-SNAPSHOT.jar -> async-http-client.jar
lrwxrwxrwx  root/root   /usr/share/java/async-http-client-netty-utils-2.12.4-SNAPSHOT.jar -> async-http-client-netty-utils.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client-netty-utils/2.12.4-SNAPSHOT/async-http-client-netty-utils-2.12.4-SNAPSHOT.jar -> ../../../../../java/async-http-client-netty-utils.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client/2.12.4-SNAPSHOT/async-http-client-2.12.4-SNAPSHOT.jar -> ../../../../../java/async-http-client.jar

Files in first set of .debs but not in second

-rw-r--r--  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client-netty-utils/2.12.3/async-http-client-netty-utils-2.12.3.pom
-rw-r--r--  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client-project/2.12.3/async-http-client-project-2.12.3.pom
-rw-r--r--  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client/2.12.3/async-http-client-2.12.3.pom
lrwxrwxrwx  root/root   /usr/share/java/async-http-client-2.12.3.jar -> async-http-client.jar
lrwxrwxrwx  root/root   /usr/share/java/async-http-client-netty-utils-2.12.3.jar -> async-http-client-netty-utils.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client-netty-utils/2.12.3/async-http-client-netty-utils-2.12.3.jar -> ../../../../../java/async-http-client-netty-utils.jar
lrwxrwxrwx  root/root   /usr/share/maven-repo/org/asynchttpclient/async-http-client/2.12.3/async-http-client-2.12.3.jar -> ../../../../../java/async-http-client.jar

No differences were encountered in the control files

More details

Full run details