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 2017/08/01 19:07:04 UTC

[6/6] httpcomponents-core git commit: Improved request handler mapping and registration (classic and asynchronous)

Improved request handler mapping and registration (classic and asynchronous)


Project: http://git-wip-us.apache.org/repos/asf/httpcomponents-core/repo
Commit: http://git-wip-us.apache.org/repos/asf/httpcomponents-core/commit/479ad348
Tree: http://git-wip-us.apache.org/repos/asf/httpcomponents-core/tree/479ad348
Diff: http://git-wip-us.apache.org/repos/asf/httpcomponents-core/diff/479ad348

Branch: refs/heads/master
Commit: 479ad3486882fb4ac56a3704016c63cf08dcd1a6
Parents: 41291fa
Author: Oleg Kalnichevski <ol...@apache.org>
Authored: Mon Jul 31 16:20:13 2017 +0200
Committer: Oleg Kalnichevski <ol...@apache.org>
Committed: Tue Aug 1 21:01:23 2017 +0200

----------------------------------------------------------------------
 .../hc/core5/http/benchmark/SmokeTest.java      |   6 +-
 .../impl/nio/ClientPushHttp2StreamHandler.java  |   2 +-
 .../impl/nio/ServerHttp2StreamHandler.java      |   2 +-
 .../nio/bootstrap/H2RequesterBootstrap.java     |  59 ++++----
 .../impl/nio/bootstrap/H2ServerBootstrap.java   |  74 +++++++---
 .../http2/impl/nio/bootstrap/HandlerEntry.java  |  41 ++++++
 .../nio/support/AsyncPushConsumerRegistry.java  | 103 --------------
 .../DefaultAsyncPushConsumerFactory.java        |  63 +++++++++
 .../testing/classic/ClassicTestServer.java      |  16 ++-
 .../testing/framework/TestingFramework.java     |   2 +-
 .../hc/core5/testing/nio/Http1TestServer.java   |  11 +-
 .../hc/core5/testing/nio/Http2TestClient.java   |  13 +-
 .../hc/core5/testing/nio/Http2TestServer.java   |  13 +-
 .../core5/testing/nio/Http1IntegrationTest.java |   4 +-
 .../http/examples/ClassicFileServerExample.java |   2 +-
 .../examples/ClassicReverseProxyExample.java    |   2 +-
 .../org/apache/hc/core5/function/Factory.java   |  39 ++++++
 .../apache/hc/core5/http/HttpRequestMapper.java |  49 +++++++
 .../impl/bootstrap/AsyncServerBootstrap.java    |  76 +++++++----
 .../core5/http/impl/bootstrap/HandlerEntry.java |  41 ++++++
 .../http/impl/bootstrap/ServerBootstrap.java    |  88 +++++++-----
 .../hc/core5/http/impl/io/HttpService.java      |  14 +-
 .../http/impl/nio/ServerHttp1StreamHandler.java |   2 +-
 .../core5/http/io/HttpRequestHandlerMapper.java |  53 --------
 .../io/support/UriHttpRequestHandlerMapper.java | 122 -----------------
 .../http/nio/AsyncServerRequestHandler.java     |   2 +-
 .../hc/core5/http/nio/HandlerFactory.java       |   3 +-
 .../AsyncServerExchangeHandlerRegistry.java     | 107 ---------------
 ...aultAsyncResponseExchangeHandlerFactory.java |  64 +++++++++
 .../http/protocol/RequestHandlerRegistry.java   | 135 +++++++++++++++++++
 .../hc/core5/http/protocol/UriPatternType.java  |  49 +++++++
 .../hc/core5/http/impl/io/TestHttpService.java  |  16 +--
 .../TestUriHttpRequestHandlerMapper.java        | 108 ---------------
 .../TestAsyncServerExchangeHandlerRegistry.java |  72 ----------
 .../protocol/TestRequestHandlerRegistry.java    |  92 +++++++++++++
 35 files changed, 824 insertions(+), 721 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-ab/src/test/java/org/apache/hc/core5/http/benchmark/SmokeTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-ab/src/test/java/org/apache/hc/core5/http/benchmark/SmokeTest.java b/httpcore5-ab/src/test/java/org/apache/hc/core5/http/benchmark/SmokeTest.java
