You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@hc.apache.org by ol...@apache.org on 2021/02/09 18:55:01 UTC

[httpcomponents-core] 02/02: Improved message builder support

This is an automated email from the ASF dual-hosted git repository.

olegk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/httpcomponents-core.git

commit d375a17a2e69b707498202a334c75045f0a10f6a
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Tue Feb 9 17:53:22 2021 +0100

    Improved message builder support
---
 .../http/io/support/ClassicRequestBuilder.java     | 216 ++++++---------
 .../http/io/support/ClassicResponseBuilder.java    | 103 ++-----
 .../core5/http/nio/support/AsyncPushBuilder.java   |  94 +++----
 .../http/nio/support/AsyncRequestBuilder.java      | 182 ++++---------
 .../http/nio/support/AsyncResponseBuilder.java     | 103 ++-----
 .../core5/http/support/AbstractMessageBuilder.java | 143 ++++++++++
 .../core5/http/support/AbstractRequestBuilder.java | 183 +++++++++++++
 .../http/support/AbstractResponseBuilder.java      | 106 ++++++++
 .../hc/core5/http/support/BasicRequestBuilder.java | 295 +++++++++++++++++++++
 .../core5/http/support/BasicResponseBuilder.java   | 120 +++++++++
 .../org/apache/hc/core5/http/HeaderMatcher.java    |  66 +++++
 .../org/apache/hc/core5/http/HeadersMatcher.java   |  72 +++++
 .../http/support/TestBasicMessageBuilders.java     | 197 ++++++++++++++
 13 files changed, 1398 insertions(+), 482 deletions(-)

diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java
index fd621ed..5c407f5 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilder.java
@@ -27,19 +27,10 @@
 
 package org.apache.hc.core5.http.io.support;
 
-import java.net.URI;
-import java.net.URISyntaxException;
-import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
-import java.util.List;
-
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.Method;
 import org.apache.hc.core5.http.NameValuePair;
 import org.apache.hc.core5.http.ProtocolVersion;
@@ -47,12 +38,16 @@ import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
 import org.apache.hc.core5.http.io.entity.HttpEntities;
 import org.apache.hc.core5.http.io.entity.StringEntity;
 import org.apache.hc.core5.http.message.BasicClassicHttpRequest;
-import org.apache.hc.core5.http.message.BasicHeader;
-import org.apache.hc.core5.http.message.BasicNameValuePair;
-import org.apache.hc.core5.http.message.HeaderGroup;
+import org.apache.hc.core5.http.support.AbstractRequestBuilder;
 import org.apache.hc.core5.net.URIBuilder;
 import org.apache.hc.core5.util.Args;
 
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.List;
+
 /**
  * Builder for {@link ClassicHttpRequest} instances.
  * <p>
@@ -65,44 +60,32 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 5.0
  */
-public class ClassicRequestBuilder {
+public class ClassicRequestBuilder extends AbstractRequestBuilder<ClassicHttpRequest> {
 
-    private String method;
-    private URI uri;
-    private Charset charset;
-    private ProtocolVersion version;
-    private HeaderGroup headerGroup;
     private HttpEntity entity;
-    private List<NameValuePair> parameters;
-
-    ClassicRequestBuilder() {
-    }
 
     ClassicRequestBuilder(final String method) {
-        super();
-        this.method = method;
+        super(method);
     }
 
     ClassicRequestBuilder(final Method method) {
-        this(method.name());
+        super(method);
     }
 
     ClassicRequestBuilder(final String method, final URI uri) {
-        super();
-        this.method = method;
-        this.uri = uri;
+        super(method, uri);
     }
 
     ClassicRequestBuilder(final Method method, final URI uri) {
-        this(method.name(), uri);
+        super(method, uri);
     }
 
     ClassicRequestBuilder(final Method method, final String uri) {
-        this(method.name(), uri != null ? URI.create(uri) : null);
+        super(method, uri);
     }
 
     ClassicRequestBuilder(final String method, final String uri) {
-        this(method, uri != null ? URI.create(uri) : null);
+        super(method, uri);
     }
 
     public static ClassicRequestBuilder create(final String method) {
@@ -206,112 +189,87 @@ public class ClassicRequestBuilder {
         return new ClassicRequestBuilder(Method.OPTIONS, uri);
     }
 
-    public ClassicRequestBuilder setCharset(final Charset charset) {
-        this.charset = charset;
-        return this;
-    }
-
-    public Charset getCharset() {
-        return charset;
-    }
-
-    public String getMethod() {
-        return method;
-    }
-
-    public ProtocolVersion getVersion() {
-        return version;
-    }
-
+    @Override
     public ClassicRequestBuilder setVersion(final ProtocolVersion version) {
-        this.version = version;
+        super.setVersion(version);
         return this;
     }
 
-    public URI getUri() {
-        return uri;
-    }
-
+    @Override
     public ClassicRequestBuilder setUri(final URI uri) {
-        this.uri = uri;
+        super.setUri(uri);
         return this;
     }
 
+    @Override
     public ClassicRequestBuilder setUri(final String uri) {
-        this.uri = uri != null ? URI.create(uri) : null;
+        super.setUri(uri);
         return this;
     }
 
-    public Header[] getHeaders(final String name) {
-        return headerGroup != null ? headerGroup.getHeaders(name) : null;
-    }
-
+    @Override
     public ClassicRequestBuilder setHeaders(final Header... headers) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.setHeaders(headers);
+        super.setHeaders(headers);
         return this;
     }
 
-    public Header getFirstHeader(final String name) {
-        return headerGroup != null ? headerGroup.getFirstHeader(name) : null;
-    }
-
-    public Header getLastHeader(final String name) {
-        return headerGroup != null ? headerGroup.getLastHeader(name) : null;
-    }
-
+    @Override
     public ClassicRequestBuilder addHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.addHeader(header);
+        super.addHeader(header);
         return this;
     }
 
+    @Override
     public ClassicRequestBuilder addHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.addHeader(new BasicHeader(name, value));
+        super.addHeader(name, value);
         return this;
     }
 
+    @Override
     public ClassicRequestBuilder removeHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.removeHeader(header);
+        super.removeHeader(header);
         return this;
     }
 
+    @Override
     public ClassicRequestBuilder removeHeaders(final String name) {
-        if (name == null || headerGroup == null) {
-            return this;
-        }
-        for (final Iterator<Header> i = headerGroup.headerIterator(); i.hasNext(); ) {
-            final Header header = i.next();
-            if (name.equalsIgnoreCase(header.getName())) {
-                i.remove();
-            }
-        }
+        super.removeHeaders(name);
         return this;
     }
 
+    @Override
     public ClassicRequestBuilder setHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(header);
+        super.setHeader(header);
         return this;
     }
 
+    @Override
     public ClassicRequestBuilder setHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(new BasicHeader(name, value));
+        super.setHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public ClassicRequestBuilder setCharset(final Charset charset) {
+        super.setCharset(charset);
+        return this;
+    }
+
+    @Override
+    public ClassicRequestBuilder addParameter(final NameValuePair nvp) {
+        super.addParameter(nvp);
+        return this;
+    }
+
+    @Override
+    public ClassicRequestBuilder addParameter(final String name, final String value) {
+        super.addParameter(name, value);
+        return this;
+    }
+
+    @Override
+    public ClassicRequestBuilder addParameters(final NameValuePair... nvps) {
+        super.addParameters(nvps);
         return this;
     }
 
@@ -339,41 +297,21 @@ public class ClassicRequestBuilder {
         return this;
     }
 
