diff --git a/common/src/main/java/io/netty/util/DomainNameMapping.java b/common/src/main/java/io/netty/util/DomainNameMapping.java
new file mode 100644
index 0000000000..1d83431689
--- /dev/null
+++ b/common/src/main/java/io/netty/util/DomainNameMapping.java
@@ -0,0 +1,144 @@
+/*
+ * Copyright 2014 The Netty Project
+ *
+ * The Netty Project 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.
+ */
+
+package io.netty.util;
+
+import io.netty.util.internal.StringUtil;
+
+import java.net.IDN;
+import java.util.LinkedHashMap;
+import java.util.Locale;
+import java.util.Map;
+import java.util.regex.Pattern;
+
+/**
+ * Maps a domain name to its associated value object.
+ *
+ * DNS wildcard is supported as hostname, so you can use {@code *.netty.io} to match both {@code netty.io}
+ * and {@code downloads.netty.io}.
+ *
+ */
+public class DomainNameMapping implements Mapping {
+
+ private static final Pattern DNS_WILDCARD_PATTERN = Pattern.compile("^\\*\\..*");
+
+ private final Map map;
+
+ private final V defaultValue;
+
+ /**
+ * Creates a default, order-sensitive mapping. If your hostnames are in conflict, the mapping
+ * will choose the one you add first.
+ *
+ * @param defaultValue the default value for {@link #map(String)} to return when nothing matches the input
+ */
+ public DomainNameMapping(V defaultValue) {
+ this(4, defaultValue);
+ }
+
+ /**
+ * Creates a default, order-sensitive mapping. If your hostnames are in conflict, the mapping
+ * will choose the one you add first.
+ *
+ * @param initialCapacity initial capacity for the internal map
+ * @param defaultValue the default value for {@link #map(String)} to return when nothing matches the input
+ */
+ public DomainNameMapping(int initialCapacity, V defaultValue) {
+ if (defaultValue == null) {
+ throw new NullPointerException("defaultValue");
+ }
+ map = new LinkedHashMap(initialCapacity);
+ this.defaultValue = defaultValue;
+ }
+
+ /**
+ * Adds a mapping that maps the specified (optionally wildcard) host name to the specified output value.
+ *
+ * DNS wildcard is supported as hostname.
+ * For example, you can use {@code *.netty.io} to match {@code netty.io} and {@code downloads.netty.io}.
+ *
+ *
+ * @param hostname the host name (optionally wildcard)
+ * @param output the output value that will be returned by {@link #map(String)} when the specified host name
+ * matches the specified input host name
+ */
+ public DomainNameMapping add(String hostname, V output) {
+ if (hostname == null) {
+ throw new NullPointerException("input");
+ }
+
+ if (output == null) {
+ throw new NullPointerException("output");
+ }
+
+ map.put(normalizeHostname(hostname), output);
+ return this;
+ }
+
+ /**
+ * Simple function to match DNS wildcard.
+ */
+ private static boolean matches(String hostNameTemplate, String hostName) {
+ // note that inputs are converted and lowercased already
+ if (DNS_WILDCARD_PATTERN.matcher(hostNameTemplate).matches()) {
+ return hostNameTemplate.substring(2).equals(hostName) ||
+ hostName.endsWith(hostNameTemplate.substring(1));
+ } else {
+ return hostNameTemplate.equals(hostName);
+ }
+ }
+
+ /**
+ * IDNA ASCII conversion and case normalization
+ */
+ private static String normalizeHostname(String hostname) {
+ if (needsNormalization(hostname)) {
+ hostname = IDN.toASCII(hostname, IDN.ALLOW_UNASSIGNED);
+
+ }
+ return hostname.toLowerCase(Locale.US);
+ }
+
+ private static boolean needsNormalization(String hostname) {
+ final int length = hostname.length();
+ for (int i = 0; i < length; i ++) {
+ int c = hostname.charAt(i);
+ if (c > 0x7F) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public V map(String input) {
+ if (input != null) {
+ input = normalizeHostname(input);
+
+ for (Map.Entry entry : map.entrySet()) {
+ if (matches(entry.getKey(), input)) {
+ return entry.getValue();
+ }
+ }
+ }
+
+ return defaultValue;
+ }
+
+ public String toString() {
+ return StringUtil.simpleClassName(this) + "(default: " + defaultValue + ", map: " + map + ')';
+ }
+}
diff --git a/common/src/main/java/io/netty/util/Mapping.java b/common/src/main/java/io/netty/util/Mapping.java
index 2af3ffdf79..ca7738f8d4 100644
--- a/common/src/main/java/io/netty/util/Mapping.java
+++ b/common/src/main/java/io/netty/util/Mapping.java
@@ -16,12 +16,12 @@
package io.netty.util;
/**
- * An mapping which maintains a relationship from type of IN to type of OUT.
+ * Maintains the mapping from the objects of one type to the objects of the other type.
*/
public interface Mapping {
/**
- * Returns mapped value of input.
+ * Returns mapped value of the specified input.
*/
OUT map(IN input);
}
diff --git a/handler/src/main/java/io/netty/handler/ssl/DomainNameMapping.java b/handler/src/main/java/io/netty/handler/ssl/DomainNameMapping.java
deleted file mode 100644
index ff4c818445..0000000000
--- a/handler/src/main/java/io/netty/handler/ssl/DomainNameMapping.java
+++ /dev/null
@@ -1,126 +0,0 @@
-/*
- * Copyright 2014 The Netty Project
- *
- * The Netty Project 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.
- */
-package io.netty.handler.ssl;
-
-import io.netty.util.Mapping;
-import io.netty.util.internal.logging.InternalLogger;
-import io.netty.util.internal.logging.InternalLoggerFactory;
-
-import java.net.IDN;
-import java.util.LinkedHashMap;
-import java.util.Map;
-import java.util.regex.Pattern;
-
-/**
- *
This class maps a domain name to a configured {@link SslContext}.
- *
- *
DNS wildcard is supported as hostname, so you can use {@code *.netty.io} to match both {@code netty.io}
- * and {@code downloads.netty.io}.
- */
-public class DomainNameMapping implements Mapping {
-
- private static final InternalLogger logger =
- InternalLoggerFactory.getInstance(DomainNameMapping.class);
-
- private static final Pattern DNS_WILDCARD_PATTERN = Pattern.compile("^\\*\\..*");
-
- private final Map userProvidedContexts;
-
- private final SslContext defaultContext;
-
- /**
- * Create a default, order-sensitive mapping. If your hostnames are in conflict, the mapping
- * will choose the one you add first.
- *
- * @param defaultContext default {@link SslContext} when the nothing matches input.
- */
- public DomainNameMapping(SslContext defaultContext) {
- this(4, defaultContext);
- }
-
- /**
- * Create a default, order-sensitive mapping. If your hostnames are in conflict, the mapping
- * will choose the one you add first.
- *
- * @param initialCapacity initial capacity for internal map
- * @param defaultContext default {@link SslContext} when the handler fails to detect SNI extension
- */
- public DomainNameMapping(int initialCapacity, SslContext defaultContext) {
- if (defaultContext == null) {
- throw new NullPointerException("defaultContext");
- }
- userProvidedContexts = new LinkedHashMap(initialCapacity);
- this.defaultContext = defaultContext;
- }
-
- /**
- * Add a {@link SslContext} to the handler.
- *
- * DNS wildcard is supported as hostname.
- * For example, you can use {@code *.netty.io} to match {@code netty.io} and {@code downloads.netty.io}.
- *
- * @param hostname hostname for the certificate.
- * @param context the {@link SslContext}
- */
- public DomainNameMapping addContext(String hostname, SslContext context) {
- if (hostname == null) {
- throw new NullPointerException("hostname");
- }
-
- if (context == null) {
- throw new NullPointerException("context");
- }
-
- userProvidedContexts.put(normalizeHostname(hostname), context);
- return this;
- }
-
- /**
- *