View Javadoc
1   /*
2    * ====================================================================
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *   http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing,
14   * software distributed under the License is distributed on an
15   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16   * KIND, either express or implied.  See the License for the
17   * specific language governing permissions and limitations
18   * under the License.
19   * ====================================================================
20   *
21   * This software consists of voluntary contributions made by many
22   * individuals on behalf of the Apache Software Foundation.  For more
23   * information on the Apache Software Foundation, please see
24   * <http://www.apache.org/>.
25   *
26   */
27  
28  package org.apache.hc.core5.net;
29  
30  import java.io.Serializable;
31  import java.net.URISyntaxException;
32  import java.util.BitSet;
33  import java.util.Locale;
34  
35  import org.apache.hc.core5.annotation.Contract;
36  import org.apache.hc.core5.annotation.ThreadingBehavior;
37  import org.apache.hc.core5.util.LangUtils;
38  import org.apache.hc.core5.util.TextUtils;
39  import org.apache.hc.core5.util.Tokenizer;
40  
41  /**
42   * Represents authority component of request {@link java.net.URI}.
43   *
44   * @since 5.0
45   */
46  @Contract(threading = ThreadingBehavior.IMMUTABLE)
47  public final class URIAuthority implements NamedEndpoint, Serializable {
48  
49      private static final long serialVersionUID = 1L;
50      private final String userInfo;
51      private final String hostname;
52      private final int port;
53  
54      private static final BitSet HOST_SEPARATORS = new BitSet(256);
55      private static final BitSet PORT_SEPARATORS = new BitSet(256);
56      private static final BitSet TERMINATORS = new BitSet(256);
57  
58      static {
59          TERMINATORS.set('/');
60          TERMINATORS.set('#');
61          TERMINATORS.set('?');
62          HOST_SEPARATORS.or(TERMINATORS);
63          HOST_SEPARATORS.set('@');
64          PORT_SEPARATORS.or(TERMINATORS);
65          PORT_SEPARATORS.set(':');
66      }
67  
68      static URISyntaxException createException(
69              final CharSequence input, final Tokenizer.Cursor cursor, final String reason) {
70          return new URISyntaxException(
71                  input.subSequence(cursor.getLowerBound(), cursor.getUpperBound()).toString(),
72                  reason,
73                  cursor.getPos());
74      }
75  
76      static URIAuthority parse(final CharSequence s, final Tokenizer.Cursor cursor) throws URISyntaxException {
77          final Tokenizer tokenizer = Tokenizer.INSTANCE;
78          String userInfo = null;
79          final int initPos = cursor.getPos();
80          final String token = tokenizer.parseContent(s, cursor, HOST_SEPARATORS);
81          if (!cursor.atEnd() && s.charAt(cursor.getPos()) == '@') {
82              cursor.updatePos(cursor.getPos() + 1);
83              if (!TextUtils.isBlank(token)) {
84                  userInfo = token;
85              }
86          } else {
87              //Rewind
88              cursor.updatePos(initPos);
89          }
90          final String hostName = tokenizer.parseContent(s, cursor, PORT_SEPARATORS);
91          String portText = null;
92          if (!cursor.atEnd() && s.charAt(cursor.getPos()) == ':') {
93              cursor.updatePos(cursor.getPos() + 1);
94              portText = tokenizer.parseContent(s, cursor, TERMINATORS);
95          }
96          final int port;
97          if (!TextUtils.isBlank(portText)) {
98              try {
99                  port = Integer.parseInt(portText);
100             } catch (final NumberFormatException ex) {
101                 throw createException(s, cursor, "Authority port is invalid");
102             }
103         } else {
104             port = -1;
105         }
106         return new URIAuthority(userInfo, hostName.toLowerCase(Locale.ROOT), port, true);
107     }
108 
109     static URIAuthority parse(final CharSequence s) throws URISyntaxException {
110         final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length());
111         return parse(s, cursor);
112     }
113 
114     static void format(final StringBuilder buf, final URIAuthority uriAuthority) {
115         if (uriAuthority.userInfo != null) {
116             buf.append(uriAuthority.userInfo);
117             buf.append("@");
118         }
119         buf.append(uriAuthority.hostname);
120         if (uriAuthority.port != -1) {
121             buf.append(":");
122             buf.append(uriAuthority.port);
123         }
124     }
125 
126     static String format(final URIAuthority uriAuthority) {
127         final StringBuilder buf = new StringBuilder();
128         format(buf, uriAuthority);
129         return buf.toString();
130     }
131 
132     /**
133      * @throws IllegalArgumentException
134      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
135      *             65535, inclusive. {@code -1} indicates the scheme default port.
136      */
137     private URIAuthority(final String userInfo, final String hostname, final int port, final boolean internal) {
138         super();
139         this.userInfo = userInfo;
140         this.hostname = hostname;
141         this.port = Ports.checkWithDefault(port);
142     }
143 
144     /**
145      * @throws IllegalArgumentException
146      *             If the port parameter is outside the specified range of valid port values, which is between 0 and
147      *             65535, inclusive. {@code -1} indicates the scheme default port.
148      */
149     public URIAuthority(final String userInfo, final String hostname, final int port) {
150         super();
151         this.userInfo = userInfo;
152         this.hostname = hostname != null ? hostname.toLowerCase(Locale.ROOT) : null;
153         this.port = Ports.checkWithDefault(port);
154     }
155 
156     public URIAuthority(final String hostname, final int port) {
157         this(null, hostname, port);
158     }
159 
160     public URIAuthority(final NamedEndpoint namedEndpoint) {
161         this(null, namedEndpoint.getHostName(), namedEndpoint.getPort());
162     }
163 
164     /**
165      * Creates {@code URIHost} instance from string. Text may not contain any blanks.
166      */
167     public static URIAuthority create(final String s) throws URISyntaxException {
168         if (TextUtils.isBlank(s)) {
169             return null;
170         }
171         final Tokenizer.Cursor cursor = new Tokenizer.Cursor(0, s.length());
172         final URIAuthority uriAuthority = parse(s, cursor);
173         if (!cursor.atEnd()) {
174             throw createException(s, cursor, "Unexpected content");
175         }
176         return uriAuthority;
177     }
178 
179     public URIAuthority(final String hostname) {
180         this(null, hostname, -1);
181     }
182 
183     public String getUserInfo() {
184         return userInfo;
185     }
186 
187     @Override
188     public String getHostName() {
189         return hostname;
190     }
191 
192     @Override
193     public int getPort() {
194         return port;
195     }
196 
197     @Override
198     public String toString() {
199         return format(this);
200     }
201 
202     @Override
203     public boolean equals(final Object obj) {
204         if (this == obj) {
205             return true;
206         }
207         if (obj instanceof URIAuthority) {
208             final URIAuthority./../../../org/apache/hc/core5/net/URIAuthority.html#URIAuthority">URIAuthority that = (URIAuthority) obj;
209             return LangUtils.equals(this.userInfo, that.userInfo) &&
210                     LangUtils.equals(this.hostname, that.hostname) &&
211                     this.port == that.port;
212         }
213         return false;
214     }
215 
216     @Override
217     public int hashCode() {
218         int hash = LangUtils.HASH_SEED;
219         hash = LangUtils.hashCode(hash, userInfo);
220         hash = LangUtils.hashCode(hash, hostname);
221         hash = LangUtils.hashCode(hash, port);
222         return hash;
223     }
224 
225 }