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 2022/02/05 12:34:00 UTC

[httpcomponents-core] branch master updated: Test coverage

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

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


The following commit(s) were added to refs/heads/master by this push:
     new e4cc649  Test coverage
e4cc649 is described below

commit e4cc64912af822585d15a439bcc8a714c2ec0f35
Author: Arturo Bernal <ar...@gmail.com>
AuthorDate: Sun Dec 5 21:30:58 2021 +0100

    Test coverage
---
 .../apache/hc/core5/http2/config/H2ConfigTest.java |  90 ++++++++
 .../core5/reactive/TestReactiveEntityProducer.java | 117 ++++++++++
 .../ThreadingBehaviorTest.java}                    |  55 ++---
 ...Callback.java => DefaultThreadFactoryTest.java} |  57 ++---
 .../hc/core5/concurrent/TestBasicFuture.java       |  41 ++--
 ...utureCallback.java => TestCompletedFuture.java} |  56 ++---
 .../core5/http/config/TestNamedElementChain.java   |  26 ++-
 .../core5/http/impl/IncomingEntityDetailsTest.java |  96 +++++++++
 .../impl/io/TestDefaultBHttpServerConnection.java  |   6 +
 .../http/io/support/ClassicRequestBuilderTest.java | 239 +++++++++++++++++++++
 .../io/support/ClassicResponseBuilderTest.java     | 153 +++++++++++++
 .../core5/http/message/HttpRequestWrapperTest.java | 162 ++++++++++++++
 .../http/message/HttpResponseWrapperTest.java      |  85 ++++++++
 .../apache/hc/core5/http/ssl/TestTlsCiphers.java   |  54 +++++
 .../org/apache/hc/core5/pool/TestLaxConnPool.java  |  21 ++
 .../org/apache/hc/core5/ssl/SSLContextsTest.java   |  88 ++++++++
 16 files changed, 1194 insertions(+), 152 deletions(-)

diff --git a/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/config/H2ConfigTest.java b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/config/H2ConfigTest.java
new file mode 100644
index 0000000..39e2b9d
--- /dev/null
+++ b/httpcore5-h2/src/test/java/org/apache/hc/core5/http2/config/H2ConfigTest.java
@@ -0,0 +1,90 @@
+/*
+ * ====================================================================
+ * 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.http2.config;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import org.junit.jupiter.api.Test;
+
+public class H2ConfigTest {
+
+    @Test
+    void builder() {
+        // Create and start requester
+        final H2Config h2Config = H2Config.custom()
+                .setPushEnabled(false)
+                .build();
+        assertNotNull(h2Config);
+    }
+
+    @Test
+    void checkValues() {
+        // Create and start requester
+        final H2Config h2Config = H2Config.custom()
+                .setHeaderTableSize(1)
+                .setMaxConcurrentStreams(1)
+                .setMaxFrameSize(16384)
+                .setPushEnabled(true)
+                .setCompressionEnabled(true)
+                .build();
+
+        assertEquals(1, h2Config.getHeaderTableSize());
+        assertEquals(1, h2Config.getMaxConcurrentStreams());
+        assertEquals(16384, h2Config.getMaxFrameSize());
+        assertTrue(h2Config.isPushEnabled());
+        assertTrue(h2Config.isCompressionEnabled());
+    }
+
+    @Test
+    void copy() {
+        // Create and start requester
+        final H2Config h2Config = H2Config.custom()
+                .setHeaderTableSize(1)
+                .setMaxConcurrentStreams(1)
+                .setMaxFrameSize(16384)
+                .setPushEnabled(true)
+                .setCompressionEnabled(true)
+                .build();
+
+        final H2Config.Builder builder = H2Config.copy(h2Config);
+        final H2Config h2Config2 = builder.build();
+
+        assertAll(
+                () -> assertEquals(h2Config.getHeaderTableSize(), h2Config2.getHeaderTableSize()),
+                () -> assertEquals(h2Config.getInitialWindowSize(), h2Config2.getInitialWindowSize()),
+                () -> assertEquals(h2Config.getMaxConcurrentStreams(), h2Config2.getMaxConcurrentStreams()),
+                () -> assertEquals(h2Config.getMaxFrameSize(), h2Config2.getMaxFrameSize()),
+                () -> assertEquals(h2Config.getMaxHeaderListSize(), h2Config2.getMaxHeaderListSize())
+        );
+
+    }
+
+}
\ No newline at end of file
diff --git a/httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/TestReactiveEntityProducer.java b/httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/TestReactiveEntityProducer.java
new file mode 100644
index 0000000..369df16
--- /dev/null
+++ b/httpcore5-reactive/src/test/java/org/apache/hc/core5/reactive/TestReactiveEntityProducer.java
@@ -0,0 +1,117 @@
+/*
+ * ====================================================================
+ * 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.reactive;
+
+import java.nio.ByteBuffer;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpStreamResetException;
+import org.apache.hc.core5.http.nio.DataStreamChannel;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+import io.reactivex.Flowable;
+
+public class TestReactiveEntityProducer {
+
+    private static final long CONTENT_LENGTH = 1;
+    private static final ContentType CONTENT_TYPE = ContentType.APPLICATION_JSON;
+    private static final String GZIP_CONTENT_ENCODING = "gzip";
+
+    @Test
+    public void testStreamThatEndsNormally() throws Exception {
+        final Flowable<ByteBuffer> publisher = Flowable.just(
+                ByteBuffer.wrap(new byte[]{'1', '2', '3'}),
+                ByteBuffer.wrap(new byte[]{'4', '5', '6'}));
+        final ReactiveEntityProducer entityProducer = new ReactiveEntityProducer(publisher, CONTENT_LENGTH, CONTENT_TYPE, GZIP_CONTENT_ENCODING);
+
+        final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024);
+        final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel);
+
+        entityProducer.produce(streamChannel);
+
+        Assertions.assertTrue(byteChannel.isOpen(), "Should be open");
+        Assertions.assertEquals("123456", byteChannel.dump(StandardCharsets.US_ASCII));
+
+        entityProducer.produce(streamChannel);
+
+        Assertions.assertFalse(byteChannel.isOpen(), "Should be closed");
+        Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII));
+        Assertions.assertFalse(entityProducer.isChunked());
+        Assertions.assertEquals(GZIP_CONTENT_ENCODING, entityProducer.getContentEncoding());
+        Assertions.assertNull(entityProducer.getTrailerNames());
+        Assertions.assertEquals(CONTENT_LENGTH, entityProducer.getContentLength());
+        Assertions.assertEquals(CONTENT_TYPE.toString(), entityProducer.getContentType());
+        Assertions.assertFalse(entityProducer.isRepeatable());
+        Assertions.assertEquals(1, entityProducer.available());
+
+        entityProducer.releaseResources();
+    }
+
+    @Test
+
+    public void testStreamThatEndsWithError() throws Exception {
+        final Flowable<ByteBuffer> publisher = Flowable.concatArray(
+                Flowable.just(
+                        ByteBuffer.wrap(new byte[]{'1'}),
+                        ByteBuffer.wrap(new byte[]{'2'}),
+                        ByteBuffer.wrap(new byte[]{'3'}),
+                        ByteBuffer.wrap(new byte[]{'4'}),
+                        ByteBuffer.wrap(new byte[]{'5'}),
+                        ByteBuffer.wrap(new byte[]{'6'})),
+                Flowable.error(new RuntimeException())
+        );
+        final ReactiveEntityProducer entityProducer = new ReactiveEntityProducer(publisher, CONTENT_LENGTH, CONTENT_TYPE, GZIP_CONTENT_ENCODING);
+
+        final WritableByteChannelMock byteChannel = new WritableByteChannelMock(1024);
+        final DataStreamChannel streamChannel = new BasicDataStreamChannel(byteChannel);
+
+        entityProducer.produce(streamChannel);
+        Assertions.assertEquals("12345", byteChannel.dump(StandardCharsets.US_ASCII));
+
+        final HttpStreamResetException exception = Assertions.assertThrows(HttpStreamResetException.class, () ->
+                entityProducer.produce(streamChannel));
+        Assertions.assertTrue(exception.getCause() instanceof RuntimeException, "Expected published exception to be rethrown");
+        Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII));
+        entityProducer.failed(exception);
+        Assertions.assertEquals(1, entityProducer.available());
+
+        Assertions.assertTrue(byteChannel.isOpen());
+        Assertions.assertEquals("", byteChannel.dump(StandardCharsets.US_ASCII));
+        Assertions.assertFalse(entityProducer.isChunked());
+        Assertions.assertEquals(GZIP_CONTENT_ENCODING, entityProducer.getContentEncoding());
+        Assertions.assertNull(entityProducer.getTrailerNames());
+        Assertions.assertEquals(CONTENT_LENGTH, entityProducer.getContentLength());
+        Assertions.assertEquals(CONTENT_TYPE.toString(), entityProducer.getContentType());
+        Assertions.assertFalse(entityProducer.isRepeatable());
+        Assertions.assertEquals(1, entityProducer.available());
+
+        entityProducer.releaseResources();
+    }
+
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java b/httpcore5/src/test/java/org/apache/hc/core5/annotation/ThreadingBehaviorTest.java
similarity index 57%
copy from httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java
copy to httpcore5/src/test/java/org/apache/hc/core5/annotation/ThreadingBehaviorTest.java
index 44236dc..2ed0b14 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/annotation/ThreadingBehaviorTest.java
@@ -24,52 +24,23 @@
  * <http://www.apache.org/>.
  *
  */
