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 2019/02/13 16:02:19 UTC

[httpcomponents-core] branch immutable-httpentities created (now 9153a9c)

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

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


      at 9153a9c  Added HttpEntityTemplate

This branch includes the following new commits:

     new e056a12  Made standard HttpEntity implementations immutable
     new fb8c67f  Added abstract I/O callback
     new 9153a9c  Added HttpEntityTemplate

The 3 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[httpcomponents-core] 03/03: Added HttpEntityTemplate

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit 9153a9ca6550a1b054138790f2c95490bb74e252
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Wed Feb 13 17:01:43 2019 +0100

    Added HttpEntityTemplate
---
 .../core5/http/io/entity/HttpContentProducer.java  |  44 ---------
 .../core5/http/io/entity/HttpEntityTemplate.java   | 101 +++++++++++++++++++++
 2 files changed, 101 insertions(+), 44 deletions(-)

diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpContentProducer.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpContentProducer.java
deleted file mode 100644
index 742426b..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpContentProducer.java
+++ /dev/null
@@ -1,44 +0,0 @@
-/*
- * ====================================================================
- * 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.io.entity;
-
-import java.io.IOException;
-import java.io.OutputStream;
-
-/**
- * An abstract entity content producer.
- *<p>Content producers are expected to be able to produce their
- * content multiple times</p>
- *
- * @since 5.0
- */
-public interface HttpContentProducer {
-
-    void writeTo(OutputStream outStream) throws IOException;
-
-}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityTemplate.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityTemplate.java
new file mode 100644
index 0000000..db4c94b
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityTemplate.java
@@ -0,0 +1,101 @@
+/*
+ * ====================================================================
+ * 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.io.entity;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.function.IOCallback;
+import org.apache.hc.core5.http.ContentType;
+
+/**
+ * A generic non-repeatable entity template that delegates its content generation to {@link IOCallback}.
+ *
+ * @since 5.0
+ */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
+public final class HttpEntityTemplate extends AbstractHttpEntity {
+
+    private final long contentLength;
+    private final IOCallback<OutputStream> contentCallback;
+
+    public HttpEntityTemplate(
+            final long contentLength,
+            final ContentType contentType,
+            final String contentEncoding,
+            final IOCallback<OutputStream> contentCallback) {
+        super(contentType, contentEncoding, false);
+        this.contentLength = contentLength;
+        this.contentCallback = contentCallback;
+    }
+
+    public HttpEntityTemplate(
+            final long contentLength,
+            final ContentType contentType,
+            final IOCallback<OutputStream> contentCallback) {
+        this(contentLength, contentType, null, contentCallback);
+    }
+
+    public HttpEntityTemplate(final ContentType contentType, final IOCallback<OutputStream> contentCallback) {
+        this(-1, contentType, null, contentCallback);
+    }
+
+    @Override
+    public long getContentLength() {
+        return contentLength;
+    }
+
+    @Override
+    public InputStream getContent() throws UnsupportedOperationException {
+        throw new UnsupportedOperationException();
+    }
+
+    @Override
+    public void writeTo(final OutputStream outStream) throws IOException {
+        contentCallback.execute(outStream);
+        outStream.flush();
+    }
+
+    @Override
+    public final boolean isRepeatable() {
+        return false;
+    }
+
+    @Override
+    public final boolean isStreaming() {
+        return false;
+    }
+
+    @Override
+    public final void close() throws IOException {
+    }
+
+}


[httpcomponents-core] 01/03: Made standard HttpEntity implementations immutable

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit e056a12141e136317029f682a6313b5128cb4ad2
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Sat Feb 9 18:14:32 2019 +0100

    Made standard HttpEntity implementations immutable
---
 .../testing/classic/ClassicIntegrationTest.java    | 116 ++++-----
 .../hc/core5/testing/classic/EchoHandler.java      |  17 +-
 .../hc/core5/http/impl/io/IncomingHttpEntity.java  |  35 ++-
 .../core5/http/io/entity/AbstractHttpEntity.java   |  94 +++++--
 .../io/entity/AbstractImmutableHttpEntity.java     |  82 -------
 .../hc/core5/http/io/entity/BasicHttpEntity.java   |  95 ++++---
 .../hc/core5/http/io/entity/ByteArrayEntity.java   |  89 ++++---
 .../hc/core5/http/io/entity/ByteBufferEntity.java  |  74 +++---
 .../hc/core5/http/io/entity/EntityUtils.java       |  97 +-------
 .../apache/hc/core5/http/io/entity/FileEntity.java |  44 +---
 .../http/io/entity/HttpEntityWithTrailers.java     |   5 +-
 .../hc/core5/http/io/entity/HttpEntityWrapper.java |   5 +-
 .../hc/core5/http/io/entity/InputStreamEntity.java |  71 ++----
 .../core5/http/io/entity/SerializableEntity.java   |  50 ++--
 .../hc/core5/http/io/entity/StringEntity.java      |  51 ++--
 .../impl/io/TestDefaultBHttpClientConnection.java  |  24 +-
 .../hc/core5/http/impl/io/TestHttpService.java     |   4 +-
 .../core5/http/io/entity/TestBasicHttpEntity.java  |  63 +----
 .../http/io/entity/TestBufferedHttpEntity.java     |  23 +-
 .../core5/http/io/entity/TestByteArrayEntity.java  |  44 ++--
 .../core5/http/io/entity/TestByteBufferEntity.java |   6 +-
 .../hc/core5/http/io/entity/TestEntityUtils.java   | 273 +++++++--------------
 .../http/io/entity/TestHttpEntityWrapper.java      |  26 +-
 .../http/io/entity/TestInputStreamEntity.java      |  42 ++--
 .../http/io/entity/TestSerializableEntity.java     |  10 +-
 .../http/protocol/TestStandardInterceptors.java    | 103 +++-----
 26 files changed, 587 insertions(+), 956 deletions(-)

diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java
index aa80ebc..902c3d2 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/ClassicIntegrationTest.java
@@ -53,9 +53,9 @@ import org.apache.hc.core5.http.HttpRequest;
 import org.apache.hc.core5.http.HttpRequestInterceptor;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.HttpVersion;
-import org.apache.hc.core5.http.io.SocketConfig;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
 import org.apache.hc.core5.http.io.HttpServerRequestHandler;
+import org.apache.hc.core5.http.io.SocketConfig;
 import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
 import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
 import org.apache.hc.core5.http.io.entity.EntityUtils;
@@ -161,7 +161,7 @@ public class ClassicIntegrationTest {
                 }
                 final int index = Integer.parseInt(s);
                 final byte[] data = testData.get(index);
-                final ByteArrayEntity entity = new ByteArrayEntity(data);
+                final ByteArrayEntity entity = new ByteArrayEntity(data, null);
                 response.setEntity(entity);
             }
 