index 28b3559..cbe9684 100644
--- a/httpcore5-ab/src/test/java/org/apache/hc/core5/http/benchmark/SmokeTest.java
+++ b/httpcore5-ab/src/test/java/org/apache/hc/core5/http/benchmark/SmokeTest.java
@@ -52,7 +52,7 @@ public class SmokeTest {
     @Before
     public void setup() throws Exception {
         server = ServerBootstrap.bootstrap()
-                .registerHandler("/", new HttpRequestHandler() {
+                .register("/", new HttpRequestHandler() {
                     @Override
                     public void handle(
                             final ClassicHttpRequest request,
@@ -68,7 +68,9 @@ public class SmokeTest {
 
     @After
     public void shutdown() throws Exception {
-        server.shutdown(ShutdownType.IMMEDIATE);
+        if (server != null) {
+            server.shutdown(ShutdownType.IMMEDIATE);
+        }
     }
 
     @Test

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java
index ffaabdc..106d88c 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ClientPushHttp2StreamHandler.java
@@ -100,7 +100,7 @@ class ClientPushHttp2StreamHandler implements Http2StreamHandler {
 
             final AsyncPushConsumer handler;
             try {
-                handler = pushHandlerFactory != null ? pushHandlerFactory.create(request) : null;
+                handler = pushHandlerFactory != null ? pushHandlerFactory.create(request, context) : null;
             } catch (final ProtocolException ex) {
                 throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, ex.getMessage());
             }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
index e856218..bbd6a56 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/ServerHttp2StreamHandler.java
@@ -187,7 +187,7 @@ public class ServerHttp2StreamHandler implements Http2StreamHandler {
 
                 final AsyncServerExchangeHandler handler;
                 try {
-                    handler = exchangeHandlerFactory != null ? exchangeHandlerFactory.create(request) : null;
+                    handler = exchangeHandlerFactory != null ? exchangeHandlerFactory.create(request, context) : null;
                 } catch (final ProtocolException ex) {
                     throw new H2StreamResetException(H2Error.PROTOCOL_ERROR, ex.getMessage());
                 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
index 4d1043f..6bba2d9 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2RequesterBootstrap.java
@@ -40,13 +40,15 @@ import org.apache.hc.core5.http.impl.nio.ClientHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.nio.AsyncPushConsumer;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
+import org.apache.hc.core5.http.protocol.UriPatternType;
 import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.Http2Processors;
 import org.apache.hc.core5.http2.impl.nio.ClientHttp2StreamMultiplexerFactory;
 import org.apache.hc.core5.http2.impl.nio.ClientHttpProtocolNegotiatorFactory;
 import org.apache.hc.core5.http2.impl.nio.Http2StreamListener;
-import org.apache.hc.core5.http2.nio.support.AsyncPushConsumerRegistry;
+import org.apache.hc.core5.http2.nio.support.DefaultAsyncPushConsumerFactory;
 import org.apache.hc.core5.http2.ssl.H2ClientTlsStrategy;
 import org.apache.hc.core5.pool.ConnPoolListener;
 import org.apache.hc.core5.pool.ConnPoolPolicy;
@@ -63,8 +65,8 @@ import org.apache.hc.core5.util.TimeValue;
  */
 public class H2RequesterBootstrap {
 
-    private final List<PushConsumerEntry> pushConsumerList;
-
+    private final List<HandlerEntry<Supplier<AsyncPushConsumer>>> pushConsumerList;
+    private UriPatternType uriPatternType;
     private IOReactorConfig ioReactorConfig;
     private HttpProcessor httpProcessor;
     private CharCodingConfig charCodingConfig;
@@ -209,18 +211,41 @@ public class H2RequesterBootstrap {
         return this;
     }
 
+    /**
+     * Assigns {@link UriPatternType} for handler registration.
+     */
+    public final H2RequesterBootstrap setUriPatternType(final UriPatternType uriPatternType) {
+        this.uriPatternType = uriPatternType;
+        return this;
+    }
+
+    /**
+     * Registers the given {@link AsyncPushConsumer} {@link Supplier} as a default handler for URIs
+     * matching the given pattern.
+     *
+     * @param uriPattern the pattern to register the handler for.
+     * @param supplier the handler supplier.
+     */
     public final H2RequesterBootstrap register(final String uriPattern, final Supplier<AsyncPushConsumer> supplier) {
         Args.notBlank(uriPattern, "URI pattern");
         Args.notNull(supplier, "Supplier");
-        pushConsumerList.add(new PushConsumerEntry(null, uriPattern, supplier));
+        pushConsumerList.add(new HandlerEntry<>(null, uriPattern, supplier));
         return this;
     }
 
-    public final H2RequesterBootstrap register(final String hostname, final String uriPattern, final Supplier<AsyncPushConsumer> supplier) {
+    /**
+     * Registers the given {@link AsyncPushConsumer} {@link Supplier} as a handler for URIs
+     * matching the given host and the pattern.
+     *
+     * @param hostname the host name
+     * @param uriPattern the pattern to register the handler for.
+     * @param supplier the handler supplier.
+     */
+    public final H2RequesterBootstrap registerVirtual(final String hostname, final String uriPattern, final Supplier<AsyncPushConsumer> supplier) {
         Args.notBlank(hostname, "Hostname");
         Args.notBlank(uriPattern, "URI pattern");
         Args.notNull(supplier, "Supplier");
-        pushConsumerList.add(new PushConsumerEntry(hostname, uriPattern, supplier));
+        pushConsumerList.add(new HandlerEntry<>(hostname, uriPattern, supplier));
         return this;
     }
 
@@ -231,9 +256,9 @@ public class H2RequesterBootstrap {
                 timeToLive,
                 connPoolPolicy,
                 connPoolListener);
-        final AsyncPushConsumerRegistry pushConsumerRegistry = new AsyncPushConsumerRegistry();
-        for (final PushConsumerEntry entry: pushConsumerList) {
-            pushConsumerRegistry.register(entry.hostname, entry.uriPattern, entry.supplier);
+        final RequestHandlerRegistry<Supplier<AsyncPushConsumer>> registry = new RequestHandlerRegistry<>(uriPatternType);
+        for (final HandlerEntry<Supplier<AsyncPushConsumer>> entry: pushConsumerList) {
+            registry.register(entry.hostname, entry.uriPattern, entry.handler);
         }
         final ClientHttp1StreamDuplexerFactory http1StreamHandlerFactory = new ClientHttp1StreamDuplexerFactory(
                 httpProcessor != null ? httpProcessor : HttpProcessors.client(),
@@ -242,7 +267,7 @@ public class H2RequesterBootstrap {
                 http1StreamListener);
         final ClientHttp2StreamMultiplexerFactory http2StreamHandlerFactory = new ClientHttp2StreamMultiplexerFactory(
                 httpProcessor != null ? httpProcessor : Http2Processors.client(),
-                pushConsumerRegistry,
+                new DefaultAsyncPushConsumerFactory(registry),
                 h2Config != null ? h2Config : H2Config.DEFAULT,
                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 streamListener);
@@ -260,18 +285,4 @@ public class H2RequesterBootstrap {
                 tlsStrategy != null ? tlsStrategy : new H2ClientTlsStrategy());
     }
 
-    private static class PushConsumerEntry {
-
-        final String hostname;
-        final String uriPattern;
-        final Supplier<AsyncPushConsumer> supplier;
-
-        public PushConsumerEntry(final String hostname, final String uriPattern, final Supplier<AsyncPushConsumer> supplier) {
-            this.hostname = hostname;
-            this.uriPattern = uriPattern;
-            this.supplier = supplier;
-        }
-
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
index 288bb2a..0a25098 100644
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/H2ServerBootstrap.java
@@ -37,16 +37,19 @@ import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.impl.DefaultContentLengthStrategy;
 import org.apache.hc.core5.http.impl.Http1StreamListener;
 import org.apache.hc.core5.http.impl.HttpProcessors;
-import org.apache.hc.core5.http.nio.support.AsyncServerExchangeHandlerRegistry;
 import org.apache.hc.core5.http.impl.bootstrap.HttpAsyncServer;
 import org.apache.hc.core5.http.impl.nio.DefaultHttpRequestParserFactory;
 import org.apache.hc.core5.http.impl.nio.DefaultHttpResponseWriterFactory;
 import org.apache.hc.core5.http.impl.nio.ServerHttp1StreamDuplexerFactory;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
+import org.apache.hc.core5.http.nio.HandlerFactory;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
 import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
+import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
+import org.apache.hc.core5.http.protocol.UriPatternType;
 import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.Http2Processors;
@@ -66,8 +69,9 @@ import org.apache.hc.core5.util.Args;
  */
 public class H2ServerBootstrap {
 
-    private final List<HandlerEntry> handlerList;
+    private final List<HandlerEntry<Supplier<AsyncServerExchangeHandler>>> handlerList;
     private String canonicalHostName;
+    private UriPatternType uriPatternType;
     private IOReactorConfig ioReactorConfig;
     private HttpProcessor httpProcessor;
     private CharCodingConfig charCodingConfig;
@@ -186,21 +190,51 @@ public class H2ServerBootstrap {
         return this;
     }
 
+    /**
+     * Assigns {@link UriPatternType} for handler registration.
+     */
+    public final H2ServerBootstrap setUriPatternType(final UriPatternType uriPatternType) {
+        this.uriPatternType = uriPatternType;
+        return this;
+    }
+
+    /**
+     * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a default handler for URIs
+     * matching the given pattern.
+     *
+     * @param uriPattern the pattern to register the handler for.
+     * @param supplier the handler supplier.
+     */
     public final H2ServerBootstrap register(final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
         Args.notBlank(uriPattern, "URI pattern");
         Args.notNull(supplier, "Supplier");
-        handlerList.add(new HandlerEntry(null, uriPattern, supplier));
+        handlerList.add(new HandlerEntry<>(null, uriPattern, supplier));
         return this;
     }
 
+    /**
+     * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a handler for URIs
+     * matching the given host and the pattern.
+     *
+     * @param hostname the host name
+     * @param uriPattern the pattern to register the handler for.
+     * @param supplier the handler supplier.
+     */
     public final H2ServerBootstrap registerVirtual(final String hostname, final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
         Args.notBlank(hostname, "Hostname");
         Args.notBlank(uriPattern, "URI pattern");
         Args.notNull(supplier, "Supplier");
-        handlerList.add(new HandlerEntry(hostname, uriPattern, supplier));
+        handlerList.add(new HandlerEntry<>(hostname, uriPattern, supplier));
         return this;
     }
 
+    /**
+     * Registers the given {@link AsyncServerRequestHandler} as a default handler for URIs
+     * matching the given pattern.
+     *
+     * @param uriPattern the pattern to register the handler for.
+     * @param requestHandler the handler.
+     */
     public final <T> H2ServerBootstrap register(
             final String uriPattern,
             final AsyncServerRequestHandler<T> requestHandler) {
@@ -215,6 +249,14 @@ public class H2ServerBootstrap {
         return this;
     }
 
+    /**
+     * Registers the given {@link AsyncServerRequestHandler} as a handler for URIs
+     * matching the given host and the pattern.
+     *
+     * @param hostname the host name
+     * @param uriPattern the pattern to register the handler for.
+     * @param requestHandler the handler.
+     */
     public final <T> H2ServerBootstrap registerVirtual(
             final String hostname,
             final String uriPattern,
@@ -231,11 +273,13 @@ public class H2ServerBootstrap {
     }
 
     public HttpAsyncServer create() {
-        final AsyncServerExchangeHandlerRegistry exchangeHandlerFactory = new AsyncServerExchangeHandlerRegistry(
-                canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName());
-        for (final HandlerEntry entry: handlerList) {
-            exchangeHandlerFactory.register(entry.hostname, entry.uriPattern, entry.supplier);
+        final RequestHandlerRegistry<Supplier<AsyncServerExchangeHandler>> registry = new RequestHandlerRegistry<>(
+                canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName(),
+                uriPatternType);
+        for (final HandlerEntry<Supplier<AsyncServerExchangeHandler>> entry: handlerList) {
+            registry.register(entry.hostname, entry.uriPattern, entry.handler);
         }
+        final HandlerFactory<AsyncServerExchangeHandler> exchangeHandlerFactory = new DefaultAsyncResponseExchangeHandlerFactory(registry);
         final ServerHttp2StreamMultiplexerFactory http2StreamHandlerFactory = new ServerHttp2StreamMultiplexerFactory(
                 httpProcessor != null ? httpProcessor : Http2Processors.server(),
                 exchangeHandlerFactory,
@@ -261,18 +305,4 @@ public class H2ServerBootstrap {
         return new HttpAsyncServer(ioEventHandlerFactory, ioReactorConfig, ioSessionDecorator, sessionListener);
     }
 
-    private static class HandlerEntry {
-
-        final String hostname;
-        final String uriPattern;
-        final Supplier<AsyncServerExchangeHandler> supplier;
-
-        public HandlerEntry(final String hostname, final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
-            this.hostname = hostname;
-            this.uriPattern = uriPattern;
-            this.supplier = supplier;
-        }
-
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/HandlerEntry.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/HandlerEntry.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/HandlerEntry.java
new file mode 100644
index 0000000..caf0e68
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/impl/nio/bootstrap/HandlerEntry.java
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http2.impl.nio.bootstrap;
+
+final class HandlerEntry<T> {
+
+    final String hostname;
+    final String uriPattern;
+    final T handler;
+
+    public HandlerEntry(final String hostname, final String uriPattern, final T handler) {
+        this.hostname = hostname;
+        this.uriPattern = uriPattern;
+        this.handler = handler;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/AsyncPushConsumerRegistry.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/AsyncPushConsumerRegistry.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/AsyncPushConsumerRegistry.java
deleted file mode 100644
index b44ccf4..0000000
--- a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/AsyncPushConsumerRegistry.java
+++ /dev/null
@@ -1,103 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.http2.nio.support;
-
-import java.util.Locale;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import org.apache.hc.core5.function.Supplier;
-import org.apache.hc.core5.http.HttpException;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.nio.AsyncPushConsumer;
-import org.apache.hc.core5.http.nio.HandlerFactory;
-import org.apache.hc.core5.http.protocol.UriPatternMatcher;
-import org.apache.hc.core5.net.URIAuthority;
-import org.apache.hc.core5.util.Args;
-
-public class AsyncPushConsumerRegistry implements HandlerFactory<AsyncPushConsumer> {
-
-    private final UriPatternMatcher<Supplier<AsyncPushConsumer>> primary;
-    private final ConcurrentMap<String, UriPatternMatcher<Supplier<AsyncPushConsumer>>> hostMap;
-
-    public AsyncPushConsumerRegistry() {
-        this.primary = new UriPatternMatcher<>();
-        this.hostMap = new ConcurrentHashMap<>();
-    }
-
-    private UriPatternMatcher<Supplier<AsyncPushConsumer>> getPatternMatcher(final String hostname) {
-        if (hostname == null) {
-            return primary;
-        }
-        final UriPatternMatcher<Supplier<AsyncPushConsumer>> hostMatcher = hostMap.get(hostname);
-        if (hostMatcher != null) {
-            return hostMatcher;
-        }
-        return primary;
-    }
-
-    @Override
-    public AsyncPushConsumer create(final HttpRequest request) throws HttpException {
-        final URIAuthority authority = request.getAuthority();
-        final String key = authority != null ? authority.getHostName().toLowerCase(Locale.ROOT) : null;
-        final UriPatternMatcher<Supplier<AsyncPushConsumer>> patternMatcher = getPatternMatcher(key);
-        if (patternMatcher == null) {
-            return null;
-        }
-        String path = request.getPath();
-        final int i = path.indexOf("?");
-        if (i != -1) {
-            path = path.substring(0, i - 1);
-        }
-        final Supplier<AsyncPushConsumer> supplier = patternMatcher.lookup(path);
-        if (supplier != null) {
-            return supplier.get();
-        }
-        return null;
-    }
-
-    public void register(final String hostname, final String uriPattern, final Supplier<AsyncPushConsumer> supplier) {
-        Args.notBlank(uriPattern, "URI pattern");
-        Args.notNull(supplier, "Supplier");
-        if (hostname == null) {
-            primary.register(uriPattern, supplier);
-        } else {
-            final String key = hostname.toLowerCase(Locale.ROOT);
-            UriPatternMatcher<Supplier<AsyncPushConsumer>> matcher = hostMap.get(key);
-            if (matcher == null) {
-                final UriPatternMatcher<Supplier<AsyncPushConsumer>> newMatcher = new UriPatternMatcher<>();
-                matcher = hostMap.putIfAbsent(key, newMatcher);
-                if (matcher == null) {
-                    matcher = newMatcher;
-                }
-            }
-            matcher.register(uriPattern, supplier);
-        }
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/DefaultAsyncPushConsumerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/DefaultAsyncPushConsumerFactory.java b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/DefaultAsyncPushConsumerFactory.java
new file mode 100644
index 0000000..2e76c23
--- /dev/null
+++ b/httpcore5-h2/src/main/java/org/apache/hc/core5/http2/nio/support/DefaultAsyncPushConsumerFactory.java
@@ -0,0 +1,63 @@
+/*
+ * ====================================================================
+ * 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.nio.support;
+
+import org.apache.hc.core5.function.Supplier;
+import org.apache.hc.core5.http.HttpException;
+import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.HttpRequestMapper;
+import org.apache.hc.core5.http.MisdirectedRequestException;
+import org.apache.hc.core5.http.nio.AsyncPushConsumer;
+import org.apache.hc.core5.http.nio.HandlerFactory;
+import org.apache.hc.core5.http.protocol.HttpContext;
+import org.apache.hc.core5.util.Args;
+
+/**
+ * @since 5.0
+ */
+public final class DefaultAsyncPushConsumerFactory implements HandlerFactory<AsyncPushConsumer> {
+
+    private final HttpRequestMapper<Supplier<AsyncPushConsumer>> mapper;
+
+    public DefaultAsyncPushConsumerFactory(final HttpRequestMapper<Supplier<AsyncPushConsumer>> mapper) {
+        this.mapper = Args.notNull(mapper, "Request handler mapper");
+    }
+
+    @Override
+    public AsyncPushConsumer create(final HttpRequest request, final HttpContext context) throws HttpException {
+        try {
+            final Supplier<AsyncPushConsumer> supplier = mapper.resolve(request, context);
+            if (supplier != null) {
+                return supplier.get();
+            } else {
+                return null;
+            }
+        } catch (final MisdirectedRequestException ex) {
+            return null;
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
index 323fd2d..84937dc 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/classic/ClassicTestServer.java
@@ -46,15 +46,15 @@ import org.apache.hc.core5.http.impl.io.DefaultClassicHttpResponseFactory;
 import org.apache.hc.core5.http.impl.io.HttpService;
 import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
-import org.apache.hc.core5.http.io.support.UriHttpRequestHandlerMapper;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
 import org.apache.hc.core5.io.ShutdownType;
 
 public class ClassicTestServer {
 
     private final SSLContext sslContext;
     private final SocketConfig socketConfig;
-    private final UriHttpRequestHandlerMapper registry;
+    private final RequestHandlerRegistry<HttpRequestHandler> registry;
 
     private final AtomicReference<HttpServer> serverRef;
 
@@ -62,7 +62,7 @@ public class ClassicTestServer {
         super();
         this.sslContext = sslContext;
         this.socketConfig = socketConfig != null ? socketConfig : SocketConfig.DEFAULT;
-        this.registry = new UriHttpRequestHandlerMapper();
+        this.registry = new RequestHandlerRegistry<>();
         this.serverRef = new AtomicReference<>(null);
     }
 
@@ -74,10 +74,12 @@ public class ClassicTestServer {
         this(null, null);
     }
 
-    public void registerHandler(
-            final String pattern,
-            final HttpRequestHandler handler) {
-        this.registry.register(pattern, handler);
+    public void registerHandler(final String pattern, final HttpRequestHandler handler) {
+        this.registry.register(null, pattern, handler);
+    }
+
+    public void registerHandlerVirtual(final String hostname, final String pattern, final HttpRequestHandler handler) {
+        this.registry.register(hostname, pattern, handler);
     }
 
     public int getPort() {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java
index ead8792..f5ffea9 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/framework/TestingFramework.java
@@ -231,7 +231,7 @@ public class TestingFramework {
 
         final ServerBootstrap serverBootstrap = ServerBootstrap.bootstrap()
                                           .setSocketConfig(socketConfig)
-                                          .registerHandler("/*", requestHandler);
+                                          .register("/*", requestHandler);
 
         server = serverBootstrap.create();
         try {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java
index 9960d6e..9193a05 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http1TestServer.java
@@ -38,23 +38,24 @@ import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.impl.HttpProcessors;
-import org.apache.hc.core5.http.nio.support.AsyncServerExchangeHandlerRegistry;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
 import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
+import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.IOReactorConfig;
 import org.apache.hc.core5.reactor.ListenerEndpoint;
 
 public class Http1TestServer extends AsyncServer {
 
-    private final AsyncServerExchangeHandlerRegistry handlerRegistry;
+    private final RequestHandlerRegistry<Supplier<AsyncServerExchangeHandler>> registry;
     private final SSLContext sslContext;
 
     public Http1TestServer(final IOReactorConfig ioReactorConfig, final SSLContext sslContext) throws IOException {
         super(ioReactorConfig);
-        this.handlerRegistry = new AsyncServerExchangeHandlerRegistry("localhost");
+        this.registry = new RequestHandlerRegistry<>();
         this.sslContext = sslContext;
     }
 
@@ -63,7 +64,7 @@ public class Http1TestServer extends AsyncServer {
     }
 
     public void register(final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
-        handlerRegistry.register(null, uriPattern, supplier);
+        registry.register(null, uriPattern, supplier);
     }
 
     public <T> void register(
@@ -89,7 +90,7 @@ public class Http1TestServer extends AsyncServer {
     public InetSocketAddress start(final HttpProcessor httpProcessor, final H1Config h1Config) throws Exception {
         return start(new InternalServerHttp1EventHandlerFactory(
                 httpProcessor,
-                handlerRegistry,
+                new DefaultAsyncResponseExchangeHandlerFactory(registry),
                 h1Config,
                 CharCodingConfig.DEFAULT,
                 DefaultConnectionReuseStrategy.INSTANCE,

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java
index 1f2f2eb..df6a56c 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestClient.java
@@ -40,10 +40,11 @@ import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.nio.AsyncPushConsumer;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
 import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.Http2Processors;
-import org.apache.hc.core5.http2.nio.support.AsyncPushConsumerRegistry;
+import org.apache.hc.core5.http2.nio.support.DefaultAsyncPushConsumerFactory;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.IOReactorConfig;
 import org.apache.hc.core5.reactor.IOSession;
@@ -53,12 +54,12 @@ import org.apache.hc.core5.util.TimeValue;
 public class Http2TestClient extends AsyncRequester {
 
     private final SSLContext sslContext;
-    private final AsyncPushConsumerRegistry pushConsumerRegistry;
+    private final RequestHandlerRegistry<Supplier<AsyncPushConsumer>> registry;
 
     public Http2TestClient(final IOReactorConfig ioReactorConfig, final SSLContext sslContext) throws IOException {
         super(ioReactorConfig);
         this.sslContext = sslContext;
-        this.pushConsumerRegistry = new AsyncPushConsumerRegistry();
+        this.registry = new RequestHandlerRegistry<>();
     }
 
     public Http2TestClient() throws IOException {
@@ -68,7 +69,7 @@ public class Http2TestClient extends AsyncRequester {
     public void register(final String uriPattern, final Supplier<AsyncPushConsumer> supplier) {
         Args.notNull(uriPattern, "URI pattern");
         Args.notNull(supplier, "Supplier");
-        pushConsumerRegistry.register(null, uriPattern, supplier);
+        registry.register(null, uriPattern, supplier);
     }
 
     public void start(final IOEventHandlerFactory handlerFactory) throws IOException {
@@ -78,7 +79,7 @@ public class Http2TestClient extends AsyncRequester {
     public void start(final HttpProcessor httpProcessor, final H2Config h2Config) throws IOException {
         start(new InternalClientHttp2EventHandlerFactory(
                 httpProcessor,
-                pushConsumerRegistry,
+                new DefaultAsyncPushConsumerFactory(registry),
                 HttpVersionPolicy.FORCE_HTTP_2,
                 h2Config,
                 H1Config.DEFAULT,
@@ -89,7 +90,7 @@ public class Http2TestClient extends AsyncRequester {
     public void start(final HttpProcessor httpProcessor, final H1Config h1Config) throws IOException {
         start(new InternalClientHttp2EventHandlerFactory(
                 httpProcessor,
-                pushConsumerRegistry,
+                new DefaultAsyncPushConsumerFactory(registry),
                 HttpVersionPolicy.FORCE_HTTP_1,
                 H2Config.DEFAULT,
                 h1Config,

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
index 7f7cb3f..171f9c0 100644
--- a/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
+++ b/httpcore5-testing/src/main/java/org/apache/hc/core5/testing/nio/Http2TestServer.java
@@ -37,11 +37,12 @@ import org.apache.hc.core5.function.Supplier;
 import org.apache.hc.core5.http.config.CharCodingConfig;
 import org.apache.hc.core5.http.config.H1Config;
 import org.apache.hc.core5.http.impl.HttpProcessors;
-import org.apache.hc.core5.http.nio.support.AsyncServerExchangeHandlerRegistry;
 import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
 import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
+import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
 import org.apache.hc.core5.http2.HttpVersionPolicy;
 import org.apache.hc.core5.http2.config.H2Config;
 import org.apache.hc.core5.http2.impl.Http2Processors;
@@ -52,12 +53,12 @@ import org.apache.hc.core5.reactor.ListenerEndpoint;
 public class Http2TestServer extends AsyncServer {
 
     private final SSLContext sslContext;
-    private final AsyncServerExchangeHandlerRegistry handlerRegistry;
+    private final RequestHandlerRegistry<Supplier<AsyncServerExchangeHandler>> registry;
 
     public Http2TestServer(final IOReactorConfig ioReactorConfig, final SSLContext sslContext) throws IOException {
         super(ioReactorConfig);
         this.sslContext = sslContext;
-        this.handlerRegistry = new AsyncServerExchangeHandlerRegistry("localhost");
+        this.registry = new RequestHandlerRegistry<>();
     }
 
     public Http2TestServer() throws IOException {
@@ -65,7 +66,7 @@ public class Http2TestServer extends AsyncServer {
     }
 
     public void register(final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
-        handlerRegistry.register(null, uriPattern, supplier);
+        registry.register(null, uriPattern, supplier);
     }
 
     public <T> void register(
@@ -88,7 +89,7 @@ public class Http2TestServer extends AsyncServer {
     public InetSocketAddress start(final HttpProcessor httpProcessor, final H2Config h2Config) throws Exception {
         start(new InternalServerHttp2EventHandlerFactory(
                 httpProcessor,
-                handlerRegistry,
+                new DefaultAsyncResponseExchangeHandlerFactory(registry),
                 HttpVersionPolicy.FORCE_HTTP_2,
                 h2Config,
                 H1Config.DEFAULT,
@@ -102,7 +103,7 @@ public class Http2TestServer extends AsyncServer {
     public InetSocketAddress start(final HttpProcessor httpProcessor, final H1Config h1Config) throws Exception {
         start(new InternalServerHttp2EventHandlerFactory(
                 httpProcessor,
-                handlerRegistry,
+                new DefaultAsyncResponseExchangeHandlerFactory(registry),
                 HttpVersionPolicy.FORCE_HTTP_1,
                 H2Config.DEFAULT,
                 h1Config,

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
----------------------------------------------------------------------
diff --git a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
index 0bc01f2..825fb6f 100644
--- a/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
+++ b/httpcore5-testing/src/test/java/org/apache/hc/core5/testing/nio/Http1IntegrationTest.java
@@ -1348,7 +1348,9 @@ public class Http1IntegrationTest extends InternalHttp1ServerTestBase {
                 new HandlerFactory<AsyncServerExchangeHandler>() {
 
                     @Override
-                    public AsyncServerExchangeHandler create(final HttpRequest request) throws HttpException {
+                    public AsyncServerExchangeHandler create(
+                            final HttpRequest request,
+                            final HttpContext context) throws HttpException {
                         return new MessageExchangeHandler<String>(new StringAsyncEntityConsumer()) {
 
                             @Override

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicFileServerExample.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicFileServerExample.java b/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicFileServerExample.java
index 0202338..1f1d7cd 100644
--- a/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicFileServerExample.java
+++ b/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicFileServerExample.java
@@ -120,7 +120,7 @@ public class ClassicFileServerExample {
                     }
 
                 })
-                .registerHandler("*", new HttpFileHandler(docRoot))
+                .register("*", new HttpFileHandler(docRoot))
                 .create();
 
         server.start();

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicReverseProxyExample.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicReverseProxyExample.java b/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicReverseProxyExample.java
index b5f2c34..6bb04c5 100644
--- a/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicReverseProxyExample.java
+++ b/httpcore5/src/examples/org/apache/hc/core5/http/examples/ClassicReverseProxyExample.java
@@ -173,7 +173,7 @@ public class ClassicReverseProxyExample {
                     }
 
                 })
-                .registerHandler("*", new ProxyHandler(targetHost, requester))
+                .register("*", new ProxyHandler(targetHost, requester))
                 .create();
 
         server.start();

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/function/Factory.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/function/Factory.java b/httpcore5/src/main/java/org/apache/hc/core5/function/Factory.java
new file mode 100644
index 0000000..ea9cb95
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/function/Factory.java
@@ -0,0 +1,39 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+
+package org.apache.hc.core5.function;
+
+/**
+ * Abstract object factory.
+ *
+ * @since 5.0
+ */
+public interface Factory<T> {
+
+    T create();
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/HttpRequestMapper.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/HttpRequestMapper.java b/httpcore5/src/main/java/org/apache/hc/core5/http/HttpRequestMapper.java
new file mode 100644
index 0000000..15431ba
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/HttpRequestMapper.java
@@ -0,0 +1,49 @@
+/*
+ * ====================================================================
+ * 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;
+
+import org.apache.hc.core5.http.protocol.HttpContext;
+
+/**
+ * This class can be used to resolve an object matching a particular {@link HttpRequest}.
+ * Usually the mapped object will be a request handler to process the request.
+ *
+ * @since 5.0
+ */
+public interface HttpRequestMapper<T> {
+
+    /**
+     * Resolves a handler matching the given request.
+     *
+     * @param request the request to map to a handler
+     * @return HTTP request handler or {@code null} if no match
+     * is found.
+     */
+    T resolve(HttpRequest request, HttpContext context) throws HttpException;
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java
index 18c9064..8e5be10 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/AsyncServerBootstrap.java
@@ -46,9 +46,11 @@ import org.apache.hc.core5.http.nio.AsyncServerExchangeHandler;
 import org.apache.hc.core5.http.nio.AsyncServerRequestHandler;
 import org.apache.hc.core5.http.nio.ssl.BasicServerTlsStrategy;
 import org.apache.hc.core5.http.nio.ssl.TlsStrategy;
-import org.apache.hc.core5.http.nio.support.AsyncServerExchangeHandlerRegistry;
 import org.apache.hc.core5.http.nio.support.BasicServerExchangeHandler;
+import org.apache.hc.core5.http.nio.support.DefaultAsyncResponseExchangeHandlerFactory;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
+import org.apache.hc.core5.http.protocol.UriPatternType;
 import org.apache.hc.core5.net.InetAddressUtils;
 import org.apache.hc.core5.reactor.IOEventHandlerFactory;
 import org.apache.hc.core5.reactor.IOReactorConfig;
@@ -61,8 +63,9 @@ import org.apache.hc.core5.util.Args;
  */
 public class AsyncServerBootstrap {
 
-    private final List<HandlerEntry> handlerList;
+    private final List<HandlerEntry<Supplier<AsyncServerExchangeHandler>>> handlerList;
     private String canonicalHostName;
+    private UriPatternType uriPatternType;
     private IOReactorConfig ioReactorConfig;
     private H1Config h1Config;
     private CharCodingConfig charCodingConfig;
@@ -83,8 +86,6 @@ public class AsyncServerBootstrap {
 
     /**
      * Sets canonical name (fully qualified domain name) of the server.
-     *
-     * @since 5.0
      */
     public final AsyncServerBootstrap setCanonicalHostName(final String canonicalHostName) {
         this.canonicalHostName = canonicalHostName;
@@ -165,21 +166,51 @@ public class AsyncServerBootstrap {
         return this;
     }
 
+    /**
+     * Assigns {@link UriPatternType} for handler registration.
+     */
+    public final AsyncServerBootstrap setUriPatternType(final UriPatternType uriPatternType) {
+        this.uriPatternType = uriPatternType;
+        return this;
+    }
+
+    /**
+     * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a default handler for URIs
+     * matching the given pattern.
+     *
+     * @param uriPattern the pattern to register the handler for.
+     * @param supplier the handler supplier.
+     */
     public final AsyncServerBootstrap register(final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
         Args.notBlank(uriPattern, "URI pattern");
         Args.notNull(supplier, "Supplier");
-        handlerList.add(new HandlerEntry(null, uriPattern, supplier));
+        handlerList.add(new HandlerEntry<>(null, uriPattern, supplier));
         return this;
     }
 
+    /**
+     * Registers the given {@link AsyncServerExchangeHandler} {@link Supplier} as a handler for URIs
+     * matching the given host and the pattern.
+     *
+     * @param hostname the host name
+     * @param uriPattern the pattern to register the handler for.
+     * @param supplier the handler supplier.
+     */
     public final AsyncServerBootstrap registerVirtual(final String hostname, final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
         Args.notBlank(hostname, "Hostname");
         Args.notBlank(uriPattern, "URI pattern");
         Args.notNull(supplier, "Supplier");
-        handlerList.add(new HandlerEntry(hostname, uriPattern, supplier));
+        handlerList.add(new HandlerEntry<>(hostname, uriPattern, supplier));
         return this;
     }
 
+    /**
+     * Registers the given {@link AsyncServerRequestHandler} as a default handler for URIs
+     * matching the given pattern.
+     *
+     * @param uriPattern the pattern to register the handler for.
+     * @param requestHandler the handler.
+     */
     public final <T> AsyncServerBootstrap register(
             final String uriPattern,
             final AsyncServerRequestHandler<T> requestHandler) {
@@ -194,6 +225,14 @@ public class AsyncServerBootstrap {
         return this;
     }
 
+    /**
+     * Registers the given {@link AsyncServerRequestHandler} as a handler for URIs
+     * matching the given host and the pattern.
+     *
+     * @param hostname the host name
+     * @param uriPattern the pattern to register the handler for.
+     * @param requestHandler the handler.
+     */
     public final <T> AsyncServerBootstrap registerVirtual(
             final String hostname,
             final String uriPattern,
@@ -210,14 +249,15 @@ public class AsyncServerBootstrap {
     }
 
     public HttpAsyncServer create() {
-        final AsyncServerExchangeHandlerRegistry exchangeHandlerFactory = new AsyncServerExchangeHandlerRegistry(
-                canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName());
-        for (final HandlerEntry entry: handlerList) {
-            exchangeHandlerFactory.register(entry.hostname, entry.uriPattern, entry.supplier);
+        final RequestHandlerRegistry<Supplier<AsyncServerExchangeHandler>> registry = new RequestHandlerRegistry<>(
+                canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName(),
+                uriPatternType);
+        for (final HandlerEntry<Supplier<AsyncServerExchangeHandler>> entry: handlerList) {
+            registry.register(entry.hostname, entry.uriPattern, entry.handler);
         }
         final ServerHttp1StreamDuplexerFactory streamHandlerFactory = new ServerHttp1StreamDuplexerFactory(
                 httpProcessor != null ? httpProcessor : HttpProcessors.server(),
-                exchangeHandlerFactory,
+                new DefaultAsyncResponseExchangeHandlerFactory(registry),
                 h1Config != null ? h1Config : H1Config.DEFAULT,
                 charCodingConfig != null ? charCodingConfig : CharCodingConfig.DEFAULT,
                 connStrategy != null ? connStrategy : DefaultConnectionReuseStrategy.INSTANCE,
@@ -232,18 +272,4 @@ public class AsyncServerBootstrap {
         return new HttpAsyncServer(ioEventHandlerFactory, ioReactorConfig, ioSessionDecorator, sessionListener);
     }
 
-    private static class HandlerEntry {
-
-        final String hostname;
-        final String uriPattern;
-        final Supplier<AsyncServerExchangeHandler> supplier;
-
-        public HandlerEntry(final String hostname, final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
-            this.hostname = hostname;
-            this.uriPattern = uriPattern;
-            this.supplier = supplier;
-        }
-
-    }
-
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HandlerEntry.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HandlerEntry.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HandlerEntry.java
new file mode 100644
index 0000000..f2f4f48
--- /dev/null
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/HandlerEntry.java
@@ -0,0 +1,41 @@
+/*
+ * ====================================================================
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ * ====================================================================
+ *
+ * This software consists of voluntary contributions made by many
+ * individuals on behalf of the Apache Software Foundation.  For more
+ * information on the Apache Software Foundation, please see
+ * <http://www.apache.org/>.
+ *
+ */
+package org.apache.hc.core5.http.impl.bootstrap;
+
+final class HandlerEntry<T> {
+
+    final String hostname;
+    final String uriPattern;
+    final T handler;
+
+    public HandlerEntry(final String hostname, final String uriPattern, final T handler) {
+        this.hostname = hostname;
+        this.uriPattern = uriPattern;
+        this.handler = handler;
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java
index 84b4516..4bdbba7 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/bootstrap/ServerBootstrap.java
@@ -27,8 +27,8 @@
 package org.apache.hc.core5.http.impl.bootstrap;
 
 import java.net.InetAddress;
-import java.util.LinkedHashMap;
-import java.util.Map;
+import java.util.ArrayList;
+import java.util.List;
 
 import javax.net.ServerSocketFactory;
 import javax.net.ssl.SSLContext;
@@ -52,15 +52,20 @@ import org.apache.hc.core5.http.impl.io.HttpService;
 import org.apache.hc.core5.http.io.HttpConnectionFactory;
 import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
-import org.apache.hc.core5.http.io.HttpRequestHandlerMapper;
-import org.apache.hc.core5.http.io.support.UriHttpRequestHandlerMapper;
 import org.apache.hc.core5.http.protocol.HttpProcessor;
+import org.apache.hc.core5.http.protocol.RequestHandlerRegistry;
+import org.apache.hc.core5.http.protocol.UriPatternType;
+import org.apache.hc.core5.net.InetAddressUtils;
+import org.apache.hc.core5.util.Args;
 
 /**
  * @since 4.4
  */
 public class ServerBootstrap {
 
+    private final List<HandlerEntry<HttpRequestHandler>> handlerList;
+    private String canonicalHostName;
+    private UriPatternType uriPatternType;
     private int listenerPort;
     private InetAddress localAddress;
     private SocketConfig socketConfig;
@@ -69,8 +74,6 @@ public class ServerBootstrap {
     private HttpProcessor httpProcessor;
     private ConnectionReuseStrategy connStrategy;
     private HttpResponseFactory<ClassicHttpResponse> responseFactory;
-    private HttpRequestHandlerMapper handlerMapper;
-    private Map<String, HttpRequestHandler> handlerMap;
     private HttpExpectationVerifier expectationVerifier;
     private ServerSocketFactory serverSocketFactory;
     private SSLContext sslContext;
@@ -80,6 +83,7 @@ public class ServerBootstrap {
     private Http1StreamListener streamListener;
 
     private ServerBootstrap() {
+        this.handlerList = new ArrayList<>();
     }
 
     public static ServerBootstrap bootstrap() {
@@ -87,6 +91,16 @@ public class ServerBootstrap {
     }
 
     /**
+     * Sets canonical name (fully qualified domain name) of the server.
+     *
+     * @since 5.0
+     */
+    public final ServerBootstrap setCanonicalHostName(final String canonicalHostName) {
+        this.canonicalHostName = canonicalHostName;
+        return this;
+    }
+
+    /**
      * Sets listener port number.
      */
     public final ServerBootstrap setListenerPort(final int listenerPort) {
@@ -151,31 +165,40 @@ public class ServerBootstrap {
     }
 
     /**
-     * Assigns {@link HttpRequestHandlerMapper} instance.
+     * Assigns {@link UriPatternType} for handler registration.
      */
-    public final ServerBootstrap setHandlerMapper(final HttpRequestHandlerMapper handlerMapper) {
-        this.handlerMapper = handlerMapper;
+    public final ServerBootstrap  setUriPatternType(final UriPatternType uriPatternType) {
+        this.uriPatternType = uriPatternType;
         return this;
     }
 
     /**
-     * Registers the given {@link HttpRequestHandler} as a handler for URIs
+     * Registers the given {@link HttpRequestHandler} as a default handler for URIs
      * matching the given pattern.
-     * <p>
-     * Please note this value can be overridden by the {@link #setHandlerMapper(
-     *HttpRequestHandlerMapper)} method.
      *
-     * @param pattern the pattern to register the handler for.
-     * @param handler the handler.
+     * @param uriPattern the pattern to register the handler for.
+     * @param requestHandler the handler.
      */
-    public final ServerBootstrap registerHandler(final String pattern, final HttpRequestHandler handler) {
-        if (pattern == null || handler == null) {
-            return this;
-        }
-        if (handlerMap == null) {
-            handlerMap = new LinkedHashMap<>();
-        }
-        handlerMap.put(pattern, handler);
+    public final ServerBootstrap register(final String uriPattern, final HttpRequestHandler requestHandler) {
+        Args.notBlank(uriPattern, "URI pattern");
+        Args.notNull(requestHandler, "Supplier");
+        handlerList.add(new HandlerEntry<>(null, uriPattern, requestHandler));
+        return this;
+    }
+
+    /**
+     * Registers the given {@link HttpRequestHandler} as a handler for URIs
+     * matching the given host and the pattern.
+     *
+     * @param hostname
+     * @param uriPattern the pattern to register the handler for.
+     * @param requestHandler the handler.
+     */
+    public final ServerBootstrap registerVirtual(final String hostname, final String uriPattern, final HttpRequestHandler requestHandler) {
+        Args.notBlank(hostname, "Hostname");
+        Args.notBlank(uriPattern, "URI pattern");
+        Args.notNull(requestHandler, "Supplier");
+        handlerList.add(new HandlerEntry<>(hostname, uriPattern, requestHandler));
         return this;
     }
 
@@ -240,23 +263,18 @@ public class ServerBootstrap {
     }
 
     public HttpServer create() {
+        final RequestHandlerRegistry<HttpRequestHandler> requestHandlerRegistry = new RequestHandlerRegistry<>(
+                canonicalHostName != null ? canonicalHostName : InetAddressUtils.getCanonicalLocalHostName(),
+                uriPatternType);
+        for (final HandlerEntry<HttpRequestHandler> entry: handlerList) {
+            requestHandlerRegistry.register(entry.hostname, entry.uriPattern, entry.handler);
+        }
 
         HttpProcessor httpProcessorCopy = this.httpProcessor;
         if (httpProcessorCopy == null) {
             httpProcessorCopy = HttpProcessors.server();
         }
 
-        HttpRequestHandlerMapper handlerMapperCopy = this.handlerMapper;
-        if (handlerMapperCopy == null) {
-            final UriHttpRequestHandlerMapper reqistry = new UriHttpRequestHandlerMapper();
-            if (handlerMap != null) {
-                for (final Map.Entry<String, HttpRequestHandler> entry: handlerMap.entrySet()) {
-                    reqistry.register(entry.getKey(), entry.getValue());
-                }
-            }
-            handlerMapperCopy = reqistry;
-        }
-
         ConnectionReuseStrategy connStrategyCopy = this.connStrategy;
         if (connStrategyCopy == null) {
             connStrategyCopy = DefaultConnectionReuseStrategy.INSTANCE;
@@ -268,7 +286,7 @@ public class ServerBootstrap {
         }
 
         final HttpService httpService = new HttpService(
-                httpProcessorCopy, connStrategyCopy, responseFactoryCopy, handlerMapperCopy,
+                httpProcessorCopy, connStrategyCopy, responseFactoryCopy, requestHandlerRegistry,
                 this.expectationVerifier, this.streamListener);
 
         ServerSocketFactory serverSocketFactoryCopy = this.serverSocketFactory;

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
index 216d1f4..ae6057f 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/io/HttpService.java
@@ -40,6 +40,7 @@ import org.apache.hc.core5.http.Header;
 import org.apache.hc.core5.http.HttpEntity;
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpHeaders;
+import org.apache.hc.core5.http.HttpRequestMapper;
 import org.apache.hc.core5.http.HttpResponseFactory;
 import org.apache.hc.core5.http.HttpStatus;
 import org.apache.hc.core5.http.MethodNotSupportedException;
@@ -51,7 +52,6 @@ import org.apache.hc.core5.http.impl.DefaultConnectionReuseStrategy;
 import org.apache.hc.core5.http.impl.Http1StreamListener;
 import org.apache.hc.core5.http.io.HttpExpectationVerifier;
 import org.apache.hc.core5.http.io.HttpRequestHandler;
-import org.apache.hc.core5.http.io.HttpRequestHandlerMapper;
 import org.apache.hc.core5.http.io.HttpServerConnection;
 import org.apache.hc.core5.http.io.entity.StringEntity;
 import org.apache.hc.core5.http.protocol.HttpContext;
@@ -69,7 +69,7 @@ import org.apache.hc.core5.util.Args;
  * individual {@link HttpRequestHandler}s are expected to implement
  * application specific content generation and processing.
  * <p>
- * {@code HttpService} uses {@link HttpRequestHandlerMapper} to map
+ * {@code HttpService} uses {@link HttpRequestMapper} to map
  * matching request handler for a particular request URI of an incoming HTTP
  * request.
  * <p>
@@ -82,7 +82,7 @@ import org.apache.hc.core5.util.Args;
 public class HttpService {
 
     private final HttpProcessor processor;
-    private final HttpRequestHandlerMapper handlerMapper;
+    private final HttpRequestMapper<HttpRequestHandler> handlerMapper;
     private final ConnectionReuseStrategy connReuseStrategy;
     private final HttpResponseFactory<ClassicHttpResponse> responseFactory;
     private final HttpExpectationVerifier expectationVerifier;
@@ -105,7 +105,7 @@ public class HttpService {
             final HttpProcessor processor,
             final ConnectionReuseStrategy connReuseStrategy,
             final HttpResponseFactory<ClassicHttpResponse> responseFactory,
-            final HttpRequestHandlerMapper handlerMapper,
+            final HttpRequestMapper<HttpRequestHandler> handlerMapper,
             final HttpExpectationVerifier expectationVerifier,
             final Http1StreamListener streamListener) {
         super();
@@ -135,7 +135,7 @@ public class HttpService {
             final HttpProcessor processor,
             final ConnectionReuseStrategy connReuseStrategy,
             final HttpResponseFactory<ClassicHttpResponse> responseFactory,
-            final HttpRequestHandlerMapper handlerMapper) {
+            final HttpRequestMapper<HttpRequestHandler> handlerMapper) {
         this(processor, connReuseStrategy, responseFactory, handlerMapper, null, null);
     }
 
@@ -148,7 +148,7 @@ public class HttpService {
      * @since 4.3
      */
     public HttpService(
-            final HttpProcessor processor, final HttpRequestHandlerMapper handlerMapper) {
+            final HttpProcessor processor, final HttpRequestMapper<HttpRequestHandler> handlerMapper) {
         this(processor, null, null, handlerMapper);
     }
 
@@ -314,7 +314,7 @@ public class HttpService {
             final HttpContext context) throws HttpException, IOException {
         HttpRequestHandler handler = null;
         if (this.handlerMapper != null) {
-            handler = this.handlerMapper.lookup(request, context);
+            handler = this.handlerMapper.resolve(request, context);
         }
         if (handler != null) {
             handler.handle(request, response, context);

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
index 7b2b698..2dd95e7 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/impl/nio/ServerHttp1StreamHandler.java
@@ -238,7 +238,7 @@ class ServerHttp1StreamHandler implements ResourceHolder {
 
         AsyncServerExchangeHandler handler;
         try {
-            handler = exchangeHandlerFactory.create(request);
+            handler = exchangeHandlerFactory.create(request, context);
         } catch (final MisdirectedRequestException ex) {
             handler =  new ImmediateResponseExchangeHandler(HttpStatus.SC_MISDIRECTED_REQUEST, ex.getMessage());
         } catch (final HttpException ex) {

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpRequestHandlerMapper.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpRequestHandlerMapper.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpRequestHandlerMapper.java
deleted file mode 100644
index 4fc2c17..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/HttpRequestHandlerMapper.java
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.http.io;
-
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.protocol.HttpContext;
-
-/**
- * HttpRequestHandlerMapper can be used to resolve an instance of
- * {@link HttpRequestHandler} matching a particular {@link HttpRequest}. Usually the
- * mapped request handler will be used to process the request.
- *
- * @since 4.3
- */
-public interface HttpRequestHandlerMapper {
-
-    /**
-     * Looks up a handler matching the given request.
-     *
-     * @param request the request to map to a handler
-     * @return HTTP request handler or {@code null} if no match
-     * is found.
-     *
-     * @since 5.0
-     */
-    HttpRequestHandler lookup(HttpRequest request, HttpContext context);
-
-}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/UriHttpRequestHandlerMapper.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/UriHttpRequestHandlerMapper.java b/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/UriHttpRequestHandlerMapper.java
deleted file mode 100644
index ec3d62c..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/io/support/UriHttpRequestHandlerMapper.java
+++ /dev/null
@@ -1,122 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.http.io.support;
-
-import org.apache.hc.core5.annotation.Contract;
-import org.apache.hc.core5.annotation.ThreadingBehavior;
-import org.apache.hc.core5.http.HttpRequest;
-import org.apache.hc.core5.http.io.HttpRequestHandler;
-import org.apache.hc.core5.http.io.HttpRequestHandlerMapper;
-import org.apache.hc.core5.http.protocol.HttpContext;
-import org.apache.hc.core5.http.protocol.LookupRegistry;
-import org.apache.hc.core5.http.protocol.UriPatternMatcher;
-import org.apache.hc.core5.util.Args;
-
-/**
- * Maintains a map of HTTP request handlers keyed by a request URI pattern.
- * <br>
- * Patterns may have three formats:
- * <ul>
- * <li>{@code *}</li>
- * <li>{@code *<uri>}</li>
- * <li>{@code <uri>*}</li>
- * </ul>
- * <br>
- * This class can be used to map an instance of
- * {@link HttpRequestHandler} matching a particular request URI. Usually the
- * mapped request handler will be used to process the request with the
- * specified request URI.
- *
- * @since 4.3
- */
-@Contract(threading = ThreadingBehavior.SAFE)
-public class UriHttpRequestHandlerMapper implements HttpRequestHandlerMapper {
-
-    private final LookupRegistry<HttpRequestHandler> matcher;
-
-    public UriHttpRequestHandlerMapper(final LookupRegistry<HttpRequestHandler> matcher) {
-        super();
-        this.matcher = Args.notNull(matcher, "Pattern matcher");
-    }
-
-    public UriHttpRequestHandlerMapper() {
-        this(new UriPatternMatcher<HttpRequestHandler>());
-    }
-
-    /**
-     * Registers the given {@link HttpRequestHandler} as a handler for URIs
-     * matching the given pattern.
-     *
-     * @param pattern the pattern to register the handler for.
-     * @param handler the handler.
-     */
-    public void register(final String pattern, final HttpRequestHandler handler) {
-        Args.notNull(pattern, "Pattern");
-        Args.notNull(handler, "Handler");
-        matcher.register(pattern, handler);
-    }
-
-    /**
-     * Removes registered handler, if exists, for the given pattern.
-     *
-     * @param pattern the pattern to unregister the handler for.
-     */
-    public void unregister(final String pattern) {
-        matcher.unregister(pattern);
-    }
-
-    /**
-     * Extracts request path from the given {@link HttpRequest}
-     */
-    protected String getRequestPath(final HttpRequest request) {
-        String uriPath = request.getPath();
-        int index = uriPath.indexOf("?");
-        if (index != -1) {
-            uriPath = uriPath.substring(0, index);
-        } else {
-            index = uriPath.indexOf("#");
-            if (index != -1) {
-                uriPath = uriPath.substring(0, index);
-            }
-        }
-        return uriPath;
-    }
-
-    /**
-     * Looks up a handler matching the given request URI.
-     *
-     * @param request the request
-     * @return handler or {@code null} if no match is found.
-     */
-    @Override
-    public HttpRequestHandler lookup(final HttpRequest request, final HttpContext context) {
-        Args.notNull(request, "HTTP request");
-        return matcher.lookup(getRequestPath(request));
-    }
-
-}

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
index 91ed25a..a59471e 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/AsyncServerRequestHandler.java
@@ -37,7 +37,7 @@ import org.apache.hc.core5.http.protocol.HttpContext;
 /**
  * @since 5.0
  */
-@Contract(threading = ThreadingBehavior.SAFE)
+@Contract(threading = ThreadingBehavior.STATELESS)
 public interface AsyncServerRequestHandler<T> {
 
     AsyncRequestConsumer<T> prepare(HttpRequest request, HttpContext context) throws HttpException;

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HandlerFactory.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HandlerFactory.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HandlerFactory.java
index a367dd7..abcbf8c 100644
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HandlerFactory.java
+++ b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/HandlerFactory.java
@@ -28,6 +28,7 @@ package org.apache.hc.core5.http.nio;
 
 import org.apache.hc.core5.http.HttpException;
 import org.apache.hc.core5.http.HttpRequest;
+import org.apache.hc.core5.http.protocol.HttpContext;
 
 /**
  * Abstract handler factory.
@@ -38,6 +39,6 @@ import org.apache.hc.core5.http.HttpRequest;
  */
 public interface HandlerFactory<T extends ResourceHolder> {
 
-    T create(HttpRequest request) throws HttpException;
+    T create(HttpRequest request, HttpContext context) throws HttpException;
 
 }

http://git-wip-us.apache.org/repos/asf/httpcomponents-core/blob/479ad348/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncServerExchangeHandlerRegistry.java
----------------------------------------------------------------------
diff --git a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncServerExchangeHandlerRegistry.java b/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncServerExchangeHandlerRegistry.java
deleted file mode 100644
index a37d8e7..0000000
--- a/httpcore5/src/main/java/org/apache/hc/core5/http/nio/support/AsyncServerExchangeHandlerRegistry.java
+++ /dev/null
@@ -1,107 +0,0 @@
-/*
- * ====================================================================
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- * ====================================================================
- *
- * This software consists of voluntary contributions made by many
- * individuals on behalf of the Apache Software Foundation.  For more
- * information on the Apache Software Foundation, please see
- * <http://www.apache.org/>.
- *
- */
-
-package org.apache.hc.core5.http.nio.support;
-
-import java.util.Locale;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.ConcurrentMap;
-
-import org.apache.hc.core5.function.Supplier;
-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.nio.AsyncServerExchangeHandler;
-import org.apache.hc.core5.http.nio.HandlerFactory;
-import org.apache.hc.core5.http.protocol.UriPatternMatcher;
-import org.apache.hc.core5.net.URIAuthority;
-import org.apache.hc.core5.util.Args;
-
-public class AsyncServerExchangeHandlerRegistry implements HandlerFactory<AsyncServerExchangeHandler> {
-
-    private final static String LOCALHOST = "localhost";
-
-    private final String canonicalHostName;
-    private final UriPatternMatcher<Supplier<AsyncServerExchangeHandler>> primary;
-    private final ConcurrentMap<String, UriPatternMatcher<Supplier<AsyncServerExchangeHandler>>> virtualMap;
-
-    public AsyncServerExchangeHandlerRegistry(final String canonicalHostName) {
-        this.canonicalHostName = Args.notNull(canonicalHostName, "Canonical hostname").toLowerCase(Locale.ROOT);
-        this.primary = new UriPatternMatcher<>();
-        this.virtualMap = new ConcurrentHashMap<>();
-    }
-
-    private UriPatternMatcher<Supplier<AsyncServerExchangeHandler>> getPatternMatcher(final String hostname) {
-        if (hostname == null) {
-            return primary;
-        }
-        if (hostname.equals(canonicalHostName) || hostname.equals(LOCALHOST)) {
-            return primary;
-        }
-        return virtualMap.get(hostname);
-    }
-
-    @Override
-    public AsyncServerExchangeHandler create(final HttpRequest request) throws HttpException {
-        final URIAuthority authority = request.getAuthority();
-        final String key = authority != null ? authority.getHostName().toLowerCase(Locale.ROOT) : null;
-        final UriPatternMatcher<Supplier<AsyncServerExchangeHandler>> patternMatcher = getPatternMatcher(key);
-        if (patternMatcher == null) {
-            return new ImmediateResponseExchangeHandler(HttpStatus.SC_MISDIRECTED_REQUEST, "Not authoritative");
-        }
-        String path = request.getPath();
-        final int i = path.indexOf("?");
-        if (i != -1) {
-            path = path.substring(0, i);
-        }
-        final Supplier<AsyncServerExchangeHandler> supplier = patternMatcher.lookup(path);
-        if (supplier != null) {
-            return supplier.get();
-        }
-        return new ImmediateResponseExchangeHandler(HttpStatus.SC_NOT_FOUND, "Resource not found");
-    }
-
-    public void register(final String hostname, final String uriPattern, final Supplier<AsyncServerExchangeHandler> supplier) {
-        Args.notBlank(uriPattern, "URI pattern");
-        Args.notNull(supplier, "Supplier");
-        final String key = hostname != null ? hostname.toLowerCase(Locale.ROOT) : null;
-        if (hostname == null || hostname.equals(canonicalHostName) || hostname.equals(LOCALHOST)) {
-            primary.register(uriPattern, supplier);
-        } else {
-            UriPatternMatcher<Supplier<AsyncServerExchangeHandler>> matcher = virtualMap.get(key);
-            if (matcher == null) {
-                final UriPatternMatcher<Supplier<AsyncServerExchangeHandler>> newMatcher = new UriPatternMatcher<>();
-                matcher = virtualMap.putIfAbsent(key, newMatcher);
-                if (matcher == null) {
-                    matcher = newMatcher;
-                }
-            }
-            matcher.register(uriPattern, supplier);
-        }
-    }
-
-}