-package org.apache.hc.core5.concurrent;
 
+package org.apache.hc.core5.annotation;
 
-class BasicFutureCallback<T> implements FutureCallback<T> {
+import static org.junit.jupiter.api.Assertions.assertEquals;
 
-    private T result;
-    private Exception ex;
-    private boolean completed;
-    private boolean failed;
-    private boolean cancelled;
+import org.junit.jupiter.api.Test;
 
-    @Override
-    public void completed(final T result) {
-        this.result = result;
-        this.completed = true;
-    }
-
-    public T getResult() {
-        return this.result;
-    }
-
-    public Exception getException() {
-        return this.ex;
-    }
-
-    @Override
-    public void failed(final Exception ex) {
-        this.ex = ex;
-        this.failed = true;
-    }
-
-    @Override
-    public void cancelled() {
-        this.cancelled = true;
-    }
-
-    public boolean isCompleted() {
-        return this.completed;
-    }
-
-    public boolean isFailed() {
-        return this.failed;
-    }
+public class ThreadingBehaviorTest {
 
-    public boolean isCancelled() {
-        return this.cancelled;
+    @Test
+    void testName(){
+        assertEquals("SAFE", ThreadingBehavior.SAFE.name());
+        assertEquals("SAFE_CONDITIONAL", ThreadingBehavior.SAFE_CONDITIONAL.name());
+        assertEquals("IMMUTABLE_CONDITIONAL", ThreadingBehavior.IMMUTABLE_CONDITIONAL.name());
+        assertEquals("IMMUTABLE", ThreadingBehavior.IMMUTABLE.name());
+        assertEquals("STATELESS", ThreadingBehavior.STATELESS.name());
+        assertEquals("UNSAFE", ThreadingBehavior.UNSAFE.name());
     }
 
-}
+}
\ No newline at end of file
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java b/httpcore5/src/test/java/org/apache/hc/core5/concurrent/DefaultThreadFactoryTest.java
similarity index 58%
copy from httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java
copy to httpcore5/src/test/java/org/apache/hc/core5/concurrent/DefaultThreadFactoryTest.java
index 44236dc..7ffc392 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/concurrent/DefaultThreadFactoryTest.java
@@ -26,50 +26,23 @@
  */
 package org.apache.hc.core5.concurrent;
 
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.ThreadFactory;
+import java.util.concurrent.TimeUnit;
 
-class BasicFutureCallback<T> implements FutureCallback<T> {
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
-    private T result;
-    private Exception ex;
-    private boolean completed;
-    private boolean failed;
-    private boolean cancelled;
+public class DefaultThreadFactoryTest {
 
-    @Override
-    public void completed(final T result) {
-        this.result = result;
-        this.completed = true;
+    @Test
+    void newThread() throws Exception {
+        final ThreadFactory defaultThreadFactory = new DefaultThreadFactory("I/O server dispatch", true);
+        final CountDownLatch lockHeld = new CountDownLatch(1);
+        final Thread thread = defaultThreadFactory.newThread(lockHeld::countDown);
+        Assertions.assertNotNull(thread);
+        thread.start();
+        Assertions.assertTrue(lockHeld.await(100, TimeUnit.MILLISECONDS));
     }
 
-    public T getResult() {
-        return this.result;
-    }
-
-    public Exception getException() {
-        return this.ex;
-    }
-
-    @Override
-    public void failed(final Exception ex) {
-        this.ex = ex;
-        this.failed = true;
-    }
-
-    @Override
-    public void cancelled() {
-        this.cancelled = true;
-    }
-
-    public boolean isCompleted() {
-        return this.completed;
-    }
-
-    public boolean isFailed() {
-        return this.failed;
-    }
-
-    public boolean isCancelled() {
-        return this.cancelled;
-    }
-
-}
+}
\ No newline at end of file
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestBasicFuture.java b/httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestBasicFuture.java
index f901b0a..a95e3a8 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestBasicFuture.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestBasicFuture.java
@@ -34,12 +34,13 @@ import java.util.concurrent.TimeoutException;
 import org.apache.hc.core5.util.TimeoutValueException;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.Test;
+import org.mockito.Mockito;
 
 public class TestBasicFuture {
 
     @Test
     public void testCompleted() throws Exception {
-        final BasicFutureCallback<Object> callback = new BasicFutureCallback<>();
+        final FutureCallback<Object> callback = Mockito.mock(FutureCallback.class);
         final BasicFuture<Object> future = new BasicFuture<>(callback);
 
         Assertions.assertFalse(future.isDone());
@@ -48,11 +49,9 @@ public class TestBasicFuture {
         final Exception boom = new Exception();
         future.completed(result);
         future.failed(boom);
-        Assertions.assertTrue(callback.isCompleted());
-        Assertions.assertSame(result, callback.getResult());
-        Assertions.assertFalse(callback.isFailed());
-        Assertions.assertNull(callback.getException());
-        Assertions.assertFalse(callback.isCancelled());
+        Mockito.verify(callback).completed(result);
+        Mockito.verify(callback, Mockito.never()).failed(Mockito.any());
+        Mockito.verify(callback, Mockito.never()).cancelled();
 
         Assertions.assertSame(result, future.get());
         Assertions.assertTrue(future.isDone());
@@ -62,7 +61,7 @@ public class TestBasicFuture {
 
     @Test
     public void testCompletedWithTimeout() throws Exception {
-        final BasicFutureCallback<Object> callback = new BasicFutureCallback<>();
+        final FutureCallback<Object> callback = Mockito.mock(FutureCallback.class);
         final BasicFuture<Object> future = new BasicFuture<>(callback);
 
         Assertions.assertFalse(future.isDone());
@@ -71,11 +70,9 @@ public class TestBasicFuture {
         final Exception boom = new Exception();
         future.completed(result);
         future.failed(boom);
-        Assertions.assertTrue(callback.isCompleted());
-        Assertions.assertSame(result, callback.getResult());
-        Assertions.assertFalse(callback.isFailed());
-        Assertions.assertNull(callback.getException());
-        Assertions.assertFalse(callback.isCancelled());
+        Mockito.verify(callback).completed(result);
+        Mockito.verify(callback, Mockito.never()).failed(Mockito.any());
+        Mockito.verify(callback, Mockito.never()).cancelled();
 
         Assertions.assertSame(result, future.get(1, TimeUnit.MILLISECONDS));
         Assertions.assertTrue(future.isDone());
@@ -84,17 +81,15 @@ public class TestBasicFuture {
 
     @Test
     public void testFailed() throws Exception {
-        final BasicFutureCallback<Object> callback = new BasicFutureCallback<>();
+        final FutureCallback<Object> callback = Mockito.mock(FutureCallback.class);
         final BasicFuture<Object> future = new BasicFuture<>(callback);
         final Object result = new Object();
         final Exception boom = new Exception();
         future.failed(boom);
         future.completed(result);
-        Assertions.assertFalse(callback.isCompleted());
-        Assertions.assertNull(callback.getResult());
-        Assertions.assertTrue(callback.isFailed());
-        Assertions.assertSame(boom, callback.getException());
-        Assertions.assertFalse(callback.isCancelled());
+        Mockito.verify(callback, Mockito.never()).completed(Mockito.any());
+        Mockito.verify(callback).failed(boom);
+        Mockito.verify(callback, Mockito.never()).cancelled();
 
         try {
             future.get();
@@ -107,18 +102,16 @@ public class TestBasicFuture {
 
     @Test
     public void testCancelled() throws Exception {
-        final BasicFutureCallback<Object> callback = new BasicFutureCallback<>();
+        final FutureCallback<Object> callback = Mockito.mock(FutureCallback.class);
         final BasicFuture<Object> future = new BasicFuture<>(callback);
         final Object result = new Object();
         final Exception boom = new Exception();
         future.cancel(true);
         future.failed(boom);
         future.completed(result);
-        Assertions.assertFalse(callback.isCompleted());
-        Assertions.assertNull(callback.getResult());
-        Assertions.assertFalse(callback.isFailed());
-        Assertions.assertNull(callback.getException());
-        Assertions.assertTrue(callback.isCancelled());
+        Mockito.verify(callback, Mockito.never()).completed(Mockito.any());
+        Mockito.verify(callback, Mockito.never()).failed(Mockito.any());
+        Mockito.verify(callback).cancelled();
 
         Assertions.assertThrows(CancellationException.class, future::get);
         Assertions.assertTrue(future.isDone());
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java b/httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestCompletedFuture.java
similarity index 58%
rename from httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java
rename to httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestCompletedFuture.java
index 44236dc..93768fb 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/concurrent/BasicFutureCallback.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/concurrent/TestCompletedFuture.java
@@ -26,50 +26,20 @@
  */
 package org.apache.hc.core5.concurrent;
 
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
 
-class BasicFutureCallback<T> implements FutureCallback<T> {
+public class TestCompletedFuture {
 
-    private T result;
-    private Exception ex;
-    private boolean completed;
-    private boolean failed;
-    private boolean cancelled;
-
-    @Override
-    public void completed(final T result) {
-        this.result = result;
-        this.completed = true;
-    }
-
-    public T getResult() {
-        return this.result;
-    }
-
-    public Exception getException() {
-        return this.ex;
-    }
-
-    @Override
-    public void failed(final Exception ex) {
-        this.ex = ex;
-        this.failed = true;
-    }
-
-    @Override
-    public void cancelled() {
-        this.cancelled = true;
-    }
-
-    public boolean isCompleted() {
-        return this.completed;
-    }
-
-    public boolean isFailed() {
-        return this.failed;
-    }
-
-    public boolean isCancelled() {
-        return this.cancelled;
+    @Test
+    public void testCompleted() {
+        final Object result = new Object();
+        final CompletedFuture<Object> future = new CompletedFuture<>(result);
+        Assertions.assertSame(result, future.get());
+        Assertions.assertTrue(future.isDone());
+        Assertions.assertFalse(future.isCancelled());
+        future.cancel(true);
+        future.cancel();
     }
 
-}
+}
\ No newline at end of file
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/config/TestNamedElementChain.java b/httpcore5/src/test/java/org/apache/hc/core5/http/config/TestNamedElementChain.java
index 68683b6..e64fca9 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/config/TestNamedElementChain.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/config/TestNamedElementChain.java
@@ -28,6 +28,10 @@
 package org.apache.hc.core5.http.config;
 
 import static org.hamcrest.MatcherAssert.assertThat;
+import static org.hamcrest.Matchers.equalTo;
+import static org.hamcrest.Matchers.notNullValue;
+import static org.hamcrest.Matchers.nullValue;
+import static org.hamcrest.CoreMatchers.is;
 
 import org.hamcrest.CoreMatchers;
 import org.junit.jupiter.api.Test;
@@ -95,4 +99,24 @@ public class TestNamedElementChain {
         assertThat(list.getSize(), CoreMatchers.equalTo(2));
     }
 
-}
\ No newline at end of file
+    @Test
+    public void testFind() {
+        final NamedElementChain<Character> list = new NamedElementChain<>();
+        list.addLast('c', "c");
+        assertThat(list.find("c"), notNullValue());
+        assertThat(list.find("a"), nullValue());
+    }
+
+    @Test
+    public void testReplace() {
+        final NamedElementChain<Character> list = new NamedElementChain<>();
+        list.addLast('c', "c");
+        final boolean found = list.replace("c",'z' );
+        assertThat(found, is(true));
+        assertThat(list.find("c").getValue(), equalTo('z'));
+        assertThat(list.find("c").getName(), equalTo("c"));
+        final boolean notFound = list.replace("X",'z' );
+        assertThat(notFound, is(false));
+
+    }
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/IncomingEntityDetailsTest.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/IncomingEntityDetailsTest.java
new file mode 100644
index 0000000..71204ae
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/IncomingEntityDetailsTest.java
@@ -0,0 +1,96 @@
+/*
+ * ====================================================================
+ * 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.impl;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNull;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.util.HashSet;
+import java.util.Set;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.MessageHeaders;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.HeaderGroup;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class IncomingEntityDetailsTest {
+
+    @Test
+    public void getContentLengthEmpty() {
+        final MessageHeaders messageHeaders = new HeaderGroup();
+        final IncomingEntityDetails incomingEntityDetails = new IncomingEntityDetails(messageHeaders);
+        assertAll(
+                () -> assertEquals(-1, incomingEntityDetails.getContentLength()),
+                () -> assertNull(incomingEntityDetails.getContentType()),
+                () -> assertNull(incomingEntityDetails.getContentEncoding()),
+                () -> assertEquals(incomingEntityDetails.getTrailerNames().size(), 0)
+        );
+    }
+
+    @Test
+    public void messageHeadersNull() {
+        Assertions.assertThrows(NullPointerException.class, () -> new IncomingEntityDetails(null),
+                "Message Header Null");
+    }
+
+    @Test
+    public void getContentLength() {
+        final MessageHeaders messageHeaders = new HeaderGroup();
+        final HeaderGroup headerGroup = new HeaderGroup();
+        final Header header = new BasicHeader("name", "value");
+        headerGroup.addHeader(header);
+        final IncomingEntityDetails incomingEntityDetails = new IncomingEntityDetails(messageHeaders);
+        assertAll(
+                () -> assertEquals(-1, incomingEntityDetails.getContentLength()),
+                () -> assertTrue(incomingEntityDetails.isChunked())
+        );
+    }
+
+    @Test
+    public void getTrailerNames() {
+        final HeaderGroup messageHeaders = new HeaderGroup();
+        final Header header = new BasicHeader(HttpHeaders.TRAILER, "a, b, c, c");
+        messageHeaders.setHeaders(header);
+        final IncomingEntityDetails incomingEntityDetails = new IncomingEntityDetails(messageHeaders);
+        final Set<String> incomingSet = incomingEntityDetails.getTrailerNames();
+        assertAll(
+                () -> assertFalse(incomingSet.isEmpty()),
+                () -> assertTrue(incomingSet.containsAll(Stream.of("a", "b", "c")
+                        .collect(Collectors.toCollection(HashSet::new))))
+        );
+    }
+
+}
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpServerConnection.java b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpServerConnection.java
index 743098a..78fb072 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpServerConnection.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/impl/io/TestDefaultBHttpServerConnection.java
@@ -43,6 +43,7 @@ import org.apache.hc.core5.http.config.Http1Config;
 import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
 import org.apache.hc.core5.http.io.entity.StringEntity;
 import org.apache.hc.core5.http.message.BasicClassicHttpResponse;
+import org.apache.hc.core5.util.Timeout;
 import org.junit.jupiter.api.Assertions;
 import org.junit.jupiter.api.BeforeEach;
 import org.junit.jupiter.api.Test;
@@ -90,6 +91,11 @@ public class TestDefaultBHttpServerConnection {
         Assertions.assertEquals(Method.GET.name(), request.getMethod());
         Assertions.assertTrue(request.containsHeader("User-Agent"));
         Assertions.assertEquals(1, conn.getEndpointDetails().getRequestCount());
+        Assertions.assertNull(conn.getEndpointDetails().getRemoteAddress());
+        Assertions.assertNull(conn.getEndpointDetails().getLocalAddress());
+
+        Assertions.assertEquals(Timeout.ofMilliseconds(0), conn.getEndpointDetails().getSocketTimeout());
+        Assertions.assertEquals("null<->null", conn.getEndpointDetails().toString());
     }
 
     @Test
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilderTest.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilderTest.java
new file mode 100644
index 0000000..33a1f9e
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicRequestBuilderTest.java
@@ -0,0 +1,239 @@
+/*
+ * ====================================================================
+ * 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.support;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.net.URISyntaxException;
+import java.net.UnknownHostException;
+import java.nio.charset.StandardCharsets;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.http.io.entity.ByteArrayEntity;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.net.URIAuthority;
+import org.apache.hc.core5.net.URIBuilder;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+class ClassicRequestBuilderTest {
+
+    @Test
+    void constructor() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = new ClassicRequestBuilder(Method.HEAD);
+        assertEquals(Method.HEAD.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder2 = new ClassicRequestBuilder(Method.HEAD.name());
+        assertEquals(Method.HEAD.name(), classicRequestBuilder2.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = new ClassicRequestBuilder(Method.HEAD.name(), URIBuilder.localhost().build());
+        assertEquals(Method.HEAD.name(), classicRequestBuilder3.getMethod());
+        assertEquals(URIBuilder.localhost().getHost(), classicRequestBuilder3.getAuthority().getHostName());
+
+        final ClassicRequestBuilder classicRequestBuilder4 = new ClassicRequestBuilder(Method.HEAD, URIBuilder.localhost().build());
+        assertEquals(Method.HEAD.name(), classicRequestBuilder4.getMethod());
+        assertEquals(URIBuilder.localhost().getHost(), classicRequestBuilder4.getAuthority().getHostName());
+
+        final ClassicRequestBuilder classicRequestBuilder5 = new ClassicRequestBuilder(Method.HEAD, "/localhost");
+        assertEquals(Method.HEAD.name(), classicRequestBuilder5.getMethod());
+        assertEquals("/localhost", classicRequestBuilder5.getPath());
+
+        final ClassicRequestBuilder classicRequestBuilder6 = new ClassicRequestBuilder(Method.HEAD.name(), "/localhost");
+        assertEquals(Method.HEAD.name(), classicRequestBuilder6.getMethod());
+        assertEquals("/localhost", classicRequestBuilder6.getPath());
+    }
+
+    @Test
+    public void create() {
+        final ClassicHttpRequest classicHttpRequest = ClassicRequestBuilder.create(Method.HEAD.name()).build();
+        assertEquals(Method.HEAD.name(), classicHttpRequest.getMethod());
+    }
+
+    @Test
+    public void get() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.get();
+        assertEquals(Method.GET.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.get(URIBuilder.localhost().build());
+        assertEquals(Method.GET.name(), classicRequestBuilder1.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.get("/localhost");
+        assertEquals(Method.GET.name(), classicRequestBuilder3.getMethod());
+        assertEquals("/localhost", classicRequestBuilder3.getPath());
+    }
+
+    @Test
+    public void head() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.head();
+        assertEquals(Method.HEAD.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.head(URIBuilder.localhost().build());
+        assertEquals(Method.HEAD.name(), classicRequestBuilder1.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.head("/localhost");
+        assertEquals(Method.HEAD.name(), classicRequestBuilder3.getMethod());
+        assertEquals("/localhost", classicRequestBuilder3.getPath());
+    }
+
+    @Test
+    public void patch() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.patch();
+        assertEquals(Method.PATCH.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.patch(URIBuilder.localhost().build());
+        assertEquals(Method.PATCH.name(), classicRequestBuilder1.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.patch("/localhost");
+        assertEquals(Method.PATCH.name(), classicRequestBuilder3.getMethod());
+        assertEquals("/localhost", classicRequestBuilder3.getPath());
+    }
+
+    @Test
+    public void post() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.post();
+        assertEquals(Method.POST.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.post(URIBuilder.localhost().build());
+        assertEquals(Method.POST.name(), classicRequestBuilder1.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.post("/localhost");
+        assertEquals(Method.POST.name(), classicRequestBuilder3.getMethod());
+        assertEquals("/localhost", classicRequestBuilder3.getPath());
+    }
+
+    @Test
+    public void put() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.put();
+        assertEquals(Method.PUT.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.put(URIBuilder.localhost().build());
+        assertEquals(Method.PUT.name(), classicRequestBuilder1.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.put("/localhost");
+        assertEquals(Method.PUT.name(), classicRequestBuilder3.getMethod());
+        assertEquals("/localhost", classicRequestBuilder3.getPath());
+    }
+
+    @Test
+    public void delete() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.delete();
+        assertEquals(Method.DELETE.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.delete(URIBuilder.localhost().build());
+        assertEquals(Method.DELETE.name(), classicRequestBuilder1.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.delete("/localhost");
+        assertEquals(Method.DELETE.name(), classicRequestBuilder3.getMethod());
+        assertEquals("/localhost", classicRequestBuilder3.getPath());
+    }
+
+    @Test
+    public void trace() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.trace();
+        assertEquals(Method.TRACE.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.trace(URIBuilder.localhost().build());
+        assertEquals(Method.TRACE.name(), classicRequestBuilder1.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.trace("/localhost");
+        assertEquals(Method.TRACE.name(), classicRequestBuilder3.getMethod());
+        assertEquals("/localhost", classicRequestBuilder3.getPath());
+    }
+
+    @Test
+    public void option() throws UnknownHostException, URISyntaxException {
+        final ClassicRequestBuilder classicRequestBuilder = ClassicRequestBuilder.options();
+        assertEquals(Method.OPTIONS.name(), classicRequestBuilder.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder1 = ClassicRequestBuilder.options(URIBuilder.localhost().build());
+        assertEquals(Method.OPTIONS.name(), classicRequestBuilder1.getMethod());
+
+        final ClassicRequestBuilder classicRequestBuilder3 = ClassicRequestBuilder.options("/localhost");
+        assertEquals(Method.OPTIONS.name(), classicRequestBuilder3.getMethod());
+        assertEquals("/localhost", classicRequestBuilder3.getPath());
+    }
+
+    @Test
+    public void builder() {
+        final Header header = new BasicHeader("header2", "blah");
+        final ClassicHttpRequest classicHttpRequest = ClassicRequestBuilder.get()
+                .setVersion(HttpVersion.HTTP_1_1)
+                .setCharset(StandardCharsets.US_ASCII)
+                .setAuthority(new URIAuthority("host"))
+                .setEntity("<html><body><h1>Access denied</h1></body></html>", ContentType.TEXT_HTML)
+                .setHeader(new BasicHeader("header2", "blah"))
+                .setHeader("X-Test-Filter", "active")
+                .setHeader(header)
+                .setPath("path/")
+                .setScheme("http")
+                .addHeader(header)
+                .addHeader("header", ".addHeader(header)")
+                .addParameter(new BasicHeader("header2", "blah"))
+                .addParameter("param1", "value1")
+                .addParameters(new BasicNameValuePair("param3", "value3"), new BasicNameValuePair("param4", null))
+                .setAbsoluteRequestUri(true)
+                .setEntity(new StringEntity("requestBody"))
+                .setEntity(new ByteArrayEntity(new byte[10240], ContentType.TEXT_PLAIN))
+                .setEntity(new byte[10240], ContentType.TEXT_HTML)
+                .setEntity("requestBody")
+                .setUri("theUri")
+                .setHttpHost(new HttpHost("httpbin.org"))
+                .build();
+
+        assertAll("Should return address of Oracle's headquarter",
+                () -> assertNotNull(classicHttpRequest.getEntity()),
+                () -> assertEquals(Method.GET.name(), classicHttpRequest.getMethod()),
+                () -> assertEquals("http", classicHttpRequest.getScheme()),
+                () -> assertEquals("httpbin.org", classicHttpRequest.getAuthority().getHostName()),
+                () -> assertEquals(HttpVersion.HTTP_1_1, classicHttpRequest.getVersion()),
+                () -> assertEquals(4, classicHttpRequest.getHeaders().length),
+                () -> assertNotNull(ClassicRequestBuilder.get().toString()),
+                () -> assertEquals("http://httpbin.org/theUri?header2=blah&param1=value1&param3=value3&param4", new String(classicHttpRequest.getRequestUri().getBytes()))
+        );
+    }
+
+
+    @Test
+    public void builderTraceThrowsIllegalStateException() {
+        Assertions.assertThrows(IllegalStateException.class, () ->
+                ClassicRequestBuilder.trace()
+                        .setVersion(HttpVersion.HTTP_1_1)
+                        .setEntity(new ByteArrayEntity(new byte[10240], ContentType.TEXT_PLAIN))
+                        .build());
+    }
+
+}
\ No newline at end of file
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilderTest.java b/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilderTest.java
new file mode 100644
index 0000000..32a3777
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/io/support/ClassicResponseBuilderTest.java
@@ -0,0 +1,153 @@
+/*
+ * ====================================================================
+ * 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.support;
+
+import java.io.ByteArrayOutputStream;
+import java.io.IOException;
+import java.net.Socket;
+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.HttpException;
+import org.apache.hc.core5.http.HttpVersion;
+import org.apache.hc.core5.http.config.Http1Config;
+import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
+import org.apache.hc.core5.http.impl.io.DefaultBHttpServerConnection;
+import org.apache.hc.core5.http.impl.io.DefaultHttpRequestParserFactory;
+import org.apache.hc.core5.http.impl.io.DefaultHttpResponseWriterFactory;
+import org.apache.hc.core5.http.io.entity.StringEntity;
+import org.apache.hc.core5.http.message.BasicHeader;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
+
+class ClassicResponseBuilderTest {
+
+    @Mock
+    Socket socket;
+
+    DefaultBHttpServerConnection conn;
+
+    ByteArrayOutputStream outStream;
+
+    @BeforeEach
+    public void prepareMocks() throws IOException {
+        MockitoAnnotations.openMocks(this);
+        conn = new DefaultBHttpServerConnection("http", Http1Config.DEFAULT,
+                null, null,
+                DefaultContentLengthStrategy.INSTANCE,
+                DefaultContentLengthStrategy.INSTANCE,
+                DefaultHttpRequestParserFactory.INSTANCE,
+                DefaultHttpResponseWriterFactory.INSTANCE);
+        outStream = new ByteArrayOutputStream();
+        Mockito.when(socket.getOutputStream()).thenReturn(outStream);
+        conn.bind(socket);
+        Assertions.assertEquals(0, conn.getEndpointDetails().getResponseCount());
+    }
+
+    @Test
+    void create() throws IOException, HttpException {
+
+        final ClassicHttpResponse response = ClassicResponseBuilder.create(200)
+                .setHeader("X-Test-Filter", "active")
+                .addHeader("header1", "blah")
+                .setHeader(new BasicHeader("header2", "blah"))
+                .addHeader(new BasicHeader("header3", "blah"))
+                .setVersion(HttpVersion.HTTP_1_1)
+                .setEntity("<html><body><h1>Access OK</h1></body></html>", ContentType.TEXT_HTML)
+                .setEntity("Another entity")
+                .build();
+
+        response.addHeader("User-Agent", "test");
+
+        conn.sendResponseHeader(response);
+        conn.flush();
+
+        Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
+        Assertions.assertEquals("HTTP/1.1 200 OK\r\nX-Test-Filter: active\r\nheader1: blah\r\nheader2: blah\r\nheader3: blah\r\nUser-Agent: test\r\n\r\n", s);
+        Assertions.assertNotNull(response.getEntity());
+    }
+
+    @Test
+    void remove() throws IOException, HttpException {
+        final Header header = new BasicHeader("header2", "blah");
+        final ClassicHttpResponse response = ClassicResponseBuilder.create(200)
+                .setEntity(new StringEntity("123", ContentType.TEXT_PLAIN))
+                .setHeader("X-Test-Filter", "active")
+                .addHeader("header1", "blah")
+                .setHeader(header)
+                .addHeader(new BasicHeader("header3", "blah"))
+                .setVersion(HttpVersion.HTTP_1_1)
+                .setEntity("<html><body><h1>Access OK</h1></body></html>", ContentType.TEXT_HTML)
+                .setEntity("Another entity")
+                .removeHeader(header)
+                .build();
+
+        response.removeHeaders("X-Test-Filter");
+
+        conn.sendResponseHeader(response);
+        conn.flush();
+
+        Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
+        Assertions.assertEquals("HTTP/1.1 200 OK\r\nheader1: blah\r\nheader3: blah\r\n\r\n", s);
+        Assertions.assertNotNull(response.getEntity());
+
+    }
+
+    @Test
+    void copy() throws IOException, HttpException {
+        final Header header = new BasicHeader("header2", "blah");
+        final ClassicHttpResponse response = ClassicResponseBuilder.create(200)
+                .setEntity(new StringEntity("123", ContentType.TEXT_PLAIN))
+                .addHeader("header1", "blah")
+                .setHeader(header)
+                .addHeader(new BasicHeader("header3", "blah"))
+                .setVersion(HttpVersion.HTTP_1_1)
+                .setEntity("<html><body><h1>Access OK</h1></body></html>", ContentType.TEXT_HTML)
+                .setEntity("Another entity")
+                .removeHeader(header)
+                .build();
+
+        final ClassicResponseBuilder classicResponseBuilder = ClassicResponseBuilder.copy(response);
+        final ClassicHttpResponse response2 = classicResponseBuilder.build();
+        conn.sendResponseHeader(response2);
+        conn.flush();
+
+        Assertions.assertEquals(1, conn.getEndpointDetails().getResponseCount());
+        final String s = new String(outStream.toByteArray(), StandardCharsets.US_ASCII);
+        Assertions.assertEquals("HTTP/1.1 200 OK\r\nheader1: blah\r\nheader3: blah\r\n\r\n", s);
+        Assertions.assertNotNull(response.getEntity());
+        Assertions.assertNotNull(classicResponseBuilder.toString());
+    }
+}
\ No newline at end of file
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/message/HttpRequestWrapperTest.java b/httpcore5/src/test/java/org/apache/hc/core5/http/message/HttpRequestWrapperTest.java
new file mode 100644
index 0000000..a1b0636
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/message/HttpRequestWrapperTest.java
@@ -0,0 +1,162 @@
+/*
+ * ====================================================================
+ * 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.message;
+
+import java.net.URI;
+
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.net.URIAuthority;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class HttpRequestWrapperTest {
+
+    @Test
+    public void testRequestBasics() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, "/stuff");
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/stuff", httpRequestWrapper.getPath());
+        Assertions.assertNull(httpRequestWrapper.getAuthority());
+        Assertions.assertEquals(new URI("/stuff"), httpRequestWrapper.getUri());
+        httpRequestWrapper.setPath("/another-stuff");
+        Assertions.assertEquals("/another-stuff", httpRequestWrapper.getPath());
+    }
+
+    @Test
+    public void testDefaultRequestConstructors() {
+        final HttpRequest request1 = new BasicHttpRequest("WHATEVER", "/");
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request1);
+        Assertions.assertEquals("WHATEVER", httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/", httpRequestWrapper.getPath());
+
+        final HttpRequest request2 = new BasicHttpRequest(Method.GET, "/");
+        final HttpRequestWrapper httpRequestWrapper2 = new HttpRequestWrapper(request2);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper2.getMethod());
+        Assertions.assertEquals("/", httpRequestWrapper2.getPath());
+
+        Assertions.assertThrows(NullPointerException.class, () ->
+                new BasicHttpRequest(Method.GET, (URI) null));
+    }
+
+
+    @Test
+    public void testRequestWithRelativeURI() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("/stuff"));
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/stuff", httpRequestWrapper.getPath());
+        Assertions.assertNull(httpRequestWrapper.getAuthority());
+        Assertions.assertEquals(new URI("/stuff"), httpRequestWrapper.getUri());
+    }
+
+    @Test
+    public void testRequestWithAbsoluteURI() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("https://host:9443/stuff?param=value"));
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/stuff?param=value", httpRequestWrapper.getPath());
+        Assertions.assertEquals(new URIAuthority("host", 9443), httpRequestWrapper.getAuthority());
+        Assertions.assertEquals("https", httpRequestWrapper.getScheme());
+        Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), httpRequestWrapper.getUri());
+        httpRequestWrapper.setScheme((URIScheme.HTTP.id));
+        Assertions.assertEquals("http", httpRequestWrapper.getScheme());
+    }
+
+    @Test
+    public void testRequestWithAbsoluteURIAsPath() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, "https://host:9443/stuff?param=value");
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/stuff?param=value", httpRequestWrapper.getPath());
+        Assertions.assertEquals(new URIAuthority("host", 9443), httpRequestWrapper.getAuthority());
+        Assertions.assertEquals("https", httpRequestWrapper.getScheme());
+        Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), httpRequestWrapper.getUri());
+    }
+
+    @Test
+    public void testRequestWithNoPath() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("http://host"));
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/", httpRequestWrapper.getPath());
+        Assertions.assertEquals(new URIAuthority("host"), httpRequestWrapper.getAuthority());
+        Assertions.assertEquals("http", httpRequestWrapper.getScheme());
+        Assertions.assertEquals(new URI("http://host/"), httpRequestWrapper.getUri());
+    }
+
+    @Test
+    public void testRequestWithUserInfo() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, new URI("https://user:pwd@host:9443/stuff?param=value"));
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/stuff?param=value", httpRequestWrapper.getPath());
+        Assertions.assertEquals(new URIAuthority("user:pwd", "host", 9443), httpRequestWrapper.getAuthority());
+        Assertions.assertEquals("https", httpRequestWrapper.getScheme());
+        Assertions.assertEquals(new URI("https://host:9443/stuff?param=value"), httpRequestWrapper.getUri());
+    }
+
+    @Test
+    public void testRequestWithAuthority() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, new HttpHost("http", "somehost", -1), "/stuff");
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/stuff", httpRequestWrapper.getPath());
+        Assertions.assertEquals(new URIAuthority("somehost"), httpRequestWrapper.getAuthority());
+        Assertions.assertEquals(new URI("http://somehost/stuff"), httpRequestWrapper.getUri());
+
+        httpRequestWrapper.setAuthority(new URIAuthority("newHost"));
+        Assertions.assertEquals(new URIAuthority("newHost"), httpRequestWrapper.getAuthority());
+
+    }
+
+    @Test
+    public void testRequestWithAuthorityRelativePath() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, new HttpHost("http", "somehost", -1), "stuff");
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("stuff", httpRequestWrapper.getPath());
+        Assertions.assertEquals("stuff", httpRequestWrapper.getRequestUri());
+        Assertions.assertEquals(new URIAuthority("somehost"), httpRequestWrapper.getAuthority());
+        Assertions.assertEquals(new URI("http://somehost/stuff"), httpRequestWrapper.getUri());
+    }
+
+    @Test
+    public void testRequestHostWithReservedChars() throws Exception {
+        final HttpRequest request = new BasicHttpRequest(Method.GET, URI.create("http://someuser%21@%21example%21.com/stuff"));
+        final HttpRequestWrapper httpRequestWrapper = new HttpRequestWrapper(request);
+        Assertions.assertEquals(Method.GET.name(), httpRequestWrapper.getMethod());
+        Assertions.assertEquals("/stuff", httpRequestWrapper.getPath());
+        Assertions.assertEquals(new URIAuthority("someuser%21", "%21example%21.com", -1), httpRequestWrapper.getAuthority());
+        Assertions.assertEquals(new URI("http://%21example%21.com/stuff"), httpRequestWrapper.getUri());
+    }
+}
\ No newline at end of file
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/message/HttpResponseWrapperTest.java b/httpcore5/src/test/java/org/apache/hc/core5/http/message/HttpResponseWrapperTest.java
new file mode 100644
index 0000000..8630b73
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/message/HttpResponseWrapperTest.java
@@ -0,0 +1,85 @@
+/*
+ * ====================================================================
+ * 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.message;
+
+import java.util.Locale;
+
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.HttpStatus;
+import org.junit.jupiter.api.Assertions;
+import org.junit.jupiter.api.Test;
+
+public class HttpResponseWrapperTest {
+
+    @Test
+    void testDefaultResponseConstructors() {
+        final HttpResponse response1 = new BasicHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request");
+        final HttpResponseWrapper httpResponseWrapper1 = new HttpResponseWrapper(response1);
+
+        Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, httpResponseWrapper1.getCode());
+
+        final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "whatever");
+        final HttpResponseWrapper httpResponseWrapper2 = new HttpResponseWrapper(response2);
+        Assertions.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, httpResponseWrapper2.getCode());
+        Assertions.assertEquals("whatever", httpResponseWrapper2.getReasonPhrase());
+
+        httpResponseWrapper2.setReasonPhrase("another-whatever");
+        Assertions.assertEquals("another-whatever", httpResponseWrapper2.getReasonPhrase());
+    }
+
+    @Test
+    void testSetResponseStatus() {
+        final HttpResponse response1 = new BasicHttpResponse(200, "OK");
+        final HttpResponseWrapper httpResponseWrapper1 = new HttpResponseWrapper(response1);
+
+        Assertions.assertNotNull(httpResponseWrapper1.getCode());
+        Assertions.assertEquals(200, httpResponseWrapper1.getCode());
+
+        final HttpResponse response2 = new BasicHttpResponse(HttpStatus.SC_BAD_REQUEST, "Bad Request");
+        final HttpResponseWrapper httpResponseWrapper2 = new HttpResponseWrapper(response2);
+        Assertions.assertEquals(HttpStatus.SC_BAD_REQUEST, httpResponseWrapper2.getCode());
+
+        final HttpResponse response3 = new BasicHttpResponse(HttpStatus.SC_INTERNAL_SERVER_ERROR, "whatever");
+        final HttpResponseWrapper httpResponseWrapper3 = new HttpResponseWrapper(response3);
+        Assertions.assertEquals(HttpStatus.SC_INTERNAL_SERVER_ERROR, httpResponseWrapper3.getCode());
+        Assertions.assertEquals("whatever", httpResponseWrapper3.getReasonPhrase());
+
+        final HttpResponse response4 = new BasicHttpResponse(HttpStatus.SC_OK, "OK");
+        final HttpResponseWrapper httpResponseWrapper4 = new HttpResponseWrapper(response4);
+        Assertions.assertThrows(IllegalArgumentException.class, () -> httpResponseWrapper4.setCode(-23));
+    }
+
+    @Test
+    void testLocale() {
+        final HttpResponse response = new BasicHttpResponse(200, "OK");
+        final HttpResponseWrapper httpResponseWrapper = new HttpResponseWrapper(response);
+        httpResponseWrapper.setLocale(Locale.US);
+        Assertions.assertEquals("US", httpResponseWrapper.getLocale().getCountry());
+    }
+
+}
\ No newline at end of file
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java
index 234b831..eeec5d7 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/http/ssl/TestTlsCiphers.java
@@ -74,4 +74,58 @@ public class TestTlsCiphers {
         }
     }
 
+   @Test
+    void excludeH2Blacklisted (){
+       final String[] mixCipherSuites = {
+               "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+               "TLS_RSA_WITH_AES_256_CBC_SHA256",
+               "AES_SHA_US",
+               "TLS_RSA_WITH_AES_128_CBC_SHA",
+               "NULL_SHA",
+               "TLS_RSA_WITH_AES_256_GCM_SHA384"
+       };
+
+       final String[] strongCipherSuites = TlsCiphers.excludeH2Blacklisted(mixCipherSuites);
+       for (final String cipherSuite : strongCipherSuites) {
+           Assertions.assertFalse(TlsCiphers.isWeak(cipherSuite));
+       }
+   }
+
+    @Test
+    void excludeWeak (){
+        final String[] weakCiphersSuites = {
+                "SSL_RSA_WITH_RC4_128_SHA",
+                "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+                "TLS_DH_anon_WITH_AES_128_CBC_SHA",
+                "SSL_RSA_EXPORT_WITH_DES40_CBC_SHA",
+                "SSL_RSA_WITH_NULL_SHA",
+                "SSL_RSA_WITH_3DES_EDE_CBC_SHA",
+                "TLS_ECDHE_ECDSA_WITH_RC4_128_SHA",
+                "TLS_ECDH_ECDSA_WITH_3DES_EDE_CBC_SHA",
+                "TLS_DH_anon_WITH_AES_256_GCM_SHA384",
+                "TLS_ECDH_anon_WITH_AES_256_CBC_SHA",
+                "TLS_RSA_WITH_NULL_SHA256",
+                "SSL_RSA_EXPORT_WITH_RC4_40_MD5",
+                "SSL_DH_anon_EXPORT_WITH_RC4_40_MD5",
+                "TLS_KRB5_EXPORT_WITH_RC4_40_SHA",
+                "SSL_RSA_EXPORT_WITH_RC2_CBC_40_MD5",
+                "TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA384",
+                "TLS_RSA_WITH_AES_256_CBC_SHA256",
+                "TLS_DHE_RSA_WITH_AES_256_CBC_SHA256",
+                "TLS_RSA_WITH_AES_128_CBC_SHA",
+                "TLS_DHE_DSS_WITH_AES_128_CBC_SHA",
+                "TLS_RSA_WITH_AES_256_GCM_SHA384"
+        };
+
+        final String[] strongCipherSuites = TlsCiphers.excludeWeak(weakCiphersSuites);
+        for (final String cipherSuite : strongCipherSuites) {
+            Assertions.assertFalse(TlsCiphers.isWeak(cipherSuite));
+        }
+    }
+
+    @Test
+    void excludeWeakNull(){
+        Assertions.assertNull(TlsCiphers.excludeWeak(null));
+    }
+
 }
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/pool/TestLaxConnPool.java b/httpcore5/src/test/java/org/apache/hc/core5/pool/TestLaxConnPool.java
index d9a6cee..2677b31 100644
--- a/httpcore5/src/test/java/org/apache/hc/core5/pool/TestLaxConnPool.java
+++ b/httpcore5/src/test/java/org/apache/hc/core5/pool/TestLaxConnPool.java
@@ -319,6 +319,8 @@ public class TestLaxConnPool {
         Assertions.assertEquals(0, stats.getAvailable());
         Assertions.assertEquals(0, stats.getLeased());
         Assertions.assertEquals(0, stats.getPending());
+
+        Assertions.assertFalse( pool.isShutdown());
     }
 
     @Test
@@ -390,4 +392,23 @@ public class TestLaxConnPool {
         pool.release(new PoolEntry<>("somehost"), true);
     }
 
+    @Test
+    public void testClose() {
+        final LaxConnPool<String, HttpConnection> pool = new LaxConnPool<>(2);
+        pool.setMaxPerRoute("someRoute", 2);
+        pool.close();
+        Assertions.assertThrows(IllegalStateException.class, () -> pool.lease("someHost", null));
+        // Ignored if shut down
+        pool.release(new PoolEntry<>("someHost"), true);
+
+    }
+
+    @Test
+    public void testGetMaxPerRoute() {
+        final String route  = "someRoute";
+        final int max = 2;
+        final LaxConnPool<String, HttpConnection> pool = new LaxConnPool<>(2);
+        pool.setMaxPerRoute(route, max);
+        Assertions.assertEquals(max, pool.getMaxPerRoute(route));
+    }
 }
diff --git a/httpcore5/src/test/java/org/apache/hc/core5/ssl/SSLContextsTest.java b/httpcore5/src/test/java/org/apache/hc/core5/ssl/SSLContextsTest.java
new file mode 100644
index 0000000..49e0867
--- /dev/null
+++ b/httpcore5/src/test/java/org/apache/hc/core5/ssl/SSLContextsTest.java
@@ -0,0 +1,88 @@
+/*
+ * ====================================================================
+ * 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.ssl;
+
+import static org.junit.jupiter.api.Assertions.assertAll;
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
+
+import java.security.KeyManagementException;
+import java.security.KeyStore;
+import java.security.KeyStoreException;
+import java.security.NoSuchAlgorithmException;
+import java.security.UnrecoverableKeyException;
+
+import javax.net.ssl.KeyManagerFactory;
+import javax.net.ssl.SSLContext;
+import javax.net.ssl.TrustManagerFactory;
+
+import org.junit.jupiter.api.Test;
+
+public class SSLContextsTest {
+
+    @Test
+    void createDefault() {
+        final SSLContext sslContext = SSLContexts.createDefault();
+        assertAll(
+                () -> assertNotNull(sslContext),
+                () -> assertEquals(SSLContextBuilder.TLS, sslContext.getProtocol()),
+                () -> assertNotNull(sslContext.getProvider())
+        );
+    }
+
+    @Test
+    void createSystemDefault() {
+        final SSLContext sslContext = SSLContexts.createSystemDefault();
+        assertAll(
+                () -> assertNotNull(sslContext),
+                () -> assertEquals("Default", sslContext.getProtocol()),
+                () -> assertNotNull(sslContext.getProvider())
+        );
+    }
+
+    @Test
+    void custom() throws NoSuchAlgorithmException, KeyStoreException, UnrecoverableKeyException, KeyManagementException {
+
+        final SSLContext sslContext = SSLContexts.custom()
+                .setKeyStoreType(KeyStore.getDefaultType())
+                .setKeyManagerFactoryAlgorithm(KeyManagerFactory.getDefaultAlgorithm())
+                .setTrustManagerFactoryAlgorithm(TrustManagerFactory.getDefaultAlgorithm())
+                .setProvider("SunJSSE")
+                .setProtocol("TLS")
+                .setSecureRandom(null)
+                .loadTrustMaterial((KeyStore) null, null)
+                .loadKeyMaterial((KeyStore) null, null, null)
+                .build();
+
+        assertAll(
+                () -> assertNotNull(sslContext),
+                () -> assertEquals(SSLContextBuilder.TLS, sslContext.getProtocol()),
+                () -> assertEquals("SunJSSE", sslContext.getProvider().getName())
+        );
+    }
+}
\ No newline at end of file