diff --git a/pom.xml b/pom.xml
index d1bc3b9..bb5ec03 100644
--- a/pom.xml
+++ b/pom.xml
@@ -39,6 +39,11 @@
2005
pom
+
+ 17
+ 17
+
+
scm:git:https://gitbox.apache.org/repos/asf/httpcomponents-website.git
scm:git:https://gitbox.apache.org/repos/asf/httpcomponents-website.git
diff --git a/samples/pom.xml b/samples/pom.xml
index ff0eea4..3930d36 100644
--- a/samples/pom.xml
+++ b/samples/pom.xml
@@ -38,12 +38,9 @@
jar
- 1.8
- 1.8
4.5.14
- 5.6
- 2.20.1
- 0.4.0
+ 5.5-beta1
+ 5.6.1
@@ -57,6 +54,16 @@
fluent-hc
${hc4.client.version}
+
+ org.apache.httpcomponents.core5
+ httpcore5-jackson2
+ ${hc5.core.version}
+
+
+ org.apache.httpcomponents.core5
+ httpcore5-testing
+ ${hc5.core.version}
+
org.apache.httpcomponents.client5
httpclient5
@@ -67,16 +74,6 @@
httpclient5-fluent
${hc5.client.version}
-
- com.fasterxml.jackson.core
- jackson-databind
- ${jackson.version}
-
-
- com.github.ok2c.hc5
- hc5-async-json
- ${asyncjson.version}
-
diff --git a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient4Example.java b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient4Example.java
index 21221b6..a5ee654 100644
--- a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient4Example.java
+++ b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient4Example.java
@@ -51,7 +51,7 @@ public class HttpClient4Example {
public static void main(String... args) throws Exception {
CloseableHttpClient client = HttpClients.custom()
.setSSLSocketFactory(new SSLConnectionSocketFactory(
- SSLContexts.createSystemDefault(),
+ SSLContexts.createDefault(),
new String[] { "TLSv1.2" },
null,
SSLConnectionSocketFactory.getDefaultHostnameVerifier()))
diff --git a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncSimpleExample.java b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncSimpleExample.java
index a55e1f7..f9c9123 100644
--- a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncSimpleExample.java
+++ b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncSimpleExample.java
@@ -59,7 +59,7 @@ public class HttpClient5AsyncSimpleExample {
public static void main(String... args) throws Exception {
PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(ClientTlsStrategyBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(SSLContexts.createDefault())
.setTlsVersions(TLS.V_1_3)
.buildAsync())
.setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
diff --git a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncStreamExample.java b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncStreamExample.java
index 6881879..f977e5f 100644
--- a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncStreamExample.java
+++ b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncStreamExample.java
@@ -22,8 +22,6 @@
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.ok2c.hc5.json.http.JsonRequestProducers;
-import com.ok2c.hc5.json.http.JsonResponseConsumers;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.config.ConnectionConfig;
@@ -46,6 +44,8 @@
import org.apache.hc.core5.http.support.BasicRequestBuilder;
import org.apache.hc.core5.http2.HttpVersionPolicy;
import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.jackson2.http.JsonRequestProducers;
+import org.apache.hc.core5.jackson2.http.JsonResponseConsumers;
import org.apache.hc.core5.pool.PoolConcurrencyPolicy;
import org.apache.hc.core5.pool.PoolReusePolicy;
import org.apache.hc.core5.reactor.IOReactorConfig;
@@ -58,7 +58,7 @@ public class HttpClient5AsyncStreamExample {
public static void main(String... args) throws Exception {
PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(ClientTlsStrategyBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(SSLContexts.createDefault())
.setTlsVersions(TLS.V_1_3)
.buildAsync())
.setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
@@ -107,7 +107,7 @@ public static void main(String... args) throws Exception {
new org.apache.http.message.BasicNameValuePair("name2", "value2")),
objectMapper),
JsonResponseConsumers.create(jsonFactory),
- new FutureCallback>() {
+ new FutureCallback<>() {
@Override
public void completed(Message message) {
diff --git a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncStreamHttp2Example.java b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncStreamHttp2Example.java
index 0ecc35a..6332843 100644
--- a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncStreamHttp2Example.java
+++ b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5AsyncStreamHttp2Example.java
@@ -16,15 +16,11 @@
package org.apache.hc.client5.migration.examples;
-import java.util.Arrays;
-import java.util.concurrent.Future;
-
import com.fasterxml.jackson.core.JsonFactory;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import com.ok2c.hc5.json.http.JsonRequestProducers;
-import com.ok2c.hc5.json.http.JsonResponseConsumers;
-
+import java.util.Arrays;
+import java.util.concurrent.Future;
import org.apache.hc.client5.http.auth.CredentialsProvider;
import org.apache.hc.client5.http.config.ConnectionConfig;
import org.apache.hc.client5.http.config.RequestConfig;
@@ -43,6 +39,8 @@
import org.apache.hc.core5.http.ssl.TLS;
import org.apache.hc.core5.http.support.BasicRequestBuilder;
import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.jackson2.http.JsonRequestProducers;
+import org.apache.hc.core5.jackson2.http.JsonResponseConsumers;
import org.apache.hc.core5.reactor.IOReactorConfig;
import org.apache.hc.core5.ssl.SSLContexts;
import org.apache.hc.core5.util.TimeValue;
@@ -53,7 +51,7 @@ public class HttpClient5AsyncStreamHttp2Example {
public static void main(String... args) throws Exception {
CloseableHttpAsyncClient client = HttpAsyncClients.customHttp2()
.setTlsStrategy(ClientTlsStrategyBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(SSLContexts.createDefault())
.setTlsVersions(TLS.V_1_3)
.buildAsync())
.setIOReactorConfig(IOReactorConfig.custom()
diff --git a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5ClassicExample.java b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5ClassicExample.java
index 7dda82b..a96d1bd 100644
--- a/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5ClassicExample.java
+++ b/samples/src/main/java/org/apache/hc/client5/migration/examples/HttpClient5ClassicExample.java
@@ -58,7 +58,7 @@ public class HttpClient5ClassicExample {
public static void main(String... args) throws Exception {
PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(ClientTlsStrategyBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(SSLContexts.createDefault())
.setTlsVersions(TLS.V_1_3)
.buildClassic())
.setDefaultSocketConfig(SocketConfig.custom()
diff --git a/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncRequesterBootstrapExample.java b/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncRequesterBootstrapExample.java
new file mode 100644
index 0000000..514dea9
--- /dev/null
+++ b/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncRequesterBootstrapExample.java
@@ -0,0 +1,121 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+package org.apache.hc.core5.http.examples;
+
+import java.util.concurrent.CountDownLatch;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.Message;
+import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.jackson2.http.AsyncJsonClientPipeline;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.testing.classic.LoggingConnPoolListener;
+import org.apache.hc.core5.testing.nio.LoggingExceptionCallback;
+import org.apache.hc.core5.testing.nio.LoggingH2StreamListener;
+import org.apache.hc.core5.testing.nio.LoggingHttp1StreamListener;
+import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator;
+import org.apache.hc.core5.testing.nio.LoggingIOSessionListener;
+import org.apache.hc.core5.testing.nio.LoggingReactorMetricsListener;
+import org.apache.hc.core5.util.Timeout;
+
+public class AsyncRequesterBootstrapExample {
+
+ public static void main(final String[] args) throws Exception {
+
+ final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
+ .setIOReactorConfig(IOReactorConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+ .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
+
+ // Pluggable logging
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT)
+ .setStreamListener(LoggingH2StreamListener.INSTANCE)
+ .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
+ .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
+ .setExceptionCallback(LoggingExceptionCallback.INSTANCE)
+ .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
+ .setIOReactorMetricsListener(LoggingReactorMetricsListener.INSTANCE)
+
+ .create();
+ requester.start();
+
+ final HttpHost target = new HttpHost("httpbin.org");
+ final HttpCoreContext context = HttpCoreContext.create();
+
+ final ObjectMapper objectMapper = new ObjectMapper();
+
+ final CountDownLatch latch = new CountDownLatch(1);
+ requester.execute(
+ AsyncJsonClientPipeline.assemble(objectMapper)
+ .request()
+ .post(target, "/post")
+ .asObject(new BasicNameValuePair("name", "value"))
+ .response()
+ .asObject(RequestData.class)
+ .result(new FutureCallback<>() {
+
+ @Override
+ public void completed(final Message m) {
+ final HttpResponse response = m.head();
+ final RequestData requestData = m.body();
+ System.out.println(target + "->" + response.getCode());
+ System.out.println(requestData);
+ latch.countDown();
+ }
+
+ @Override
+ public void failed(final Exception ex) {
+ ex.printStackTrace(System.out);
+ latch.countDown();
+ }
+
+ @Override
+ public void cancelled() {
+ latch.countDown();
+ }
+
+ })
+ .create(),
+ Timeout.ofMinutes(1),
+ context);
+
+ latch.await();
+
+ requester.close(CloseMode.GRACEFUL);
+ }
+
+}
diff --git a/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncRequesterMultiplexingExample.java b/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncRequesterMultiplexingExample.java
new file mode 100644
index 0000000..92b0306
--- /dev/null
+++ b/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncRequesterMultiplexingExample.java
@@ -0,0 +1,136 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+package org.apache.hc.core5.http.examples;
+
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.Future;
+import java.util.concurrent.atomic.AtomicBoolean;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.hc.core5.concurrent.FutureCallback;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.HttpResponse;
+import org.apache.hc.core5.http.Message;
+import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncRequester;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.http.nio.AsyncClientEndpoint;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.apache.hc.core5.http2.HttpVersionPolicy;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.H2RequesterBootstrap;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.jackson2.http.AsyncJsonClientPipeline;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.testing.classic.LoggingConnPoolListener;
+import org.apache.hc.core5.testing.nio.LoggingExceptionCallback;
+import org.apache.hc.core5.testing.nio.LoggingH2StreamListener;
+import org.apache.hc.core5.testing.nio.LoggingHttp1StreamListener;
+import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator;
+import org.apache.hc.core5.testing.nio.LoggingIOSessionListener;
+import org.apache.hc.core5.testing.nio.LoggingReactorMetricsListener;
+import org.apache.hc.core5.util.Timeout;
+
+public class AsyncRequesterMultiplexingExample {
+
+ public static void main(final String[] args) throws Exception {
+
+ final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
+ .setIOReactorConfig(IOReactorConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+ .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
+
+ // Pluggable logging
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT)
+ .setStreamListener(LoggingH2StreamListener.INSTANCE)
+ .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
+ .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
+ .setExceptionCallback(LoggingExceptionCallback.INSTANCE)
+ .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
+ .setIOReactorMetricsListener(LoggingReactorMetricsListener.INSTANCE)
+
+ .create();
+ requester.start();
+
+ final HttpHost target = new HttpHost("nghttp2.org");
+
+ final ObjectMapper objectMapper = new ObjectMapper();
+
+ final Future future = requester.connect(target, Timeout.ofSeconds(30));
+ final AsyncClientEndpoint clientEndpoint = future.get();
+
+ int n = 3;
+ final CountDownLatch latch = new CountDownLatch(n);
+ final AtomicBoolean failure = new AtomicBoolean();
+ for (int i = 1; i <= 3; i++) {
+ requester.execute(
+ AsyncJsonClientPipeline.assemble(objectMapper)
+ .request()
+ .post(target, "/post")
+ .asObject(new BasicNameValuePair("name", "value"))
+ .response()
+ .asObject(RequestData.class)
+ .result(new FutureCallback<>() {
+
+ @Override
+ public void completed(final Message m) {
+ final HttpResponse response = m.head();
+ final RequestData requestData = m.body();
+ System.out.println(target + "->" + response.getCode());
+ System.out.println(requestData);
+ latch.countDown();
+ }
+
+ @Override
+ public void failed(final Exception ex) {
+ ex.printStackTrace(System.out);
+ failure.set(true);
+ latch.countDown();
+ }
+
+ @Override
+ public void cancelled() {
+ latch.countDown();
+ }
+
+ })
+ .create(),
+ Timeout.ofMinutes(1),
+ HttpCoreContext.create());
+ }
+
+ latch.await();
+ if (failure.get()) {
+ clientEndpoint.releaseAndDiscard();
+ } else {
+ clientEndpoint.releaseAndReuse();
+ }
+
+ requester.close(CloseMode.GRACEFUL);
+ }
+
+}
diff --git a/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncServerBootstrapExample.java b/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncServerBootstrapExample.java
new file mode 100644
index 0000000..9051a01
--- /dev/null
+++ b/samples/src/main/java/org/apache/hc/core5/http/examples/AsyncServerBootstrapExample.java
@@ -0,0 +1,170 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+package org.apache.hc.core5.http.examples;
+
+import java.net.InetSocketAddress;
+import java.net.URISyntaxException;
+import java.util.Objects;
+import java.util.concurrent.Future;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.hc.core5.function.Supplier;
+import org.apache.hc.core5.http.EndpointDetails;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpStatus;
+import org.apache.hc.core5.http.Message;
+import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.URIScheme;
+import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
+import org.apache.hc.core5.http.impl.bootstrap.StandardFilter;
+import org.apache.hc.core5.http.impl.routing.RequestRouter;
+import org.apache.hc.core5.http.message.BasicHttpResponse;
+import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
+import org.apache.hc.core5.http.nio.support.AbstractAsyncServerAuthFilter;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.apache.hc.core5.http2.impl.nio.bootstrap.H2ServerBootstrap;
+import org.apache.hc.core5.jackson2.http.AsyncJsonServerPipeline;
+import org.apache.hc.core5.net.URIAuthority;
+import org.apache.hc.core5.reactor.IOReactorConfig;
+import org.apache.hc.core5.reactor.ListenerEndpoint;
+import org.apache.hc.core5.testing.nio.LoggingExceptionCallback;
+import org.apache.hc.core5.testing.nio.LoggingH2StreamListener;
+import org.apache.hc.core5.testing.nio.LoggingHttp1StreamListener;
+import org.apache.hc.core5.testing.nio.LoggingIOSessionDecorator;
+import org.apache.hc.core5.testing.nio.LoggingIOSessionListener;
+import org.apache.hc.core5.testing.nio.LoggingReactorMetricsListener;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+
+public class AsyncServerBootstrapExample {
+
+ /**
+ * Example command line args: {@code 8080}
+ */
+ public static void main(final String[] args) throws Exception {
+ int port = 8080;
+ if (args.length >= 1) {
+ port = Integer.parseInt(args[1]);
+ }
+
+ final ObjectMapper objectMapper = new ObjectMapper();
+
+ final Supplier exchangeHandlerSupplier = AsyncJsonServerPipeline.assemble(objectMapper)
+ // Read GET / HEAD requests by consuming content stream as JSON nodes
+ .request(Method.GET, Method.HEAD, Method.POST, Method.PUT, Method.PATCH)
+ .asJsonNode()
+ // Write out responses by streaming out content of JSON object
+ .response()
+ .asObject(RequestData.class)
+ // Map exceptions to a response message
+ .errorMessage(Throwable::getMessage)
+ // Generate a response to a request
+ .handle((m, context) -> {
+ final HttpRequest request = m.head();
+ final RequestData rd = new RequestData();
+ try {
+ rd.setUrl(request.getUri());
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException("Invalid request URI");
+ }
+ rd.generateHeaders(request.getHeaders());
+ rd.setJson(m.body());
+ rd.setData(Objects.toString(m.error()));
+
+ final HttpCoreContext coreContext = HttpCoreContext.cast(context);
+ final EndpointDetails endpointDetails = coreContext.getEndpointDetails();
+
+ final InetSocketAddress remoteAddress = (InetSocketAddress) endpointDetails.getRemoteAddress();
+ rd.setOrigin(Objects.toString(remoteAddress.getAddress()));
+
+ return Message.of(new BasicHttpResponse(HttpStatus.SC_OK), rd);
+ })
+ .supplier();
+
+ final AbstractAsyncServerAuthFilter authFilter = new AbstractAsyncServerAuthFilter<>(true) {
+
+ @Override
+ protected String parseChallengeResponse(
+ final String authorizationValue, final HttpContext context) throws HttpException {
+ // Parse the authorizaton header value into a custom token representation
+ return authorizationValue;
+ }
+
+ @Override
+ protected boolean authenticate(
+ final String token,
+ final URIAuthority authority,
+ final String requestUri,
+ final HttpContext context) {
+ // Validate token and return true if the user has been autheticated
+ return "let me pass".equals(token);
+ }
+
+ @Override
+ protected String generateChallenge(
+ final String token,
+ final URIAuthority authority,
+ final String requestUri,
+ final HttpContext context) {
+ // Generate a challenge in case the user has not been autheticated
+ return "who goes there?";
+ }
+
+ };
+
+ final HttpAsyncServer server = H2ServerBootstrap.bootstrap()
+ .setIOReactorConfig(IOReactorConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+ .setRequestRouter(RequestRouter.>builder()
+ .addRoute(RequestRouter.LOCAL_AUTHORITY, "*", exchangeHandlerSupplier)
+ .resolveAuthority(RequestRouter.LOCAL_AUTHORITY_RESOLVER)
+ .build())
+ .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), authFilter)
+
+ // Pluggable logging
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE_SERVER)
+ .setStreamListener(LoggingH2StreamListener.INSTANCE)
+ .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
+ .setExceptionCallback(LoggingExceptionCallback.INSTANCE)
+ .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
+ .setIOReactorMetricsListener(LoggingReactorMetricsListener.INSTANCE)
+
+ .create();
+
+ server.start();
+ final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP);
+ final ListenerEndpoint listenerEndpoint = future.get();
+ System.out.println("Listening on " + listenerEndpoint.getAddress());
+ server.awaitShutdown(TimeValue.MAX_VALUE);
+ }
+
+}
\ No newline at end of file
diff --git a/samples/src/main/java/org/apache/hc/core5/http/examples/ClassicRequesterBootstrapExample.java b/samples/src/main/java/org/apache/hc/core5/http/examples/ClassicRequesterBootstrapExample.java
new file mode 100644
index 0000000..7189282
--- /dev/null
+++ b/samples/src/main/java/org/apache/hc/core5/http/examples/ClassicRequesterBootstrapExample.java
@@ -0,0 +1,92 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+
+package org.apache.hc.core5.http.examples;
+
+import java.io.InputStream;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.hc.core5.http.ClassicHttpRequest;
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpHost;
+import org.apache.hc.core5.http.impl.bootstrap.HttpRequester;
+import org.apache.hc.core5.http.impl.bootstrap.RequesterBootstrap;
+import org.apache.hc.core5.http.io.SocketConfig;
+import org.apache.hc.core5.http.io.entity.EntityTemplate;
+import org.apache.hc.core5.http.io.support.ClassicRequestBuilder;
+import org.apache.hc.core5.http.message.BasicNameValuePair;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.apache.hc.core5.io.CloseMode;
+import org.apache.hc.core5.testing.classic.LoggingBHttpClientConnectionFactory;
+import org.apache.hc.core5.testing.classic.LoggingHttp1StreamListener;
+import org.apache.hc.core5.util.Timeout;
+
+public class ClassicRequesterBootstrapExample {
+
+ public static void main(final String[] args) throws Exception {
+ final HttpRequester requester = RequesterBootstrap.bootstrap()
+ .setSocketConfig(SocketConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+
+ // Pluggable logging
+ .setConnectionFactory(LoggingBHttpClientConnectionFactory.INSTANCE)
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
+
+ .create();
+
+ final HttpHost target = new HttpHost("httpbin.org");
+ final HttpCoreContext context = HttpCoreContext.create();
+
+ final ObjectMapper objectMapper = new ObjectMapper();
+
+ final ClassicHttpRequest request = ClassicRequestBuilder.get()
+ .setHttpHost(target)
+ .setEntity(new EntityTemplate(ContentType.APPLICATION_JSON, out -> {
+ objectMapper.writeValue(out, new BasicNameValuePair("name", "value"));
+ }))
+ .setPath("/post")
+ .build();
+ final RequestData requestData = requester.execute(target, request, Timeout.ofSeconds(5), context, response -> {
+ System.out.println(target + "->" + response.getCode());
+ final HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ try (InputStream inputStream = entity.getContent()) {
+ return objectMapper.readValue(inputStream, RequestData.class);
+ }
+ } else {
+ return null;
+ }
+ });
+ System.out.println(requestData);
+
+ requester.close(CloseMode.GRACEFUL);
+ }
+
+}
diff --git a/samples/src/main/java/org/apache/hc/core5/http/examples/ClassicServerBootstrapExample.java b/samples/src/main/java/org/apache/hc/core5/http/examples/ClassicServerBootstrapExample.java
new file mode 100644
index 0000000..543ca55
--- /dev/null
+++ b/samples/src/main/java/org/apache/hc/core5/http/examples/ClassicServerBootstrapExample.java
@@ -0,0 +1,163 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+
+package org.apache.hc.core5.http.examples;
+
+import java.io.InputStream;
+import java.net.InetSocketAddress;
+import java.net.URISyntaxException;
+import java.util.Objects;
+
+import com.fasterxml.jackson.databind.ObjectMapper;
+
+import org.apache.hc.core5.http.ContentType;
+import org.apache.hc.core5.http.EndpointDetails;
+import org.apache.hc.core5.http.HttpEntity;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.Method;
+import org.apache.hc.core5.http.MethodNotSupportedException;
+import org.apache.hc.core5.http.ProtocolException;
+import org.apache.hc.core5.http.impl.bootstrap.HttpServer;
+import org.apache.hc.core5.http.impl.bootstrap.ServerBootstrap;
+import org.apache.hc.core5.http.impl.bootstrap.StandardFilter;
+import org.apache.hc.core5.http.impl.routing.RequestRouter;
+import org.apache.hc.core5.http.io.HttpRequestHandler;
+import org.apache.hc.core5.http.io.SocketConfig;
+import org.apache.hc.core5.http.io.entity.EntityTemplate;
+import org.apache.hc.core5.http.io.support.AbstractHttpServerAuthFilter;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.http.protocol.HttpCoreContext;
+import org.apache.hc.core5.net.URIAuthority;
+import org.apache.hc.core5.testing.classic.LoggingBHttpServerConnectionFactory;
+import org.apache.hc.core5.testing.classic.LoggingExceptionListener;
+import org.apache.hc.core5.testing.classic.LoggingHttp1StreamListener;
+import org.apache.hc.core5.util.TimeValue;
+import org.apache.hc.core5.util.Timeout;
+
+/**
+ * Example of embedded HTTP/1.1 file server using classic I/O.
+ */
+public class ClassicServerBootstrapExample {
+
+ public static void main(final String[] args) throws Exception {
+ int port = 8080;
+ if (args.length >= 1) {
+ port = Integer.parseInt(args[1]);
+ }
+
+ final ObjectMapper objectMapper = new ObjectMapper();
+
+ final HttpRequestHandler requestHandler = (request, response, localContext) -> {
+
+ final String method = request.getMethod();
+ if (!Method.GET.isSame(method) &&
+ !Method.HEAD.isSame(method) &&
+ !Method.POST.isSame(method) &&
+ !Method.PUT.isSame(method) &&
+ !Method.PATCH.isSame(method)) {
+ throw new MethodNotSupportedException(method + " method not supported");
+ }
+
+ final RequestData rd = new RequestData();
+ try {
+ rd.setUrl(request.getUri());
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException("Invalid request URI");
+ }
+ rd.generateHeaders(request.getHeaders());
+ final HttpEntity requestEntity = request.getEntity();
+ if (requestEntity != null) {
+ try (final InputStream inputStream = requestEntity.getContent()) {
+ rd.setJson(objectMapper.readTree(inputStream));
+ }
+ }
+
+ final HttpCoreContext coreContext = HttpCoreContext.cast(localContext);
+ final EndpointDetails endpointDetails = coreContext.getEndpointDetails();
+
+ final InetSocketAddress remoteAddress = (InetSocketAddress) endpointDetails.getRemoteAddress();
+ rd.setOrigin(Objects.toString(remoteAddress.getAddress()));
+ response.setEntity(new EntityTemplate(ContentType.APPLICATION_JSON, out -> {
+ objectMapper.writeValue(out, rd);
+ }));
+ };
+
+ final AbstractHttpServerAuthFilter authFilter = new AbstractHttpServerAuthFilter<>(false) {
+
+ @Override
+ protected String parseChallengeResponse(
+ final String authorizationValue, final HttpContext context) throws HttpException {
+ // Parse the authorizaton header value into a custom token representation
+ return authorizationValue;
+ }
+
+ @Override
+ protected boolean authenticate(
+ final String token,
+ final URIAuthority authority,
+ final String requestUri,
+ final HttpContext context) {
+ // Validate token and return true if the user has been autheticated
+ return "let me pass".equals(token);
+ }
+
+ @Override
+ protected String generateChallenge(
+ final String challengeResponse,
+ final URIAuthority authority,
+ final String requestUri,
+ final HttpContext context) {
+ // Generate a challenge in case the user has not been autheticated
+ return "who goes there?";
+ }
+
+ };
+
+ final HttpServer server = ServerBootstrap.bootstrap()
+ .setListenerPort(port)
+ .setSocketConfig(SocketConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+ .setRequestRouter(RequestRouter.builder()
+ .addRoute(RequestRouter.LOCAL_AUTHORITY, "*", requestHandler)
+ .resolveAuthority(RequestRouter.LOCAL_AUTHORITY_RESOLVER)
+ .build())
+ .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), authFilter)
+
+ // Pluggable logging
+ .setConnectionFactory(LoggingBHttpServerConnectionFactory.INSTANCE)
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
+ .setExceptionListener(LoggingExceptionListener.INSTANCE)
+
+ .create();
+
+ server.start();
+ System.out.println("Listening on port " + port);
+ server.awaitTermination(TimeValue.MAX_VALUE);
+ }
+
+}
diff --git a/samples/src/main/java/org/apache/hc/core5/http/examples/RequestData.java b/samples/src/main/java/org/apache/hc/core5/http/examples/RequestData.java
new file mode 100644
index 0000000..11e95d5
--- /dev/null
+++ b/samples/src/main/java/org/apache/hc/core5/http/examples/RequestData.java
@@ -0,0 +1,123 @@
+/*
+ * ====================================================================
+ * 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
+ * .
+ *
+ */
+package org.apache.hc.core5.http.examples;
+
+import java.net.URI;
+import java.util.Arrays;
+import java.util.Map;
+import java.util.stream.Collectors;
+
+import com.fasterxml.jackson.databind.JsonNode;
+
+import org.apache.hc.core5.http.Header;
+import org.apache.hc.core5.http.NameValuePair;
+
+public class RequestData {
+
+ private int id;
+ private URI url;
+ private String origin;
+ private Map headers;
+ private Map args;
+ private String data;
+ private JsonNode json;
+
+ public int getId() {
+ return id;
+ }
+
+ public void setId(final int id) {
+ this.id = id;
+ }
+
+ public URI getUrl() {
+ return url;
+ }
+
+ public void setUrl(final URI url) {
+ this.url = url;
+ }
+
+ public String getOrigin() {
+ return origin;
+ }
+
+ public void setOrigin(final String origin) {
+ this.origin = origin;
+ }
+
+ public Map getHeaders() {
+ return headers;
+ }
+
+ public void setHeaders(final Map headers) {
+ this.headers = headers;
+ }
+
+ public void generateHeaders(final Header... headers) {
+ this.headers = Arrays.stream(headers)
+ .collect(Collectors.toMap(NameValuePair::getName, NameValuePair::getValue));
+ }
+
+ public Map getArgs() {
+ return args;
+ }
+
+ public void setArgs(final Map args) {
+ this.args = args;
+ }
+
+ public String getData() {
+ return data;
+ }
+
+ public void setData(final String data) {
+ this.data = data;
+ }
+
+ public JsonNode getJson() {
+ return json;
+ }
+
+ public void setJson(final JsonNode json) {
+ this.json = json;
+ }
+
+ @Override
+ public String toString() {
+ return "RequestData{" +
+ "id=" + id +
+ ", url=" + url +
+ ", origin='" + origin + '\'' +
+ ", headers=" + headers +
+ ", args=" + args +
+ ", data='" + data + '\'' +
+ ", json=" + json +
+ '}';
+ }
+
+}
diff --git a/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-http2.md b/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-http2.md
index c8fccde..a1c17bb 100644
--- a/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-http2.md
+++ b/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-http2.md
@@ -37,7 +37,7 @@ over a single HTTP/2 connection.
```java
CloseableHttpAsyncClient client = HttpAsyncClients.customHttp2()
.setTlsStrategy(ClientTlsStrategyBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(SSLContexts.createDefault())
.setTlsVersions(TLS.V_1_3)
.buildAsync())
.setIOReactorConfig(IOReactorConfig.custom()
diff --git a/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-simple.md b/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-simple.md
index e393342..60dc0c7 100644
--- a/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-simple.md
+++ b/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-simple.md
@@ -43,7 +43,7 @@ either known to be well-behaved or specifically designed for simple message hand
```java
PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(ClientTlsStrategyBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(SSLContexts.createDefault())
.setTlsVersions(TLS.V_1_3)
.buildAsync())
.setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
diff --git a/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-streaming.md b/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-streaming.md
index 1733c25..f38d697 100644
--- a/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-streaming.md
+++ b/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-async-streaming.md
@@ -64,7 +64,7 @@ for [JSON message processing](https://github.com/ok2c/httpcomponents-jackson) us
```java
PoolingAsyncClientConnectionManager connectionManager = PoolingAsyncClientConnectionManagerBuilder.create()
.setTlsStrategy(ClientTlsStrategyBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(SSLContexts.createDefault())
.setTlsVersions(TLS.V_1_3)
.buildAsync())
.setPoolConcurrencyPolicy(PoolConcurrencyPolicy.STRICT)
diff --git a/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-classic.md b/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-classic.md
index 4e85539..fe51dba 100644
--- a/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-classic.md
+++ b/src/site/markdown/httpcomponents-client-5.6.x/migration-guide/migration-to-classic.md
@@ -63,7 +63,7 @@ There are also some important differences with URL normalization and encoding.
```java
PoolingHttpClientConnectionManager connectionManager = PoolingHttpClientConnectionManagerBuilder.create()
.setTlsSocketStrategy(ClientTlsStrategyBuilder.create()
- .setSslContext(SSLContexts.createSystemDefault())
+ .setSslContext(SSLContexts.createDefault())
.setTlsVersions(TLS.V_1_3)
.buildClassic())
.setDefaultSocketConfig(SocketConfig.custom()
diff --git a/src/site/markdown/httpcomponents-client-5.6.x/spki-pinning.md b/src/site/markdown/httpcomponents-client-5.6.x/spki-pinning.md
index eb2352b..f4420a8 100644
--- a/src/site/markdown/httpcomponents-client-5.6.x/spki-pinning.md
+++ b/src/site/markdown/httpcomponents-client-5.6.x/spki-pinning.md
@@ -112,7 +112,7 @@ import org.apache.hc.core5.ssl.SSLContexts;
public final class ClientSpkiPinningExample {
public static void main(final String[] args) throws Exception {
- final SSLContext sslContext = SSLContexts.createSystemDefault();
+ final SSLContext sslContext = SSLContexts.createDefault();
final SpkiPinningClientTlsStrategy pinning = SpkiPinningClientTlsStrategy
.newBuilder(sslContext)
@@ -179,7 +179,7 @@ import org.apache.hc.core5.ssl.SSLContexts;
public final class AsyncSpkiPinningExample {
public static void main(final String[] args) throws Exception {
- final SSLContext sslContext = SSLContexts.createSystemDefault();
+ final SSLContext sslContext = SSLContexts.createDefault();
final SpkiPinningClientTlsStrategy pinning = SpkiPinningClientTlsStrategy
.newBuilder(sslContext)
diff --git a/src/site/markdown/httpcomponents-core-5.5.x/getting-started.md b/src/site/markdown/httpcomponents-core-5.5.x/getting-started.md
new file mode 100644
index 0000000..2747145
--- /dev/null
+++ b/src/site/markdown/httpcomponents-core-5.5.x/getting-started.md
@@ -0,0 +1,590 @@
+Getting started with HttpCore
+=================
+
+Please note HttpCore is a set of customizable components, not an embeddable server or a
+client. It is not intended for deployment to productive environments, especially
+potentially hostile, without prior customization and configuration.
+
+Supported I/O models and HTTP protocol versions
+------------------
+
+HttpCore supports two i/o models: the classic i/o model based on blocking InputStream /
+OutputStream APIs and the event-driven async i/o model. Both models have their advantages
+and their special use cases.
+
+The async i/o model is believed to be more scalable and better suited for working with
+thousands of concurrent connections while utilizing just a few reactor threads. This can
+especially be relevant if many of connections can remain idle for a considerable period
+of time. However, this may no longer be the case for newer Java Runtimes that support
+virtual threads.
+
+The classic i/o model tends to be simpler and more convenient due to its seamless
+integration with any InputStream / OutputStream based content producer and consumer
+and much more straight-forward exception handling.
+
+The async i/o model on the other hand tends to be more complex due to its event-driven
+nature. It also does not work well with InputStream / OutputStream based content
+producers and consumers and usually may still need to maintain a worker thread if the
+message exchange handler can get blocked while producing or consuming data.
+
+The classic i/o model requires a dedicated thread for each active connection in order
+to be able to read or write data. But it cannot do both reading and writing while using
+a single thread. This makes the classic i/o model ill-suited for multiplexing protocols
+such as HTTP/2. However, it works very well with request / response based protocols such
+as HTTP/1.1.
+
+Ultimately the choice of an i/o model boils down to whether or not an application can
+make an effective use of message exchange multiplexing with multiple long message streams
+running concurrently over the same physical connection. If so, one would be better off
+choosing the async (event-driven) model. Otherwise, one may choose the classic i/o model
+as in many common use cases HTTP/2 provides no tangible advantage over HTTP/1.1,
+especially if the application is based on the REST request / response style of
+communicaton.
+
+HTTP requesters
+------------------
+HttpCore ships with several HTTP requester implementations. Please note that HttpCore
+requesters are not full-featured clients. They can efficiently execute HTTP message
+exchanges over multiple pooled connections but do not support advanced client features
+such as automatic redirect handling, content compression / decompression, response
+caching, state management and different authentication schemes and credentials management.
+For advanced client features one should use Apache HttpClient instead.
+
+HttpCore requesters can however be efficiently used to execute JSON formatted message
+exchanges with remote REST endpoints using a Bearer tokenor similar for authetication.
+
+### Async requester
+
+The async requester can efficiently execute multiple message exchanges over thousands of
+concurrent connections using HTTP/1.1 and HTTP/2 protocol.
+
+Please note async message exchange handlers are stateful and cannot be executed multiple
+times (unless their state can be reset). One must create a new instance for each new
+message exchange execution.
+
+One can use`AsyncClientPipeline` and `AsyncJsonClientPipeline` utility classes to
+simplify assembly of async message exchange handlers.
+
+```java
+final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
+ .setIOReactorConfig(IOReactorConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+ .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
+ .create();
+requester.
+
+start();
+
+final HttpHost target = new HttpHost("httpbin.org");
+final HttpCoreContext context = HttpCoreContext.create();
+
+final ObjectMapper objectMapper = new ObjectMapper();
+
+final CountDownLatch latch = new CountDownLatch(1);
+requester.
+
+execute(
+ AsyncJsonClientPipeline.assemble(objectMapper)
+ .
+
+request()
+ .
+
+post(target, "/post")
+ .
+
+asObject(new BasicNameValuePair("name", "value"))
+ .
+
+response()
+ .
+
+asObject(RequestData .class)
+ .
+
+result(new FutureCallback<>() {
+
+ @Override
+ public void completed ( final Message m){
+ final HttpResponse response = m.head();
+ System.out.println(target + "->" + response.getCode());
+ latch.countDown();
+ }
+
+ @Override
+ public void failed ( final Exception ex){
+ ex.printStackTrace(System.out);
+ latch.countDown();
+ }
+
+ @Override
+ public void cancelled () {
+ latch.countDown();
+ }
+
+})
+ .
+
+create(),
+ Timeout.
+
+ofMinutes(1),
+
+context);
+
+ latch.
+
+await();
+requester.
+
+close(CloseMode.GRACEFUL);
+```
+
+### Classic requester
+
+The classic transport generally tends to be more convenient to use and integrate with
+content libraries based on standard `InputStream`/`OutputStream` APIs. One should consider
+choosing the classic requester for use scenarios with the HTTP/2 protocol support being
+optional.
+
+Classic message exchange handlers can be stateless and they generally tend to have
+a simpler, more predictable execution flow and exception handling.
+
+```java
+final HttpRequester requester = RequesterBootstrap.bootstrap()
+ .setSocketConfig(SocketConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+ .create();
+
+final HttpHost target = new HttpHost("httpbin.org");
+final HttpCoreContext context = HttpCoreContext.create();
+
+final ObjectMapper objectMapper = new ObjectMapper();
+
+final ClassicHttpRequest request = ClassicRequestBuilder.get()
+ .setHttpHost(target)
+ .setEntity(new EntityTemplate(ContentType.APPLICATION_JSON, out -> {
+ objectMapper.writeValue(out, new BasicNameValuePair("name", "value"));
+ }))
+ .setPath("/post")
+ .build();
+final RequestData requestData = requester.execute(target, request, Timeout.ofSeconds(5), context, response -> {
+ System.out.println(target + "->" + response.getCode());
+ final HttpEntity entity = response.getEntity();
+ if (entity != null) {
+ try (InputStream inputStream = entity.getContent()) {
+ return objectMapper.readValue(inputStream, RequestData.class);
+ }
+ } else {
+ return null;
+ }
+});
+System.out.
+
+println(requestData);
+
+requester.
+
+close(CloseMode.GRACEFUL);
+```
+
+HTTP servers
+------------------
+HttpCore ships with several HTTP embeddable server implementations. The servers are fully
+capable of efficiently managing multiple concurrent message exchanges over multiple
+connections. Incoming requrests can be routed to individual request handlers for
+processing based on predefined request route definitions.
+
+Please note however HttpCore servers do not support advanced server features such as
+different authentication schemes, access control and user identity management out of the
+box. These features however can be plugged in using custom message exchange interceptors.
+
+HttpCore servers can be efficiently used to handle JSON formatted message exchanges with
+remote REST client using Bearer token scheme or similar for authentication.
+
+### Async server
+
+The async server can efficiently execute multiple message exchanges over thousands of
+concurrent connections using HTTP/1.1 and HTTP/2 protocol.
+
+Please note async message exchange handlers are state-ful and cannot be executed multiple
+times. One must create a new instance for each new message exchange execution.
+
+One can use`AsyncServerPipeline` and `AsyncJsonServerPipeline` utility classes to
+simplify assembly of async message exchange handlers.
+
+```java
+final IOReactorConfig config = IOReactorConfig.custom()
+ .setSoTimeout(15, TimeUnit.SECONDS)
+ .build();
+
+final ObjectMapper objectMapper = new ObjectMapper();
+
+final Supplier exchangeHandlerSupplier = AsyncJsonServerPipeline.assemble(objectMapper)
+ // Read GET / HEAD requests by consuming content stream as JSON nodes
+ .request(Method.GET, Method.HEAD, Method.POST, Method.PUT, Method.PATCH)
+ .asJsonNode()
+ // Write out responses by streaming out content of JSON object
+ .response()
+ .asObject(RequestData.class)
+ // Map exceptions to a response message
+ .errorMessage(Throwable::getMessage)
+ // Generate a response to a request
+ .handle((m, context) -> {
+ final HttpRequest request = m.head();
+ final RequestData rd = new RequestData();
+ try {
+ rd.setUrl(request.getUri());
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException("Invalid request URI");
+ }
+ rd.generateHeaders(request.getHeaders());
+ rd.setJson(m.body());
+ rd.setData(Objects.toString(m.error()));
+
+ final HttpCoreContext coreContext = HttpCoreContext.cast(context);
+ final EndpointDetails endpointDetails = coreContext.getEndpointDetails();
+
+ final InetSocketAddress remoteAddress = (InetSocketAddress) endpointDetails.getRemoteAddress();
+ rd.setOrigin(Objects.toString(remoteAddress.getAddress()));
+
+ return Message.of(new BasicHttpResponse(HttpStatus.SC_OK), rd);
+ })
+ .supplier();
+
+final AbstractAsyncServerAuthFilter authFilter = new AbstractAsyncServerAuthFilter<>(true) {
+
+ @Override
+ protected String parseChallengeResponse(
+ final String authorizationValue, final HttpContext context) throws HttpException {
+ // Parse the authorizaton header value into a custom token representation
+ return authorizationValue;
+ }
+
+ @Override
+ protected boolean authenticate(
+ final String token,
+ final URIAuthority authority,
+ final String requestUri,
+ final HttpContext context) {
+ // Validate token and return true if the user has been autheticated
+ return "let me pass".equals(challengeResponse);
+ }
+
+ @Override
+ protected String generateChallenge(
+ final String challengeResponse,
+ final URIAuthority authority,
+ final String requestUri,
+ final HttpContext context) {
+ // Generate a challenge in case the user has not been autheticated
+ return "who goes there?";
+ }
+
+};
+final HttpAsyncServer server = H2ServerBootstrap.bootstrap()
+ .setExceptionCallback(e -> e.printStackTrace())
+ .setIOReactorConfig(config)
+ .setRequestRouter(RequestRouter.>builder()
+ .addRoute(RequestRouter.LOCAL_AUTHORITY, "*", exchangeHandlerSupplier)
+ .resolveAuthority(RequestRouter.LOCAL_AUTHORITY_RESOLVER)
+ .build())
+ .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), authFilter)
+ .create();
+
+
+server.
+
+start();
+
+final Future future = server.listen(new InetSocketAddress(port), URIScheme.HTTP);
+final ListenerEndpoint listenerEndpoint = future.get();
+System.out.
+
+println("Listening on "+listenerEndpoint.getAddress());
+ server.
+
+awaitShutdown(TimeValue.MAX_VALUE);
+```
+
+### Classic server
+
+The classic server can be a good choice when one controls both the client and the server
+sides, does not need to handle requests with large or unexpected content body and support
+thousands of concurrent connections.
+
+Classic message exchange handlers can be stateless and they generally tend to have a
+simpler, more predictable execution flow and exception handling.
+
+```java
+final ObjectMapper objectMapper = new ObjectMapper();
+
+final HttpRequestHandler requestHandler = (request, response, localContext) -> {
+
+ final String method = request.getMethod();
+ if (!Method.GET.isSame(method) &&
+ !Method.HEAD.isSame(method) &&
+ !Method.POST.isSame(method) &&
+ !Method.PUT.isSame(method) &&
+ !Method.PATCH.isSame(method)) {
+ throw new MethodNotSupportedException(method + " method not supported");
+ }
+
+ final RequestData rd = new RequestData();
+ try {
+ rd.setUrl(request.getUri());
+ } catch (final URISyntaxException ex) {
+ throw new ProtocolException("Invalid request URI");
+ }
+ rd.generateHeaders(request.getHeaders());
+ final HttpEntity requestEntity = request.getEntity();
+ if (requestEntity != null) {
+ try (final InputStream inputStream = requestEntity.getContent()) {
+ rd.setJson(objectMapper.readTree(inputStream));
+ }
+ }
+
+ final HttpCoreContext coreContext = HttpCoreContext.cast(localContext);
+ final EndpointDetails endpointDetails = coreContext.getEndpointDetails();
+
+ final InetSocketAddress remoteAddress = (InetSocketAddress) endpointDetails.getRemoteAddress();
+ rd.setOrigin(Objects.toString(remoteAddress.getAddress()));
+ response.setEntity(new EntityTemplate(ContentType.APPLICATION_JSON, out -> {
+ objectMapper.writeValue(out, rd);
+ }));
+};
+
+final AbstractHttpServerAuthFilter authFilter = new AbstractHttpServerAuthFilter<>(false) {
+
+ @Override
+ protected String parseChallengeResponse(
+ final String authorizationValue, final HttpContext context) throws HttpException {
+ // Parse the authorizaton header value into a custom token representation
+ return authorizationValue;
+ }
+
+ @Override
+ protected boolean authenticate(
+ final String token,
+ final URIAuthority authority,
+ final String requestUri,
+ final HttpContext context) {
+ // Validate token and return true if the user has been autheticated
+ return "let me pass".equals(token);
+ }
+
+ @Override
+ protected String generateChallenge(
+ final String challengeResponse,
+ final URIAuthority authority,
+ final String requestUri,
+ final HttpContext context) {
+ // Generate a challenge in case the user has not been autheticated
+ return "who goes there?";
+ }
+
+};
+
+final HttpServer server = ServerBootstrap.bootstrap()
+ .setListenerPort(port)
+ .setSocketConfig(SocketConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+ .setRequestRouter(RequestRouter.builder()
+ .addRoute(RequestRouter.LOCAL_AUTHORITY, "*", requestHandler)
+ .resolveAuthority(RequestRouter.LOCAL_AUTHORITY_RESOLVER)
+ .build())
+ .replaceFilter(StandardFilter.EXPECT_CONTINUE.name(), authFilter)
+ .create();
+
+server.
+
+start();
+System.out.
+
+println("Listening on port "+port);
+server.
+
+awaitTermination(TimeValue.MAX_VALUE);
+```
+
+Request multiplexing / pipelining
+------------------
+
+HttpCore supports message exchange multiplexing over HTTP/2 connections and message
+exchange pipelining over HTTP/1.1 connections by default. However initiation of concurrent
+message exchanges on the client side require some manual management of the connection
+lease.
+One needs to lease a connection from the connection pool to execute multiple messages
+over the same connection, and release it back to the pool when done. One also must
+aqcuire a new connection from the connection in case a message exchange failure due to
+an I/O error.
+
+```java
+final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
+ .setIOReactorConfig(IOReactorConfig.custom()
+ .setSoTimeout(Timeout.ofMinutes(1))
+ .build())
+ .setVersionPolicy(HttpVersionPolicy.NEGOTIATE)
+ .create();
+requester.
+
+start();
+
+final HttpHost target = new HttpHost("nghttp2.org");
+
+final ObjectMapper objectMapper = new ObjectMapper();
+
+final Future future = requester.connect(target, Timeout.ofSeconds(30));
+final AsyncClientEndpoint clientEndpoint = future.get();
+
+int n = 3;
+final CountDownLatch latch = new CountDownLatch(n);
+final AtomicBoolean failure = new AtomicBoolean();
+for(
+int i = 1;
+i <=3;i++){
+ requester.
+
+execute(
+ AsyncJsonClientPipeline.assemble(objectMapper)
+ .
+
+request()
+ .
+
+post(target, "/post")
+ .
+
+asObject(new BasicNameValuePair("name", "value"))
+ .
+
+response()
+ .
+
+asObject(RequestData .class)
+ .
+
+result(new FutureCallback<>() {
+
+ @Override
+ public void completed ( final Message m){
+ final HttpResponse response = m.head();
+ final RequestData requestData = m.body();
+ System.out.println(target + "->" + response.getCode());
+ System.out.println(requestData);
+ latch.countDown();
+ }
+
+ @Override
+ public void failed ( final Exception ex){
+ ex.printStackTrace(System.out);
+ failure.set(true);
+ latch.countDown();
+ }
+
+ @Override
+ public void cancelled () {
+ latch.countDown();
+ }
+
+})
+ .
+
+create(),
+ Timeout.
+
+ofMinutes(1),
+ HttpCoreContext.
+
+create());
+ }
+
+ latch.
+
+await();
+if(failure.
+
+get()){
+ clientEndpoint.
+
+releaseAndDiscard();
+}else{
+ clientEndpoint.
+
+releaseAndReuse();
+}
+
+ requester.
+
+close(CloseMode.GRACEFUL);
+```
+
+Logging
+------------------
+
+HttpCore does not depend on a specific logging toolkit or logging facade. This is a
+conscious decision as HttpCore has no direct dependencies on any other libraties by
+design.
+
+HttpCore requesters and servers however expose several listener interfaces that can
+be used to plug-in custom logging.
+
+HttpCore ships with `httpcore5-testing` module that provides a number of listener
+implementations that log DEBUG level priority events through `slf4j` facade.
+
+* Async server logging
+
+```java
+final HttpAsyncServer server = H2ServerBootstrap.bootstrap()
+ // Pluggable logging
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE_SERVER)
+ .setStreamListener(LoggingH2StreamListener.INSTANCE)
+ .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
+ .setExceptionCallback(LoggingExceptionCallback.INSTANCE)
+ .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
+ .setIOReactorMetricsListener(LoggingReactorMetricsListener.INSTANCE)
+
+ .create();
+```
+
+* Classic server logging
+
+```java
+final HttpServer server = ServerBootstrap.bootstrap()
+ // Pluggable logging
+ .setConnectionFactory(LoggingBHttpServerConnectionFactory.INSTANCE)
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
+ .setExceptionListener(LoggingExceptionListener.INSTANCE)
+
+ .create();
+```
+
+* Async requester logging
+
+```java
+final HttpAsyncRequester requester = H2RequesterBootstrap.bootstrap()
+ // Pluggable logging
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE_CLIENT)
+ .setStreamListener(LoggingH2StreamListener.INSTANCE)
+ .setConnPoolListener(LoggingConnPoolListener.INSTANCE)
+ .setIOSessionDecorator(LoggingIOSessionDecorator.INSTANCE)
+ .setExceptionCallback(LoggingExceptionCallback.INSTANCE)
+ .setIOSessionListener(LoggingIOSessionListener.INSTANCE)
+ .setIOReactorMetricsListener(LoggingReactorMetricsListener.INSTANCE)
+
+ .create();
+```
+
+* Classic requester logging
+
+```java
+final HttpRequester requester = RequesterBootstrap.bootstrap()
+ // Pluggable logging
+ .setConnectionFactory(LoggingBHttpClientConnectionFactory.INSTANCE)
+ .setStreamListener(LoggingHttp1StreamListener.INSTANCE)
+
+ .create();
+```
diff --git a/src/site/markdown/httpcomponents-core-5.5.x/index.md b/src/site/markdown/httpcomponents-core-5.5.x/index.md
index c20730e..ffdb1d9 100644
--- a/src/site/markdown/httpcomponents-core-5.5.x/index.md
+++ b/src/site/markdown/httpcomponents-core-5.5.x/index.md
@@ -20,35 +20,53 @@
HttpCore Overview
=================
-HttpCore is a set of low level HTTP transport components that can be used to build custom client and server side HTTP
-services with a minimal footprint. HttpCore supports two I/O models: blocking I/O model based on the classic Java I/O
-and non-blocking, event driven I/O model based on Java NIO.
+HttpCore is a set of low level HTTP transport components that can be used to build custom
+client and server side HTTP services with a minimal footprint. HttpCore supports two I/O
+models: blocking I/O model based on the classic Java I/O and non-blocking, event driven
+I/O model based on Java NIO.
+
+Design objectives and project scope
+-----------------
+
+* Small footprint with no mandatory dependencies beyond JRE
+* Self-sufficient HTTP transport impmementation capable of executing message exchanges
+ from any endpoint in the network topology (client, server, proxy)
+* Common message primitives, control interfaces and strategies shared by the client and
+ server side protocol implementations
+* Consistent APIs for all supported HTTP protocol versions
+* HTTP protocol logic can be executed and tested against itself on the opposite endpoint
+* A foundation for Apache HttpClient
+* Potentially a foundation for an embeddable server similar in scope to Apache HttpClient
Documentation
=============
+1. Guides
+ - [Getting started](getting-started.md)
+ - [JSON processing](json-jackson2.md)
+
1. Examples demonstrating some common as well as more complex use cases
- - [HttpCore Cclassic APIs](examples.md)
- - [HttpCore Async APIs](examples-async.md)
- - [HttpCore Reactive Streams APIs](examples-reactive.md)
- - [HttpCore JSON bindings](examples-jackson2.md)
+ - [HttpCore Cclassic APIs](examples.md)
+ - [HttpCore Async APIs](examples-async.md)
+ - [HttpCore Reactive Streams APIs](examples-reactive.md)
+ - [HttpCore JSON bindings](examples-jackson2.md)
1. Javadocs
- - [HttpCore HTTP/1.1](./current/httpcore5/apidocs/)
- - [HttpCore HTTP/2](./current/httpcore5-h2/apidocs/)
- - [HttpCore Reactive Streams](./current/httpcore5-reactive/apidocs/)
- - [HttpCore JSON bindings](./current/httpcore5-jackson2/apidocs/)
+ - [HttpCore HTTP/1.1](./current/httpcore5/apidocs/)
+ - [HttpCore HTTP/2](./current/httpcore5-h2/apidocs/)
+ - [HttpCore Reactive Streams](./current/httpcore5-reactive/apidocs/)
+ - [HttpCore JSON bindings](./current/httpcore5-jackson2/apidocs/)
1. API compatibility reports
- - [HttpCore HTTP/1.1](./current/httpcore5/japicmp.html)
- - [HttpCore HTTP/2](./current/httpcore5-h2/japicmp.html)
- - [HttpCore Reactive Streams](./current/httpcore5-reactive/japicmp.html)
+ - [HttpCore HTTP/1.1](./current/httpcore5/japicmp.html)
+ - [HttpCore HTTP/2](./current/httpcore5-h2/japicmp.html)
+ - [HttpCore Reactive Streams](./current/httpcore5-reactive/japicmp.html)
Standards Compliance
--------------------
-HttpCore components strive to conform to the following specifications endorsed by the Internet Engineering Task Force
-(IETF) and the internet at large:
+HttpCore components strive to conform to the following specifications endorsed by the
+Internet Engineering Task Force (IETF) and the internet at large:
- [RFC 9110](https://datatracker.ietf.org/doc/html/rfc9110) - HTTP Semantics
- [RFC 9112](https://datatracker.ietf.org/doc/html/rfc9112) - Hypertext Transfer Protocol Version 1.1 (HTTP/1.1)
diff --git a/src/site/markdown/httpcomponents-core-5.5.x/json-jackson2.md b/src/site/markdown/httpcomponents-core-5.5.x/json-jackson2.md
index 5ec05f8..188ce6f 100644
--- a/src/site/markdown/httpcomponents-core-5.5.x/json-jackson2.md
+++ b/src/site/markdown/httpcomponents-core-5.5.x/json-jackson2.md
@@ -37,4 +37,4 @@ content and map onto a higher level Java object model.
Presently HttpCore JSON bindings use version 2 of Jackson JSON processor.
-Some examples of HttpCore JSON bindings in action cab be found [here](examples-jackson2.md)
\ No newline at end of file
+Some examples of HttpCore JSON bindings in action can be found [here](examples-jackson2.md)
\ No newline at end of file
diff --git a/src/site/site.xml b/src/site/site.xml
index 69e3cb2..6e98a35 100644
--- a/src/site/site.xml
+++ b/src/site/site.xml
@@ -99,6 +99,7 @@ under the License.
-
+