View Javadoc
1   /*
2    * Copyright 2014 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  
17  package io.netty5.util;
18  
19  import io.netty5.util.internal.StringUtil;
20  
21  import java.net.IDN;
22  import java.util.Collections;
23  import java.util.LinkedHashMap;
24  import java.util.Locale;
25  import java.util.Map;
26  
27  import static io.netty5.util.internal.StringUtil.commonSuffixOfLength;
28  import static java.util.Objects.requireNonNull;
29  
30  /**
31   * Maps a domain name to its associated value object.
32   * <p>
33   * DNS wildcard is supported as hostname, so you can use {@code *.netty.io} to match both {@code netty.io}
34   * and {@code downloads.netty.io}.
35   * </p>
36   * @deprecated Use {@link DomainWildcardMappingBuilder}}
37   */
38  @Deprecated
39  public class DomainNameMapping<V> implements Mapping<String, V> {
40  
41      final V defaultValue;
42      private final Map<String, V> map;
43      private final Map<String, V> unmodifiableMap;
44  
45      /**
46       * Creates a default, order-sensitive mapping. If your hostnames are in conflict, the mapping
47       * will choose the one you add first.
48       *
49       * @param defaultValue the default value for {@link #map(String)} to return when nothing matches the input
50       * @deprecated use {@link DomainNameMappingBuilder} to create and fill the mapping instead
51       */
52      @Deprecated
53      public DomainNameMapping(V defaultValue) {
54          this(4, defaultValue);
55      }
56  
57      /**
58       * Creates a default, order-sensitive mapping. If your hostnames are in conflict, the mapping
59       * will choose the one you add first.
60       *
61       * @param initialCapacity initial capacity for the internal map
62       * @param defaultValue    the default value for {@link #map(String)} to return when nothing matches the input
63       * @deprecated use {@link DomainNameMappingBuilder} to create and fill the mapping instead
64       */
65      @Deprecated
66      public DomainNameMapping(int initialCapacity, V defaultValue) {
67          this(new LinkedHashMap<>(initialCapacity), defaultValue);
68      }
69  
70      DomainNameMapping(Map<String, V> map, V defaultValue) {
71          this.defaultValue = requireNonNull(defaultValue, "defaultValue");
72          this.map = map;
73          unmodifiableMap = map != null ? Collections.unmodifiableMap(map)
74                                        : null;
75      }
76  
77      /**
78       * Adds a mapping that maps the specified (optionally wildcard) host name to the specified output value.
79       * <p>
80       * <a href="https://en.wikipedia.org/wiki/Wildcard_DNS_record">DNS wildcard</a> is supported as hostname.
81       * For example, you can use {@code *.netty.io} to match {@code netty.io} and {@code downloads.netty.io}.
82       * </p>
83       *
84       * @param hostname the host name (optionally wildcard)
85       * @param output   the output value that will be returned by {@link #map(String)} when the specified host name
86       *                 matches the specified input host name
87       * @deprecated use {@link DomainNameMappingBuilder} to create and fill the mapping instead
88       */
89      @Deprecated
90      public DomainNameMapping<V> add(String hostname, V output) {
91          map.put(normalizeHostname(requireNonNull(hostname, "hostname")), requireNonNull(output, "output"));
92          return this;
93      }
94  
95      /**
96       * Simple function to match <a href="https://en.wikipedia.org/wiki/Wildcard_DNS_record">DNS wildcard</a>.
97       */
98      static boolean matches(String template, String hostName) {
99          if (template.startsWith("*.")) {
100             return template.regionMatches(2, hostName, 0, hostName.length())
101                 || commonSuffixOfLength(hostName, template, template.length() - 1);
102         }
103         return template.equals(hostName);
104     }
105 
106     /**
107      * IDNA ASCII conversion and case normalization
108      */
109     static String normalizeHostname(String hostname) {
110         if (needsNormalization(hostname)) {
111             hostname = IDN.toASCII(hostname, IDN.ALLOW_UNASSIGNED);
112         }
113         return hostname.toLowerCase(Locale.US);
114     }
115 
116     private static boolean needsNormalization(String hostname) {
117         final int length = hostname.length();
118         for (int i = 0; i < length; i++) {
119             int c = hostname.charAt(i);
120             if (c > 0x7F) {
121                 return true;
122             }
123         }
124         return false;
125     }
126 
127     @Override
128     public V map(String hostname) {
129         if (hostname != null) {
130             hostname = normalizeHostname(hostname);
131 
132             for (Map.Entry<String, V> entry : map.entrySet()) {
133                 if (matches(entry.getKey(), hostname)) {
134                     return entry.getValue();
135                 }
136             }
137         }
138         return defaultValue;
139     }
140 
141     /**
142      * Returns a read-only {@link Map} of the domain mapping patterns and their associated value objects.
143      */
144     public Map<String, V> asMap() {
145         return unmodifiableMap;
146     }
147 
148     @Override
149     public String toString() {
150         return StringUtil.simpleClassName(this) + "(default: " + defaultValue + ", map: " + map + ')';
151     }
152 }