@@ -216,13 +216,10 @@ public class ClassicIntegrationTest {
                     final ClassicHttpResponse response,
                     final HttpContext context) throws HttpException, IOException {
 
-                final HttpEntity incoming = request.getEntity();
-                if (incoming != null) {
-                    final byte[] data = EntityUtils.toByteArray(incoming);
-
-                    final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-                    outgoing.setChunked(false);
-                    response.setEntity(outgoing);
+                final HttpEntity entity = request.getEntity();
+                if (entity != null) {
+                    final byte[] data = EntityUtils.toByteArray(entity);
+                    response.setEntity(new ByteArrayEntity(data, null));
                 }
             }
 
@@ -237,8 +234,7 @@ public class ClassicIntegrationTest {
         for (int r = 0; r < reqNo; r++) {
             final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
             final byte[] data = testData.get(r);
-            final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-            post.setEntity(outgoing);
+            post.setEntity(new ByteArrayEntity(data, null));
 
             try (final ClassicHttpResponse response = this.client.execute(host, post, context)) {
                 final byte[] received = EntityUtils.toByteArray(response.getEntity());
@@ -284,9 +280,7 @@ public class ClassicIntegrationTest {
                 final HttpEntity entity = request.getEntity();
                 if (entity != null) {
                     final byte[] data = EntityUtils.toByteArray(entity);
-                    final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-                    outgoing.setChunked(true);
-                    response.setEntity(outgoing);
+                    response.setEntity(new ByteArrayEntity(data, null, true));
                 }
             }
 
@@ -301,9 +295,7 @@ public class ClassicIntegrationTest {
         for (int r = 0; r < reqNo; r++) {
             final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
             final byte[] data = testData.get(r);
-            final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-            outgoing.setChunked(true);
-            post.setEntity(outgoing);
+            post.setEntity(new ByteArrayEntity(data, null, true));
 
             try (final ClassicHttpResponse response = this.client.execute(host, post, context)) {
                 final byte[] received = EntityUtils.toByteArray(response.getEntity());
@@ -345,12 +337,10 @@ public class ClassicIntegrationTest {
                     final ClassicHttpResponse response,
                     final HttpContext context) throws HttpException, IOException {
 
-                final HttpEntity incoming = request.getEntity();
-                if (incoming != null) {
-                    final byte[] data = EntityUtils.toByteArray(incoming);
-                    final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-                    outgoing.setChunked(false);
-                    response.setEntity(outgoing);
+                final HttpEntity entity = request.getEntity();
+                if (entity != null) {
+                    final byte[] data = EntityUtils.toByteArray(entity);
+                    response.setEntity(new ByteArrayEntity(data, null));
                 }
                 if (HttpVersion.HTTP_1_0.equals(request.getVersion())) {
                     response.addHeader("Version", "1.0");
@@ -370,8 +360,7 @@ public class ClassicIntegrationTest {
             final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
             post.setVersion(HttpVersion.HTTP_1_0);
             final byte[] data = testData.get(r);
-            final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-            post.setEntity(outgoing);
+            post.setEntity(new ByteArrayEntity(data, null));
 
             try (final ClassicHttpResponse response = this.client.execute(host, post, context)) {
                 Assert.assertEquals(HttpVersion.HTTP_1_1, response.getVersion());
@@ -418,13 +407,10 @@ public class ClassicIntegrationTest {
                     final ClassicHttpResponse response,
                     final HttpContext context) throws HttpException, IOException {
 
-                final HttpEntity incoming = request.getEntity();
-                if (incoming != null) {
-                    final byte[] data = EntityUtils.toByteArray(incoming);
-
-                    final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-                    outgoing.setChunked(true);
-                    response.setEntity(outgoing);
+                final HttpEntity entity = request.getEntity();
+                if (entity != null) {
+                    final byte[] data = EntityUtils.toByteArray(entity);
+                    response.setEntity(new ByteArrayEntity(data, null, true));
                 }
             }
 
@@ -439,9 +425,7 @@ public class ClassicIntegrationTest {
         for (int r = 0; r < reqNo; r++) {
             final BasicClassicHttpRequest post = new BasicClassicHttpRequest("POST", "/");
             final byte[] data = testData.get(r);
-            final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-            outgoing.setChunked(true);
-            post.setEntity(outgoing);
+            post.setEntity(new ByteArrayEntity(data, null, true));
 
             try (final ClassicHttpResponse response = this.client.execute(host, post, context)) {
                 final byte[] received = EntityUtils.toByteArray(response.getEntity());
@@ -473,8 +457,7 @@ public class ClassicIntegrationTest {
                     final ClassicHttpResponse response,
                     final HttpContext context) throws HttpException, IOException {
 
-                final StringEntity outgoing = new StringEntity("No content");
-                response.setEntity(outgoing);
+                response.setEntity(new StringEntity("No content"));
             }
 
         });
@@ -523,9 +506,7 @@ public class ClassicIntegrationTest {
             for (int i = 0; i < b.length; i++) {
                 b[i] = (byte) ('a' + r);
             }
-            final ByteArrayEntity requestEntity = new ByteArrayEntity(b, ContentType.TEXT_PLAIN);
-            requestEntity.setChunked(false);
-            post.setEntity(requestEntity);
+            post.setEntity(new ByteArrayEntity(b, ContentType.TEXT_PLAIN));
 
             try (final ClassicHttpResponse response = this.client.execute(host, post, context)) {
                 final HttpEntity responseEntity = response.getEntity();
@@ -546,8 +527,8 @@ public class ClassicIntegrationTest {
         private final byte[] raw;
         private final int n;
 
-        public RepeatingEntity(final String content, final Charset charset, final int n) {
-            super();
+        public RepeatingEntity(final String content, final Charset charset, final int n, final boolean chunked) {
+            super(ContentType.TEXT_PLAIN.withCharset(charset), null, chunked);
             final Charset cs = charset != null ? charset : Charset.forName("US-ASCII");
             this.raw = content.getBytes(cs);
             this.n = n;
@@ -637,17 +618,15 @@ public class ClassicIntegrationTest {
                     }
                 }
 
-                final HttpEntity incoming = request.getEntity();
-                if (incoming != null) {
-                    final String line = EntityUtils.toString(incoming);
-                    final ContentType contentType = EntityUtils.getContentTypeOrDefault(incoming);
+                final HttpEntity entity = request.getEntity();
+                if (entity != null) {
+                    final String line = EntityUtils.toString(entity);
+                    final ContentType contentType = ContentType.parse(entity.getContentType());
                     Charset charset = contentType.getCharset();
                     if (charset == null) {
                         charset = StandardCharsets.ISO_8859_1;
                     }
-                    final RepeatingEntity outgoing = new RepeatingEntity(line, charset, n);
-                    outgoing.setChunked(n % 2 == 0);
-                    response.setEntity(outgoing);
+                    response.setEntity(new RepeatingEntity(line, charset, n, n % 2 == 0));
                 }
             }
 
@@ -663,15 +642,13 @@ public class ClassicIntegrationTest {
             for (int n = 1000; n < 1020; n++) {
                 final BasicClassicHttpRequest post = new BasicClassicHttpRequest(
                         "POST", "/?n=" + n);
-                final StringEntity outgoing = new StringEntity(pattern);
-                outgoing.setChunked(n % 2 == 0);
-                post.setEntity(outgoing);
+                post.setEntity(new StringEntity(pattern, ContentType.TEXT_PLAIN, n % 2 == 0));
 
                 try (final ClassicHttpResponse response = this.client.execute(host, post, context)) {
-                    final HttpEntity incoming = response.getEntity();
-                    Assert.assertNotNull(incoming);
-                    final InputStream inStream = incoming.getContent();
-                    final ContentType contentType = EntityUtils.getContentTypeOrDefault(incoming);
+                    final HttpEntity entity = response.getEntity();
+                    Assert.assertNotNull(entity);
+                    final InputStream inStream = entity.getContent();
+                    final ContentType contentType = ContentType.parse(entity.getContentType());
                     Charset charset = contentType.getCharset();
                     if (charset == null) {
                         charset = StandardCharsets.ISO_8859_1;
@@ -701,11 +678,10 @@ public class ClassicIntegrationTest {
                     final ClassicHttpResponse response,
                     final HttpContext context) throws HttpException, IOException {
 
-                final HttpEntity incoming = request.getEntity();
-                if (incoming != null) {
-                    final byte[] data = EntityUtils.toByteArray(incoming);
-                    final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-                    response.setEntity(outgoing);
+                final HttpEntity entity = request.getEntity();
+                if (entity != null) {
+                    final byte[] data = EntityUtils.toByteArray(entity);
+                    response.setEntity(new ByteArrayEntity(data, null));
                 }
             }
 
@@ -737,11 +713,10 @@ public class ClassicIntegrationTest {
                     final ClassicHttpResponse response,
                     final HttpContext context) throws HttpException, IOException {
 
-                final HttpEntity incoming = request.getEntity();
-                if (incoming != null) {
-                    final byte[] data = EntityUtils.toByteArray(incoming);
-                    final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-                    response.setEntity(outgoing);
+                final HttpEntity entity = request.getEntity();
+                if (entity != null) {
+                    final byte[] data = EntityUtils.toByteArray(entity);
+                    response.setEntity(new ByteArrayEntity(data, null));
                 }
             }
 
@@ -777,11 +752,10 @@ public class ClassicIntegrationTest {
                     final ClassicHttpResponse response,
                     final HttpContext context) throws HttpException, IOException {
 
-                final HttpEntity incoming = request.getEntity();
-                if (incoming != null) {
-                    final byte[] data = EntityUtils.toByteArray(incoming);
-                    final ByteArrayEntity outgoing = new ByteArrayEntity(data);
-                    response.setEntity(outgoing);
+                final HttpEntity entity = request.getEntity();
+                if (entity != null) {
+                    final byte[] data = EntityUtils.toByteArray(entity);
+                    response.setEntity(new ByteArrayEntity(data, null));
                 }
             }
 
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/EchoHandler.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/EchoHandler.java
index 01891e2..85b49a1 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/EchoHandler.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/classic/EchoHandler.java
@@ -31,6 +31,7 @@ import java.io.IOException;
 
 import org.apache.hc.core5.http.ClassicHttpRequest;
 import org.apache.hc.core5.http.ClassicHttpResponse;
+import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpStatus;
@@ -47,23 +48,13 @@ public class EchoHandler implements HttpRequestHandler {
             final ClassicHttpResponse response,
             final HttpContext context) throws HttpException, IOException {
 
+        response.setCode(HttpStatus.SC_OK);
         final HttpEntity entity = request.getEntity();
-        // For some reason, just putting the incoming entity into
-        // the response will not work. We have to buffer the message.
         final byte[] data;
-        if (entity == null) {
-            data = new byte[0];
-        } else {
-            data = EntityUtils.toByteArray(entity);
-        }
-
-        final ByteArrayEntity bae = new ByteArrayEntity(data);
         if (entity != null) {
-            bae.setContentType(entity.getContentType());
+            data = EntityUtils.toByteArray(entity);
+            response.setEntity(new ByteArrayEntity(data, ContentType.parse(entity.getContentType())));
         }
-
-        response.setCode(HttpStatus.SC_OK);
-        response.setEntity(bae);
     }
 
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/IncomingHttpEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/IncomingHttpEntity.java
index 8da8065..e831325 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/IncomingHttpEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/IncomingHttpEntity.java
@@ -29,16 +29,18 @@ package org.apache.hc.core5.http.impl.io;
 
 import java.io.IOException;
 import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
-import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.function.Supplier;
-import org.apache.hc.core5.http.io.entity.AbstractImmutableHttpEntity;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.io.entity.AbstractHttpEntity;
 import org.apache.hc.core5.io.Closer;
 
-class IncomingHttpEntity extends AbstractImmutableHttpEntity {
+class IncomingHttpEntity implements HttpEntity {
 
     private final InputStream content;
     private final long len;
@@ -90,6 +92,11 @@ class IncomingHttpEntity extends AbstractImmutableHttpEntity {
     }
 
     @Override
+    public void writeTo(final OutputStream outStream) throws IOException {
+        AbstractHttpEntity.writeTo(this, outStream);
+    }
+
+    @Override
     public Supplier<List<? extends Header>> getTrailers() {
         return null;
     }
@@ -104,4 +111,26 @@ class IncomingHttpEntity extends AbstractImmutableHttpEntity {
         Closer.close(content);
     }
 
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append('[');
+        sb.append("Content-Type: ");
+        sb.append(getContentType());
+        sb.append(',');
+        sb.append("Content-Encoding: ");
+        sb.append(getContentEncoding());
+        sb.append(',');
+        final long len = getContentLength();
+        if (len >= 0) {
+            sb.append("Content-Length: ");
+            sb.append(len);
+            sb.append(',');
+        }
+        sb.append("Chunked: ");
+        sb.append(isChunked());
+        sb.append(']');
+        return sb.toString();
+    }
+
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java
index 9c56b35..c757e28 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractHttpEntity.java
@@ -27,12 +27,18 @@
 
 package org.apache.hc.core5.http.io.entity;
 
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
 import java.util.Collections;
 import java.util.List;
 import java.util.Set;
 
-import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.function.Supplier;
+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.util.Args;
 
 /**
  * Abstract base class for mutable entities. Provides the commonly used attributes for streamed and
@@ -40,44 +46,71 @@ import org.apache.hc.core5.function.Supplier;
  *
  * @since 4.0
  */
-public abstract class AbstractHttpEntity extends AbstractImmutableHttpEntity {
+public abstract class AbstractHttpEntity implements HttpEntity {
 
-    /**
-     * Buffer size for output stream processing.
-     *
-     * @since 4.3
-     */
     static final int OUTPUT_BUFFER_SIZE = 4096;
 
-    private String contentType;
-    private String contentEncoding;
-    private boolean chunked;
+    private final String contentType;
+    private final String contentEncoding;
+    private final boolean chunked;
 
-    @Override
-    public String getContentType() {
-        return this.contentType;
+    protected AbstractHttpEntity(final String contentType, final String contentEncoding, final boolean chunked) {
+        this.contentType = contentType;
+        this.contentEncoding = contentEncoding;
+        this.chunked = chunked;
+    }
+
+    protected AbstractHttpEntity(final ContentType contentType, final String contentEncoding, final boolean chunked) {
+        this.contentType = contentType != null ? contentType.toString() : null;
+        this.contentEncoding = contentEncoding;
+        this.chunked = chunked;
+    }
+
+    protected AbstractHttpEntity(final String contentType, final String contentEncoding) {
+        this(contentType, contentEncoding, false);
+    }
+
+    protected AbstractHttpEntity(final ContentType contentType, final String contentEncoding) {
+        this(contentType, contentEncoding, false);
+    }
+
+    public static void writeTo(final HttpEntity entity, final OutputStream outStream) throws IOException {
+        Args.notNull(entity, "Entity");
+        Args.notNull(outStream, "Output stream");
+        try (final InputStream inStream = entity.getContent()) {
+            if (inStream != null) {
+                int count;
+                final byte[] tmp = new byte[OUTPUT_BUFFER_SIZE];
+                while ((count = inStream.read(tmp)) != -1) {
+                    outStream.write(tmp, 0, count);
+                }
+            }
+        }
     }
 
     @Override
-    public String getContentEncoding() {
-        return this.contentEncoding;
+    public void writeTo(final OutputStream outStream) throws IOException {
+        writeTo(this, outStream);
     }
 
     @Override
-    public boolean isChunked() {
-        return this.chunked;
+    public final String getContentType() {
+        return contentType;
     }
 
-    public void setContentType(final String contentType) {
-        this.contentType = contentType;
+    @Override
+    public final String getContentEncoding() {
+        return contentEncoding;
     }
 
-    public void setContentEncoding(final String contentEncoding) {
-        this.contentEncoding = contentEncoding;
+    @Override
+    public final boolean isChunked() {
+        return chunked;
     }
 
-    public void setChunked(final boolean b) {
-        this.chunked = b;
+    @Override
+    public boolean isRepeatable() {
+        return false;
     }
 
     @Override
@@ -90,4 +123,19 @@ public abstract class AbstractHttpEntity extends AbstractImmutableHttpEntity {
         return Collections.emptySet();
     }
 
+    @Override
+    public String toString() {
+        final StringBuilder sb = new StringBuilder();
+        sb.append("[Entity-Class: ");
+        sb.append(getClass().getSimpleName());
+        sb.append(", Content-Type: ");
+        sb.append(contentType);
+        sb.append(", Content-Encoding: ");
+        sb.append(contentEncoding);
+        sb.append(", chunked: ");
+        sb.append(chunked);
+        sb.append(']');
+        return sb.toString();
+    }
+
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractImmutableHttpEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractImmutableHttpEntity.java
deleted file mode 100644
index 9761782..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/AbstractImmutableHttpEntity.java
+++ /dev/null
@@ -1,82 +0,0 @@
-/*
- * ====================================================================
- * 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.io.entity;
-
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.util.Args;
-
-/**
- * Abstract base class for immutable entities.
- *
- * @since 5.0
- */
-public abstract class AbstractImmutableHttpEntity implements HttpEntity {
-
-    static final int OUTPUT_BUFFER_SIZE = 4096;
-
-    @Override
-    public void writeTo(final OutputStream outStream) throws IOException {
-        Args.notNull(outStream, "Output stream");
-        try (final InputStream inStream = getContent()) {
-            if (inStream != null) {
-                int count;
-                final byte[] tmp = new byte[OUTPUT_BUFFER_SIZE];
-                while ((count = inStream.read(tmp)) != -1) {
-                    outStream.write(tmp, 0, count);
-                }
-            }
-        }
-    }
-
-    @Override
-    public String toString() {
-        final StringBuilder sb = new StringBuilder();
-        sb.append('[');
-        sb.append("Content-Type: ");
-        sb.append(getContentType());
-        sb.append(',');
-        sb.append("Content-Encoding: ");
-        sb.append(getContentEncoding());
-        sb.append(',');
-        final long len = getContentLength();
-        if (len >= 0) {
-            sb.append("Content-Length: ");
-            sb.append(len);
-            sb.append(',');
-        }
-        sb.append("Chunked: ");
-        sb.append(isChunked());
-        sb.append(']');
-        return sb.toString();
-    }
-
-}
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/BasicHttpEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/BasicHttpEntity.java
index c7ccec1..1d9407e 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/BasicHttpEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/BasicHttpEntity.java
@@ -30,88 +30,75 @@ package org.apache.hc.core5.http.io.entity;
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.impl.io.EmptyInputStream;
 import org.apache.hc.core5.io.Closer;
-import org.apache.hc.core5.util.Asserts;
+import org.apache.hc.core5.util.Args;
 
 /**
- * A generic streamed, non-repeatable entity that obtains its content
- * from an {@link InputStream}.
+ * A generic streamed, non-repeatable entity that obtains its content from an {@link InputStream}.
  *
  * @since 4.0
  */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 public class BasicHttpEntity extends AbstractHttpEntity {
 
-    private InputStream content;
-    private long length;
-
-    /**
-     * Creates a new basic entity.
-     * The content is initially missing, the content length
-     * is set to a negative number.
-     */
-    public BasicHttpEntity() {
-        super();
-        this.length = -1;
+    private final InputStream content;
+    private final long length;
+
+    public BasicHttpEntity(
+            final InputStream content, final long length, final ContentType contentType, final String contentEncoding,
+            final boolean chunked) {
+        super(contentType, contentEncoding, chunked);
+        this.content = Args.notNull(content, "Content stream");
+        this.length = length;
+    }
+
+    public BasicHttpEntity(
+            final InputStream content, final long length, final ContentType contentType, final String contentEncoding) {
+        this(content, length, contentType, contentEncoding, false);
+    }
+
+    public BasicHttpEntity(final InputStream content, final long length, final ContentType contentType) {
+        this(content, length, contentType, null);
+    }
+
+    public BasicHttpEntity(final InputStream content, final ContentType contentType, final String contentEncoding) {
+        this(content, -1, contentType, contentEncoding);
+    }
+
+    public BasicHttpEntity(final InputStream content, final ContentType contentType) {
+        this(content, -1, contentType, null);
+    }
+
+    public BasicHttpEntity(final InputStream content, final ContentType contentType, final boolean chunked) {
+        this(content, -1, contentType, null, chunked);
     }
 
     @Override
-    public long getContentLength() {
+    public final long getContentLength() {
         return this.length;
     }
 
-    /**
-     * Obtains the content, once only.
-     *
-     * @return  the content, if this is the first call to this method
-     *          since {@link #setContent setContent} has been called
-     *
-     * @throws IllegalStateException
-     *          if the content has not been provided
-     */
     @Override
-    public InputStream getContent() throws IllegalStateException {
-        Asserts.check(this.content != null, "Content has not been provided");
+    public final InputStream getContent() throws IllegalStateException {
         return this.content;
     }
 
-    /**
-     * Tells that this entity is not repeatable.
-     *
-     * @return {@code false}
-     */
     @Override
-    public boolean isRepeatable() {
+    public final boolean isRepeatable() {
         return false;
     }
 
-    /**
-     * Specifies the length of the content.
-     *
-     * @param len       the number of bytes in the content, or
-     *                  a negative number to indicate an unknown length
-     */
-    public void setContentLength(final long len) {
-        this.length = len;
-    }
-
-    /**
-     * Specifies the content.
-     *
-     * @param inStream          the stream to return with the next call to
-     *                          {@link #getContent getContent}
-     */
-    public void setContent(final InputStream inStream) {
-        this.content = inStream;
-    }
-
     @Override
-    public boolean isStreaming() {
+    public final boolean isStreaming() {
         return this.content != null && this.content != EmptyInputStream.INSTANCE;
     }
 
     @Override
-    public void close() throws IOException {
+    public final void close() throws IOException {
         Closer.close(content);
     }
 
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteArrayEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteArrayEntity.java
index cd7c78f..331fb29 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteArrayEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteArrayEntity.java
@@ -32,6 +32,8 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.util.Args;
 
@@ -40,30 +42,19 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 4.0
  */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 public class ByteArrayEntity extends AbstractHttpEntity {
 
     private final byte[] b;
     private final int off, len;
 
     /**
-     * @since 4.2
+     * @since 5.0
      */
-    public ByteArrayEntity(final byte[] b, final ContentType contentType) {
-        super();
-        Args.notNull(b, "Source byte array");
-        this.b = b;
-        this.off = 0;
-        this.len = this.b.length;
-        if (contentType != null) {
-            setContentType(contentType.toString());
-        }
-    }
-
-    /**
-     * @since 4.2
-     */
-    public ByteArrayEntity(final byte[] b, final int off, final int len, final ContentType contentType) {
-        super();
+    public ByteArrayEntity(
+            final byte[] b, final int off, final int len, final ContentType contentType, final String contentEncoding,
+            final boolean chunked) {
+        super(contentType, contentEncoding, chunked);
         Args.notNull(b, "Source byte array");
         if ((off < 0) || (off > b.length) || (len < 0) ||
                 ((off + len) < 0) || ((off + len) > b.length)) {
@@ -72,53 +63,81 @@ public class ByteArrayEntity extends AbstractHttpEntity {
         this.b = b;
         this.off = off;
         this.len = len;
-        if (contentType != null) {
-            setContentType(contentType.toString());
-        }
     }
 
-    public ByteArrayEntity(final byte[] b) {
-        this(b, null);
+    /**
+     * @since 5.0
+     */
+    public ByteArrayEntity(
+            final byte[] b, final int off, final int len, final ContentType contentType, final String contentEncoding) {
+        this(b, off, len, contentType, contentEncoding, false);
+    }
+
+    /**
+     * @since 5.0
+     */
+    public ByteArrayEntity(
+            final byte[] b, final ContentType contentType, final String contentEncoding, final boolean chunked) {
+        super(contentType, contentEncoding, chunked);
+        Args.notNull(b, "Source byte array");
+        this.b = b;
+        this.off = 0;
+        this.len = this.b.length;
+    }
+
+    /**
+     * @since 5.0
+     */
+    public ByteArrayEntity(final byte[] b, final ContentType contentType, final String contentEncoding) {
+        this(b, contentType, contentEncoding, false);
+    }
+
+    public ByteArrayEntity(final byte[] b, final ContentType contentType, final boolean chunked) {
+        this(b, contentType, null, chunked);
     }
 
-    public ByteArrayEntity(final byte[] b, final int off, final int len) {
-        this(b, off, len, null);
+    public ByteArrayEntity(final byte[] b, final ContentType contentType) {
+        this(b, contentType, null, false);
+    }
+
+    public ByteArrayEntity(
+            final byte[] b, final int off, final int len, final ContentType contentType,  final boolean chunked) {
+        this(b, off, len, contentType, null, chunked);
+    }
+
+    public ByteArrayEntity(final byte[] b, final int off, final int len, final ContentType contentType) {
+        this(b, off, len, contentType, null, false);
     }
 
     @Override
-    public boolean isRepeatable() {
+    public final boolean isRepeatable() {
         return true;
     }
 
     @Override
-    public long getContentLength() {
+    public final long getContentLength() {
         return this.len;
     }
 
     @Override
-    public InputStream getContent() {
+    public final InputStream getContent() {
         return new ByteArrayInputStream(this.b, this.off, this.len);
     }
 
     @Override
-    public void writeTo(final OutputStream outStream) throws IOException {
+    public final void writeTo(final OutputStream outStream) throws IOException {
         Args.notNull(outStream, "Output stream");
         outStream.write(this.b, this.off, this.len);
         outStream.flush();
     }
 
-    /**
-     * Tells that this entity is not streaming.
-     *
-     * @return {@code false}
-     */
     @Override
-    public boolean isStreaming() {
+    public final boolean isStreaming() {
         return false;
     }
 
     @Override
-    public void close() throws IOException {
+    public final void close() throws IOException {
     }
 
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteBufferEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteBufferEntity.java
index 33853db..eabe2a2 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteBufferEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/ByteBufferEntity.java
@@ -34,72 +34,66 @@ import java.nio.ByteBuffer;
 import java.nio.channels.Channels;
 import java.nio.channels.WritableByteChannel;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.util.Args;
 
 /**
  * An entity that delivers the contents of a {@link ByteBuffer}.
  */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 public class ByteBufferEntity extends AbstractHttpEntity {
 
     private final ByteBuffer buffer;
 
-    private class ByteBufferInputStream extends InputStream {
-
-        ByteBufferInputStream() {
-            buffer.position(0);
-        }
-
-        @Override
-        public int read() throws IOException {
-            if (!buffer.hasRemaining()) {
-                return -1;
-            }
-            return buffer.get() & 0xFF;
-        }
-
-        @Override
-        public int read(final byte[] bytes, final int off, final int len) throws IOException {
-            if (!buffer.hasRemaining()) {
-                return -1;
-            }
-
-            final int chunk = Math.min(len, buffer.remaining());
-            buffer.get(bytes, off, chunk);
-            return chunk;
-        }
-    }
-
-    public ByteBufferEntity(final ByteBuffer buffer, final ContentType contentType) {
-        super();
+    public ByteBufferEntity(final ByteBuffer buffer, final ContentType contentType, final String contentEncoding) {
+        super(contentType, contentEncoding);
         Args.notNull(buffer, "Source byte buffer");
         this.buffer = buffer;
-        if (contentType != null) {
-            setContentType(contentType.toString());
-        }
     }
 
-    public ByteBufferEntity(final ByteBuffer buffer) {
-        this(buffer, null);
+    public ByteBufferEntity(final ByteBuffer buffer, final ContentType contentType) {
+        this(buffer, contentType, null);
     }
 
     @Override
-    public boolean isRepeatable() {
+    public final boolean isRepeatable() {
         return true;
     }
 
     @Override
-    public long getContentLength() {
+    public final long getContentLength() {
         return buffer.capacity();
     }
 
     @Override
-    public InputStream getContent() throws IOException, UnsupportedOperationException {
-        return new ByteBufferInputStream();
+    public final InputStream getContent() throws IOException, UnsupportedOperationException {
+        buffer.position(0);
+        return new InputStream() {
+
+            @Override
+            public int read() throws IOException {
+                if (!buffer.hasRemaining()) {
+                    return -1;
+                }
+                return buffer.get() & 0xFF;
+            }
+
+            @Override
+            public int read(final byte[] bytes, final int off, final int len) throws IOException {
+                if (!buffer.hasRemaining()) {
+                    return -1;
+                }
+                final int chunk = Math.min(len, buffer.remaining());
+                buffer.get(bytes, off, chunk);
+                return chunk;
+            }
+        };
     }
 
     @Override
-    public void writeTo(final OutputStream outStream) throws IOException {
+    public final void writeTo(final OutputStream outStream) throws IOException {
         Args.notNull(outStream, "Output stream");
         final WritableByteChannel channel = Channels.newChannel(outStream);
         channel.write(buffer);
@@ -107,12 +101,12 @@ public class ByteBufferEntity extends AbstractHttpEntity {
     }
 
     @Override
-    public boolean isStreaming() {
+    public final boolean isStreaming() {
         return false;
     }
 
     @Override
-    public void close() throws IOException {
+    public final void close() throws IOException {
     }
 
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java
index fc25bc3..5dd89da 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/EntityUtils.java
@@ -38,7 +38,6 @@ import java.nio.charset.UnsupportedCharsetException;
 import java.util.Collections;
 import java.util.List;
 
-import org.apache.hc.core5.http.ClassicHttpResponse;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.NameValuePair;
@@ -94,96 +93,6 @@ public final class EntityUtils {
     }
 
     /**
-     * Updates an entity in a response by first consuming an existing entity, then setting the new one.
-     *
-     * @param response the response with an entity to update; must not be null.
-     * @param entity the entity to set in the response.
-     * @throws IOException if an error occurs while reading the input stream on the existing
-     * entity.
-     * @throws IllegalArgumentException if response is null.
-     *
-     * @since 4.3
-     */
-    public static void updateEntity(
-            final ClassicHttpResponse response, final HttpEntity entity) throws IOException {
-        Args.notNull(response, "Response");
-        consume(response.getEntity());
-        response.setEntity(entity);
-    }
-
-    /**
-     * Extracts {@code Content-Type} value from {@link HttpEntity} exactly as
-     * specified by the {@code Content-Type} header of the entity. Returns {@code null}
-     * if not specified.
-     *
-     * @param entity HTTP entity
-     * @return content type
-     * {@code Content-Type} value.
-     * @throws UnsupportedCharsetException Thrown when the named charset is not available in
-     * this instance of the Java virtual machine
-     */
-    public static ContentType getContentType(final HttpEntity entity) throws UnsupportedCharsetException {
-        if (entity == null) {
-            return null;
-        }
-        final String contentType = entity.getContentType();
-        if (contentType != null) {
-            return ContentType.parse(contentType);
-        }
-        return null;
-    }
-
-    /**
-     * Extracts {@code Content-Type} value from {@link HttpEntity}. Returns {@code null}
-     * if not specified or incorrect (could not be parsed)..
-     *
-     * @param entity HTTP entity
-     * @return content type
-     *
-     * @since 4.4
-     *
-     */
-    public static ContentType getContentTypeLenient(final HttpEntity entity) {
-        if (entity == null) {
-            return null;
-        }
-        final String contentType = entity.getContentType();
-        if (contentType != null) {
-            return ContentType.parseLenient(contentType);
-        }
-        return null;
-    }
-
-    /**
-     * Extracts {@code Content-Type} value from {@link HttpEntity} or returns the default value
-     * {@link ContentType#DEFAULT_TEXT} if not explicitly specified.
-     *
-     * @param entity HTTP entity
-     * @return content type
-     * {@code Content-Type} value.
-     * @throws UnsupportedCharsetException Thrown when the named charset is not available in
-     * this instance of the Java virtual machine
-     */
-    public static ContentType getContentTypeOrDefault(final HttpEntity entity) throws UnsupportedCharsetException {
-        final ContentType contentType = getContentType(entity);
-        return contentType != null ? contentType : ContentType.DEFAULT_TEXT;
-    }
-
-    /**
-     * Extracts {@code Content-Type} value from {@link HttpEntity} or returns the default value
-     * {@link ContentType#DEFAULT_TEXT} if not explicitly specified or incorrect (could not be parsed).
-     *
-     * @param entity HTTP entity
-     * @return content type
-     *
-     * @since 4.4
-     */
-    public static ContentType getContentTypeLenientOrDefault(final HttpEntity entity) throws UnsupportedCharsetException {
-        final ContentType contentType = getContentType(entity);
-        return contentType != null ? contentType : ContentType.DEFAULT_TEXT;
-    }
-
-    /**
      * Read the contents of an entity and return it as a byte array.
      *
      * @param entity the entity to read from=
@@ -266,7 +175,7 @@ public final class EntityUtils {
         Args.notNull(entity, "Entity");
         ContentType contentType = null;
         try {
-            contentType = getContentType(entity);
+            contentType = ContentType.parse(entity.getContentType());
         } catch (final UnsupportedCharsetException ex) {
             if (defaultCharset == null) {
                 throw new UnsupportedEncodingException(ex.getMessage());
@@ -317,7 +226,7 @@ public final class EntityUtils {
      */
     public static String toString(final HttpEntity entity) throws IOException, ParseException {
         Args.notNull(entity, "Entity");
-        return toString(entity, getContentType(entity));
+        return toString(entity, ContentType.parse(entity.getContentType()));
     }
 
     /**
@@ -334,7 +243,7 @@ public final class EntityUtils {
      */
     public static List<NameValuePair> parse(final HttpEntity entity) throws IOException {
         Args.notNull(entity, "HTTP entity");
-        final ContentType contentType = EntityUtils.getContentType(entity);
+        final ContentType contentType = ContentType.parse(entity.getContentType());
         if (contentType == null || !contentType.getMimeType().equalsIgnoreCase(URLEncodedUtils.CONTENT_TYPE)) {
             return Collections.emptyList();
         }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/FileEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/FileEntity.java
index 6470c96..64d02e8 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/FileEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/FileEntity.java
@@ -32,6 +32,8 @@ import java.io.FileInputStream;
 import java.io.IOException;
 import java.io.InputStream;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.util.Args;
 
@@ -40,65 +42,43 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 4.0
  */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 public class FileEntity extends AbstractHttpEntity {
 
     private final File file;
 
-    /**
-     * Creates a new instance.
-     *
-     * @param file The file to serve.
-     * @param contentType  The content type for the given {@code file}.
-     *
-     * @since 4.2
-     */
-    public FileEntity(final File file, final ContentType contentType) {
-        super();
+    public FileEntity(final File file, final ContentType contentType, final String contentEncoding) {
+        super(contentType, contentEncoding);
         this.file = Args.notNull(file, "File");
-        if (contentType != null) {
-            setContentType(contentType.toString());
-        }
     }
 
-    /**
-     * Creates a new instance.
-     *
-     * @param file The file to serve.
-     *
-     * @since 4.2
-     */
-    public FileEntity(final File file) {
-        super();
+    public FileEntity(final File file, final ContentType contentType) {
+        super(contentType, null);
         this.file = Args.notNull(file, "File");
     }
 
     @Override
-    public boolean isRepeatable() {
+    public final boolean isRepeatable() {
         return true;
     }
 
     @Override
-    public long getContentLength() {
+    public final long getContentLength() {
         return this.file.length();
     }
 
     @Override
-    public InputStream getContent() throws IOException {
+    public final InputStream getContent() throws IOException {
         return new FileInputStream(this.file);
     }
 
-    /**
-     * Tells that this entity is not streaming.
-     *
-     * @return {@code false}
-     */
     @Override
-    public boolean isStreaming() {
+    public final boolean isStreaming() {
         return false;
     }
 
     @Override
-    public void close() throws IOException {
+    public final void close() throws IOException {
         // do nothing
     }
 
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java
index a676cfc..4775451 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWithTrailers.java
@@ -35,9 +35,11 @@ import java.util.LinkedHashSet;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -45,6 +47,7 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 5.0
  */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 public class HttpEntityWithTrailers implements HttpEntity {
 
     private final HttpEntity wrappedEntity;
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java
index 154f95f..a3ede51 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/HttpEntityWrapper.java
@@ -33,9 +33,11 @@ import java.io.OutputStream;
 import java.util.List;
 import java.util.Set;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpEntity;
-import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -47,6 +49,7 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 4.0
  */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 public class HttpEntityWrapper implements HttpEntity {
 
     /** The wrapped entity. */
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/InputStreamEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/InputStreamEntity.java
index 9180183..019abaf 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/InputStreamEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/InputStreamEntity.java
@@ -31,74 +31,39 @@ import java.io.IOException;
 import java.io.InputStream;
 import java.io.OutputStream;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.util.Args;
 
 /**
- * A streamed, non-repeatable entity that obtains its content from
- * an {@link InputStream}.
+ * A streamed, non-repeatable entity that obtains its content from an {@link InputStream}.
  *
  * @since 4.0
  */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 public class InputStreamEntity extends AbstractHttpEntity {
 
     private final InputStream content;
     private final long length;
 
-    /**
-     * Creates an entity with an unknown length.
-     * Equivalent to {@code new InputStreamEntity(inStream, -1)}.
-     *
-     * @param inStream input stream
-     * @throws IllegalArgumentException if {@code inStream} is {@code null}
-     * @since 4.3
-     */
-    public InputStreamEntity(final InputStream inStream) {
-        this(inStream, -1);
+    public InputStreamEntity(
+            final InputStream inStream, final long length, final ContentType contentType, final String contentEncoding) {
+        super(contentType, contentEncoding);
+        this.content = Args.notNull(inStream, "Source input stream");
+        this.length = length;
     }
 
-    /**
-     * Creates an entity with a specified content length.
-     *
-     * @param inStream input stream
-     * @param length of the input stream, {@code -1} if unknown
-     * @throws IllegalArgumentException if {@code inStream} is {@code null}
-     */
-    public InputStreamEntity(final InputStream inStream, final long length) {
-        this(inStream, length, null);
+    public InputStreamEntity(final InputStream inStream, final long length, final ContentType contentType) {
+        this(inStream, length, contentType, null);
     }
 
-    /**
-     * Creates an entity with a content type and unknown length.
-     * Equivalent to {@code new InputStreamEntity(inStream, -1, contentType)}.
-     *
-     * @param inStream input stream
-     * @param contentType content type
-     * @throws IllegalArgumentException if {@code inStream} is {@code null}
-     * @since 4.3
-     */
     public InputStreamEntity(final InputStream inStream, final ContentType contentType) {
-        this(inStream, -1, contentType);
-    }
-
-    /**
-     * @param inStream input stream
-     * @param length of the input stream, {@code -1} if unknown
-     * @param contentType for specifying the {@code Content-Type} header, may be {@code null}
-     * @throws IllegalArgumentException if {@code inStream} is {@code null}
-     * @since 4.2
-     */
-    public InputStreamEntity(final InputStream inStream, final long length, final ContentType contentType) {
-        super();
-        this.content = Args.notNull(inStream, "Source input stream");
-        this.length = length;
-        if (contentType != null) {
-            setContentType(contentType.toString());
-        }
+        this(inStream, -1, contentType, null);
     }
 
     @Override
-    public boolean isRepeatable() {
+    public final boolean isRepeatable() {
         return false;
     }
 
@@ -106,12 +71,12 @@ public class InputStreamEntity extends AbstractHttpEntity {
      * @return the content length or {@code -1} if unknown
      */
     @Override
-    public long getContentLength() {
+    public final long getContentLength() {
         return this.length;
     }
 
     @Override
-    public InputStream getContent() throws IOException {
+    public final InputStream getContent() throws IOException {
         return this.content;
     }
 
@@ -123,7 +88,7 @@ public class InputStreamEntity extends AbstractHttpEntity {
      *
      */
     @Override
-    public void writeTo(final OutputStream outStream) throws IOException {
+    public final void writeTo(final OutputStream outStream) throws IOException {
         Args.notNull(outStream, "Output stream");
         try (final InputStream inStream = this.content) {
             final byte[] buffer = new byte[OUTPUT_BUFFER_SIZE];
@@ -149,12 +114,12 @@ public class InputStreamEntity extends AbstractHttpEntity {
     }
 
     @Override
-    public boolean isStreaming() {
+    public final boolean isStreaming() {
         return true;
     }
 
     @Override
-    public void close() throws IOException {
+    public final void close() throws IOException {
         content.close();
     }
 
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/SerializableEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/SerializableEntity.java
index b7f0beb..7bd8f47 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/SerializableEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/SerializableEntity.java
@@ -35,6 +35,9 @@ import java.io.ObjectOutputStream;
 import java.io.OutputStream;
 import java.io.Serializable;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
+import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.util.Args;
 
 /**
@@ -45,12 +48,12 @@ import org.apache.hc.core5.util.Args;
  *
  * @since 4.0
  */
+@Contract(threading = ThreadingBehavior.IMMUTABLE_CONDITIONAL)
 public class SerializableEntity extends AbstractHttpEntity {
 
-    private byte[] objSer;
-
-    private Serializable objRef;
+    private final Serializable objRef;
 
+    private byte[] objSer;
     /**
      * Creates new instance of this class.
      *
@@ -59,29 +62,35 @@ public class SerializableEntity extends AbstractHttpEntity {
      *        stored in an internal buffer
      * @throws IOException in case of an I/O error
      */
-    public SerializableEntity(final Serializable ser, final boolean bufferize) throws IOException {
-        super();
+    public SerializableEntity(
+            final Serializable ser, final boolean bufferize, final ContentType contentType,
+            final String contentEncoding) throws IOException {
+        super(contentType, contentEncoding);
         Args.notNull(ser, "Source object");
         if (bufferize) {
             createBytes(ser);
+            this.objRef = null;
         } else {
             this.objRef = ser;
         }
     }
 
-    /**
-     * Creates new instance of this class.
-     *
-     * @param serializable The object to serialize.
-     *
-     * @since 4.3
-     */
-    public SerializableEntity(final Serializable serializable) {
-        super();
+    public SerializableEntity(
+            final Serializable serializable, final ContentType contentType, final String contentEncoding) {
+        super(contentType, contentEncoding);
         Args.notNull(serializable, "Source object");
         this.objRef = serializable;
     }
 
+    public SerializableEntity(
+            final Serializable ser, final boolean bufferize, final ContentType contentType) throws IOException {
+        this(ser, bufferize, contentType, null);
+    }
+
+    public SerializableEntity(final Serializable serializable, final ContentType contentType) {
+        this(serializable, contentType, null);
+    }
+
     private void createBytes(final Serializable ser) throws IOException {
         final ByteArrayOutputStream baos = new ByteArrayOutputStream();
         final ObjectOutputStream out = new ObjectOutputStream(baos);
@@ -91,7 +100,7 @@ public class SerializableEntity extends AbstractHttpEntity {
     }
 
     @Override
-    public InputStream getContent() throws IOException, IllegalStateException {
+    public final InputStream getContent() throws IOException, IllegalStateException {
         if (this.objSer == null) {
             createBytes(this.objRef);
         }
@@ -99,7 +108,7 @@ public class SerializableEntity extends AbstractHttpEntity {
     }
 
     @Override
-    public long getContentLength() {
+    public final long getContentLength() {
         if (this.objSer ==  null) {
             return -1;
         }
@@ -107,17 +116,17 @@ public class SerializableEntity extends AbstractHttpEntity {
     }
 
     @Override
-    public boolean isRepeatable() {
+    public final boolean isRepeatable() {
         return true;
     }
 
     @Override
-    public boolean isStreaming() {
+    public final boolean isStreaming() {
         return this.objSer == null;
     }
 
     @Override
-    public void writeTo(final OutputStream outStream) throws IOException {
+    public final void writeTo(final OutputStream outStream) throws IOException {
         Args.notNull(outStream, "Output stream");
         if (this.objSer == null) {
             final ObjectOutputStream out = new ObjectOutputStream(outStream);
@@ -130,8 +139,7 @@ public class SerializableEntity extends AbstractHttpEntity {
     }
 
     @Override
-    public void close() throws IOException {
-        // do nothing.
+    public final void close() throws IOException {
     }
 
 }
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/StringEntity.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/StringEntity.java
index 96164ae..8df9532 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/StringEntity.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/io/entity/StringEntity.java
@@ -34,16 +34,18 @@ import java.io.OutputStream;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
 
+import org.apache.hc.core5.annotation.Contract;
+import org.apache.hc.core5.annotation.ThreadingBehavior;
 import org.apache.hc.core5.http.ContentType;
 import org.apache.hc.core5.util.Args;
 
 /**
- * A self contained, repeatable entity that obtains its content from
- * a {@link String}.
+ * A self contained, repeatable entity that obtains its content from a {@link String}.
  *
  * @since 4.0
  */
-public class StringEntity extends AbstractHttpEntity implements Cloneable {
+@Contract(threading = ThreadingBehavior.IMMUTABLE)
+public class StringEntity extends AbstractHttpEntity {
 
     private final byte[] content;
 
@@ -54,21 +56,25 @@ public class StringEntity extends AbstractHttpEntity implements Cloneable {
      * @param contentType content type to be used. May be {@code null}, in which case the default
      *   MIME type {@link ContentType#TEXT_PLAIN} is assumed.
      *
-     * @throws IllegalArgumentException if the string parameter is null
-     * this instance of the Java virtual machine
-     * @since 4.2
+     * @since 5.0
      */
-    public StringEntity(final String string, final ContentType contentType) {
-        super();
+    public StringEntity(
+            final String string, final ContentType contentType, final String contentEncoding, final boolean chunked) {
+        super(contentType, contentEncoding, chunked);
         Args.notNull(string, "Source string");
         Charset charset = contentType != null ? contentType.getCharset() : null;
         if (charset == null) {
             charset = StandardCharsets.ISO_8859_1;
         }
         this.content = string.getBytes(charset);
-        if (contentType != null) {
-            setContentType(contentType.toString());
-        }
+    }
+
+    public StringEntity(final String string, final ContentType contentType, final boolean chunked) {
+        this(string, contentType, null, chunked);
+    }
+
+    public StringEntity(final String string, final ContentType contentType) {
+        this(string, contentType, null, false);
     }
 
     /**
@@ -79,14 +85,16 @@ public class StringEntity extends AbstractHttpEntity implements Cloneable {
      * @param charset character set to be used. May be {@code null}, in which case the default
      *   is {@link StandardCharsets#ISO_8859_1} is assumed
      *
-     * @throws IllegalArgumentException if the string parameter is null
-     *
      * @since 4.2
      */
     public StringEntity(final String string, final Charset charset) {
         this(string, ContentType.TEXT_PLAIN.withCharset(charset));
     }
 
+    public StringEntity(final String string, final Charset charset, final boolean chunked) {
+        this(string, ContentType.TEXT_PLAIN.withCharset(charset), chunked);
+    }
+
     /**
      * Creates a StringEntity with the specified content. The content type defaults to
      * {@link ContentType#TEXT_PLAIN}.
@@ -100,39 +108,34 @@ public class StringEntity extends AbstractHttpEntity implements Cloneable {
     }
 
     @Override
-    public boolean isRepeatable() {
+    public final boolean isRepeatable() {
         return true;
     }
 
     @Override
-    public long getContentLength() {
+    public final long getContentLength() {
         return this.content.length;
     }
 
     @Override
-    public InputStream getContent() throws IOException {
+    public final InputStream getContent() throws IOException {
         return new ByteArrayInputStream(this.content);
     }
 
     @Override
-    public void writeTo(final OutputStream outStream) throws IOException {
+    public final void writeTo(final OutputStream outStream) throws IOException {
         Args.notNull(outStream, "Output stream");
         outStream.write(this.content);
         outStream.flush();
     }
 
-    /**
-     * Tells that this entity is not streaming.
-     *
-     * @return {@code false}
-     */
     @Override
-    public boolean isStreaming() {
+    public final boolean isStreaming() {
         return false;
     }
 
     @Override
-    public void close() throws IOException {
+    public final void close() throws IOException {
         // nothing to do
     }
 
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpClientConnection.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpClientConnection.java
index 59505ea..16e91eb 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpClientConnection.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpClientConnection.java
@@ -243,7 +243,7 @@ public class TestDefaultBHttpClientConnection {
         conn.flush();
 
         Assert.assertEquals(1, conn.getEndpointDetails().getRequestCount());
-        final String s = new String(outStream.toByteArray(), "ASCII");
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
         Assert.assertEquals("GET /stuff HTTP/1.1\r\nUser-Agent: test\r\n\r\n", s);
     }
 
@@ -266,7 +266,7 @@ public class TestDefaultBHttpClientConnection {
         conn.flush();
 
         Assert.assertEquals(1, conn.getEndpointDetails().getRequestCount());
-        final String s = new String(outStream.toByteArray(), "ASCII");
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
         Assert.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: 3\r\n\r\n123", s);
     }
 
@@ -289,7 +289,7 @@ public class TestDefaultBHttpClientConnection {
         conn.flush();
 
         Assert.assertEquals(1, conn.getEndpointDetails().getRequestCount());
-        final String s = new String(outStream.toByteArray(), "ASCII");
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
         Assert.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nTransfer-Encoding: " +
                 "chunked\r\n\r\n3\r\n123\r\n0\r\n\r\n", s);
     }
@@ -328,7 +328,7 @@ public class TestDefaultBHttpClientConnection {
         conn.flush();
 
         Assert.assertEquals(1, conn.getEndpointDetails().getRequestCount());
-        final String s = new String(outStream.toByteArray(), "ASCII");
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
         Assert.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\n\r\n", s);
     }
 
@@ -344,8 +344,8 @@ public class TestDefaultBHttpClientConnection {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/stuff");
         request.addHeader("User-Agent", "test");
         request.addHeader("Transfer-Encoding", "chunked");
-        final StringEntity entity = new StringEntity("123", ContentType.TEXT_PLAIN);
-        entity.setChunked(true);
+        final StringEntity entity = new StringEntity("123", ContentType.TEXT_PLAIN, true);
+
         request.setEntity(entity);
 
         conn.sendRequestHeader(request);
@@ -353,7 +353,7 @@ public class TestDefaultBHttpClientConnection {
         conn.flush();
 
         Assert.assertEquals(1, conn.getEndpointDetails().getRequestCount());
-        final String s = new String(outStream.toByteArray(), "ASCII");
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
         Assert.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nTransfer-Encoding: " +
                 "chunked\r\n\r\n0\r\n\r\n", s);
         Assert.assertTrue(conn.isConsistent());
@@ -371,8 +371,7 @@ public class TestDefaultBHttpClientConnection {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/stuff");
         request.addHeader("User-Agent", "test");
         request.addHeader("Content-Length", "3");
-        final StringEntity entity = new StringEntity("123", ContentType.TEXT_PLAIN);
-        entity.setChunked(false);
+        final StringEntity entity = new StringEntity("123", ContentType.TEXT_PLAIN, true);
         request.setEntity(entity);
 
         conn.sendRequestHeader(request);
@@ -380,7 +379,7 @@ public class TestDefaultBHttpClientConnection {
         conn.flush();
 
         Assert.assertEquals(1, conn.getEndpointDetails().getRequestCount());
-        final String s = new String(outStream.toByteArray(), "ASCII");
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
         Assert.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: " +
                 "3\r\n\r\n123", s);
         Assert.assertTrue(conn.isConsistent());
@@ -398,8 +397,7 @@ public class TestDefaultBHttpClientConnection {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/stuff");
         request.addHeader("User-Agent", "test");
         request.addHeader("Content-Length", "3000");
-        final ByteArrayEntity entity = new ByteArrayEntity(new byte[3000], ContentType.TEXT_PLAIN);
-        entity.setChunked(false);
+        final ByteArrayEntity entity = new ByteArrayEntity(new byte[3000], ContentType.TEXT_PLAIN, true);
         request.setEntity(entity);
 
         conn.sendRequestHeader(request);
@@ -407,7 +405,7 @@ public class TestDefaultBHttpClientConnection {
         conn.flush();
 
         Assert.assertEquals(1, conn.getEndpointDetails().getRequestCount());
-        final String s = new String(outStream.toByteArray(), "ASCII");
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
         Assert.assertEquals("POST /stuff HTTP/1.1\r\nUser-Agent: test\r\nContent-Length: " +
                 "3000\r\n\r\n", s);
         Assert.assertFalse(conn.isConsistent());
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java
index b73feef..ff9e44c 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestHttpService.java
@@ -134,7 +134,7 @@ public class TestHttpService {
         final HttpCoreContext context = HttpCoreContext.create();
         final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
         final InputStream inStream = Mockito.mock(InputStream.class);
-        final InputStreamEntity entity = new InputStreamEntity(inStream, -1);
+        final InputStreamEntity entity = new InputStreamEntity(inStream, -1, null);
         request.setEntity(entity);
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
@@ -174,7 +174,7 @@ public class TestHttpService {
         final ClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
         request.addHeader(HttpHeaders.EXPECT, HeaderElements.CONTINUE);
         final InputStream inStream = Mockito.mock(InputStream.class);
-        final InputStreamEntity entity = new InputStreamEntity(inStream, -1);
+        final InputStreamEntity entity = new InputStreamEntity(inStream, -1, null);
         request.setEntity(entity);
 
         Mockito.when(conn.receiveRequestHeader()).thenReturn(request);
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBasicHttpEntity.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBasicHttpEntity.java
index 6d24c04..b2ac16e 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBasicHttpEntity.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBasicHttpEntity.java
@@ -29,9 +29,10 @@ package org.apache.hc.core5.http.io.entity;
 
 import java.io.ByteArrayInputStream;
 import java.io.ByteArrayOutputStream;
-import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.impl.io.EmptyInputStream;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -43,12 +44,8 @@ public class TestBasicHttpEntity {
 
     @Test
     public void testBasics() throws Exception {
-
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final InputStream content = new ByteArrayInputStream(bytes);
-        final BasicHttpEntity httpentity = new BasicHttpEntity();
-        httpentity.setContent(content);
-        httpentity.setContentLength(bytes.length);
+        final BasicHttpEntity httpentity = new BasicHttpEntity(new ByteArrayInputStream(bytes), bytes.length, null);
 
         Assert.assertEquals(bytes.length, httpentity.getContentLength());
         Assert.assertFalse(httpentity.isRepeatable());
@@ -57,46 +54,19 @@ public class TestBasicHttpEntity {
 
     @Test
     public void testToString() throws Exception {
-        final BasicHttpEntity httpentity = new BasicHttpEntity();
-        httpentity.setContentType("blah");
-        httpentity.setContentEncoding("yada");
-        httpentity.setContentLength(10);
-        httpentity.setChunked(true);
-        Assert.assertEquals("[Content-Type: blah,Content-Encoding: yada,Content-Length: 10,Chunked: true]",
+        final BasicHttpEntity httpentity = new BasicHttpEntity(EmptyInputStream.INSTANCE, 10,
+                ContentType.parseLenient("blah"), "yada", true);
+        Assert.assertEquals("[Entity-Class: BasicHttpEntity, Content-Type: blah, Content-Encoding: yada, chunked: true]",
                 httpentity.toString());
     }
 
     @Test
-    public void testContent() throws Exception {
-        final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final InputStream content = new ByteArrayInputStream(bytes);
-        final BasicHttpEntity httpentity = new BasicHttpEntity();
-        try {
-            httpentity.getContent();
-            Assert.fail("IllegalStateException should have been thrown");
-        } catch (final IllegalStateException ex) {
-            // expected
-        }
-        httpentity.setContent(content);
-        Assert.assertEquals(content, httpentity.getContent());
-
-        httpentity.setContent(null);
-        try {
-            httpentity.getContent();
-            Assert.fail("IllegalStateException should have been thrown");
-        } catch (final IllegalStateException ex) {
-            // expected
-        }
-    }
-
-    @Test
     public void testWriteTo() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final InputStream content = new ByteArrayInputStream(bytes);
-        final BasicHttpEntity httpentity = new BasicHttpEntity();
-        httpentity.setContent(content);
+        final BasicHttpEntity httpentity = new BasicHttpEntity(new ByteArrayInputStream(bytes), bytes.length,
+                ContentType.TEXT_PLAIN);
 
-        ByteArrayOutputStream out = new ByteArrayOutputStream();
+        final ByteArrayOutputStream out = new ByteArrayOutputStream();
         httpentity.writeTo(out);
         final byte[] bytes2 = out.toByteArray();
         Assert.assertNotNull(bytes2);
@@ -104,21 +74,6 @@ public class TestBasicHttpEntity {
         for (int i = 0; i < bytes.length; i++) {
             Assert.assertEquals(bytes[i], bytes2[i]);
         }
-        httpentity.setContent(null);
-        out = new ByteArrayOutputStream();
-        try {
-            httpentity.writeTo(out);
-            Assert.fail("IllegalStateException should have been thrown");
-        } catch (final IllegalStateException ex) {
-            // expected
-        }
-
-        try {
-            httpentity.writeTo(null);
-            Assert.fail("IllegalArgumentException should have been thrown");
-        } catch (final IllegalArgumentException ex) {
-            // expected
-        }
     }
 
 }
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBufferedHttpEntity.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBufferedHttpEntity.java
index 73c638d..858b1c3 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBufferedHttpEntity.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestBufferedHttpEntity.java
@@ -43,23 +43,22 @@ public class TestBufferedHttpEntity {
     @Test
     public void testBufferingEntity() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final InputStreamEntity httpentity = new InputStreamEntity(new ByteArrayInputStream(bytes), -1);
-        final BufferedHttpEntity bufentity = new BufferedHttpEntity(httpentity);
-        Assert.assertEquals(bytes.length, bufentity.getContentLength());
-        Assert.assertTrue(bufentity.isRepeatable());
-        Assert.assertFalse(bufentity.isChunked());
-        Assert.assertFalse(bufentity.isStreaming());
+        final BufferedHttpEntity entity = new BufferedHttpEntity(
+                new InputStreamEntity(new ByteArrayInputStream(bytes), -1, null));
+        Assert.assertEquals(bytes.length, entity.getContentLength());
+        Assert.assertTrue(entity.isRepeatable());
+        Assert.assertFalse(entity.isChunked());
+        Assert.assertFalse(entity.isStreaming());
 
         // test if we can obtain contain multiple times
-        Assert.assertNotNull(bufentity.getContent ());
-        Assert.assertNotNull(bufentity.getContent ());
+        Assert.assertNotNull(entity.getContent ());
+        Assert.assertNotNull(entity.getContent ());
     }
 
     @Test
     public void testWrappingEntity() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final ByteArrayEntity httpentity = new ByteArrayEntity(bytes);
-        httpentity.setChunked(true);
+        final ByteArrayEntity httpentity = new ByteArrayEntity(bytes, null, true);
         final BufferedHttpEntity bufentity = new BufferedHttpEntity(httpentity);
         Assert.assertEquals(bytes.length, bufentity.getContentLength());
         Assert.assertTrue(bufentity.isRepeatable());
@@ -84,7 +83,7 @@ public class TestBufferedHttpEntity {
     @Test
     public void testWriteToBuffered() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final InputStreamEntity httpentity = new InputStreamEntity(new ByteArrayInputStream(bytes), -1);
+        final InputStreamEntity httpentity = new InputStreamEntity(new ByteArrayInputStream(bytes), -1, null);
         final BufferedHttpEntity bufentity = new BufferedHttpEntity(httpentity);
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
@@ -116,7 +115,7 @@ public class TestBufferedHttpEntity {
     @Test
     public void testWriteToWrapped() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final ByteArrayEntity httpentity = new ByteArrayEntity(bytes);
+        final ByteArrayEntity httpentity = new ByteArrayEntity(bytes, null);
         final BufferedHttpEntity bufentity = new BufferedHttpEntity(httpentity);
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteArrayEntity.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteArrayEntity.java
index 244ce9c..16971d9 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteArrayEntity.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteArrayEntity.java
@@ -42,55 +42,55 @@ public class TestByteArrayEntity {
     @Test
     public void testBasics() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final ByteArrayEntity httpentity = new ByteArrayEntity(bytes);
+        final ByteArrayEntity entity = new ByteArrayEntity(bytes, null);
 
-        Assert.assertEquals(bytes.length, httpentity.getContentLength());
-        Assert.assertNotNull(httpentity.getContent());
-        Assert.assertTrue(httpentity.isRepeatable());
-        Assert.assertFalse(httpentity.isStreaming());
+        Assert.assertEquals(bytes.length, entity.getContentLength());
+        Assert.assertNotNull(entity.getContent());
+        Assert.assertTrue(entity.isRepeatable());
+        Assert.assertFalse(entity.isStreaming());
     }
 
     @Test
     public void testBasicOffLen() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final ByteArrayEntity httpentity = new ByteArrayEntity(bytes, 8, 7);
+        final ByteArrayEntity entity = new ByteArrayEntity(bytes, 8, 7, null);
 
-        Assert.assertEquals(7, httpentity.getContentLength());
-        Assert.assertNotNull(httpentity.getContent());
-        Assert.assertTrue(httpentity.isRepeatable());
-        Assert.assertFalse(httpentity.isStreaming());
+        Assert.assertEquals(7, entity.getContentLength());
+        Assert.assertNotNull(entity.getContent());
+        Assert.assertTrue(entity.isRepeatable());
+        Assert.assertFalse(entity.isStreaming());
     }
 
     @Test(expected=IllegalArgumentException.class)
     public void testIllegalConstructorNullByteArray() throws Exception {
-        new ByteArrayEntity(null);
+        new ByteArrayEntity(null, null);
     }
 
     @Test(expected=IndexOutOfBoundsException.class)
     public void testIllegalConstructorBadLen() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        new ByteArrayEntity(bytes, 0, bytes.length + 1);
+        new ByteArrayEntity(bytes, 0, bytes.length + 1, null);
     }
 
     @Test(expected=IndexOutOfBoundsException.class)
     public void testIllegalConstructorBadOff1() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        new ByteArrayEntity(bytes, -1, bytes.length);
+        new ByteArrayEntity(bytes, -1, bytes.length, null);
     }
 
     @Test(expected=IndexOutOfBoundsException.class)
     public void testIllegalConstructorBadOff2() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        new ByteArrayEntity(bytes, bytes.length + 1, bytes.length);
+        new ByteArrayEntity(bytes, bytes.length + 1, bytes.length, null);
     }
 
     @Test
     public void testWriteTo() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
-        final ByteArrayEntity httpentity = new ByteArrayEntity(bytes);
+        final ByteArrayEntity entity = new ByteArrayEntity(bytes, null);
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
-        httpentity.writeTo(out);
+        entity.writeTo(out);
         byte[] bytes2 = out.toByteArray();
         Assert.assertNotNull(bytes2);
         Assert.assertEquals(bytes.length, bytes2.length);
@@ -99,7 +99,7 @@ public class TestByteArrayEntity {
         }
 
         out = new ByteArrayOutputStream();
-        httpentity.writeTo(out);
+        entity.writeTo(out);
         bytes2 = out.toByteArray();
         Assert.assertNotNull(bytes2);
         Assert.assertEquals(bytes.length, bytes2.length);
@@ -108,7 +108,7 @@ public class TestByteArrayEntity {
         }
 
         try {
-            httpentity.writeTo(null);
+            entity.writeTo(null);
             Assert.fail("IllegalArgumentException should have been thrown");
         } catch (final IllegalArgumentException ex) {
             // expected
@@ -120,10 +120,10 @@ public class TestByteArrayEntity {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.US_ASCII);
         final int off = 8;
         final int len = 7;
-        final ByteArrayEntity httpentity = new ByteArrayEntity(bytes, off, len);
+        final ByteArrayEntity entity = new ByteArrayEntity(bytes, off, len, null);
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
-        httpentity.writeTo(out);
+        entity.writeTo(out);
         byte[] bytes2 = out.toByteArray();
         Assert.assertNotNull(bytes2);
         Assert.assertEquals(len, bytes2.length);
@@ -132,7 +132,7 @@ public class TestByteArrayEntity {
         }
 
         out = new ByteArrayOutputStream();
-        httpentity.writeTo(out);
+        entity.writeTo(out);
         bytes2 = out.toByteArray();
         Assert.assertNotNull(bytes2);
         Assert.assertEquals(len, bytes2.length);
@@ -141,7 +141,7 @@ public class TestByteArrayEntity {
         }
 
         try {
-            httpentity.writeTo(null);
+            entity.writeTo(null);
             Assert.fail("IllegalArgumentException should have been thrown");
         } catch (final IllegalArgumentException ex) {
             // expected
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteBufferEntity.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteBufferEntity.java
index c15d810..24ea27b 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteBufferEntity.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestByteBufferEntity.java
@@ -43,7 +43,7 @@ public class TestByteBufferEntity {
     @Test
     public void testBasics() throws Exception {
         final ByteBuffer bytes = ByteBuffer.wrap("Message content".getBytes(StandardCharsets.US_ASCII));
-        final ByteBufferEntity httpentity = new ByteBufferEntity(bytes);
+        final ByteBufferEntity httpentity = new ByteBufferEntity(bytes, null);
 
         Assert.assertEquals(bytes.capacity(), httpentity.getContentLength());
         Assert.assertNotNull(httpentity.getContent());
@@ -54,13 +54,13 @@ public class TestByteBufferEntity {
 
     @Test(expected=IllegalArgumentException.class)
     public void testIllegalConstructorNullByteArray() throws Exception {
-        new ByteBufferEntity(null);
+        new ByteBufferEntity(null, null);
     }
 
     @Test
     public void testWriteTo() throws Exception {
         final ByteBuffer bytes = ByteBuffer.wrap("Message content".getBytes(StandardCharsets.US_ASCII));
-        final ByteBufferEntity httpentity = new ByteBufferEntity(bytes);
+        final ByteBufferEntity httpentity = new ByteBufferEntity(bytes, null);
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         httpentity.writeTo(out);
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java
index a49bde2..2fe5cbf 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestEntityUtils.java
@@ -28,14 +28,15 @@
 package org.apache.hc.core5.http.io.entity;
 
 import java.io.ByteArrayInputStream;
+import java.io.IOException;
 import java.io.InputStream;
 import java.nio.charset.Charset;
 import java.nio.charset.StandardCharsets;
-import java.nio.charset.UnsupportedCharsetException;
 import java.util.ArrayList;
 import java.util.List;
 
 import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.NameValuePair;
 import org.apache.hc.core5.http.message.BasicNameValuePair;
 import org.apache.hc.core5.net.URLEncodedUtils;
@@ -48,66 +49,42 @@ import org.junit.Test;
  */
 public class TestEntityUtils {
 
-    @Test
+    @Test(expected = IllegalArgumentException.class)
     public void testNullEntityToByteArray() throws Exception {
-        try {
-            EntityUtils.toByteArray(null);
-            Assert.fail("IllegalArgumentException should have been thrown");
-        } catch (final IllegalArgumentException ex) {
-            // expected
-        }
+        EntityUtils.toByteArray(null);
     }
 
-    @Test
-    public void testEmptyContentToByteArray() throws Exception {
-        try (final NullHttpEntity httpentity = new NullHttpEntity()) {
-            final byte[] bytes = EntityUtils.toByteArray(httpentity);
-            Assert.assertNull(bytes);
-        }
-    }
-
-    @Test
+    @Test(expected = IllegalArgumentException.class)
     public void testMaxIntContentToByteArray() throws Exception {
         final byte[] content = "Message content".getBytes(StandardCharsets.ISO_8859_1);
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(content));
-            httpentity.setContentLength(Integer.MAX_VALUE + 100L);
-            try {
-                EntityUtils.toByteArray(httpentity);
-                Assert.fail("IllegalArgumentException should have been thrown");
-            } catch (final IllegalArgumentException ex) {
-                // expected
-            }
-        }}
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(content),
+                Integer.MAX_VALUE + 100L, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1));
+        EntityUtils.toByteArray(entity);
+    }
 
     @Test
     public void testUnknownLengthContentToByteArray() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1);
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(bytes));
-            httpentity.setContentLength(-1L);
-            final byte[] bytes2 = EntityUtils.toByteArray(httpentity);
-            Assert.assertNotNull(bytes2);
-            Assert.assertEquals(bytes.length, bytes2.length);
-            for (int i = 0; i < bytes.length; i++) {
-                Assert.assertEquals(bytes[i], bytes2[i]);
-            }
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), -1, null);
+        final byte[] bytes2 = EntityUtils.toByteArray(entity);
+        Assert.assertNotNull(bytes2);
+        Assert.assertEquals(bytes.length, bytes2.length);
+        for (int i = 0; i < bytes.length; i++) {
+            Assert.assertEquals(bytes[i], bytes2[i]);
         }
     }
 
     @Test
     public void testKnownLengthContentToByteArray() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1);
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(bytes));
-            httpentity.setContentLength(bytes.length);
-            final byte[] bytes2 = EntityUtils.toByteArray(httpentity);
-            Assert.assertNotNull(bytes2);
-            Assert.assertEquals(bytes.length, bytes2.length);
-            for (int i = 0; i < bytes.length; i++) {
-                Assert.assertEquals(bytes[i], bytes2[i]);
-            }
-        }}
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), bytes.length, null);
+        final byte[] bytes2 = EntityUtils.toByteArray(entity);
+        Assert.assertNotNull(bytes2);
+        Assert.assertEquals(bytes.length, bytes2.length);
+        for (int i = 0; i < bytes.length; i++) {
+            Assert.assertEquals(bytes[i], bytes2[i]);
+        }
+    }
 
     @Test
     public void testNullEntityToString() throws Exception {
@@ -119,46 +96,30 @@ public class TestEntityUtils {
         }
     }
 
-    @Test
-    public void testEmptyContentToString() throws Exception {
-        try (final NullHttpEntity httpentity = new NullHttpEntity()) {
-            final String s = EntityUtils.toString(httpentity);
-            Assert.assertNull(s);
-        }}
-
-    @Test
+    @Test(expected = IllegalArgumentException.class)
     public void testMaxIntContentToString() throws Exception {
         final byte[] content = "Message content".getBytes(StandardCharsets.ISO_8859_1);
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(content));
-            httpentity.setContentLength(Integer.MAX_VALUE + 100L);
-            try {
-                EntityUtils.toString(httpentity);
-                Assert.fail("IllegalArgumentException should have been thrown");
-            } catch (final IllegalArgumentException ex) {
-                // expected
-            }
-        }}
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(content),
+                Integer.MAX_VALUE + 100L, ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1));
+        EntityUtils.toString(entity);
+    }
 
     @Test
     public void testUnknownLengthContentToString() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1);
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(bytes));
-            httpentity.setContentLength(-1L);
-            final String s = EntityUtils.toString(httpentity, "ISO-8859-1");
-            Assert.assertEquals("Message content", s);
-        }}
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), -1, null);
+        final String s = EntityUtils.toString(entity, "ISO-8859-1");
+        Assert.assertEquals("Message content", s);
+    }
 
     @Test
     public void testKnownLengthContentToString() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1);
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(bytes));
-            httpentity.setContentLength(bytes.length);
-            final String s = EntityUtils.toString(httpentity, "ISO-8859-1");
-            Assert.assertEquals("Message content", s);
-        }}
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), bytes.length,
+                ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1));
+        final String s = EntityUtils.toString(entity, StandardCharsets.ISO_8859_1);
+        Assert.assertEquals("Message content", s);
+    }
 
     static final int SWISS_GERMAN_HELLO [] = {
         0x47, 0x72, 0xFC, 0x65, 0x7A, 0x69, 0x5F, 0x7A, 0xE4, 0x6D, 0xE4
@@ -183,107 +144,60 @@ public class TestEntityUtils {
     public void testNoCharsetContentToString() throws Exception {
         final String content = constructString(SWISS_GERMAN_HELLO);
         final byte[] bytes = content.getBytes(StandardCharsets.ISO_8859_1);
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(bytes));
-            httpentity.setContentType("text/plain");
-            final String s = EntityUtils.toString(httpentity);
-            Assert.assertEquals(content, s);
-        }}
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes), ContentType.TEXT_PLAIN);
+        final String s = EntityUtils.toString(entity);
+    }
 
     @Test
     public void testDefaultCharsetContentToString() throws Exception {
         final String content = constructString(RUSSIAN_HELLO);
         final byte[] bytes = content.getBytes(Charset.forName("KOI8-R"));
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(bytes));
-            httpentity.setContentType("text/plain");
-            final String s = EntityUtils.toString(httpentity, "KOI8-R");
-            Assert.assertEquals(content, s);
-        }}
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes),
+                ContentType.parse("text/plain"));
+        final String s = EntityUtils.toString(entity, Charset.forName("KOI8-R"));
+        Assert.assertEquals(content, s);
+    }
 
     @Test
     public void testContentWithContentTypeToString() throws Exception {
         final String content = constructString(RUSSIAN_HELLO);
         final byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(bytes));
-            httpentity.setContentType("text/plain; charset=UTF-8");
-            final String s = EntityUtils.toString(httpentity, "ISO-8859-1");
-            Assert.assertEquals(content, s);
-        }}
+        final BasicHttpEntity entity = new BasicHttpEntity(new ByteArrayInputStream(bytes),
+                ContentType.TEXT_PLAIN.withCharset(StandardCharsets.UTF_8));
+        final String s = EntityUtils.toString(entity, "ISO-8859-1");
+        Assert.assertEquals(content, s);
+    }
 
     @Test
     public void testContentWithInvalidContentTypeToString() throws Exception {
         final String content = constructString(RUSSIAN_HELLO);
-        final byte[] bytes = content.getBytes("UTF-8");
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContent(new ByteArrayInputStream(bytes));
-            httpentity.setContentType("text/plain; charset=nosuchcharset");
-            final String s = EntityUtils.toString(httpentity, "UTF-8");
-            Assert.assertEquals(content, s);
-        }}
-
-    @Test
-    public void testExtractNullInput() throws Exception {
-        Assert.assertNull(EntityUtils.getContentType(null));
-    }
+        final byte[] bytes = content.getBytes(StandardCharsets.UTF_8);
+        final HttpEntity entity = new AbstractHttpEntity("text/plain; charset=nosuchcharset", null) {
 
-    @Test
-    public void testExtractNullContentType() throws Exception {
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContentType(null);
-            Assert.assertNull(EntityUtils.getContentType(httpentity));
-        }}
+            @Override
+            public InputStream getContent() throws IOException, UnsupportedOperationException {
+                return new ByteArrayInputStream(bytes);
+            }
 
-    @Test
-    public void testExtract() throws Exception {
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContentType("text/plain; charset = UTF-8");
-            final ContentType contentType = EntityUtils.getContentType(httpentity);
-            Assert.assertNotNull(contentType);
-            Assert.assertEquals("text/plain", contentType.getMimeType());
-            Assert.assertEquals(StandardCharsets.UTF_8, contentType.getCharset());
-        }}
+            @Override
+            public boolean isStreaming() {
+                return false;
+            }
 
-    @Test
-    public void testExtractNoCharset() throws Exception {
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContentType("text/plain; param=yadayada");
-            final ContentType contentType = EntityUtils.getContentType(httpentity);
-            Assert.assertNotNull(contentType);
-            Assert.assertEquals("text/plain", contentType.getMimeType());
-            Assert.assertNull(contentType.getCharset());
-        }}
+            @Override
+            public long getContentLength() {
+                return bytes.length;
+            }
 
-    @Test(expected = UnsupportedCharsetException.class)
-    public void testExtractInvalidCharset() throws Exception {
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContentType("text/plain; charset = stuff");
-            EntityUtils.getContentType(httpentity);
-        }}
+            @Override
+            public void close() throws IOException {
+            }
 
-    @Test
-    public void testExtracLenienttNullInput() throws Exception {
-        Assert.assertNull(EntityUtils.getContentTypeLenient(null));
+        };
+        final String s = EntityUtils.toString(entity, "UTF-8");
+        Assert.assertEquals(content, s);
     }
 
-    @Test
-    public void testExtractLenientNullContentType() throws Exception {
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContentType(null);
-            Assert.assertNull(EntityUtils.getContentTypeLenient(httpentity));
-        }}
-
-    @Test
-    public void testLenientExtractInvalidCharset() throws Exception {
-        try (final BasicHttpEntity httpentity = new BasicHttpEntity()) {
-            httpentity.setContentType("text/plain; charset = stuff");
-            final ContentType contentType = EntityUtils.getContentTypeLenient(httpentity);
-            Assert.assertNotNull(contentType);
-            Assert.assertEquals("text/plain", contentType.getMimeType());
-            Assert.assertEquals(null, contentType.getCharset());
-        }}
-
     private static void assertNameValuePair (
             final NameValuePair parameter,
             final String expectedName,
@@ -294,16 +208,14 @@ public class TestEntityUtils {
 
     @Test
     public void testParseEntity() throws Exception {
-        try (final StringEntity entity = new StringEntity("Name1=Value1")) {
-
-            entity.setContentType(URLEncodedUtils.CONTENT_TYPE);
-            final List<NameValuePair> result = EntityUtils.parse(entity);
-            Assert.assertEquals(1, result.size());
-            assertNameValuePair(result.get(0), "Name1", "Value1");
+        final StringEntity entity1 = new StringEntity("Name1=Value1", ContentType.APPLICATION_FORM_URLENCODED);
+        final List<NameValuePair> result = EntityUtils.parse(entity1);
+        Assert.assertEquals(1, result.size());
+        assertNameValuePair(result.get(0), "Name1", "Value1");
 
-            entity.setContentType("text/test");
-            Assert.assertTrue(EntityUtils.parse(entity).isEmpty());
-        }}
+        final StringEntity entity2 = new StringEntity("Name1=Value1", ContentType.parse("text/test"));
+        Assert.assertTrue(EntityUtils.parse(entity2).isEmpty());
+    }
 
     @Test
     public void testParseUTF8Entity() throws Exception {
@@ -317,31 +229,12 @@ public class TestEntityUtils {
 
         Assert.assertEquals("russian=%D0%92%D1%81%D0%B5%D0%BC_%D0%BF%D1%80%D0%B8%D0%B2%D0%B5%D1%82" +
                 "&swiss=Gr%C3%BCezi_z%C3%A4m%C3%A4", s);
-
-        try (final StringEntity entity = new StringEntity(s,
-                ContentType.create(URLEncodedUtils.CONTENT_TYPE, StandardCharsets.UTF_8))) {
-            final List<NameValuePair> result = EntityUtils.parse(entity);
-            Assert.assertEquals(2, result.size());
-            assertNameValuePair(result.get(0), "russian", ru_hello);
-            assertNameValuePair(result.get(1), "swiss", ch_hello);
-        }
-    }
-
-    /**
-     * Helper class that returns {@code null} as the content.
-     */
-    public static class NullHttpEntity extends BasicHttpEntity {
-
-        /**
-         * Obtains no content.
-         * This method disables the state checks in the base class.
-         *
-         * @return {@code null}
-         */
-        @Override
-        public InputStream getContent() {
-            return null;
-        }
+        final StringEntity entity = new StringEntity(s,
+                ContentType.create(URLEncodedUtils.CONTENT_TYPE, StandardCharsets.UTF_8));
+        final List<NameValuePair> result = EntityUtils.parse(entity);
+        Assert.assertEquals(2, result.size());
+        assertNameValuePair(result.get(0), "russian", ru_hello);
+        assertNameValuePair(result.get(1), "swiss", ch_hello);
     }
 
 }
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestHttpEntityWrapper.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestHttpEntityWrapper.java
index f0092fc..3acdebf 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestHttpEntityWrapper.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestHttpEntityWrapper.java
@@ -42,17 +42,15 @@ public class TestHttpEntityWrapper {
 
     @Test
     public void testBasics() throws Exception {
-        final String s = "Message content";
-        final StringEntity httpentity = new StringEntity(s, ContentType.TEXT_PLAIN);
-        httpentity.setContentEncoding("blah");
-        final HttpEntityWrapper wrapped = new HttpEntityWrapper(httpentity);
+        final StringEntity entity = new StringEntity("Message content", ContentType.TEXT_PLAIN, "blah", false);
+        final HttpEntityWrapper wrapped = new HttpEntityWrapper(entity);
 
-        Assert.assertEquals(httpentity.getContentLength(), wrapped.getContentLength());
-        Assert.assertEquals(httpentity.getContentType(), wrapped.getContentType());
-        Assert.assertEquals(httpentity.getContentEncoding(), wrapped.getContentEncoding());
-        Assert.assertEquals(httpentity.isChunked(), wrapped.isChunked());
-        Assert.assertEquals(httpentity.isRepeatable(), wrapped.isRepeatable());
-        Assert.assertEquals(httpentity.isStreaming(), wrapped.isStreaming());
+        Assert.assertEquals(entity.getContentLength(), wrapped.getContentLength());
+        Assert.assertEquals(entity.getContentType(), wrapped.getContentType());
+        Assert.assertEquals(entity.getContentEncoding(), wrapped.getContentEncoding());
+        Assert.assertEquals(entity.isChunked(), wrapped.isChunked());
+        Assert.assertEquals(entity.isRepeatable(), wrapped.isRepeatable());
+        Assert.assertEquals(entity.isStreaming(), wrapped.isStreaming());
         Assert.assertNotNull(wrapped.getContent());
     }
 
@@ -70,8 +68,8 @@ public class TestHttpEntityWrapper {
     public void testWriteTo() throws Exception {
         final String s = "Message content";
         final byte[] bytes = s.getBytes(StandardCharsets.ISO_8859_1);
-        final StringEntity httpentity = new StringEntity(s);
-        final HttpEntityWrapper wrapped = new HttpEntityWrapper(httpentity);
+        final StringEntity entity = new StringEntity(s);
+        final HttpEntityWrapper wrapped = new HttpEntityWrapper(entity);
 
         ByteArrayOutputStream out = new ByteArrayOutputStream();
         wrapped.writeTo(out);
@@ -102,8 +100,8 @@ public class TestHttpEntityWrapper {
     @Test
     public void testConsumeContent() throws Exception {
         final String s = "Message content";
-        final StringEntity httpentity = new StringEntity(s);
-        final HttpEntityWrapper wrapped = new HttpEntityWrapper(httpentity);
+        final StringEntity entity = new StringEntity(s);
+        final HttpEntityWrapper wrapped = new HttpEntityWrapper(entity);
         EntityUtils.consume(wrapped);
         EntityUtils.consume(wrapped);
     }
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestInputStreamEntity.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestInputStreamEntity.java
index f78f870..3e5b441 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestInputStreamEntity.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestInputStreamEntity.java
@@ -32,6 +32,8 @@ import java.io.ByteArrayOutputStream;
 import java.io.InputStream;
 import java.nio.charset.StandardCharsets;
 
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.impl.io.EmptyInputStream;
 import org.junit.Assert;
 import org.junit.Test;
 
@@ -44,26 +46,23 @@ public class TestInputStreamEntity {
     @Test
     public void testBasics() throws Exception {
         final byte[] bytes = "Message content".getBytes(StandardCharsets.ISO_8859_1);
-        final InputStream inStream = new ByteArrayInputStream(bytes);
-        final InputStreamEntity httpentity = new InputStreamEntity(inStream, bytes.length);
+        final InputStreamEntity entity = new InputStreamEntity(new ByteArrayInputStream(bytes), bytes.length, null);
 
-        Assert.assertEquals(bytes.length, httpentity.getContentLength());
-        Assert.assertEquals(inStream, httpentity.getContent());
-        Assert.assertNotNull(httpentity.getContent());
-        Assert.assertFalse(httpentity.isRepeatable());
-        Assert.assertTrue(httpentity.isStreaming());
+        Assert.assertEquals(bytes.length, entity.getContentLength());
+        Assert.assertNotNull(entity.getContent());
+        Assert.assertFalse(entity.isRepeatable());
+        Assert.assertTrue(entity.isStreaming());
     }
 
     @Test(expected = IllegalArgumentException.class)
     public void testIllegalConstructor() throws Exception {
-        new InputStreamEntity(null, 0);
+        new InputStreamEntity(null, 0, null);
     }
 
     @Test
     public void testUnknownLengthConstructor() throws Exception {
-        final InputStream inStream = new ByteArrayInputStream(new byte[0]);
-        final InputStreamEntity httpentity = new InputStreamEntity(inStream);
-        Assert.assertEquals(-1, httpentity.getContentLength());
+        final InputStreamEntity entity = new InputStreamEntity(EmptyInputStream.INSTANCE, null);
+        Assert.assertEquals(-1, entity.getContentLength());
     }
 
     @Test
@@ -71,10 +70,11 @@ public class TestInputStreamEntity {
         final String message = "Message content";
         final byte[] bytes = message.getBytes(StandardCharsets.ISO_8859_1);
         final InputStream inStream = new ByteArrayInputStream(bytes);
-        final InputStreamEntity httpentity = new InputStreamEntity(inStream, bytes.length);
+        final InputStreamEntity entity = new InputStreamEntity(inStream, bytes.length,
+                ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
-        httpentity.writeTo(out);
+        entity.writeTo(out);
         final byte[] writtenBytes = out.toByteArray();
         Assert.assertNotNull(writtenBytes);
         Assert.assertEquals(bytes.length, writtenBytes.length);
@@ -89,10 +89,11 @@ public class TestInputStreamEntity {
         final byte[] bytes = message.getBytes(StandardCharsets.ISO_8859_1);
         final InputStream inStream = new ByteArrayInputStream(bytes);
         final int contentLength = 7;
-        final InputStreamEntity httpentity = new InputStreamEntity(inStream, contentLength);
+        final InputStreamEntity entity = new InputStreamEntity(inStream, contentLength,
+                ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
-        httpentity.writeTo(out);
+        entity.writeTo(out);
         final byte[] writtenBytes = out.toByteArray();
         Assert.assertNotNull(writtenBytes);
         Assert.assertEquals(contentLength, writtenBytes.length);
@@ -105,11 +106,11 @@ public class TestInputStreamEntity {
     public void testWriteToUnknownLength() throws Exception {
         final String message = "Message content";
         final byte[] bytes = message.getBytes(StandardCharsets.ISO_8859_1);
-        final InputStream inStream = new ByteArrayInputStream(bytes);
-        final InputStreamEntity httpentity = new InputStreamEntity(inStream);
+        final InputStreamEntity entity = new InputStreamEntity(new ByteArrayInputStream(bytes),
+                ContentType.TEXT_PLAIN.withCharset(StandardCharsets.ISO_8859_1));
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
-        httpentity.writeTo(out);
+        entity.writeTo(out);
         final byte[] writtenBytes = out.toByteArray();
         Assert.assertNotNull(writtenBytes);
         Assert.assertEquals(bytes.length, writtenBytes.length);
@@ -120,8 +121,7 @@ public class TestInputStreamEntity {
 
     @Test(expected = IllegalArgumentException.class)
     public void testWriteToNull() throws Exception {
-        final InputStream inStream = new ByteArrayInputStream(new byte[0]);
-        final InputStreamEntity httpentity = new InputStreamEntity(inStream, 0);
-        httpentity.writeTo(null);
+        final InputStreamEntity entity = new InputStreamEntity(EmptyInputStream.INSTANCE, 0, null);
+        entity.writeTo(null);
     }
 }
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestSerializableEntity.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestSerializableEntity.java
index a593b25..5a451b4 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestSerializableEntity.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/entity/TestSerializableEntity.java
@@ -57,7 +57,7 @@ public class TestSerializableEntity {
         final Serializable serializableObj = new SerializableObject();
         out.writeObject(serializableObj);
 
-        final SerializableEntity httpentity = new SerializableEntity(serializableObj, true);
+        final SerializableEntity httpentity = new SerializableEntity(serializableObj, true, null);
 
         Assert.assertEquals(baos.toByteArray().length, httpentity.getContentLength());
         Assert.assertNotNull(httpentity.getContent());
@@ -73,7 +73,7 @@ public class TestSerializableEntity {
         final Serializable serializableObj = new SerializableObject();
         out.writeObject(serializableObj);
 
-        final SerializableEntity httpentity = new SerializableEntity(serializableObj, false);
+        final SerializableEntity httpentity = new SerializableEntity(serializableObj, false, null);
 
         Assert.assertEquals(-1, httpentity.getContentLength());
         Assert.assertNotNull(httpentity.getContent());
@@ -84,7 +84,7 @@ public class TestSerializableEntity {
     @Test
     public void testIllegalConstructor() throws Exception {
         try {
-            new SerializableEntity(null, false);
+            new SerializableEntity(null, false, null);
             Assert.fail("IllegalArgumentException should have been thrown");
         } catch (final IllegalArgumentException ex) {
             // expected
@@ -94,7 +94,7 @@ public class TestSerializableEntity {
     @Test
     public void testWriteToBuff() throws Exception {
         final Serializable serializableObj = new SerializableObject();
-        final SerializableEntity httpentity = new SerializableEntity(serializableObj, true);
+        final SerializableEntity httpentity = new SerializableEntity(serializableObj, true, null);
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         httpentity.writeTo(out);
@@ -117,7 +117,7 @@ public class TestSerializableEntity {
     @Test
     public void testWriteToDirect() throws Exception {
         final Serializable serializableObj = new SerializableObject();
-        final SerializableEntity httpentity = new SerializableEntity(serializableObj, false);
+        final SerializableEntity httpentity = new SerializableEntity(serializableObj, false, null);
 
         final ByteArrayOutputStream out = new ByteArrayOutputStream();
         httpentity.writeTo(out);
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestStandardInterceptors.java b/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestStandardInterceptors.java
index 9002454..e114013 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestStandardInterceptors.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/protocol/TestStandardInterceptors.java
@@ -30,12 +30,14 @@ package org.apache.hc.core5.http.protocol;
 import java.nio.charset.StandardCharsets;
 
 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.HeaderElements;
 import org.apache.hc.core5.http.HttpHeaders;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.HttpVersion;
 import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.impl.io.EmptyInputStream;
 import org.apache.hc.core5.http.io.entity.BasicHttpEntity;
 import org.apache.hc.core5.http.io.entity.HttpEntityWithTrailers;
 import org.apache.hc.core5.http.io.entity.StringEntity;
@@ -145,15 +147,13 @@ public class TestStandardInterceptors {
     public void testRequestContentEntityContentLengthDelimitedHTTP11() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final String s = "whatever";
-        final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII);
-        request.setEntity(entity);
+        request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII));
 
         final RequestContent interceptor = new RequestContent();
         interceptor.process(request, request.getEntity(), context);
         final Header header = request.getFirstHeader(HttpHeaders.CONTENT_LENGTH);
         Assert.assertNotNull(header);
-        Assert.assertEquals(s.length(), Integer.parseInt(header.getValue()));
+        Assert.assertEquals(8, Integer.parseInt(header.getValue()));
         Assert.assertNull(request.getFirstHeader(HttpHeaders.TRANSFER_ENCODING));
    }
 
@@ -161,10 +161,7 @@ public class TestStandardInterceptors {
     public void testRequestContentEntityChunkedHTTP11() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final String s = "whatever";
-        final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII);
-        entity.setChunked(true);
-        request.setEntity(entity);
+        request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII, true));
 
         final RequestContent interceptor = new RequestContent();
         interceptor.process(request, request.getEntity(), context);
@@ -178,10 +175,7 @@ public class TestStandardInterceptors {
     public void testRequestContentEntityUnknownLengthHTTP11() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        entity.setContentLength(-1);
-        entity.setChunked(false);
-        request.setEntity(entity);
+        request.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, -1, null));
 
         final RequestContent interceptor = new RequestContent();
         interceptor.process(request, request.getEntity(), context);
@@ -196,10 +190,7 @@ public class TestStandardInterceptors {
         final HttpContext context = new BasicHttpContext(null);
         context.setProtocolVersion(HttpVersion.HTTP_1_0);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final String s = "whatever";
-        final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII);
-        entity.setChunked(true);
-        request.setEntity(entity);
+        request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII, true));
 
         final RequestContent interceptor = new RequestContent();
         try {
@@ -214,10 +205,8 @@ public class TestStandardInterceptors {
     public void testRequestContentTypeAndEncoding() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        entity.setContentType("whatever");
-        entity.setContentEncoding("whatever");
-        request.setEntity(entity);
+        request.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE,
+                ContentType.parseLenient("whatever"), "whatever"));
 
         final RequestContent interceptor = new RequestContent();
         interceptor.process(request, request.getEntity(), context);
@@ -233,8 +222,7 @@ public class TestStandardInterceptors {
     public void testRequestContentNullTypeAndEncoding() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        request.setEntity(entity);
+        request.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null, null));
 
         final RequestContent interceptor = new RequestContent();
         interceptor.process(request, request.getEntity(), context);
@@ -247,10 +235,7 @@ public class TestStandardInterceptors {
         final HttpContext context = new BasicHttpContext(null);
         context.setProtocolVersion(HttpVersion.HTTP_1_0);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        entity.setContentLength(-1);
-        entity.setChunked(false);
-        request.setEntity(entity);
+        request.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, -1, null));
 
         final RequestContent interceptor = new RequestContent();
         try {
@@ -313,9 +298,7 @@ public class TestStandardInterceptors {
     public void testRequestContentEntityWithTrailers() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final String s = "whatever";
-        final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII);
-        request.setEntity(new HttpEntityWithTrailers(entity,
+        request.setEntity(new HttpEntityWithTrailers(new StringEntity("whatever", StandardCharsets.US_ASCII),
                 new BasicHeader("h1", "this"), new BasicHeader("h1", "that"), new BasicHeader("h2", "this and that")));
 
         final RequestContent interceptor = new RequestContent();
@@ -331,9 +314,7 @@ public class TestStandardInterceptors {
     public void testRequestExpectContinueGenerated() throws Exception {
         final HttpCoreContext context = HttpCoreContext.create();
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final String s = "whatever";
-        final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII);
-        request.setEntity(entity);
+        request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII));
         final RequestExpectContinue interceptor = new RequestExpectContinue();
         interceptor.process(request, request.getEntity(), context);
         final Header header = request.getFirstHeader(HttpHeaders.EXPECT);
@@ -346,9 +327,7 @@ public class TestStandardInterceptors {
         final HttpCoreContext context = HttpCoreContext.create();
         context.setProtocolVersion(HttpVersion.HTTP_1_0);
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final String s = "whatever";
-        final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII);
-        request.setEntity(entity);
+        request.setEntity(new StringEntity("whatever", StandardCharsets.US_ASCII));
         final RequestExpectContinue interceptor = new RequestExpectContinue();
         interceptor.process(request, request.getEntity(), context);
         final Header header = request.getFirstHeader(HttpHeaders.EXPECT);
@@ -359,9 +338,7 @@ public class TestStandardInterceptors {
     public void testRequestExpectContinueZeroContent() throws Exception {
         final HttpCoreContext context = HttpCoreContext.create();
         final BasicClassicHttpRequest request = new BasicClassicHttpRequest("POST", "/");
-        final String s = "";
-        final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII);
-        request.setEntity(entity);
+        request.setEntity(new StringEntity("", StandardCharsets.US_ASCII));
         final RequestExpectContinue interceptor = new RequestExpectContinue();
         interceptor.process(request, request.getEntity(), context);
         final Header header = request.getFirstHeader(HttpHeaders.EXPECT);
@@ -536,8 +513,7 @@ public class TestStandardInterceptors {
     public void testResponseConnControlEntityContentLength() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final StringEntity entity = new StringEntity("whatever");
-        response.setEntity(entity);
+        response.setEntity(new StringEntity("whatever"));
         final ResponseConnControl interceptor = new ResponseConnControl();
         interceptor.process(response, response.getEntity(), context);
         final Header header = response.getFirstHeader(HttpHeaders.CONNECTION);
@@ -551,8 +527,7 @@ public class TestStandardInterceptors {
         request.addHeader(new BasicHeader(HttpHeaders.CONNECTION, "keep-alive"));
         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null));
         final ResponseConnControl interceptor = new ResponseConnControl();
         interceptor.process(response, response.getEntity(), context);
         final Header header = response.getFirstHeader(HttpHeaders.CONNECTION);
@@ -564,9 +539,7 @@ public class TestStandardInterceptors {
     public void testResponseConnControlEntityChunked() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        entity.setChunked(true);
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null, true));
         final ResponseConnControl interceptor = new ResponseConnControl();
         interceptor.process(response, response.getEntity(), context);
         final Header header = response.getFirstHeader(HttpHeaders.CONNECTION);
@@ -582,8 +555,7 @@ public class TestStandardInterceptors {
         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
         final BasicClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null));
         final ResponseConnControl interceptor = new ResponseConnControl();
         interceptor.process(response, response.getEntity(), context);
         final Header header = response.getFirstHeader(HttpHeaders.CONNECTION);
@@ -599,8 +571,7 @@ public class TestStandardInterceptors {
         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final StringEntity entity = new StringEntity("whatever");
-        response.setEntity(entity);
+        response.setEntity(new StringEntity("whatever"));
         final ResponseConnControl interceptor = new ResponseConnControl();
         interceptor.process(response, response.getEntity(), context);
         final Header header = response.getFirstHeader(HttpHeaders.CONNECTION);
@@ -615,8 +586,7 @@ public class TestStandardInterceptors {
         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final StringEntity entity = new StringEntity("whatever");
-        response.setEntity(entity);
+        response.setEntity(new StringEntity("whatever"));
         final ResponseConnControl interceptor = new ResponseConnControl();
         interceptor.process(response, response.getEntity(), context);
         final Header header = response.getFirstHeader(HttpHeaders.CONNECTION);
@@ -631,8 +601,7 @@ public class TestStandardInterceptors {
         context.setAttribute(HttpCoreContext.HTTP_REQUEST, request);
 
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final StringEntity entity = new StringEntity("whatever");
-        response.setEntity(entity);
+        response.setEntity(new StringEntity("whatever"));
         final ResponseConnControl interceptor = new ResponseConnControl();
         interceptor.process(response, response.getEntity(), context);
         final Header header = response.getFirstHeader(HttpHeaders.CONNECTION);
@@ -770,9 +739,7 @@ public class TestStandardInterceptors {
     public void testResponseContentEntityChunked() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        entity.setChunked(true);
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null, true));
         final ResponseContent interceptor = new ResponseContent();
         interceptor.process(response, response.getEntity(), context);
         final Header h1 = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING);
@@ -786,9 +753,7 @@ public class TestStandardInterceptors {
     public void testResponseContentEntityContentLenghtDelimited() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        entity.setContentLength (10);
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, 10, null));
         final ResponseContent interceptor = new ResponseContent();
         interceptor.process(response, response.getEntity(), context);
         final Header h1 = response.getFirstHeader(HttpHeaders.CONTENT_LENGTH);
@@ -802,8 +767,7 @@ public class TestStandardInterceptors {
     public void testResponseContentEntityUnknownContentLength() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null));
         final ResponseContent interceptor = new ResponseContent();
         interceptor.process(response, response.getEntity(), context);
         final Header h1 = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING);
@@ -818,9 +782,7 @@ public class TestStandardInterceptors {
         final HttpContext context = new BasicHttpContext(null);
         context.setProtocolVersion(HttpVersion.HTTP_1_0);
         final BasicClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        entity.setChunked(true);
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null, true));
         final ResponseContent interceptor = new ResponseContent();
         interceptor.process(response, response.getEntity(), context);
         final Header h1 = response.getFirstHeader(HttpHeaders.TRANSFER_ENCODING);
@@ -833,8 +795,7 @@ public class TestStandardInterceptors {
     public void testResponseContentEntityNoContentTypeAndEncoding() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE, null));
         final ResponseContent interceptor = new ResponseContent();
         interceptor.process(response, response.getEntity(), context);
         final Header h1 = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
@@ -847,10 +808,8 @@ public class TestStandardInterceptors {
     public void testResponseContentEntityContentTypeAndEncoding() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final BasicHttpEntity entity = new BasicHttpEntity();
-        entity.setContentEncoding("whatever");
-        entity.setContentType("whatever");
-        response.setEntity(entity);
+        response.setEntity(new BasicHttpEntity(EmptyInputStream.INSTANCE,
+                ContentType.parseLenient("whatever"), "whatever"));
         final ResponseContent interceptor = new ResponseContent();
         interceptor.process(response, response.getEntity(), context);
         final Header h1 = response.getFirstHeader(HttpHeaders.CONTENT_TYPE);
@@ -919,9 +878,7 @@ public class TestStandardInterceptors {
     public void testResponseContentEntityWithTrailers() throws Exception {
         final HttpContext context = new BasicHttpContext(null);
         final ClassicHttpResponse response = new BasicClassicHttpResponse(HttpStatus.SC_OK, "OK");
-        final String s = "whatever";
-        final StringEntity entity = new StringEntity(s, StandardCharsets.US_ASCII);
-        response.setEntity(new HttpEntityWithTrailers(entity,
+        response.setEntity(new HttpEntityWithTrailers(new StringEntity("whatever", StandardCharsets.US_ASCII),
                 new BasicHeader("h1", "this"), new BasicHeader("h1", "that"), new BasicHeader("h2", "this and that")));
 
         final ResponseContent interceptor = new ResponseContent();


[httpcomponents-core] 02/03: Added abstract I/O callback

Posted by ol...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

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

commit fb8c67fc0b18bd9d2988a479df721fd689677f65
Author: Oleg Kalnichevski <ol...@apache.org>
AuthorDate: Mon Feb 11 15:50:01 2019 +0100

    Added abstract I/O callback
---
 .../org/apache/hc/core5/function/IOCallback.java   | 41 ++++++++++++++++++++++
 1 file changed, 41 insertions(+)

diff --git a/httpcore5/src/main/java/org/apache/hc/core5/function/IOCallback.java b/httpcore5/src/main/java/org/apache/hc/core5/function/IOCallback.java
new file mode 100644
index 0000000..7434642
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/function/IOCallback.java
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * 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.function;
+
+import java.io.IOException;
+
+/**
+ * Abstract input / output callback.
+ *
+ * @since 5.0
+ */
+public interface IOCallback<T> {
+
+    void execute(T object) throws IOException;
+
+}