View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  package org.eclipse.aether.util.connector.transport.http;
20  
21  import java.net.InetAddress;
22  import java.net.URI;
23  import java.net.URISyntaxException;
24  import java.net.UnknownHostException;
25  import java.nio.charset.Charset;
26  import java.util.Collections;
27  import java.util.HashSet;
28  import java.util.Map;
29  import java.util.Optional;
30  import java.util.Set;
31  
32  import org.eclipse.aether.ConfigurationProperties;
33  import org.eclipse.aether.RepositorySystemSession;
34  import org.eclipse.aether.repository.RemoteRepository;
35  import org.eclipse.aether.util.ConfigUtils;
36  
37  /**
38   * A utility class to read HTTP transport related configuration. It implements all HTTP transport related configurations from
39   * {@link ConfigurationProperties} and transport implementations are free to use those that are supported by themselves.
40   *
41   * @see ConfigurationProperties
42   * @see RepositorySystemSession#getConfigProperties()
43   * @since 2.0.15
44   */
45  public final class HttpTransporterUtils {
46      private HttpTransporterUtils() {}
47  
48      /**
49       * Getter for {@link ConfigurationProperties#USER_AGENT}.
50       */
51      public static String getUserAgent(RepositorySystemSession session, RemoteRepository repository) {
52          return ConfigUtils.getString(
53                  session,
54                  ConfigurationProperties.DEFAULT_USER_AGENT,
55                  ConfigurationProperties.USER_AGENT,
56                  "aether.connector.userAgent");
57      }
58  
59      /**
60       * Getter for {@link ConfigurationProperties#HTTPS_SECURITY_MODE}.
61       */
62      public static String getHttpsSecurityMode(RepositorySystemSession session, RemoteRepository repository) {
63          String result = ConfigUtils.getString(
64                  session,
65                  ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT,
66                  ConfigurationProperties.HTTPS_SECURITY_MODE + "." + repository.getId(),
67                  ConfigurationProperties.HTTPS_SECURITY_MODE);
68          if (!ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT.equals(result)
69                  && !ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(result)) {
70              throw new IllegalArgumentException("Unsupported '" + result + "' HTTPS security mode.");
71          }
72          return result;
73      }
74  
75      /**
76       * Getter for {@link ConfigurationProperties#HTTP_CONNECTION_MAX_TTL}.
77       */
78      public static int getHttpConnectionMaxTtlSeconds(RepositorySystemSession session, RemoteRepository repository) {
79          int result = ConfigUtils.getInteger(
80                  session,
81                  ConfigurationProperties.DEFAULT_HTTP_CONNECTION_MAX_TTL,
82                  ConfigurationProperties.HTTP_CONNECTION_MAX_TTL + "." + repository.getId(),
83                  ConfigurationProperties.HTTP_CONNECTION_MAX_TTL);
84          if (result < 0) {
85              throw new IllegalArgumentException(ConfigurationProperties.HTTP_CONNECTION_MAX_TTL + " value must be >= 0");
86          }
87          return result;
88      }
89  
90      /**
91       * Getter for {@link ConfigurationProperties#HTTP_MAX_CONNECTIONS_PER_ROUTE}.
92       */
93      public static int getHttpMaxConnectionsPerRoute(RepositorySystemSession session, RemoteRepository repository) {
94          int result = ConfigUtils.getInteger(
95                  session,
96                  ConfigurationProperties.DEFAULT_HTTP_MAX_CONNECTIONS_PER_ROUTE,
97                  ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE + "." + repository.getId(),
98                  ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE);
99          if (result < 1) {
100             throw new IllegalArgumentException(
101                     ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE + " value must be > 0");
102         }
103         return result;
104     }
105 
106     /**
107      * Getter for {@link ConfigurationProperties#HTTP_HEADERS}.
108      */
109     @SuppressWarnings("unchecked")
110     public static Map<String, String> getHttpHeaders(RepositorySystemSession session, RemoteRepository repository) {
111         return (Map<String, String>) ConfigUtils.getMap(
112                 session,
113                 Collections.emptyMap(),
114                 ConfigurationProperties.HTTP_HEADERS + "." + repository.getId(),
115                 ConfigurationProperties.HTTP_HEADERS);
116     }
117 
118     /**
119      * Getter for {@link ConfigurationProperties#HTTP_PREEMPTIVE_AUTH}.
120      */
121     public static boolean isHttpPreemptiveAuth(RepositorySystemSession session, RemoteRepository repository) {
122         return ConfigUtils.getBoolean(
123                 session,
124                 ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_AUTH,
125                 ConfigurationProperties.HTTP_PREEMPTIVE_AUTH + "." + repository.getId(),
126                 ConfigurationProperties.HTTP_PREEMPTIVE_AUTH);
127     }
128 
129     /**
130      * Getter for {@link ConfigurationProperties#HTTP_PREEMPTIVE_PUT_AUTH}.
131      */
132     public static boolean isHttpPreemptivePutAuth(RepositorySystemSession session, RemoteRepository repository) {
133         return ConfigUtils.getBoolean(
134                 session,
135                 ConfigurationProperties.DEFAULT_HTTP_PREEMPTIVE_PUT_AUTH,
136                 ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH + "." + repository.getId(),
137                 ConfigurationProperties.HTTP_PREEMPTIVE_PUT_AUTH);
138     }
139 
140     /**
141      * Getter for {@link ConfigurationProperties#HTTP_SUPPORT_WEBDAV}.
142      */
143     public static boolean isHttpSupportWebDav(RepositorySystemSession session, RemoteRepository repository) {
144         return ConfigUtils.getBoolean(
145                 session,
146                 ConfigurationProperties.DEFAULT_HTTP_SUPPORT_WEBDAV,
147                 ConfigurationProperties.HTTP_SUPPORT_WEBDAV + "." + repository.getId(),
148                 ConfigurationProperties.HTTP_SUPPORT_WEBDAV);
149     }
150 
151     /**
152      * Getter for {@link ConfigurationProperties#HTTP_CREDENTIAL_ENCODING}.
153      */
154     public static Charset getHttpCredentialsEncoding(RepositorySystemSession session, RemoteRepository repository) {
155         return Charset.forName(ConfigUtils.getString(
156                 session,
157                 ConfigurationProperties.DEFAULT_HTTP_CREDENTIAL_ENCODING,
158                 ConfigurationProperties.HTTP_CREDENTIAL_ENCODING + "." + repository.getId(),
159                 ConfigurationProperties.HTTP_CREDENTIAL_ENCODING));
160     }
161 
162     /**
163      * Getter for {@link ConfigurationProperties#CONNECT_TIMEOUT}.
164      */
165     public static int getHttpConnectTimeout(RepositorySystemSession session, RemoteRepository repository) {
166         return ConfigUtils.getInteger(
167                 session,
168                 ConfigurationProperties.DEFAULT_CONNECT_TIMEOUT,
169                 ConfigurationProperties.CONNECT_TIMEOUT + "." + repository.getId(),
170                 ConfigurationProperties.CONNECT_TIMEOUT);
171     }
172 
173     /**
174      * Getter for {@link ConfigurationProperties#REQUEST_TIMEOUT}.
175      */
176     public static int getHttpRequestTimeout(RepositorySystemSession session, RemoteRepository repository) {
177         return ConfigUtils.getInteger(
178                 session,
179                 ConfigurationProperties.DEFAULT_REQUEST_TIMEOUT,
180                 ConfigurationProperties.REQUEST_TIMEOUT + "." + repository.getId(),
181                 ConfigurationProperties.REQUEST_TIMEOUT);
182     }
183 
184     /**
185      * Getter for {@link ConfigurationProperties#HTTP_RETRY_HANDLER_COUNT}.
186      */
187     public static int getHttpRetryHandlerCount(RepositorySystemSession session, RemoteRepository repository) {
188         int result = ConfigUtils.getInteger(
189                 session,
190                 ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_COUNT,
191                 ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT + "." + repository.getId(),
192                 ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT);
193         if (result < 0) {
194             throw new IllegalArgumentException(
195                     ConfigurationProperties.HTTP_RETRY_HANDLER_COUNT + " value must be >= 0");
196         }
197         return result;
198     }
199 
200     /**
201      * Getter for {@link ConfigurationProperties#HTTP_RETRY_HANDLER_INTERVAL}.
202      */
203     public static long getHttpRetryHandlerInterval(RepositorySystemSession session, RemoteRepository repository) {
204         long result = ConfigUtils.getLong(
205                 session,
206                 ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_INTERVAL,
207                 ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL + "." + repository.getId(),
208                 ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL);
209         if (result < 0) {
210             throw new IllegalArgumentException(
211                     ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL + " value must be >= 0");
212         }
213         return result;
214     }
215 
216     /**
217      * Getter for {@link ConfigurationProperties#HTTP_RETRY_HANDLER_INTERVAL_MAX}.
218      */
219     public static long getHttpRetryHandlerIntervalMax(RepositorySystemSession session, RemoteRepository repository) {
220         long result = ConfigUtils.getLong(
221                 session,
222                 ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_INTERVAL_MAX,
223                 ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX + "." + repository.getId(),
224                 ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX);
225         if (result < 0) {
226             throw new IllegalArgumentException(
227                     ConfigurationProperties.HTTP_RETRY_HANDLER_INTERVAL_MAX + " value must be >= 0");
228         }
229         return result;
230     }
231 
232     /**
233      * Getter for {@link ConfigurationProperties#HTTP_EXPECT_CONTINUE}.
234      */
235     public static Optional<Boolean> getHttpExpectContinue(
236             RepositorySystemSession session, RemoteRepository repository) {
237         String expectContinue = ConfigUtils.getString(
238                 session,
239                 null,
240                 ConfigurationProperties.HTTP_EXPECT_CONTINUE + "." + repository.getId(),
241                 ConfigurationProperties.HTTP_EXPECT_CONTINUE);
242         if (expectContinue != null) {
243             return Optional.of(Boolean.parseBoolean(expectContinue));
244         }
245         return Optional.empty();
246     }
247 
248     /**
249      * Getter for {@link ConfigurationProperties#HTTP_REUSE_CONNECTIONS}.
250      */
251     public static boolean isHttpReuseConnections(RepositorySystemSession session, RemoteRepository repository) {
252         return ConfigUtils.getBoolean(
253                 session,
254                 ConfigurationProperties.DEFAULT_HTTP_REUSE_CONNECTIONS,
255                 ConfigurationProperties.HTTP_REUSE_CONNECTIONS + "." + repository.getId(),
256                 ConfigurationProperties.HTTP_REUSE_CONNECTIONS);
257     }
258 
259     /**
260      * Getter for {@link ConfigurationProperties#HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE}.
261      */
262     public static Set<Integer> getHttpServiceUnavailableCodes(
263             RepositorySystemSession session, RemoteRepository repository) {
264         String stringValue = ConfigUtils.getString(
265                 session,
266                 ConfigurationProperties.DEFAULT_HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE,
267                 ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE + "." + repository.getId(),
268                 ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE);
269         Set<Integer> result = new HashSet<>();
270         try {
271             for (String code : ConfigUtils.parseCommaSeparatedUniqueNames(stringValue)) {
272                 result.add(Integer.parseInt(code));
273             }
274         } catch (NumberFormatException e) {
275             throw new IllegalArgumentException(
276                     "Illegal HTTP codes for " + ConfigurationProperties.HTTP_RETRY_HANDLER_SERVICE_UNAVAILABLE
277                             + " (list of integers): " + stringValue);
278         }
279         return result;
280     }
281 
282     /**
283      * Getter for {@link ConfigurationProperties#HTTP_LOCAL_ADDRESS}.
284      */
285     public static Optional<InetAddress> getHttpLocalAddress(
286             RepositorySystemSession session, RemoteRepository repository) {
287         String bindAddress = ConfigUtils.getString(
288                 session,
289                 null,
290                 ConfigurationProperties.HTTP_LOCAL_ADDRESS + "." + repository.getId(),
291                 ConfigurationProperties.HTTP_LOCAL_ADDRESS);
292         if (bindAddress != null) {
293             try {
294                 return Optional.of(InetAddress.getByName(bindAddress));
295             } catch (UnknownHostException uhe) {
296                 throw new IllegalArgumentException(
297                         "Given bind address (" + bindAddress + ") cannot be resolved for remote repository "
298                                 + repository,
299                         uhe);
300             }
301         }
302         return Optional.empty();
303     }
304 
305     /**
306      * Shared code to create "base {@link URI}" for most common HTTP remote repositories and all HTTP transports.
307      * Note: this method just applies common validation and adjustments to URI, but it does not enforce protocol
308      * to be HTTP/HTTPS!
309      * <p>
310      * Validations and adjustments applied:
311      * <ul>
312      *     <li>URI string is parsed from {@link RemoteRepository#getUrl()} returned string</li>
313      *     <li>URI must have parsable {@link URI#parseServerAuthority()}</li>
314      *     <li>URI must not be opaque</li>
315      *     <li>URI must not have fragment or query</li>
316      *     <li>URI path is adjusted to end with {@code /} (slash).</li>
317      * </ul>
318      *
319      * @since 2.0.18
320      */
321     public static URI getBaseUri(RemoteRepository repository) throws URISyntaxException {
322         URI uri = new URI(repository.getUrl()).parseServerAuthority();
323         if (uri.isOpaque()) {
324             throw new URISyntaxException(repository.getUrl(), "URL must not be opaque");
325         }
326         if (uri.getRawFragment() != null || uri.getRawQuery() != null) {
327             throw new URISyntaxException(repository.getUrl(), "URL must not have fragment or query");
328         }
329         String path = uri.getRawPath();
330         if (path == null) {
331             path = "/";
332         }
333         if (!path.startsWith("/")) {
334             path = "/" + path;
335         }
336         if (!path.endsWith("/")) {
337             path = path + "/";
338         }
339         return new URI(uri.getScheme() + "://" + uri.getRawAuthority() + path);
340     }
341 }