-    public List<NameValuePair> getParameters() {
-        return parameters != null ? new ArrayList<>(parameters) :
-                new ArrayList<NameValuePair>();
-    }
-
-    public ClassicRequestBuilder addParameter(final NameValuePair nvp) {
-        Args.notNull(nvp, "Name value pair");
-        if (parameters == null) {
-            parameters = new LinkedList<>();
-        }
-        parameters.add(nvp);
-        return this;
-    }
-
-    public ClassicRequestBuilder addParameter(final String name, final String value) {
-        return addParameter(new BasicNameValuePair(name, value));
-    }
-
-    public ClassicRequestBuilder addParameters(final NameValuePair... nvps) {
-        for (final NameValuePair nvp : nvps) {
-            addParameter(nvp);
-        }
-        return this;
-    }
-
     public ClassicHttpRequest build() {
-        URI uriCopy = this.uri != null ? this.uri : URI.create("/");
+        URI uriCopy = getUri();
+        if (uriCopy == null) {
+            uriCopy = URI.create("/");
+        }
         HttpEntity entityCopy = this.entity;
+        final String method = getMethod();
+        final List<NameValuePair> parameters = getParameters();
         if (parameters != null && !parameters.isEmpty()) {
             if (entityCopy == null && (Method.POST.isSame(method) || Method.PUT.isSame(method))) {
-                entityCopy = HttpEntities.createUrlEncoded(parameters, charset);
+                entityCopy = HttpEntities.createUrlEncoded(parameters, getCharset());
             } else {
                 try {
                     uriCopy = new URIBuilder(uriCopy)
-                            .setCharset(this.charset)
+                            .setCharset(getCharset())
                             .addParameters(parameters)
                             .build();
                 } catch (final URISyntaxException ex) {
@@ -386,11 +324,9 @@ public class ClassicRequestBuilder {
             throw new IllegalStateException(Method.TRACE + " requests may not include an entity");
         }
 
-        final ClassicHttpRequest result = new BasicClassicHttpRequest(method, uriCopy);
-        result.setVersion(this.version != null ? this.version : HttpVersion.HTTP_1_1);
-        if (this.headerGroup != null) {
-            result.setHeaders(this.headerGroup.getHeaders());
-        }
+        final BasicClassicHttpRequest result = new BasicClassicHttpRequest(method, uriCopy);
+        result.setVersion(getVersion());
+        result.setHeaders(getHeaders());
         result.setEntity(entityCopy);
         return result;
     }
@@ -399,19 +335,17 @@ public class ClassicRequestBuilder {
     public String toString() {
         final StringBuilder builder = new StringBuilder();
         builder.append("ClassicRequestBuilder [method=");
-        builder.append(method);
-        builder.append(", charset=");
-        builder.append(charset);
+        builder.append(getMethod());
         builder.append(", version=");
-        builder.append(version);
+        builder.append(getVersion());
         builder.append(", uri=");
-        builder.append(uri);
+        builder.append(getUri());
+        builder.append(", parameters=");
+        builder.append(getParameters());
         builder.append(", headerGroup=");
-        builder.append(headerGroup);
+        builder.append(Arrays.toString(getHeaders()));
         builder.append(", entity=");
         builder.append(entity != null ? entity.getClass() : null);
-        builder.append(", parameters=");
-        builder.append(parameters);
         builder.append("]");
         return builder.toString();
     }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilder.java
index c8b583d..ce6d7bf 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilder.java
@@ -27,19 +27,17 @@
 
 package org.apache.hc.core5.http.io.support;
 
-import java.util.Iterator;
+import java.util.Arrays;
 
 import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
 import org.apache.hc.core5.http.io.entity.StringEntity;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
-import org.apache.hc.core5.http.message.BasicHeader;
-import org.apache.hc.core5.http.message.HeaderGroup;
+import org.apache.hc.core5.http.support.AbstractResponseBuilder;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -47,19 +45,12 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 5.0
  */
-public class ClassicResponseBuilder {
+public class ClassicResponseBuilder extends AbstractResponseBuilder<ClassicHttpResponse> {
 
-    private int status;
-    private ProtocolVersion version;
-    private HeaderGroup headerGroup;
     private HttpEntity entity;
 
-    ClassicResponseBuilder() {
-    }
-
     ClassicResponseBuilder(final int status) {
-        super();
-        this.status = status;
+        super(status);
     }
 
     public static ClassicResponseBuilder create(final int status) {
@@ -67,85 +58,51 @@ public class ClassicResponseBuilder {
         return new ClassicResponseBuilder(status);
     }
 
-    public ProtocolVersion getVersion() {
-        return version;
-    }
-
+    @Override
     public ClassicResponseBuilder setVersion(final ProtocolVersion version) {
-        this.version = version;
+        super.setVersion(version);
         return this;
     }
 
-    public Header[] getHeaders(final String name) {
-        return headerGroup != null ? headerGroup.getHeaders(name) : null;
-    }
-
+    @Override
     public ClassicResponseBuilder setHeaders(final Header... headers) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.setHeaders(headers);
+        super.setHeaders(headers);
         return this;
     }
 
-    public Header getFirstHeader(final String name) {
-        return headerGroup != null ? headerGroup.getFirstHeader(name) : null;
-    }
-
-    public Header getLastHeader(final String name) {
-        return headerGroup != null ? headerGroup.getLastHeader(name) : null;
-    }
-
+    @Override
     public ClassicResponseBuilder addHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.addHeader(header);
+        super.addHeader(header);
         return this;
     }
 
-    public ClassicResponseBuilder addHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.addHeader(new BasicHeader(name, value));
+    @Override
+    public ClassicResponseBuilder addHeader(final String name, final    String value) {
+        super.addHeader(name, value);
         return this;
     }
 
+    @Override
     public ClassicResponseBuilder removeHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.removeHeader(header);
+        super.removeHeader(header);
         return this;
     }
 
+    @Override
     public ClassicResponseBuilder removeHeaders(final String name) {
-        if (name == null || headerGroup == null) {
-            return this;
-        }
-        for (final Iterator<Header> i = headerGroup.headerIterator(); i.hasNext(); ) {
-            final Header header = i.next();
-            if (name.equalsIgnoreCase(header.getName())) {
-                i.remove();
-            }
-        }
+        super.removeHeaders(name);
         return this;
     }
 
+    @Override
     public ClassicResponseBuilder setHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(header);
+        super.setHeader(header);
         return this;
     }
 
+    @Override
     public ClassicResponseBuilder setHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(new BasicHeader(name, value));
+        super.setHeader(name, value);
         return this;
     }
 
@@ -174,11 +131,9 @@ public class ClassicResponseBuilder {
     }
 
     public ClassicHttpResponse build() {
-        final ClassicHttpResponse result = new BasicClassicHttpResponse(status);
-        result.setVersion(this.version != null ? this.version : HttpVersion.HTTP_1_1);
-        if (this.headerGroup != null) {
-            result.setHeaders(this.headerGroup.getHeaders());
-        }
+        final BasicClassicHttpResponse result = new BasicClassicHttpResponse(getStatus());
+        result.setVersion(getVersion());
+        result.setHeaders(getHeaders());
         result.setEntity(entity);
         return result;
     }
@@ -186,14 +141,10 @@ public class ClassicResponseBuilder {
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
-        builder.append("ClassicResponseBuilder [method=");
-        builder.append(status);
-        builder.append(", status=");
-        builder.append(status);
-        builder.append(", version=");
-        builder.append(version);
+        builder.append("ClassicResponseBuilder [status=");
+        builder.append(getStatus());
         builder.append(", headerGroup=");
-        builder.append(headerGroup);
+        builder.append(Arrays.toString(getHeaders()));
         builder.append(", entity=");
         builder.append(entity != null ? entity.getClass() : null);
         builder.append("]");
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncPushBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncPushBuilder.java
index 66af20a..4155f61 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncPushBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncPushBuilder.java
@@ -27,36 +27,30 @@
 
 package org.apache.hc.core5.http.nio.support;
 
-import java.util.Iterator;
-
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpResponse;
-import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.ProtocolVersion;
 import org.apache.hc.core5.http.message.BasicHttpResponse;
-import org.apache.hc.core5.http.message.HeaderGroup;
 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
 import org.apache.hc.core5.http.nio.AsyncPushProducer;
 import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
+import org.apache.hc.core5.http.support.AbstractResponseBuilder;
 import org.apache.hc.core5.util.Args;
 
+import java.util.Arrays;
+
 /**
  * Builder for {@link AsyncPushProducer} instances.
  *
  * @since 5.0
  */
-public class AsyncPushBuilder {
+public class AsyncPushBuilder extends AbstractResponseBuilder<AsyncPushProducer> {
 
-    private int status;
-    private HeaderGroup headerGroup;
     private AsyncEntityProducer entityProducer;
 
-    AsyncPushBuilder() {
-    }
-
     AsyncPushBuilder(final int status) {
-        super();
-        this.status = status;
+        super(status);
     }
 
     public static AsyncPushBuilder create(final int status) {
@@ -64,76 +58,51 @@ public class AsyncPushBuilder {
         return new AsyncPushBuilder(status);
     }
 
-    public Header[] getHeaders(final String name) {
-        return headerGroup != null ? headerGroup.getHeaders(name) : null;
+    @Override
+    public AsyncPushBuilder setVersion(final ProtocolVersion version) {
+        super.setVersion(version);
+        return this;
     }
 
+    @Override
     public AsyncPushBuilder setHeaders(final Header... headers) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.setHeaders(headers);
+        super.setHeaders(headers);
         return this;
     }
 
-    public Header getFirstHeader(final String name) {
-        return headerGroup != null ? headerGroup.getFirstHeader(name) : null;
-    }
-
-    public Header getLastHeader(final String name) {
-        return headerGroup != null ? headerGroup.getLastHeader(name) : null;
-    }
-
+    @Override
     public AsyncPushBuilder addHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.addHeader(header);
+        super.addHeader(header);
         return this;
     }
 
+    @Override
     public AsyncPushBuilder addHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.addHeader(new BasicHeader(name, value));
+        super.addHeader(name, value);
         return this;
     }
 
+    @Override
     public AsyncPushBuilder removeHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.removeHeader(header);
+        super.removeHeader(header);
         return this;
     }
 
+    @Override
     public AsyncPushBuilder removeHeaders(final String name) {
-        if (name == null || headerGroup == null) {
-            return this;
-        }
-        for (final Iterator<Header> i = headerGroup.headerIterator(); i.hasNext(); ) {
-            final Header header = i.next();
-            if (name.equalsIgnoreCase(header.getName())) {
-                i.remove();
-            }
-        }
+        super.removeHeaders(name);
         return this;
     }
 
+    @Override
     public AsyncPushBuilder setHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(header);
+        super.setHeader(header);
         return this;
     }
 
+    @Override
     public AsyncPushBuilder setHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(new BasicHeader(name, value));
+        super.setHeader(name, value);
         return this;
     }
 
@@ -162,22 +131,19 @@ public class AsyncPushBuilder {
     }
 
     public AsyncPushProducer build() {
-        final HttpResponse response = new BasicHttpResponse(status);
-        if (this.headerGroup != null) {
-            response.setHeaders(this.headerGroup.getHeaders());
-        }
+        final HttpResponse response = new BasicHttpResponse(getStatus());
+        response.setVersion(getVersion());
+        response.setHeaders(getHeaders());
         return new BasicPushProducer(response, entityProducer);
     }
 
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
-        builder.append("AsyncPushProducer [method=");
-        builder.append(status);
-        builder.append(", status=");
-        builder.append(status);
+        builder.append("AsyncPushProducer [status=");
+        builder.append(getStatus());
         builder.append(", headerGroup=");
-        builder.append(headerGroup);
+        builder.append(Arrays.toString(getHeaders()));
         builder.append(", entity=");
         builder.append(entityProducer != null ? entityProducer.getClass() : null);
         builder.append("]");
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
index 720980e..a80737c 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncRequestBuilder.java
@@ -30,25 +30,20 @@ package org.apache.hc.core5.http.nio.support;
 import java.net.URI;
 import java.net.URISyntaxException;
 import java.nio.charset.Charset;
-import java.util.ArrayList;
-import java.util.Iterator;
-import java.util.LinkedList;
+import java.util.Arrays;
 import java.util.List;
 
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.Method;
 import org.apache.hc.core5.http.NameValuePair;
 import org.apache.hc.core5.http.ProtocolVersion;
-import org.apache.hc.core5.http.message.BasicHeader;
 import org.apache.hc.core5.http.message.BasicHttpRequest;
-import org.apache.hc.core5.http.message.BasicNameValuePair;
-import org.apache.hc.core5.http.message.HeaderGroup;
 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
 import org.apache.hc.core5.http.nio.AsyncRequestProducer;
 import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
 import org.apache.hc.core5.http.nio.entity.StringAsyncEntityProducer;
+import org.apache.hc.core5.http.support.AbstractRequestBuilder;
 import org.apache.hc.core5.net.URIBuilder;
 import org.apache.hc.core5.net.WWWFormCodec;
 import org.apache.hc.core5.util.Args;
@@ -65,32 +60,20 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 5.0
  */
-public class AsyncRequestBuilder {
+public class AsyncRequestBuilder extends AbstractRequestBuilder<AsyncRequestProducer> {
 
-    private String method;
-    private URI uri;
-    private Charset charset;
-    private ProtocolVersion version;
-    private HeaderGroup headerGroup;
     private AsyncEntityProducer entityProducer;
-    private List<NameValuePair> parameters;
-
-    AsyncRequestBuilder() {
-    }
 
     AsyncRequestBuilder(final String method) {
-        super();
-        this.method = method;
+        super(method);
     }
 
     AsyncRequestBuilder(final Method method) {
-        this(method.name());
+        super(method);
     }
 
     AsyncRequestBuilder(final String method, final URI uri) {
-        super();
-        this.method = method;
-        this.uri = uri;
+        super(method, uri);
     }
 
     AsyncRequestBuilder(final Method method, final URI uri) {
@@ -206,136 +189,87 @@ public class AsyncRequestBuilder {
         return new AsyncRequestBuilder(Method.OPTIONS, uri);
     }
 
-    public AsyncRequestBuilder setCharset(final Charset charset) {
-        this.charset = charset;
+    @Override
+    public AsyncRequestBuilder setVersion(final ProtocolVersion version) {
+        super.setVersion(version);
         return this;
     }
 
-    public Charset getCharset() {
-        return charset;
-    }
-
-    public String getMethod() {
-        return method;
-    }
-
-    public URI getUri() {
-        return uri;
-    }
-
+    @Override
     public AsyncRequestBuilder setUri(final URI uri) {
-        this.uri = uri;
+        super.setUri(uri);
         return this;
     }
 
+    @Override
     public AsyncRequestBuilder setUri(final String uri) {
-        this.uri = uri != null ? URI.create(uri) : null;
-        return this;
-    }
-
-    public ProtocolVersion getVersion() {
-        return version;
-    }
-
-    public AsyncRequestBuilder setVersion(final ProtocolVersion version) {
-        this.version = version;
+        super.setUri(uri);
         return this;
     }
 
-    public Header[] getHeaders(final String name) {
-        return headerGroup != null ? headerGroup.getHeaders(name) : null;
-    }
-
+    @Override
     public AsyncRequestBuilder setHeaders(final Header... headers) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.setHeaders(headers);
+        super.setHeaders(headers);
         return this;
     }
 
-    public Header getFirstHeader(final String name) {
-        return headerGroup != null ? headerGroup.getFirstHeader(name) : null;
-    }
-
-    public Header getLastHeader(final String name) {
-        return headerGroup != null ? headerGroup.getLastHeader(name) : null;
-    }
-
+    @Override
     public AsyncRequestBuilder addHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.addHeader(header);
+        super.addHeader(header);
         return this;
     }
 
+    @Override
     public AsyncRequestBuilder addHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.addHeader(new BasicHeader(name, value));
+        super.addHeader(name, value);
         return this;
     }
 
+    @Override
     public AsyncRequestBuilder removeHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.removeHeader(header);
+        super.removeHeader(header);
         return this;
     }
 
+    @Override
     public AsyncRequestBuilder removeHeaders(final String name) {
-        if (name == null || headerGroup == null) {
-            return this;
-        }
-        for (final Iterator<Header> i = headerGroup.headerIterator(); i.hasNext(); ) {
-            final Header header = i.next();
-            if (name.equalsIgnoreCase(header.getName())) {
-                i.remove();
-            }
-        }
+        super.removeHeaders(name);
         return this;
     }
 
+    @Override
     public AsyncRequestBuilder setHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(header);
+        super.setHeader(header);
         return this;
     }
 
