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/12 19:50:29 UTC

[httpcomponents-core] branch master updated: Basic and classic message builders to support copy operation

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


The following commit(s) were added to refs/heads/master by this push:
     new 8101bbf  Basic and classic message builders to support copy operation
8101bbf is described below

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

    Basic and classic message builders to support copy operation
---
 .../http/io/support/ClassicRequestBuilder.java     |  56 ++++++++++--
 .../http/io/support/ClassicResponseBuilder.java    |  15 ++++
 .../http/message/BasicClassicHttpRequest.java      |  19 +++-
 .../hc/core5/http/message/BasicHttpRequest.java    |  18 ++++
 .../http/nio/support/AsyncRequestBuilder.java      |  41 +++++++--
 .../core5/http/support/AbstractMessageBuilder.java |  24 ++++-
 .../core5/http/support/AbstractRequestBuilder.java | 100 +++++++++++++++++++--
 .../hc/core5/http/support/BasicRequestBuilder.java |  42 +++++++--
 .../core5/http/support/BasicResponseBuilder.java   |   8 ++
 .../http/support/TestBasicMessageBuilders.java     |  46 +++++++++-
 10 files changed, 335 insertions(+), 34 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 cd8835b..24873c9 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
@@ -39,8 +39,10 @@ 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.support.AbstractRequestBuilder;
+import org.apache.hc.core5.net.URIAuthority;
 import org.apache.hc.core5.net.URIBuilder;
 import org.apache.hc.core5.util.Args;
+import org.apache.hc.core5.util.TextUtils;
 
 import java.net.URI;
 import java.net.URISyntaxException;
@@ -189,6 +191,21 @@ public class ClassicRequestBuilder extends AbstractRequestBuilder<ClassicHttpReq
         return new ClassicRequestBuilder(Method.OPTIONS, uri);
     }
 
+    /**
+     * @since 5.1
+     */
+    public static ClassicRequestBuilder copy(final ClassicHttpRequest request) {
+        Args.notNull(request, "HTTP request");
+        final ClassicRequestBuilder builder = new ClassicRequestBuilder(request.getMethod());
+        builder.digest(request);
+        return builder;
+    }
+
+    protected void digest(final ClassicHttpRequest request) {
+        super.digest(request);
+        setEntity(request.getEntity());
+    }
+
     @Override
     public ClassicRequestBuilder setVersion(final ProtocolVersion version) {
         super.setVersion(version);
@@ -208,6 +225,24 @@ public class ClassicRequestBuilder extends AbstractRequestBuilder<ClassicHttpReq
     }
 
     @Override
+    public ClassicRequestBuilder setScheme(final String scheme) {
+        super.setScheme(scheme);
+        return this;
+    }
+
+    @Override
+    public ClassicRequestBuilder setAuthority(final URIAuthority authority) {
+        super.setAuthority(authority);
+        return this;
+    }
+
+    @Override
+    public ClassicRequestBuilder setPath(final String path) {
+        super.setPath(path);
+        return this;
+    }
+
+    @Override
     public ClassicRequestBuilder setHeaders(final Header... headers) {
         super.setHeaders(headers);
         return this;
@@ -304,9 +339,9 @@ public class ClassicRequestBuilder extends AbstractRequestBuilder<ClassicHttpReq
     }
 
     public ClassicHttpRequest build() {
-        URI uriCopy = getUri();
-        if (uriCopy == null) {
-            uriCopy = URI.create("/");
+        String path = getPath();
+        if (TextUtils.isEmpty(path)) {
+            path = "/";
         }
         HttpEntity entityCopy = this.entity;
         final String method = getMethod();
@@ -316,10 +351,11 @@ public class ClassicRequestBuilder extends AbstractRequestBuilder<ClassicHttpReq
                 entityCopy = HttpEntities.createUrlEncoded(parameters, getCharset());
             } else {
                 try {
-                    uriCopy = new URIBuilder(uriCopy)
+                    final URI uri = new URIBuilder(path)
                             .setCharset(getCharset())
                             .addParameters(parameters)
                             .build();
+                    path = uri.toASCIIString();
                 } catch (final URISyntaxException ex) {
                     // should never happen
                 }
@@ -330,7 +366,7 @@ public class ClassicRequestBuilder extends AbstractRequestBuilder<ClassicHttpReq
             throw new IllegalStateException(Method.TRACE + " requests may not include an entity");
         }
 
-        final BasicClassicHttpRequest result = new BasicClassicHttpRequest(method, uriCopy);
+        final BasicClassicHttpRequest result = new BasicClassicHttpRequest(method, getScheme(), getAuthority(), path);
         result.setVersion(getVersion());
         result.setHeaders(getHeaders());
         result.setEntity(entityCopy);
@@ -343,10 +379,12 @@ public class ClassicRequestBuilder extends AbstractRequestBuilder<ClassicHttpReq
         final StringBuilder builder = new StringBuilder();
         builder.append("ClassicRequestBuilder [method=");
         builder.append(getMethod());
-        builder.append(", version=");
-        builder.append(getVersion());
-        builder.append(", uri=");
-        builder.append(getUri());
+        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=");
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 ce6d7bf..9dd337a 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
@@ -58,6 +58,21 @@ public class ClassicResponseBuilder extends AbstractResponseBuilder<ClassicHttpR
         return new ClassicResponseBuilder(status);
     }
 
