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/03/14 17:59:36 UTC

[httpcomponents-client] branch master updated: Simple message builders

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-client.git


The following commit(s) were added to refs/heads/master by this push:
     new 1174c24  Simple message builders
1174c24 is described below

commit 1174c240e29ca4f5e0609583545e3e98b3a91263
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sun Mar 14 16:29:58 2021 +0100

    Simple message builders
---
 .../async/AbstractSimpleServerExchangeHandler.java |   7 +-
 .../async/methods/ConfigurableHttpRequest.java     |   8 +
 .../hc/client5/http/async/methods/SimpleBody.java  |   4 +-
 .../http/async/methods/SimpleHttpRequest.java      |  57 ++-
 .../http/async/methods/SimpleRequestBuilder.java   | 427 +++++++++++++++++++++
 .../http/async/methods/SimpleResponseBuilder.java  | 156 ++++++++
 ...luePairMatcher.java => ContentTypeMatcher.java} |  27 +-
 ...ameValuePairMatcher.java => HeaderMatcher.java} |  28 +-
 ...meValuePairMatcher.java => HeadersMatcher.java} |  34 +-
 .../hc/client5/http/NameValuePairMatcher.java      |   5 +
 .../hc/client5/http/NameValuePairsMatcher.java     |  89 +++++
 .../async/methods/TestSimpleMessageBuilders.java   | 279 ++++++++++++++
 12 files changed, 1074 insertions(+), 47 deletions(-)

diff --git a/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java b/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java
index e3e2881..6565dbb 100644
--- a/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java
+++ b/httpclient5-testing/src/main/java/org/apache/hc/client5/testing/async/AbstractSimpleServerExchangeHandler.java
@@ -31,6 +31,7 @@ import java.io.IOException;
 import org.apache.hc.client5.http.async.methods.SimpleBody;
 import org.apache.hc.client5.http.async.methods.SimpleHttpRequest;
 import org.apache.hc.client5.http.async.methods.SimpleHttpResponse;
+import org.apache.hc.client5.http.async.methods.SimpleRequestBuilder;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.EntityDetails;
 import org.apache.hc.core5.http.HttpException;
@@ -63,11 +64,11 @@ public abstract class AbstractSimpleServerExchangeHandler extends AbstractServer
                     final HttpRequest request,
                     final byte[] body,
                     final ContentType contentType) {
-                final SimpleHttpRequest simpleRequest = SimpleHttpRequest.copy(request);
+                final SimpleRequestBuilder builder = SimpleRequestBuilder.copy(request);
                 if (body != null) {
-                    simpleRequest.setBody(body, contentType);
+                    builder.setBody(body, contentType);
                 }
-                return simpleRequest;
+                return builder.build();
             }
 
         };
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/ConfigurableHttpRequest.java b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/ConfigurableHttpRequest.java
index d92dd7e..5dba36b 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/ConfigurableHttpRequest.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/ConfigurableHttpRequest.java
@@ -33,6 +33,7 @@ import org.apache.hc.client5.http.config.Configurable;
 import org.apache.hc.client5.http.config.RequestConfig;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.message.BasicHttpRequest;
+import org.apache.hc.core5.net.URIAuthority;
 
 /**
  * HTTP request message with a custom configuration.
@@ -52,6 +53,13 @@ public class ConfigurableHttpRequest extends BasicHttpRequest implements Configu
         super(method, host, path);
     }
 
+    /**
+     * @since 5.1
+     */
+    public ConfigurableHttpRequest(final String method, final String scheme, final URIAuthority authority, final String path) {
+        super(method, scheme, authority, path);
+    }
+
     public ConfigurableHttpRequest(final String method, final URI requestUri) {
         super(method, requestUri);
     }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleBody.java b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleBody.java
index 421baa7..b1a0294 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleBody.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleBody.java
@@ -99,8 +99,8 @@ public final class SimpleBody {
 
     @Override
     public String toString() {
-        return "content length=" + (bodyAsBytes != null ? bodyAsBytes.length : "chunked") +
-                ", content type=" + contentType;
+        return "SimpleBody{content length=" + (bodyAsBytes != null ? bodyAsBytes.length : "chunked") +
+                ", content type=" + contentType + "}";
     }
 
 }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleHttpRequest.java b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleHttpRequest.java