+    @Override
     public AsyncRequestBuilder setHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(new BasicHeader(name, value));
+        super.setHeader(name, value);
         return this;
     }
 
-    public List<NameValuePair> getParameters() {
-        return parameters != null ? new ArrayList<>(parameters) : new ArrayList<NameValuePair>();
+    @Override
+    public AsyncRequestBuilder setCharset(final Charset charset) {
+        super.setCharset(charset);
+        return this;
     }
 
+    @Override
     public AsyncRequestBuilder addParameter(final NameValuePair nvp) {
-        Args.notNull(nvp, "Name value pair");
-        if (parameters == null) {
-            parameters = new LinkedList<>();
-        }
-        parameters.add(nvp);
+        super.addParameter(nvp);
         return this;
     }
 
+    @Override
     public AsyncRequestBuilder addParameter(final String name, final String value) {
-        return addParameter(new BasicNameValuePair(name, value));
+        super.addParameter(name, value);
+        return this;
     }
 
+    @Override
     public AsyncRequestBuilder addParameters(final NameValuePair... nvps) {
-        for (final NameValuePair nvp: nvps) {
-            addParameter(nvp);
-        }
+        super.addParameters(nvps);
         return this;
     }
 
@@ -364,9 +298,15 @@ public class AsyncRequestBuilder {
     }
 
     public AsyncRequestProducer build() {
-        URI uriCopy = uri != null ? uri : URI.create("/");
+        URI uriCopy = getUri();
+        if (uriCopy == null) {
+            uriCopy = URI.create("/");
+        }
         AsyncEntityProducer entityProducerCopy = entityProducer;
+        final String method = getMethod();
+        final List<NameValuePair> parameters = getParameters();
         if (parameters != null && !parameters.isEmpty()) {
+            final Charset charset = getCharset();
             if (entityProducerCopy == null && (Method.POST.isSame(method) || Method.PUT.isSame(method))) {
                 final String content = WWWFormCodec.format(
                         parameters,
@@ -377,9 +317,9 @@ public class AsyncRequestBuilder {
             } else {
                 try {
                     uriCopy = new URIBuilder(uriCopy)
-                      .setCharset(this.charset)
-                      .addParameters(parameters)
-                      .build();
+                            .setCharset(charset)
+                            .addParameters(parameters)
+                            .build();
                 } catch (final URISyntaxException ex) {
                     // should never happen
                 }
@@ -387,16 +327,12 @@ public class AsyncRequestBuilder {
         }
 
         if (entityProducerCopy != null && Method.TRACE.isSame(method)) {
-            throw new IllegalStateException(Method.TRACE + " requests may not include an entity.");
+            throw new IllegalStateException(Method.TRACE + " requests may not include an entity");
         }
 
-        final HttpRequest request = new BasicHttpRequest(method, uriCopy);
-        if (this.headerGroup != null) {
-            request.setHeaders(this.headerGroup.getHeaders());
-        }
-        if (version != null) {
-            request.setVersion(version);
-        }
+        final BasicHttpRequest request = new BasicHttpRequest(method, uriCopy);
+        request.setVersion(getVersion());
+        request.setHeaders(getHeaders());
         return new BasicRequestProducer(request, entityProducerCopy);
     }
 
@@ -404,19 +340,17 @@ public class AsyncRequestBuilder {
     public String toString() {
         final StringBuilder builder = new StringBuilder();
         builder.append("AsyncRequestBuilder [method=");
-        builder.append(method);
-        builder.append(", charset=");
-        builder.append(charset);
+        builder.append(getMethod());
         builder.append(", version=");
-        builder.append(version);
+        builder.append(getVersion());
         builder.append(", uri=");
-        builder.append(uri);
+        builder.append(getUri());
+        builder.append(", parameters=");
+        builder.append(getParameters());
         builder.append(", headerGroup=");
-        builder.append(headerGroup);
+        builder.append(Arrays.toString(getHeaders()));
         builder.append(", entity=");
         builder.append(entityProducer != null ? entityProducer.getClass() : null);
-        builder.append(", parameters=");
-        builder.append(parameters);
         builder.append("]");
         return builder.toString();
     }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncResponseBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncResponseBuilder.java
index abb2294..42c6577 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncResponseBuilder.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncResponseBuilder.java
@@ -27,18 +27,16 @@
 
 package org.apache.hc.core5.http.nio.support;
 
-import java.util.Iterator;
+import java.util.Arrays;
 
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.Header;
-import org.apache.hc.core5.http.HttpResponse;
 import org.apache.hc.core5.http.ProtocolVersion;
-import org.apache.hc.core5.http.message.BasicHeader;
 import org.apache.hc.core5.http.message.BasicHttpResponse;
-import org.apache.hc.core5.http.message.HeaderGroup;
 import org.apache.hc.core5.http.nio.AsyncEntityProducer;
 import org.apache.hc.core5.http.nio.AsyncResponseProducer;
 import org.apache.hc.core5.http.nio.entity.BasicAsyncEntityProducer;
+import org.apache.hc.core5.http.support.AbstractResponseBuilder;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -46,19 +44,12 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 5.0
  */
-public class AsyncResponseBuilder {
+public class AsyncResponseBuilder extends AbstractResponseBuilder<AsyncResponseProducer>  {
 
-    private int status;
-    private ProtocolVersion version;
-    private HeaderGroup headerGroup;
     private AsyncEntityProducer entityProducer;
 
-    AsyncResponseBuilder() {
-    }
-
     AsyncResponseBuilder(final int status) {
-        super();
-        this.status = status;
+        super(status);
     }
 
     public static AsyncResponseBuilder create(final int status) {
@@ -66,85 +57,51 @@ public class AsyncResponseBuilder {
         return new AsyncResponseBuilder(status);
     }
 
-    public ProtocolVersion getVersion() {
-        return version;
-    }
-
+    @Override
     public AsyncResponseBuilder setVersion(final ProtocolVersion version) {
-        this.version = version;
+        super.setVersion(version);
         return this;
     }
 
-    public Header[] getHeaders(final String name) {
-        return headerGroup != null ? headerGroup.getHeaders(name) : null;
-    }
-
+    @Override
     public AsyncResponseBuilder setHeaders(final Header... headers) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.setHeaders(headers);
+        super.setHeaders(headers);
         return this;
     }
 
-    public Header getFirstHeader(final String name) {
-        return headerGroup != null ? headerGroup.getFirstHeader(name) : null;
-    }
-
-    public Header getLastHeader(final String name) {
-        return headerGroup != null ? headerGroup.getLastHeader(name) : null;
-    }
-
+    @Override
     public AsyncResponseBuilder addHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.addHeader(header);
+        super.addHeader(header);
         return this;
     }
 
+    @Override
     public AsyncResponseBuilder addHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.addHeader(new BasicHeader(name, value));
+        super.addHeader(name, value);
         return this;
     }
 
+    @Override
     public AsyncResponseBuilder removeHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        headerGroup.removeHeader(header);
+        super.removeHeader(header);
         return this;
     }
 
+    @Override
     public AsyncResponseBuilder removeHeaders(final String name) {
-        if (name == null || headerGroup == null) {
-            return this;
-        }
-        for (final Iterator<Header> i = headerGroup.headerIterator(); i.hasNext(); ) {
-            final Header header = i.next();
-            if (name.equalsIgnoreCase(header.getName())) {
-                i.remove();
-            }
-        }
+        super.removeHeaders(name);
         return this;
     }
 
+    @Override
     public AsyncResponseBuilder setHeader(final Header header) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(header);
+        super.setHeader(header);
         return this;
     }
 
+    @Override
     public AsyncResponseBuilder setHeader(final String name, final String value) {
-        if (headerGroup == null) {
-            headerGroup = new HeaderGroup();
-        }
-        this.headerGroup.setHeader(new BasicHeader(name, value));
+        super.setHeader(name, value);
         return this;
     }
 
@@ -173,27 +130,19 @@ public class AsyncResponseBuilder {
     }
 
     public AsyncResponseProducer build() {
-        final HttpResponse response = new BasicHttpResponse(status);
-        if (this.headerGroup != null) {
-            response.setHeaders(this.headerGroup.getHeaders());
-        }
-        if (version != null) {
-            response.setVersion(version);
-        }
+        final BasicHttpResponse response = new BasicHttpResponse(getStatus());
+        response.setVersion(getVersion());
+        response.setHeaders(getHeaders());
         return new BasicResponseProducer(response, entityProducer);
     }
 
     @Override
     public String toString() {
         final StringBuilder builder = new StringBuilder();
-        builder.append("AsyncResponseBuilder [method=");
-        builder.append(status);
-        builder.append(", status=");
-        builder.append(status);
-        builder.append(", version=");
-        builder.append(version);
+        builder.append("AsyncResponseBuilder [status=");
+        builder.append(getStatus());
         builder.append(", headerGroup=");
-        builder.append(headerGroup);
+        builder.append(Arrays.toString(getHeaders()));
         builder.append(", entity=");
         builder.append(entityProducer != null ? entityProducer.getClass() : null);
         builder.append("]");
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractMessageBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractMessageBuilder.java
new file mode 100644
index 0000000..143c30c
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractMessageBuilder.java
@@ -0,0 +1,143 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.support;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpMessage;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.HeaderGroup;
+
+import java.util.Iterator;
+
+/**
+ * Abstract {@link HttpMessage} builder.
+ *
+ * @since 5.1
+ */
+public abstract class AbstractMessageBuilder<T> {
+
+    private ProtocolVersion version;
+    private HeaderGroup headerGroup;
+
+    protected AbstractMessageBuilder() {
+    }
+
+    public ProtocolVersion getVersion() {
+        return version;
+    }
+
+    public AbstractMessageBuilder<T> setVersion(final ProtocolVersion version) {
+        this.version = version;
+        return this;
+    }
+
+    public Header[] getHeaders() {
+        return headerGroup != null ? headerGroup.getHeaders() : null;
+    }
+
+    public Header[] getHeaders(final String name) {
+        return headerGroup != null ? headerGroup.getHeaders(name) : null;
+    }
+
+    public AbstractMessageBuilder<T> setHeaders(final Header... headers) {
+        if (headerGroup == null) {
+            headerGroup = new HeaderGroup();
+        }
+        headerGroup.setHeaders(headers);
+        return this;
+    }
+
+    public Header[] getFirstHeaders() {
+        return headerGroup != null ? headerGroup.getHeaders() : null;
+    }
+
+    public Header getFirstHeader(final String name) {
+        return headerGroup != null ? headerGroup.getFirstHeader(name) : null;
+    }
+
+    public Header getLastHeader(final String name) {
+        return headerGroup != null ? headerGroup.getLastHeader(name) : null;
+    }
+
+    public AbstractMessageBuilder<T> addHeader(final Header header) {
+        if (headerGroup == null) {
+            headerGroup = new HeaderGroup();
+        }
+        headerGroup.addHeader(header);
+        return this;
+    }
+
+    public AbstractMessageBuilder<T> addHeader(final String name, final String value) {
+        if (headerGroup == null) {
+            headerGroup = new HeaderGroup();
+        }
+        headerGroup.addHeader(new BasicHeader(name, value));
+        return this;
+    }
+
+    public AbstractMessageBuilder<T> removeHeader(final Header header) {
+        if (headerGroup == null) {
+            headerGroup = new HeaderGroup();
+        }
+        headerGroup.removeHeader(header);
+        return this;
+    }
+
+    public AbstractMessageBuilder<T> removeHeaders(final String name) {
+        if (name == null || headerGroup == null) {
+            return this;
+        }
+        for (final Iterator<Header> i = headerGroup.headerIterator(); i.hasNext(); ) {
+            final Header header = i.next();
+            if (name.equalsIgnoreCase(header.getName())) {
+                i.remove();
+            }
+        }
+        return this;
+    }
+
+    public AbstractMessageBuilder<T> setHeader(final Header header) {
+        if (headerGroup == null) {
+            headerGroup = new HeaderGroup();
+        }
+        headerGroup.setHeader(header);
+        return this;
+    }
+
+    public AbstractMessageBuilder<T> setHeader(final String name, final String value) {
+        if (headerGroup == null) {
+            headerGroup = new HeaderGroup();
+        }
+        headerGroup.setHeader(new BasicHeader(name, value));
+        return this;
+    }
+
+    protected abstract T build();
+
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractRequestBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractRequestBuilder.java
new file mode 100644
index 0000000..9c3c801
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractRequestBuilder.java
@@ -0,0 +1,183 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.support;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.message.BasicHttpRequest;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+
+import java.net.URI;
+import java.nio.charset.Charset;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * Builder for {@link BasicHttpRequest} instances.
+ *
+ * @since 5.1
+ */
+public abstract class AbstractRequestBuilder<T> extends AbstractMessageBuilder<T> {
+
+    private String method;
+    private URI uri;
+    private Charset charset;
+    private List<NameValuePair> parameters;
+
+    protected AbstractRequestBuilder(final String method) {
+        super();
+        this.method = method;
+    }
+
+    protected AbstractRequestBuilder(final Method method) {
+        this(method.name());
+    }
+
+    protected AbstractRequestBuilder(final String method, final URI uri) {
+        super();
+        this.method = method;
+        this.uri = uri;
+    }
+
+    protected AbstractRequestBuilder(final Method method, final URI uri) {
+        this(method.name(), uri);
+    }
+
+    protected AbstractRequestBuilder(final Method method, final String uri) {
+        this(method.name(), uri != null ? URI.create(uri) : null);
+    }
+
+    protected AbstractRequestBuilder(final String method, final String uri) {
+        this(method, uri != null ? URI.create(uri) : null);
+    }
+
+    public String getMethod() {
+        return method;
+    }
+
+    @Override
+    public AbstractRequestBuilder<T> setVersion(final ProtocolVersion version) {
+        super.setVersion(version);
+        return this;
+    }
+
+    public URI getUri() {
+        return uri;
+    }
+
+    public AbstractRequestBuilder<T> setUri(final URI uri) {
+        this.uri = uri;
+        return this;
+    }
+
+    public AbstractRequestBuilder<T> setUri(final String uri) {
+        this.uri = uri != null ? URI.create(uri) : null;
+        return this;
+    }
+
+    @Override
+    public AbstractRequestBuilder<T> setHeaders(final Header... headers) {
+        super.setHeaders(headers);
+        return this;
+    }
+
+    @Override
+    public AbstractRequestBuilder<T> addHeader(final Header header) {
+        super.addHeader(header);
+        return this;
+    }
+
+    @Override
+    public AbstractRequestBuilder<T> addHeader(final String name, final String value) {
+        super.addHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public AbstractRequestBuilder<T> removeHeader(final Header header) {
+        super.removeHeader(header);
+        return this;
+    }
+
+    @Override
+    public AbstractRequestBuilder<T> removeHeaders(final String name) {
+        super.removeHeaders(name);
+        return this;
+    }
+
+    @Override
+    public AbstractRequestBuilder<T> setHeader(final Header header) {
+        super.setHeader(header);
+        return this;
+    }
+
+    @Override
+    public AbstractRequestBuilder<T> setHeader(final String name, final String value) {
+        super.setHeader(name, value);
+        return this;
+    }
+
+    public Charset getCharset() {
+        return charset;
+    }
+
+    public AbstractRequestBuilder<T> setCharset(final Charset charset) {
+        this.charset = charset;
+        return this;
+    }
+
+    public List<NameValuePair> getParameters() {
+        return parameters != null ? new ArrayList<>(parameters) : null;
+    }
+
+    public AbstractRequestBuilder<T> addParameter(final NameValuePair nvp) {
+        if (nvp == null) {
+            return this;
+        }
+        if (parameters == null) {
+            parameters = new LinkedList<>();
+        }
+        parameters.add(nvp);
+        return this;
+    }
+
+    public AbstractRequestBuilder<T> addParameter(final String name, final String value) {
+        return addParameter(new BasicNameValuePair(name, value));
+    }
+
+    public AbstractRequestBuilder<T> addParameters(final NameValuePair... nvps) {
+        for (final NameValuePair nvp : nvps) {
+            addParameter(nvp);
+        }
+        return this;
+    }
+
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractResponseBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractResponseBuilder.java
new file mode 100644
index 0000000..d371e8e
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/support/AbstractResponseBuilder.java
@@ -0,0 +1,106 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.support;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.message.BasicHttpRequest;
+
+/**
+ * Builder for {@link BasicHttpRequest} instances.
+ *
+ * @since 5.1
+ */
+public abstract class AbstractResponseBuilder<T> extends AbstractMessageBuilder<T> {
+
+    private int status;
+
+    protected AbstractResponseBuilder(final int status) {
+        super();
+        this.status = status;
+    }
+
+    public int getStatus() {
+        return status;
+    }
+
+    public void setStatus(final int status) {
+        this.status = status;
+    }
+
+    @Override
+    public AbstractResponseBuilder<T> setVersion(final ProtocolVersion version) {
+        super.setVersion(version);
+        return this;
+    }
+
+    @Override
+    public AbstractResponseBuilder<T> setHeaders(final Header... headers) {
+        super.setHeaders(headers);
+        return this;
+    }
+
+    @Override
+    public AbstractResponseBuilder<T> addHeader(final Header header) {
+        super.addHeader(header);
+        return this;
+    }
+
+    @Override
+    public AbstractResponseBuilder<T> addHeader(final String name, final String value) {
+        super.addHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public AbstractResponseBuilder<T> removeHeader(final Header header) {
+        super.removeHeader(header);
+        return this;
+    }
+
+    @Override
+    public AbstractResponseBuilder<T> removeHeaders(final String name) {
+        super.removeHeaders(name);
+        return this;
+    }
+
+    @Override
+    public AbstractResponseBuilder<T> setHeader(final Header header) {
+        super.setHeader(header);
+        return this;
+    }
+
+    @Override
+    public AbstractResponseBuilder<T> setHeader(final String name, final String value) {
+        super.setHeader(name, value);
+        return this;
+    }
+
+    protected abstract T build();
+
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicRequestBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicRequestBuilder.java
new file mode 100644
index 0000000..dab7bb7
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicRequestBuilder.java
@@ -0,0 +1,295 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.support;
+
+import java.net.URI;
+import java.net.URISyntaxException;
+import java.nio.charset.Charset;
+import java.util.Arrays;
+import java.util.List;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.message.BasicHttpRequest;
+import org.apache.hc.core5.net.URIBuilder;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * Builder for {@link BasicHttpRequest} instances.
+ *
+ * @since 5.1
+ */
+public class BasicRequestBuilder extends AbstractRequestBuilder<BasicHttpRequest> {
+
+    BasicRequestBuilder(final String method) {
+        super(method);
+    }
+
+    BasicRequestBuilder(final Method method) {
+        super(method);
+    }
+
+    BasicRequestBuilder(final String method, final URI uri) {
+        super(method, uri);
+    }
+
+    BasicRequestBuilder(final Method method, final URI uri) {
+        super(method, uri);
+    }
+
+    BasicRequestBuilder(final Method method, final String uri) {
+        super(method, uri);
+    }
+
+    BasicRequestBuilder(final String method, final String uri) {
+        super(method, uri);
+    }
+
+    public static BasicRequestBuilder create(final String method) {
+        Args.notBlank(method, "HTTP method");
+        return new BasicRequestBuilder(method);
+    }
+
+    public static BasicRequestBuilder get() {
+        return new BasicRequestBuilder(Method.GET);
+    }
+
+    public static BasicRequestBuilder get(final URI uri) {
+        return new BasicRequestBuilder(Method.GET, uri);
+    }
+
+    public static BasicRequestBuilder get(final String uri) {
+        return new BasicRequestBuilder(Method.GET, uri);
+    }
+
+    public static BasicRequestBuilder head() {
+        return new BasicRequestBuilder(Method.HEAD);
+    }
+
+    public static BasicRequestBuilder head(final URI uri) {
+        return new BasicRequestBuilder(Method.HEAD, uri);
+    }
+
+    public static BasicRequestBuilder head(final String uri) {
+        return new BasicRequestBuilder(Method.HEAD, uri);
+    }
+
+    public static BasicRequestBuilder patch() {
+        return new BasicRequestBuilder(Method.PATCH);
+    }
+
+    public static BasicRequestBuilder patch(final URI uri) {
+        return new BasicRequestBuilder(Method.PATCH, uri);
+    }
+
+    public static BasicRequestBuilder patch(final String uri) {
+        return new BasicRequestBuilder(Method.PATCH, uri);
+    }
+
+    public static BasicRequestBuilder post() {
+        return new BasicRequestBuilder(Method.POST);
+    }
+
+    public static BasicRequestBuilder post(final URI uri) {
+        return new BasicRequestBuilder(Method.POST, uri);
+    }
+
+    public static BasicRequestBuilder post(final String uri) {
+        return new BasicRequestBuilder(Method.POST, uri);
+    }
+
+    public static BasicRequestBuilder put() {
+        return new BasicRequestBuilder(Method.PUT);
+    }
+
+    public static BasicRequestBuilder put(final URI uri) {
+        return new BasicRequestBuilder(Method.PUT, uri);
+    }
+
+    public static BasicRequestBuilder put(final String uri) {
+        return new BasicRequestBuilder(Method.PUT, uri);
+    }
+
+    public static BasicRequestBuilder delete() {
+        return new BasicRequestBuilder(Method.DELETE);
+    }
+
+    public static BasicRequestBuilder delete(final URI uri) {
+        return new BasicRequestBuilder(Method.DELETE, uri);
+    }
+
+    public static BasicRequestBuilder delete(final String uri) {
+        return new BasicRequestBuilder(Method.DELETE, uri);
+    }
+
+    public static BasicRequestBuilder trace() {
+        return new BasicRequestBuilder(Method.TRACE);
+    }
+
+    public static BasicRequestBuilder trace(final URI uri) {
+        return new BasicRequestBuilder(Method.TRACE, uri);
+    }
+
+    public static BasicRequestBuilder trace(final String uri) {
+        return new BasicRequestBuilder(Method.TRACE, uri);
+    }
+
+    public static BasicRequestBuilder options() {
+        return new BasicRequestBuilder(Method.OPTIONS);
+    }
+
+    public static BasicRequestBuilder options(final URI uri) {
+        return new BasicRequestBuilder(Method.OPTIONS, uri);
+    }
+
+    public static BasicRequestBuilder options(final String uri) {
+        return new BasicRequestBuilder(Method.OPTIONS, uri);
+    }
+
+    @Override
+    public BasicRequestBuilder setVersion(final ProtocolVersion version) {
+        super.setVersion(version);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder setUri(final URI uri) {
+        super.setUri(uri);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder setUri(final String uri) {
+        super.setUri(uri);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder setHeaders(final Header... headers) {
+        super.setHeaders(headers);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder addHeader(final Header header) {
+        super.addHeader(header);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder addHeader(final String name, final String value) {
+        super.addHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder removeHeader(final Header header) {
+        super.removeHeader(header);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder removeHeaders(final String name) {
+        super.removeHeaders(name);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder setHeader(final Header header) {
+        super.setHeader(header);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder setHeader(final String name, final String value) {
+        super.setHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder setCharset(final Charset charset) {
+        super.setCharset(charset);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder addParameter(final NameValuePair nvp) {
+        super.addParameter(nvp);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder addParameter(final String name, final String value) {
+        super.addParameter(name, value);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder addParameters(final NameValuePair... nvps) {
+        super.addParameters(nvps);
+        return this;
+    }
+
+    @Override
+    public BasicHttpRequest build() {
+        URI uri = getUri();
+        final List<NameValuePair> parameters = getParameters();
+        if (parameters != null && !parameters.isEmpty()) {
+            try {
+                uri = new URIBuilder(uri)
+                        .setCharset(getCharset())
+                        .addParameters(parameters)
+                        .build();
+            } catch (final URISyntaxException ex) {
+                // should never happen
+            }
+        }
+        final BasicHttpRequest result = new BasicHttpRequest(getMethod(), uri != null ? uri : URI.create("/"));
+        result.setVersion(getVersion());
+        result.setHeaders(getHeaders());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("BasicRequestBuilder [method=");
+        builder.append(getMethod());
+        builder.append(", uri=");
+        builder.append(getUri());
+        builder.append(", parameters=");
+        builder.append(getParameters());
+        builder.append(", headerGroup=");
+        builder.append(Arrays.toString(getHeaders()));
+        builder.append("]");
+        return builder.toString();
+    }
+
+}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicResponseBuilder.java b/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicResponseBuilder.java
new file mode 100644
index 0000000..de6baa3
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/support/BasicResponseBuilder.java
@@ -0,0 +1,120 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.support;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.util.Args;
+
+import java.util.Arrays;
+
+/**
+ * Builder for {@link BasicHttpResponse} instances.
+ *
+ * @since 5.1
+ */
+public class BasicResponseBuilder extends AbstractResponseBuilder<BasicHttpResponse> {
+
+    protected BasicResponseBuilder(final int status) {
+        super(status);
+    }
+
+    public static BasicResponseBuilder create(final int status) {
+        Args.checkRange(status, 100, 599, "HTTP status code");
+        return new BasicResponseBuilder(status);
+    }
+
+    @Override
+    public BasicResponseBuilder setVersion(final ProtocolVersion version) {
+        super.setVersion(version);
+        return this;
+    }
+
+    @Override
+    public BasicResponseBuilder setHeaders(final Header... headers) {
+        super.setHeaders(headers);
+        return this;
+    }
+
+    @Override
+    public BasicResponseBuilder addHeader(final Header header) {
+        super.addHeader(header);
+        return this;
+    }
+
+    @Override
+    public BasicResponseBuilder addHeader(final String name, final String value) {
+        super.addHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public BasicResponseBuilder removeHeader(final Header header) {
+        super.removeHeader(header);
+        return this;
+    }
+
+    @Override
+    public BasicResponseBuilder removeHeaders(final String name) {
+        super.removeHeaders(name);
+        return this;
+    }
+
+    @Override
+    public BasicResponseBuilder setHeader(final Header header) {
+        super.setHeader(header);
+        return this;
+    }
+
+    @Override
+    public BasicResponseBuilder setHeader(final String name, final String value) {
+        super.setHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public BasicHttpResponse build() {
+        final BasicHttpResponse result = new BasicHttpResponse(getStatus());
+        result.setVersion(getVersion());
+        result.setHeaders(getHeaders());
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("BasicResponseBuilder [status=");
+        builder.append(getStatus());
+        builder.append(", headerGroup=");
+        builder.append(Arrays.toString(getHeaders()));
+        builder.append("]");
+        return builder.toString();
+    }
+
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/HeaderMatcher.java b/httpcore5/src/test/java/org/apache/hc/core5/http/HeaderMatcher.java
new file mode 100644
index 0000000..528cfb7
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/HeaderMatcher.java
@@ -0,0 +1,66 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http;
+
+import org.apache.hc.core5.util.LangUtils;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+
+public class HeaderMatcher extends BaseMatcher<Header> {
+
+    private final String headerName;
+    private final Object headerValue;
+
+    public HeaderMatcher(final String headerName, final Object headerValue) {
+        this.headerName = headerName;
+        this.headerValue = headerValue;
+    }
+
+    @Override
+    public boolean matches(final Object item) {
+        if (item instanceof Header) {
+            final Header header = (Header) item;
+            if (headerName.equalsIgnoreCase(header.getName()) && LangUtils.equals(headerValue, header.getValue())) {
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void describeTo(final Description description) {
+        description.appendText("same header as ").appendValue(headerValue).appendText(": ").appendValue(headerValue);
+    }
+
+    @Factory
+    public static Matcher<Header> same(final String headerName, final Object headerValue) {
+        return new HeaderMatcher(headerName, headerValue);
+    }
+
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/HeadersMatcher.java b/httpcore5/src/test/java/org/apache/hc/core5/http/HeadersMatcher.java
new file mode 100644
index 0000000..5b6ae44
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/HeadersMatcher.java
@@ -0,0 +1,72 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http;
+
+import org.apache.hc.core5.util.LangUtils;
+import org.hamcrest.BaseMatcher;
+import org.hamcrest.Description;
+import org.hamcrest.Factory;
+import org.hamcrest.Matcher;
+
+public class HeadersMatcher extends BaseMatcher<Header[]> {
+
+    private final Header[] expectedHeaders;
+
+    public HeadersMatcher(final Header... headers) {
+        this.expectedHeaders = headers;
+    }
+
+    @Override
+    public boolean matches(final Object item) {
+        if (item instanceof Header[]) {
+            final Header[] headers = (Header[]) item;
+            if (headers.length == expectedHeaders.length) {
+                for (int i = 0; i < headers.length; i++) {
+                    final Header h1 = headers[i];
+                    final Header h2 = expectedHeaders[i];
+                    if (!h1.getName().equalsIgnoreCase(h2.getName())
+                            || !LangUtils.equals(h1.getValue(), h2.getValue())) {
+                        return false;
+                    }
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void describeTo(final Description description) {
+        description.appendText("same headers as ").appendValueList("[", "; ", "]", expectedHeaders);
+    }
+
+    @Factory
+    public static Matcher<Header[]> same(final Header... headers) {
+        return new HeadersMatcher(headers);
+    }
+
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/support/TestBasicMessageBuilders.java b/httpcore5/src/test/java/org/apache/hc/core5/http/support/TestBasicMessageBuilders.java
new file mode 100644
index 0000000..b4f7923
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/support/TestBasicMessageBuilders.java
@@ -0,0 +1,197 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.http.support;
+
+import org.apache.hc.core5.http.HeaderMatcher;
+import org.apache.hc.core5.http.HeadersMatcher;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.BasicHttpRequest;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIAuthority;
+import org.hamcrest.MatcherAssert;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+/**
+ * Simple tests for {@link BasicResponseBuilder} and {@link BasicRequestBuilder}.
+ */
+public class TestBasicMessageBuilders {
+
+    @Test
+    public void testResponseBasics() throws Exception {
+        final BasicResponseBuilder builder = BasicResponseBuilder.create(200);
+        Assert.assertEquals(200, builder.getStatus());
+        Assert.assertNull(builder.getHeaders());
+        Assert.assertNull(builder.getVersion());
+
+        final BasicHttpResponse r1 = builder.build();
+        Assert.assertNotNull(r1);
+        Assert.assertEquals(200, r1.getCode());
+        Assert.assertNull(r1.getVersion());
+
+        builder.setStatus(500);
+        builder.setVersion(HttpVersion.HTTP_1_0);
+        Assert.assertEquals(500, builder.getStatus());
+        Assert.assertEquals(HttpVersion.HTTP_1_0, builder.getVersion());
+
+        final BasicHttpResponse r2 = builder.build();
+        Assert.assertEquals(500, r2.getCode());
+        Assert.assertEquals(HttpVersion.HTTP_1_0, r2.getVersion());
+
+        builder.addHeader("h1", "v1");
+        builder.addHeader("h1", "v2");
+        builder.addHeader("h2", "v2");
+        MatcherAssert.assertThat(builder.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2")));
+        MatcherAssert.assertThat(builder.getHeaders("h1"), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2")));
+        MatcherAssert.assertThat(builder.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1"));
+        MatcherAssert.assertThat(builder.getLastHeader("h1"), HeaderMatcher.same("h1", "v2"));
+
+        final BasicHttpResponse r3 = builder.build();
+        MatcherAssert.assertThat(r3.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2")));
+        MatcherAssert.assertThat(r3.getHeaders("h1"), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2")));
+        MatcherAssert.assertThat(r3.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1"));
+        MatcherAssert.assertThat(r3.getLastHeader("h1"), HeaderMatcher.same("h1", "v2"));
+
+        builder.removeHeader(new BasicHeader("h1", "v2"));
+        MatcherAssert.assertThat(builder.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1")));
+        MatcherAssert.assertThat(builder.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2")));
+
+        final BasicHttpResponse r4 = builder.build();
+        MatcherAssert.assertThat(r4.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1")));
+        MatcherAssert.assertThat(r4.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2")));
+
+        builder.removeHeaders("h1");
+        MatcherAssert.assertThat(builder.getHeaders("h1"), HeadersMatcher.same());
+        MatcherAssert.assertThat(builder.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2")));
+
+        final BasicHttpResponse r5 = builder.build();
+        MatcherAssert.assertThat(r5.getHeaders("h1"), HeadersMatcher.same());
+        MatcherAssert.assertThat(r5.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2")));
+    }
+
+    @Test
+    public void testRequestBasics() throws Exception {
+        final BasicRequestBuilder builder = BasicRequestBuilder.get();
+        Assert.assertNull(builder.getUri());
+        Assert.assertEquals("GET", builder.getMethod());
+        Assert.assertNull(builder.getHeaders());
+        Assert.assertNull(builder.getVersion());
+        Assert.assertNull(builder.getCharset());
+        Assert.assertNull(builder.getParameters());
+
+        final BasicHttpRequest r1 = builder.build();
+        Assert.assertNotNull(r1);
+        Assert.assertEquals("GET", r1.getMethod());
+        Assert.assertNull(r1.getScheme());
+        Assert.assertNull(r1.getAuthority());
+        Assert.assertEquals("/", r1.getPath());
+        Assert.assertEquals(URI.create("/"), r1.getUri());
+        Assert.assertNull(r1.getVersion());
+
+        builder.setUri(URI.create("http://host:1234/blah?param=value"));
+        builder.setVersion(HttpVersion.HTTP_1_1);
+        Assert.assertEquals(URI.create("http://host:1234/blah?param=value"), builder.getUri());
+        Assert.assertEquals(HttpVersion.HTTP_1_1, builder.getVersion());
+
+        final BasicHttpRequest r2 = builder.build();
+        Assert.assertEquals("GET", r2.getMethod());
+        Assert.assertEquals("http", r2.getScheme());
+        Assert.assertEquals(new URIAuthority("host", 1234), r2.getAuthority());
+        Assert.assertEquals("/blah?param=value", r2.getPath());
+        Assert.assertEquals(URI.create("http://host:1234/blah?param=value"), r2.getUri());
+        Assert.assertEquals(HttpVersion.HTTP_1_1, builder.getVersion());
+
+        builder.setCharset(StandardCharsets.US_ASCII);
+        builder.addParameter("param1", "value1");
+        builder.addParameter("param2", null);
+        builder.addParameters(new BasicNameValuePair("param3", "value3"), new BasicNameValuePair("param4", null));
+
+        Assert.assertEquals(builder.getParameters(), Arrays.asList(
+                new BasicNameValuePair("param1", "value1"), new BasicNameValuePair("param2", null),
+                new BasicNameValuePair("param3", "value3"), new BasicNameValuePair("param4", null)
+        ));
+        Assert.assertEquals(URI.create("http://host:1234/blah?param=value"), builder.getUri());
+
+        final BasicHttpRequest r3 = builder.build();
+        Assert.assertEquals("GET", r3.getMethod());
+        Assert.assertEquals("http", r3.getScheme());
+        Assert.assertEquals(new URIAuthority("host", 1234), r3.getAuthority());
+        Assert.assertEquals("/blah?param=value&param1=value1&param2&param3=value3&param4", r3.getPath());
+        Assert.assertEquals(URI.create("http://host:1234/blah?param=value&param1=value1&param2&param3=value3&param4"),
+                r3.getUri());
+
+        builder.addHeader("h1", "v1");
+        builder.addHeader("h1", "v2");
+        builder.addHeader("h2", "v2");
+        MatcherAssert.assertThat(builder.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2")));
+        MatcherAssert.assertThat(builder.getHeaders("h1"), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2")));
+        MatcherAssert.assertThat(builder.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1"));
+        MatcherAssert.assertThat(builder.getLastHeader("h1"), HeaderMatcher.same("h1", "v2"));
+
+        final BasicHttpRequest r4 = builder.build();
+        MatcherAssert.assertThat(r4.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2")));
+        MatcherAssert.assertThat(r4.getHeaders("h1"), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2")));
+        MatcherAssert.assertThat(r4.getFirstHeader("h1"), HeaderMatcher.same("h1", "v1"));
+        MatcherAssert.assertThat(r4.getLastHeader("h1"), HeaderMatcher.same("h1", "v2"));
+
+        builder.removeHeader(new BasicHeader("h1", "v2"));
+        MatcherAssert.assertThat(builder.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1")));
+        MatcherAssert.assertThat(builder.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2")));
+
+        final BasicHttpRequest r5 = builder.build();
+        MatcherAssert.assertThat(r5.getHeaders("h1"), HeadersMatcher.same(new BasicHeader("h1", "v1")));
+        MatcherAssert.assertThat(r5.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h2", "v2")));
+
+        builder.removeHeaders("h1");
+        MatcherAssert.assertThat(builder.getHeaders("h1"), HeadersMatcher.same());
+        MatcherAssert.assertThat(builder.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2")));
+
+        final BasicHttpRequest r6 = builder.build();
+        MatcherAssert.assertThat(r6.getHeaders("h1"), HeadersMatcher.same());
+        MatcherAssert.assertThat(r6.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2")));
+    }
+
+}