+    /**
+     * @since 5.1
+     */
+    public static ClassicResponseBuilder copy(final ClassicHttpResponse response) {
+        Args.notNull(response, "HTTP response");
+        final ClassicResponseBuilder builder = new ClassicResponseBuilder(response.getCode());
+        builder.digest(response);
+        return builder;
+    }
+
+    protected void digest(final ClassicHttpResponse response) {
+        super.digest(response);
+        setEntity(response.getEntity());
+    }
+
     @Override
     public ClassicResponseBuilder setVersion(final ProtocolVersion version) {
         super.setVersion(version);
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicClassicHttpRequest.java b/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicClassicHttpRequest.java
index 7038c17..deea4fd 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicClassicHttpRequest.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicClassicHttpRequest.java
@@ -27,12 +27,13 @@
 
 package org.apache.hc.core5.http.message;
 
-import java.net.URI;
-
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.HttpHost;
 import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.net.URIAuthority;
+
+import java.net.URI;
 
 /**
  * Basic implementation of {@link ClassicHttpRequest}.
@@ -46,6 +47,20 @@ public class BasicClassicHttpRequest extends BasicHttpRequest implements Classic
     private HttpEntity entity;
 
     /**
+     * Creates request message with the given method, host and request path.
+     *
+     * @param method request method.
+     * @param scheme request scheme.
+     * @param authority request authority.
+     * @param path request path.
+     *
+     * @since 5.1
+     */
+    public BasicClassicHttpRequest(final String method, final String scheme, final URIAuthority authority, final String path) {
+        super(method, scheme, authority, path);
+    }
+
+    /**
      * Creates request message with the given method and request path.
      *
      * @param method request method.
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHttpRequest.java b/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHttpRequest.java
index ed94598..5b1db70 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHttpRequest.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/message/BasicHttpRequest.java
@@ -57,6 +57,24 @@ public class BasicHttpRequest extends HeaderGroup implements HttpRequest {
     private boolean absoluteRequestUri;
 
     /**
+     * Creates request message with the given method, host and request path.
+     *
+     * @param method request method.
+     * @param scheme request scheme.
+     * @param authority request authority.
+     * @param path request path.
+     *
+     * @since 5.1
+     */
+    public BasicHttpRequest(final String method, final String scheme, final URIAuthority authority, final String path) {
+        super();
+        this.method = Args.notNull(method, "Method name");
+        this.scheme = scheme;
+        this.authority = authority;
+        this.path = path;
+    }
+
+    /**
      * Creates request message with the given method and request path.
      *
      * @param method request method.
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 52212b6..3093081 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
@@ -44,9 +44,11 @@ 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.URIAuthority;
 import org.apache.hc.core5.net.URIBuilder;
 import org.apache.hc.core5.net.WWWFormCodec;
 import org.apache.hc.core5.util.Args;
+import org.apache.hc.core5.util.TextUtils;
 
 /**
  * Builder for {@link AsyncRequestProducer} instances.
@@ -208,6 +210,24 @@ public class AsyncRequestBuilder extends AbstractRequestBuilder<AsyncRequestProd
     }
 
     @Override
+    public AsyncRequestBuilder setScheme(final String scheme) {
+        super.setScheme(scheme);
+        return this;
+    }
+
+    @Override
+    public AsyncRequestBuilder setAuthority(final URIAuthority authority) {
+        super.setAuthority(authority);
+        return this;
+    }
+
+    @Override
+    public AsyncRequestBuilder setPath(final String path) {
+        super.setPath(path);
+        return this;
+    }
+
+    @Override
     public AsyncRequestBuilder setHeaders(final Header... headers) {
         super.setHeaders(headers);
         return this;
@@ -304,9 +324,9 @@ public class AsyncRequestBuilder extends AbstractRequestBuilder<AsyncRequestProd
     }
 
     public AsyncRequestProducer build() {
-        URI uriCopy = getUri();
-        if (uriCopy == null) {
-            uriCopy = URI.create("/");
+        String path = getPath();
+        if (TextUtils.isEmpty(path)) {
+            path = "/";
         }
         AsyncEntityProducer entityProducerCopy = entityProducer;
         final String method = getMethod();
@@ -322,10 +342,11 @@ public class AsyncRequestBuilder extends AbstractRequestBuilder<AsyncRequestProd
                         ContentType.APPLICATION_FORM_URLENCODED);
             } else {
                 try {
-                    uriCopy = new URIBuilder(uriCopy)
+                    final URI uri = new URIBuilder(path)
                             .setCharset(charset)
                             .addParameters(parameters)
                             .build();
+                    path = uri.toASCIIString();
                 } catch (final URISyntaxException ex) {
                     // should never happen
                 }
@@ -336,7 +357,7 @@ public class AsyncRequestBuilder extends AbstractRequestBuilder<AsyncRequestProd
             throw new IllegalStateException(Method.TRACE + " requests may not include an entity");
         }
 
-        final BasicHttpRequest request = new BasicHttpRequest(method, uriCopy);
+        final BasicHttpRequest request = new BasicHttpRequest(method, getScheme(), getAuthority(), path);
         request.setVersion(getVersion());
         request.setHeaders(getHeaders());
         request.setAbsoluteRequestUri(isAbsoluteRequestUri());
@@ -348,10 +369,12 @@ public class AsyncRequestBuilder extends AbstractRequestBuilder<AsyncRequestProd
         final StringBuilder builder = new StringBuilder();
         builder.append("AsyncRequestBuilder [method=");
         builder.append(getMethod());
-        builder.append(", version=");
-        builder.append(getVersion());
-        builder.append(", uri=");
-        builder.append(getUri());
+        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=");
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
index 143c30c..006e6b5 100644
--- 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
@@ -27,14 +27,14 @@
 
 package org.apache.hc.core5.http.support;
 
+import java.util.Iterator;
+
 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.
  *
@@ -48,6 +48,14 @@ public abstract class AbstractMessageBuilder<T> {
     protected AbstractMessageBuilder() {
     }
 
+    protected void digest(final HttpMessage message) {
+        if (message == null) {
+            return;
+        }
+        setVersion(message.getVersion());
+        setHeaders(message.headerIterator());
+    }
+
     public ProtocolVersion getVersion() {
         return version;
     }
@@ -73,6 +81,18 @@ public abstract class AbstractMessageBuilder<T> {
         return this;
     }
 
+    public AbstractMessageBuilder<T> setHeaders(final Iterator<Header> it) {
+        if (headerGroup == null) {
+            headerGroup = new HeaderGroup();
+        } else {
+            headerGroup.clear();
+        }
+        while (it.hasNext()) {
+            headerGroup.addHeader(it.next());
+        }
+        return this;
+    }
+
     public Header[] getFirstHeaders() {
         return headerGroup != null ? headerGroup.getHeaders() : null;
     }
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
index 1aeb36e..3c47362 100644
--- 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
@@ -28,13 +28,18 @@
 package org.apache.hc.core5.http.support;
 
 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.URIScheme;
 import org.apache.hc.core5.http.message.BasicHttpRequest;
 import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIAuthority;
+import org.apache.hc.core5.util.TextUtils;
 
 import java.net.URI;
+import java.net.URISyntaxException;
 import java.nio.charset.Charset;
 import java.util.ArrayList;
 import java.util.LinkedList;
@@ -48,7 +53,9 @@ import java.util.List;
 public abstract class AbstractRequestBuilder<T> extends AbstractMessageBuilder<T> {
 
     final private String method;
-    private URI uri;
+    private String scheme;
+    private URIAuthority authority;
+    private String path;
     private Charset charset;
     private List<NameValuePair> parameters;
     private boolean absoluteRequestUri;
@@ -65,7 +72,7 @@ public abstract class AbstractRequestBuilder<T> extends AbstractMessageBuilder<T
     protected AbstractRequestBuilder(final String method, final URI uri) {
         super();
         this.method = method;
-        this.uri = uri;
+        setUri(uri);
     }
 
     protected AbstractRequestBuilder(final Method method, final URI uri) {
@@ -80,6 +87,17 @@ public abstract class AbstractRequestBuilder<T> extends AbstractMessageBuilder<T
         this(method, uri != null ? URI.create(uri) : null);
     }
 
+    protected void digest(final HttpRequest request) {
+        if (request == null) {
+            return;
+        }
+        setScheme(request.getScheme());
+        setAuthority(request.getAuthority());
+        setPath(request.getPath());
+        this.parameters = null;
+        super.digest(request);
+    }
+
     public String getMethod() {
         return method;
     }
@@ -90,17 +108,89 @@ public abstract class AbstractRequestBuilder<T> extends AbstractMessageBuilder<T
         return this;
     }
 
+    public String getScheme() {
+        return scheme;
+    }
+
+    public AbstractRequestBuilder<T> setScheme(final String scheme) {
+        this.scheme = scheme;
+        return this;
+    }
+
+    public URIAuthority getAuthority() {
+        return authority;
+    }
+
+    public AbstractRequestBuilder<T> setAuthority(final URIAuthority authority) {
+        this.authority = authority;
+        return this;
+    }
+
+    public String getPath() {
+        return path;
+    }
+
+    public AbstractRequestBuilder<T> setPath(final String path) {
+        this.path = path;
+        return this;
+    }
+
     public URI getUri() {
-        return uri;
+        final StringBuilder buf = new StringBuilder();
+        if (this.authority != null) {
+            buf.append(this.scheme != null ? this.scheme : URIScheme.HTTP.id).append("://");
+            buf.append(this.authority.getHostName());
+            if (this.authority.getPort() >= 0) {
+                buf.append(":").append(this.authority.getPort());
+            }
+        }
+        if (this.path == null) {
+            buf.append("/");
+        } else {
+            if (buf.length() > 0 && !this.path.startsWith("/")) {
+                buf.append("/");
+            }
+            buf.append(this.path);
+        }
+        return URI.create(buf.toString());
     }
 
     public AbstractRequestBuilder<T> setUri(final URI uri) {
-        this.uri = uri;
+        if (uri == null) {
+            this.scheme = null;
+            this.authority = null;
+            this.path = null;
+        } else {
+            this.scheme = uri.getScheme();
+            if (uri.getHost() != null) {
+                this.authority = new URIAuthority(uri.getRawUserInfo(), uri.getHost(), uri.getPort());
+            } else if (uri.getRawAuthority() != null) {
+                try {
+                    this.authority = URIAuthority.create(uri.getRawAuthority());
+                } catch (final URISyntaxException ignore) {
+                    this.authority = null;
+                }
+            } else {
+                this.authority = null;
+            }
+            final StringBuilder buf = new StringBuilder();
+            final String rawPath = uri.getRawPath();
+            if (!TextUtils.isBlank(rawPath)) {
+                buf.append(rawPath);
+            } else {
+                buf.append("/");
+            }
+            final String query = uri.getRawQuery();
+            if (query != null) {
+                buf.append('?').append(query);
+            }
+            this.path = buf.toString();
+        }
         return this;
     }
 
     public AbstractRequestBuilder<T> setUri(final String uri) {
-        this.uri = uri != null ? URI.create(uri) : null;
+        setUri(uri != null ? URI.create(uri) : null);
         return this;
     }
 
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
index 74d3001..adc94b7 100644
--- 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
@@ -34,10 +34,12 @@ import java.util.Arrays;
 import java.util.List;
 
 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.BasicHttpRequest;
+import org.apache.hc.core5.net.URIAuthority;
 import org.apache.hc.core5.net.URIBuilder;
 import org.apache.hc.core5.util.Args;
 
@@ -173,6 +175,13 @@ public class BasicRequestBuilder extends AbstractRequestBuilder<BasicHttpRequest
         return new BasicRequestBuilder(Method.OPTIONS, uri);
     }
 
+    public static BasicRequestBuilder copy(final HttpRequest request) {
+        Args.notNull(request, "HTTP request");
+        final BasicRequestBuilder builder = new BasicRequestBuilder(request.getMethod());
+        builder.digest(request);
+        return builder;
+    }
+
     @Override
     public BasicRequestBuilder setVersion(final ProtocolVersion version) {
         super.setVersion(version);
@@ -192,6 +201,24 @@ public class BasicRequestBuilder extends AbstractRequestBuilder<BasicHttpRequest
     }
 
     @Override
+    public BasicRequestBuilder setScheme(final String scheme) {
+        super.setScheme(scheme);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder setAuthority(final URIAuthority authority) {
+        super.setAuthority(authority);
+        return this;
+    }
+
+    @Override
+    public BasicRequestBuilder setPath(final String path) {
+        super.setPath(path);
+        return this;
+    }
+
+    @Override
     public BasicRequestBuilder setHeaders(final Header... headers) {
         super.setHeaders(headers);
         return this;
@@ -265,19 +292,20 @@ public class BasicRequestBuilder extends AbstractRequestBuilder<BasicHttpRequest
 
     @Override
     public BasicHttpRequest build() {
-        URI uri = getUri();
+        String path = getPath();
         final List<NameValuePair> parameters = getParameters();
         if (parameters != null && !parameters.isEmpty()) {
             try {
-                uri = new URIBuilder(uri)
+                final URI uri = new URIBuilder(path)
                         .setCharset(getCharset())
                         .addParameters(parameters)
                         .build();
+                path = uri.toASCIIString();
             } catch (final URISyntaxException ex) {
                 // should never happen
             }
         }
-        final BasicHttpRequest result = new BasicHttpRequest(getMethod(), uri != null ? uri : URI.create("/"));
+        final BasicHttpRequest result = new BasicHttpRequest(getMethod(), getScheme(), getAuthority(), path);
         result.setVersion(getVersion());
         result.setHeaders(getHeaders());
         result.setAbsoluteRequestUri(isAbsoluteRequestUri());
@@ -289,8 +317,12 @@ public class BasicRequestBuilder extends AbstractRequestBuilder<BasicHttpRequest
         final StringBuilder builder = new StringBuilder();
         builder.append("BasicRequestBuilder [method=");
         builder.append(getMethod());
-        builder.append(", uri=");
-        builder.append(getUri());
+        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=");
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
index de6baa3..39ebf65 100644
--- 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
@@ -28,6 +28,7 @@
 package org.apache.hc.core5.http.support;
 
 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.BasicHttpResponse;
 import org.apache.hc.core5.util.Args;
@@ -50,6 +51,13 @@ public class BasicResponseBuilder extends AbstractResponseBuilder<BasicHttpRespo
         return new BasicResponseBuilder(status);
     }
 
+    public static BasicResponseBuilder copy(final HttpResponse response) {
+        Args.notNull(response, "HTTP response");
+        final BasicResponseBuilder builder = new BasicResponseBuilder(response.getCode());
+        builder.digest(response);
+        return builder;
+    }
+
     @Override
     public BasicResponseBuilder setVersion(final ProtocolVersion version) {
         super.setVersion(version);
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
index b4f7923..152ad6e 100644
--- 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
@@ -29,7 +29,10 @@ 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.HttpRequest;
+import org.apache.hc.core5.http.HttpResponse;
 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.BasicHttpRequest;
 import org.apache.hc.core5.http.message.BasicHttpResponse;
@@ -109,8 +112,11 @@ public class TestBasicMessageBuilders {
     @Test
     public void testRequestBasics() throws Exception {
         final BasicRequestBuilder builder = BasicRequestBuilder.get();
-        Assert.assertNull(builder.getUri());
+        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());
@@ -121,12 +127,15 @@ public class TestBasicMessageBuilders {
         Assert.assertEquals("GET", r1.getMethod());
         Assert.assertNull(r1.getScheme());
         Assert.assertNull(r1.getAuthority());
-        Assert.assertEquals("/", r1.getPath());
+        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());
 
@@ -194,4 +203,37 @@ public class TestBasicMessageBuilders {
         MatcherAssert.assertThat(r6.getHeaders(), HeadersMatcher.same(new BasicHeader("h2", "v2")));
     }
 
+    @Test
+    public void testResponseCopy() throws Exception {
+        final HttpResponse response = new BasicHttpResponse(400);
+        response.addHeader("h1", "v1");
+        response.addHeader("h1", "v2");
+        response.addHeader("h2", "v2");
+        response.setVersion(HttpVersion.HTTP_2);
+
+        final BasicResponseBuilder builder = BasicResponseBuilder.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 HttpRequest request = new BasicHttpRequest(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 BasicRequestBuilder builder = BasicRequestBuilder.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")));
+    }
+
 }