index 3d41bfe..7ce1cc6 100644
--- a/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleHttpRequest.java
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleHttpRequest.java
@@ -35,6 +35,7 @@ import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.net.URIAuthority;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -49,6 +50,45 @@ public final class SimpleHttpRequest extends ConfigurableHttpRequest {
     private static final long serialVersionUID = 1L;
     private SimpleBody body;
 
+    /**
+     * @since 5.1
+     */
+    public static SimpleHttpRequest create(final String method, final String uri) {
+        return new SimpleHttpRequest(method, uri);
+    }
+
+    /**
+     * @since 5.1
+     */
+    public static SimpleHttpRequest create(final String method, final URI uri) {
+        return new SimpleHttpRequest(method, uri);
+    }
+
+    /**
+     * @since 5.1
+     */
+    public static SimpleHttpRequest create(final Method method, final URI uri) {
+        return new SimpleHttpRequest(method, uri);
+    }
+
+    /**
+     * @since 5.1
+     */
+    public static SimpleHttpRequest create(final Method method, final HttpHost host, final String path) {
+        return new SimpleHttpRequest(method, host, path);
+    }
+
+    /**
+     * @since 5.1
+     */
+    public static SimpleHttpRequest create(final String method, final String scheme, final URIAuthority authority, final String path) {
+        return new SimpleHttpRequest(method, scheme, authority, path);
+    }
+
+    /**
+     * @deprecated Use {@link SimpleRequestBuilder}
+     */
+    @Deprecated
     public static SimpleHttpRequest copy(final HttpRequest original) {
         Args.notNull(original, "HTTP request");
         final SimpleHttpRequest copy = new SimpleHttpRequest(original.getMethod(), original.getRequestUri());
@@ -73,14 +113,27 @@ public final class SimpleHttpRequest extends ConfigurableHttpRequest {
         super(method, requestUri);
     }
 
-    SimpleHttpRequest(final Method method, final URI requestUri) {
+    /**
+     * @since 5.1
+     */
+    public SimpleHttpRequest(final Method method, final URI requestUri) {
         this(method.name(), requestUri);
     }
 
-    SimpleHttpRequest(final Method method, final HttpHost host, final String path) {
+    /**
+     * @since 5.1
+     */
+    public SimpleHttpRequest(final Method method, final HttpHost host, final String path) {
         this(method.name(), host, path);
     }
 
+    /**
+     * @since 5.1
+     */
+    public SimpleHttpRequest(final String method, final String scheme, final URIAuthority authority, final String path) {
+        super(method, scheme, authority, path);
+    }
+
     public void setBody(final SimpleBody body) {
         this.body = body;
     }
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleRequestBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleRequestBuilder.java
new file mode 100644
index 0000000..9499d39
--- /dev/null
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleRequestBuilder.java
@@ -0,0 +1,427 @@
+/*
+ * ====================================================================
+ * 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.client5.http.async.methods;
+
+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.client5.http.config.RequestConfig;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpHost;
+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.support.AbstractRequestBuilder;
+import org.apache.hc.core5.net.URIAuthority;
+import org.apache.hc.core5.net.URIBuilder;
+import org.apache.hc.core5.net.WWWFormCodec;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * Builder for {@link SimpleHttpRequest} instances.
+ * <p>
+ * Please note that this class treats parameters differently depending on composition
+ * of the request: if the request has a content entity explicitly set with
+ * {@link #setBody(SimpleBody)} or it is not an entity enclosing method
+ * (such as POST or PUT), parameters will be added to the query component
+ * of the request URI. Otherwise, parameters will be added as a URL encoded entity.
+ * </p>
+ *
+ * @since 5.0
+ */
+public class SimpleRequestBuilder extends AbstractRequestBuilder<SimpleHttpRequest> {
+
+    private SimpleBody body;
+    private RequestConfig requestConfig;
+
+    SimpleRequestBuilder(final String method) {
+        super(method);
+    }
+
+    SimpleRequestBuilder(final Method method) {
+        super(method);
+    }
+
+    SimpleRequestBuilder(final String method, final URI uri) {
+        super(method, uri);
+    }
+
+    SimpleRequestBuilder(final Method method, final URI uri) {
+        super(method, uri);
+    }
+
+    SimpleRequestBuilder(final Method method, final String uri) {
+        super(method, uri);
+    }
+
+    SimpleRequestBuilder(final String method, final String uri) {
+        super(method, uri);
+    }
+
+    public static SimpleRequestBuilder create(final String method) {
+        Args.notBlank(method, "HTTP method");
+        return new SimpleRequestBuilder(method);
+    }
+
+    public static SimpleRequestBuilder create(final Method method) {
+        Args.notNull(method, "HTTP method");
+        return new SimpleRequestBuilder(method);
+    }
+
+    public static SimpleRequestBuilder get() {
+        return new SimpleRequestBuilder(Method.GET);
+    }
+
+    public static SimpleRequestBuilder get(final URI uri) {
+        return new SimpleRequestBuilder(Method.GET, uri);
+    }
+
+    public static SimpleRequestBuilder get(final String uri) {
+        return new SimpleRequestBuilder(Method.GET, uri);
+    }
+
+    public static SimpleRequestBuilder head() {
+        return new SimpleRequestBuilder(Method.HEAD);
+    }
+
+    public static SimpleRequestBuilder head(final URI uri) {
+        return new SimpleRequestBuilder(Method.HEAD, uri);
+    }
+
+    public static SimpleRequestBuilder head(final String uri) {
+        return new SimpleRequestBuilder(Method.HEAD, uri);
+    }
+
+    public static SimpleRequestBuilder patch() {
+        return new SimpleRequestBuilder(Method.PATCH);
+    }
+
+    public static SimpleRequestBuilder patch(final URI uri) {
+        return new SimpleRequestBuilder(Method.PATCH, uri);
+    }
+
+    public static SimpleRequestBuilder patch(final String uri) {
+        return new SimpleRequestBuilder(Method.PATCH, uri);
+    }
+
+    public static SimpleRequestBuilder post() {
+        return new SimpleRequestBuilder(Method.POST);
+    }
+
+    public static SimpleRequestBuilder post(final URI uri) {
+        return new SimpleRequestBuilder(Method.POST, uri);
+    }
+
+    public static SimpleRequestBuilder post(final String uri) {
+        return new SimpleRequestBuilder(Method.POST, uri);
+    }
+
+    public static SimpleRequestBuilder put() {
+        return new SimpleRequestBuilder(Method.PUT);
+    }
+
+    public static SimpleRequestBuilder put(final URI uri) {
+        return new SimpleRequestBuilder(Method.PUT, uri);
+    }
+
+    public static SimpleRequestBuilder put(final String uri) {
+        return new SimpleRequestBuilder(Method.PUT, uri);
+    }
+
+    public static SimpleRequestBuilder delete() {
+        return new SimpleRequestBuilder(Method.DELETE);
+    }
+
+    public static SimpleRequestBuilder delete(final URI uri) {
+        return new SimpleRequestBuilder(Method.DELETE, uri);
+    }
+
+    public static SimpleRequestBuilder delete(final String uri) {
+        return new SimpleRequestBuilder(Method.DELETE, uri);
+    }
+
+    public static SimpleRequestBuilder trace() {
+        return new SimpleRequestBuilder(Method.TRACE);
+    }
+
+    public static SimpleRequestBuilder trace(final URI uri) {
+        return new SimpleRequestBuilder(Method.TRACE, uri);
+    }
+
+    public static SimpleRequestBuilder trace(final String uri) {
+        return new SimpleRequestBuilder(Method.TRACE, uri);
+    }
+
+    public static SimpleRequestBuilder options() {
+        return new SimpleRequestBuilder(Method.OPTIONS);
+    }
+
+    public static SimpleRequestBuilder options(final URI uri) {
+        return new SimpleRequestBuilder(Method.OPTIONS, uri);
+    }
+
+    public static SimpleRequestBuilder options(final String uri) {
+        return new SimpleRequestBuilder(Method.OPTIONS, uri);
+    }
+
+    /**
+     * @since 5.1
+     */
+    public static SimpleRequestBuilder copy(final SimpleHttpRequest request) {
+        Args.notNull(request, "HTTP request");
+        final SimpleRequestBuilder builder = new SimpleRequestBuilder(request.getMethod());
+        builder.digest(request);
+        return builder;
+    }
+
+    /**
+     * @since 5.1
+     */
+    public static SimpleRequestBuilder copy(final HttpRequest request) {
+        Args.notNull(request, "HTTP request");
+        final SimpleRequestBuilder builder = new SimpleRequestBuilder(request.getMethod());
+        builder.digest(request);
+        return builder;
+    }
+
+    protected void digest(final SimpleHttpRequest request) {
+        super.digest(request);
+        setBody(request.getBody());
+    }
+
+    protected void digest(final HttpRequest request) {
+        super.digest(request);
+    }
+
+    @Override
+    public SimpleRequestBuilder setVersion(final ProtocolVersion version) {
+        super.setVersion(version);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setUri(final URI uri) {
+        super.setUri(uri);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setUri(final String uri) {
+        super.setUri(uri);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setScheme(final String scheme) {
+        super.setScheme(scheme);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setAuthority(final URIAuthority authority) {
+        super.setAuthority(authority);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setHttpHost(final HttpHost httpHost) {
+        super.setHttpHost(httpHost);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setPath(final String path) {
+        super.setPath(path);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setHeaders(final Header... headers) {
+        super.setHeaders(headers);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder addHeader(final Header header) {
+        super.addHeader(header);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder addHeader(final String name, final String value) {
+        super.addHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder removeHeader(final Header header) {
+        super.removeHeader(header);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder removeHeaders(final String name) {
+        super.removeHeaders(name);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setHeader(final Header header) {
+        super.setHeader(header);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setHeader(final String name, final String value) {
+        super.setHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setCharset(final Charset charset) {
+        super.setCharset(charset);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder addParameter(final NameValuePair nvp) {
+        super.addParameter(nvp);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder addParameter(final String name, final String value) {
+        super.addParameter(name, value);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder addParameters(final NameValuePair... nvps) {
+        super.addParameters(nvps);
+        return this;
+    }
+
+    @Override
+    public SimpleRequestBuilder setAbsoluteRequestUri(final boolean absoluteRequestUri) {
+        super.setAbsoluteRequestUri(absoluteRequestUri);
+        return this;
+    }
+
+    public SimpleBody getBody() {
+        return body;
+    }
+
+    public SimpleRequestBuilder setBody(final SimpleBody body) {
+        this.body = body;
+        return this;
+    }
+
+    public SimpleRequestBuilder setBody(final String content, final ContentType contentType) {
+        this.body = SimpleBody.create(content, contentType);
+        return this;
+    }
+
+    public SimpleRequestBuilder setBody(final byte[] content, final ContentType contentType) {
+        this.body = SimpleBody.create(content, contentType);
+        return this;
+    }
+
+    public RequestConfig getRequestConfig() {
+        return requestConfig;
+    }
+
+    public SimpleRequestBuilder setRequestConfig(final RequestConfig requestConfig) {
+        this.requestConfig = requestConfig;
+        return this;
+    }
+
+    public SimpleHttpRequest build() {
+        String path = getPath();
+        SimpleBody bodyCopy = this.body;
+        final String method = getMethod();
+        final List<NameValuePair> parameters = getParameters();
+        if (parameters != null && !parameters.isEmpty()) {
+            final Charset charsetCopy = getCharset();
+            if (bodyCopy == null && (Method.POST.isSame(method) || Method.PUT.isSame(method))) {
+                final String content = WWWFormCodec.format(
+                        parameters,
+                        charsetCopy != null ? charsetCopy : ContentType.APPLICATION_FORM_URLENCODED.getCharset());
+                bodyCopy = SimpleBody.create(content, ContentType.APPLICATION_FORM_URLENCODED);
+            } else {
+                try {
+                    final URI uri = new URIBuilder(path)
+                            .setCharset(charsetCopy)
+                            .addParameters(parameters)
+                            .build();
+                    path = uri.toASCIIString();
+                } catch (final URISyntaxException ex) {
+                    // should never happen
+                }
+            }
+        }
+
+        if (bodyCopy != null && Method.TRACE.isSame(method)) {
+            throw new IllegalStateException(Method.TRACE + " requests may not include an entity");
+        }
+
+        final SimpleHttpRequest result = new SimpleHttpRequest(method, getScheme(), getAuthority(), path);
+        result.setVersion(getVersion());
+        result.setHeaders(getHeaders());
+        result.setBody(bodyCopy);
+        result.setAbsoluteRequestUri(isAbsoluteRequestUri());
+        result.setConfig(requestConfig);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("ClassicRequestBuilder [method=");
+        builder.append(getMethod());
+        builder.append(", scheme=");
+        builder.append(getScheme());
+        builder.append(", authority=");
+        builder.append(getAuthority());
+        builder.append(", path=");
+        builder.append(getPath());
+        builder.append(", parameters=");
+        builder.append(getParameters());
+        builder.append(", headerGroup=");
+        builder.append(Arrays.toString(getHeaders()));
+        builder.append(", body=");
+        builder.append(body);
+        builder.append("]");
+        return builder.toString();
+    }
+
+}
diff --git a/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleResponseBuilder.java b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleResponseBuilder.java
new file mode 100644
index 0000000..9ddc05d
--- /dev/null
+++ b/httpclient5/src/main/java/org/apache/hc/client5/http/async/methods/SimpleResponseBuilder.java
@@ -0,0 +1,156 @@
+/*
+ * ====================================================================
+ * 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.client5.http.async.methods;
+
+import java.util.Arrays;
+
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.ProtocolVersion;
+import org.apache.hc.core5.http.support.AbstractResponseBuilder;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * Builder for {@link SimpleHttpResponse} instances.
+ *
+ * @since 5.1
+ */
+public class SimpleResponseBuilder extends AbstractResponseBuilder<SimpleHttpResponse> {
+
+    private SimpleBody body;
+
+    SimpleResponseBuilder(final int status) {
+        super(status);
+    }
+
+    public static SimpleResponseBuilder create(final int status) {
+        Args.checkRange(status, 100, 599, "HTTP status code");
+        return new SimpleResponseBuilder(status);
+    }
+
+    public static SimpleResponseBuilder copy(final SimpleHttpResponse response) {
+        Args.notNull(response, "HTTP response");
+        final SimpleResponseBuilder builder = new SimpleResponseBuilder(response.getCode());
+        builder.digest(response);
+        return builder;
+    }
+
+    protected void digest(final SimpleHttpResponse response) {
+        super.digest(response);
+        setBody(response.getBody());
+    }
+
+    @Override
+    public SimpleResponseBuilder setVersion(final ProtocolVersion version) {
+        super.setVersion(version);
+        return this;
+    }
+
+    @Override
+    public SimpleResponseBuilder setHeaders(final Header... headers) {
+        super.setHeaders(headers);
+        return this;
+    }
+
+    @Override
+    public SimpleResponseBuilder addHeader(final Header header) {
+        super.addHeader(header);
+        return this;
+    }
+
+    @Override
+    public SimpleResponseBuilder addHeader(final String name, final    String value) {
+        super.addHeader(name, value);
+        return this;
+    }
+
+    @Override
+    public SimpleResponseBuilder removeHeader(final Header header) {
+        super.removeHeader(header);
+        return this;
+    }
+
+    @Override
+    public SimpleResponseBuilder removeHeaders(final String name) {
+        super.removeHeaders(name);
+        return this;
+    }
+
+    @Override
+    public SimpleResponseBuilder setHeader(final Header header) {
+        super.setHeader(header);
+        return this;
+    }
+
+    @Override
+    public SimpleResponseBuilder setHeader(final String name, final String value) {
+        super.setHeader(name, value);
+        return this;
+    }
+
+    public SimpleBody getBody() {
+        return body;
+    }
+
+    public SimpleResponseBuilder setBody(final SimpleBody body) {
+        this.body = body;
+        return this;
+    }
+
+    public SimpleResponseBuilder setBody(final String content, final ContentType contentType) {
+        this.body = SimpleBody.create(content, contentType);
+        return this;
+    }
+
+    public SimpleResponseBuilder setBody(final byte[] content, final ContentType contentType) {
+        this.body = SimpleBody.create(content, contentType);
+        return this;
+    }
+
+    public SimpleHttpResponse build() {
+        final SimpleHttpResponse result = new SimpleHttpResponse(getStatus());
+        result.setVersion(getVersion());
+        result.setHeaders(getHeaders());
+        result.setBody(body);
+        return result;
+    }
+
+    @Override
+    public String toString() {
+        final StringBuilder builder = new StringBuilder();
+        builder.append("SimpleResponseBuilder [status=");
+        builder.append(getStatus());
+        builder.append(", headerGroup=");
+        builder.append(Arrays.toString(getHeaders()));
+        builder.append(", body=");
+        builder.append(body);
+        builder.append("]");
+        return builder.toString();
+    }
+
+}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java b/httpclient5/src/test/java/org/apache/hc/client5/http/ContentTypeMatcher.java
similarity index 66%
copy from httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
copy to httpclient5/src/test/java/org/apache/hc/client5/http/ContentTypeMatcher.java
index f5d7e57..48a0b44 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/ContentTypeMatcher.java
@@ -26,40 +26,39 @@
  */
 package org.apache.hc.client5.http;
 
-import org.apache.hc.core5.http.NameValuePair;
-import org.apache.hc.core5.util.LangUtils;
+import org.apache.hc.core5.http.ContentType;
 import org.hamcrest.BaseMatcher;
 import org.hamcrest.Description;
 import org.hamcrest.Factory;
 import org.hamcrest.Matcher;
 
-public class NameValuePairMatcher extends BaseMatcher<NameValuePair> {
+public class ContentTypeMatcher extends BaseMatcher<ContentType> {
 
-    private final String name;
-    private final String value;
+    private final ContentType expectedContentType;
 
-    public NameValuePairMatcher(final String name, final String value) {
-        this.name = name;
-        this.value = value;
+    public ContentTypeMatcher(final ContentType contentType) {
+        this.expectedContentType = contentType;
     }
 
     @Override
     public boolean matches(final Object item) {
-        if (item instanceof NameValuePair) {
-            final NameValuePair nvp = (NameValuePair) item;
-            return LangUtils.equals(nvp.getName(), name) && LangUtils.equals(nvp.getValue(), value);
+        if (item instanceof ContentType) {
+            final ContentType contentType = (ContentType) item;
+            if (contentType.isSameMimeType(expectedContentType)) {
+                return true;
+            }
         }
         return false;
     }
 
     @Override
     public void describeTo(final Description description) {
-        description.appendText("equals ").appendValue(name).appendText("=").appendValue(value);
+        description.appendText("same MIME type as ").appendValue(expectedContentType);
     }
 
     @Factory
-    public static Matcher<NameValuePair> equals(final String name, final String value) {
-        return new NameValuePairMatcher(name, value);
+    public static Matcher<ContentType> sameMimeType(final ContentType contentType) {
+        return new ContentTypeMatcher(contentType);
     }
 
 }
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java b/httpclient5/src/test/java/org/apache/hc/client5/http/HeaderMatcher.java
similarity index 65%
copy from httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
copy to httpclient5/src/test/java/org/apache/hc/client5/http/HeaderMatcher.java
index f5d7e57..487eb15 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/HeaderMatcher.java
@@ -26,40 +26,42 @@
  */
 package org.apache.hc.client5.http;
 
-import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.Header;
 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 NameValuePairMatcher extends BaseMatcher<NameValuePair> {
+public class HeaderMatcher extends BaseMatcher<Header> {
 
-    private final String name;
-    private final String value;
+    private final String headerName;
+    private final Object headerValue;
 
-    public NameValuePairMatcher(final String name, final String value) {
-        this.name = name;
-        this.value = value;
+    public HeaderMatcher(final String headerName, final Object headerValue) {
+        this.headerName = headerName;
+        this.headerValue = headerValue;
     }
 
     @Override
     public boolean matches(final Object item) {
-        if (item instanceof NameValuePair) {
-            final NameValuePair nvp = (NameValuePair) item;
-            return LangUtils.equals(nvp.getName(), name) && LangUtils.equals(nvp.getValue(), value);
+        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("equals ").appendValue(name).appendText("=").appendValue(value);
+        description.appendText("same header as ").appendValue(headerValue).appendText(": ").appendValue(headerValue);
     }
 
     @Factory
-    public static Matcher<NameValuePair> equals(final String name, final String value) {
-        return new NameValuePairMatcher(name, value);
+    public static Matcher<Header> same(final String headerName, final Object headerValue) {
+        return new HeaderMatcher(headerName, headerValue);
     }
 
 }
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java b/httpclient5/src/test/java/org/apache/hc/client5/http/HeadersMatcher.java
similarity index 60%
copy from httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
copy to httpclient5/src/test/java/org/apache/hc/client5/http/HeadersMatcher.java
index f5d7e57..5665543 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/HeadersMatcher.java
@@ -26,40 +26,48 @@
  */
 package org.apache.hc.client5.http;
 
-import org.apache.hc.core5.http.NameValuePair;
+import org.apache.hc.core5.http.Header;
 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 NameValuePairMatcher extends BaseMatcher<NameValuePair> {
+public class HeadersMatcher extends BaseMatcher<Header[]> {
 
-    private final String name;
-    private final String value;
+    private final Header[] expectedHeaders;
 
-    public NameValuePairMatcher(final String name, final String value) {
-        this.name = name;
-        this.value = value;
+    public HeadersMatcher(final Header... headers) {
+        this.expectedHeaders = headers;
     }
 
     @Override
     public boolean matches(final Object item) {
-        if (item instanceof NameValuePair) {
-            final NameValuePair nvp = (NameValuePair) item;
-            return LangUtils.equals(nvp.getName(), name) && LangUtils.equals(nvp.getValue(), value);
+        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("equals ").appendValue(name).appendText("=").appendValue(value);
+        description.appendText("same headers as ").appendValueList("[", ", ", "]", expectedHeaders);
     }
 
     @Factory
-    public static Matcher<NameValuePair> equals(final String name, final String value) {
-        return new NameValuePairMatcher(name, value);
+    public static Matcher<Header[]> same(final Header... headers) {
+        return new HeadersMatcher(headers);
     }
 
 }
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java b/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
index f5d7e57..9dcd082 100644
--- a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairMatcher.java
@@ -62,4 +62,9 @@ public class NameValuePairMatcher extends BaseMatcher<NameValuePair> {
         return new NameValuePairMatcher(name, value);
     }
 
+    @Factory
+    public static Matcher<NameValuePair> same(final String name, final String value) {
+        return new NameValuePairMatcher(name, value);
+    }
+
 }
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairsMatcher.java b/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairsMatcher.java
new file mode 100644
index 0000000..e63809a
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/NameValuePairsMatcher.java
@@ -0,0 +1,89 @@
+/*
+ * ====================================================================
+ * 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.client5.http;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.List;
+
+import org.apache.hc.core5.http.NameValuePair;
+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 NameValuePairsMatcher extends BaseMatcher<Collection<? extends NameValuePair>> {
+
+    private final List<? extends NameValuePair> expectedNameValuePairList;
+
+    public NameValuePairsMatcher(final List<? extends NameValuePair> nameValuePairList) {
+        this.expectedNameValuePairList = nameValuePairList;
+    }
+
+    @Override
+    public boolean matches(final Object item) {
+        if (item instanceof Collection<?>) {
+            final Collection<?> collection = (Collection<?>) item;
+            if (collection.size() == collection.size()) {
+                int i = 0;
+                for (final Object obj : collection) {
+                    if (obj instanceof NameValuePair) {
+                        final NameValuePair nvp1 = (NameValuePair) obj;
+                        final NameValuePair nvp2 = expectedNameValuePairList.get(i);
+                        if (!nvp1.getName().equalsIgnoreCase(nvp2.getName())
+                                || !LangUtils.equals(nvp1.getValue(), nvp2.getValue())) {
+                            return false;
+                        }
+                    } else {
+                        return false;
+                    }
+                    i++;
+                }
+                return true;
+            }
+        }
+        return false;
+    }
+
+    @Override
+    public void describeTo(final Description description) {
+        description.appendText("same name/value pairs as ").appendValueList("[", ", ", "]", expectedNameValuePairList);
+    }
+
+    @Factory
+    public static Matcher<Collection<? extends NameValuePair>> same(final Collection<? extends NameValuePair> nameValuePairs) {
+        return new NameValuePairsMatcher(new ArrayList<>(nameValuePairs));
+    }
+
+    @Factory
+    public static Matcher<Collection<? extends NameValuePair>> same(final NameValuePair... nameValuePairs) {
+        return new NameValuePairsMatcher(Arrays.asList(nameValuePairs));
+    }
+
+}
diff --git a/httpclient5/src/test/java/org/apache/hc/client5/http/async/methods/TestSimpleMessageBuilders.java b/httpclient5/src/test/java/org/apache/hc/client5/http/async/methods/TestSimpleMessageBuilders.java
new file mode 100644
index 0000000..9f786d1
--- /dev/null
+++ b/httpclient5/src/test/java/org/apache/hc/client5/http/async/methods/TestSimpleMessageBuilders.java
@@ -0,0 +1,279 @@
+/*
+ * ====================================================================
+ * 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.client5.http.async.methods;
+
+import java.net.URI;
+import java.nio.charset.StandardCharsets;
+import java.util.Arrays;
+
+import org.apache.hc.client5.http.ContentTypeMatcher;
+import org.apache.hc.client5.http.HeaderMatcher;
+import org.apache.hc.client5.http.HeadersMatcher;
+import org.apache.hc.client5.http.NameValuePairsMatcher;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIAuthority;
+import org.hamcrest.CoreMatchers;
+import org.hamcrest.MatcherAssert;
+import org.junit.Assert;
+import org.junit.Test;
+
+/**
+ * Simple tests for {@link SimpleResponseBuilder} and {@link SimpleRequestBuilder}.
+ */
+public class TestSimpleMessageBuilders {
+
+    @Test
+    public void testResponseBasics() throws Exception {
+        final SimpleResponseBuilder builder = SimpleResponseBuilder.create(200);
+        Assert.assertEquals(200, builder.getStatus());
+        Assert.assertNull(builder.getHeaders());
+        Assert.assertNull(builder.getVersion());
+
+        final SimpleHttpResponse 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 SimpleHttpResponse 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 SimpleHttpResponse 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 SimpleHttpResponse 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 SimpleHttpResponse 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 SimpleRequestBuilder builder = SimpleRequestBuilder.get();
+        Assert.assertEquals(URI.create("/"), builder.getUri());
+        Assert.assertEquals("GET", builder.getMethod());
+        Assert.assertNull(builder.getScheme());
+        Assert.assertNull(builder.getAuthority());
+        Assert.assertNull(builder.getPath());
+        Assert.assertNull(builder.getHeaders());
+        Assert.assertNull(builder.getVersion());
+        Assert.assertNull(builder.getCharset());
+        Assert.assertNull(builder.getParameters());
+
+        final SimpleHttpRequest r1 = builder.build();
+        Assert.assertNotNull(r1);
+        Assert.assertEquals("GET", r1.getMethod());
+        Assert.assertNull(r1.getScheme());
+        Assert.assertNull(r1.getAuthority());
+        Assert.assertNull(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("http", builder.getScheme());
+        Assert.assertEquals(new URIAuthority("host", 1234), builder.getAuthority());
+        Assert.assertEquals("/blah?param=value", builder.getPath());
+        Assert.assertEquals(URI.create("http://host:1234/blah?param=value"), builder.getUri());
+        Assert.assertEquals(HttpVersion.HTTP_1_1, builder.getVersion());
+
+        final SimpleHttpRequest 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 SimpleHttpRequest 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 SimpleHttpRequest 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 SimpleHttpRequest 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 SimpleHttpRequest r6 = builder.build();
+        MatcherAssert.assertThat(r6.getHeaders("h1"), HeadersMatcher.same());
+        MatcherAssert.assertThat(r6.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2")));
+    }
+
+    @Test
+    public void testResponseCopy() throws Exception {
+        final SimpleHttpResponse response = SimpleHttpResponse.create(400);
+        response.addHeader("h1", "v1");
+        response.addHeader("h1", "v2");
+        response.addHeader("h2", "v2");
+        response.setVersion(HttpVersion.HTTP_2);
+
+        final SimpleResponseBuilder builder = SimpleResponseBuilder.copy(response);
+        Assert.assertEquals(400, builder.getStatus());
+        Assert.assertEquals(HttpVersion.HTTP_2, builder.getVersion());
+        MatcherAssert.assertThat(builder.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2")));
+    }
+
+    @Test
+    public void testRequestCopy() throws Exception {
+        final SimpleHttpRequest request = SimpleHttpRequest.create(Method.GET, URI.create("https://host:3456/stuff?blah")) ;
+        request.addHeader("h1", "v1");
+        request.addHeader("h1", "v2");
+        request.addHeader("h2", "v2");
+        request.setVersion(HttpVersion.HTTP_2);
+
+        final SimpleRequestBuilder builder = SimpleRequestBuilder.copy(request);
+        Assert.assertEquals("GET", builder.getMethod());
+        Assert.assertEquals("https", builder.getScheme());
+        Assert.assertEquals(new URIAuthority("host", 3456), builder.getAuthority());
+        Assert.assertEquals("/stuff?blah", builder.getPath());
+        Assert.assertEquals(HttpVersion.HTTP_2, builder.getVersion());
+        MatcherAssert.assertThat(builder.getHeaders(), HeadersMatcher.same(
+                new BasicHeader("h1", "v1"), new BasicHeader("h1", "v2"), new BasicHeader("h2", "v2")));
+    }
+
+    @Test
+    public void testGetParameters() throws Exception {
+        final SimpleRequestBuilder builder = SimpleRequestBuilder.get(URI.create("https://host:3456/stuff?p0=p0"));
+        builder.addParameter("p1", "v1");
+        builder.addParameters(new BasicNameValuePair("p2", "v2"), new BasicNameValuePair("p3", "v3"));
+        builder.addParameter(new BasicNameValuePair("p3", "v3.1"));
+        Assert.assertEquals("GET", builder.getMethod());
+        Assert.assertEquals("https", builder.getScheme());
+        Assert.assertEquals(new URIAuthority("host", 3456), builder.getAuthority());
+        Assert.assertEquals("/stuff?p0=p0", builder.getPath());
+        MatcherAssert.assertThat(builder.getParameters(), NameValuePairsMatcher.same(
+                new BasicNameValuePair("p1", "v1"), new BasicNameValuePair("p2", "v2"),
+                new BasicNameValuePair("p3", "v3"), new BasicNameValuePair("p3", "v3.1")));
+        final SimpleHttpRequest request = builder.build();
+        MatcherAssert.assertThat(request.getPath(), CoreMatchers.equalTo("/stuff?p0=p0&p1=v1&p2=v2&p3=v3&p3=v3.1"));
+        Assert.assertNull(request.getBody());
+    }
+
+    @Test
+    public void testPostParameters() throws Exception {
+        final SimpleRequestBuilder builder = SimpleRequestBuilder.post(URI.create("https://host:3456/stuff?p0=p0"));
+        builder.addParameter("p1", "v1");
+        builder.addParameters(new BasicNameValuePair("p2", "v2"), new BasicNameValuePair("p3", "v3"));
+        builder.addParameter(new BasicNameValuePair("p3", "v3.1"));
+        Assert.assertEquals("POST", builder.getMethod());
+        Assert.assertEquals("https", builder.getScheme());
+        Assert.assertEquals(new URIAuthority("host", 3456), builder.getAuthority());
+        Assert.assertEquals("/stuff?p0=p0", builder.getPath());
+        MatcherAssert.assertThat(builder.getParameters(), NameValuePairsMatcher.same(
+                new BasicNameValuePair("p1", "v1"), new BasicNameValuePair("p2", "v2"),
+                new BasicNameValuePair("p3", "v3"), new BasicNameValuePair("p3", "v3.1")));
+        final SimpleHttpRequest request = builder.build();
+        MatcherAssert.assertThat(request.getPath(), CoreMatchers.equalTo("/stuff?p0=p0"));
+        Assert.assertNotNull(request.getBody());
+        MatcherAssert.assertThat(request.getBody().getContentType(),
+                ContentTypeMatcher.sameMimeType(ContentType.APPLICATION_FORM_URLENCODED));
+        MatcherAssert.assertThat(request.getBody().getBodyText(),
+                CoreMatchers.equalTo("p1=v1&p2=v2&p3=v3&p3=v3.1"));
+    }
+
+}