001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, 013 * software distributed under the License is distributed on an 014 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 015 * KIND, either express or implied. See the License for the 016 * specific language governing permissions and limitations 017 * under the License. 018 */ 019package org.eclipse.aether.util.connector.transport.http; 020 021import java.net.InetAddress; 022import java.net.URI; 023import java.net.URISyntaxException; 024import java.net.UnknownHostException; 025import java.nio.charset.Charset; 026import java.util.Collections; 027import java.util.HashSet; 028import java.util.Map; 029import java.util.Optional; 030import java.util.Set; 031 032import org.eclipse.aether.ConfigurationProperties; 033import org.eclipse.aether.RepositorySystemSession; 034import org.eclipse.aether.repository.RemoteRepository; 035import org.eclipse.aether.util.ConfigUtils; 036 037/** 038 * A utility class to read HTTP transport related configuration. It implements all HTTP transport related configurations from 039 * {@link ConfigurationProperties} and transport implementations are free to use those that are supported by themselves. 040 * 041 * @see ConfigurationProperties 042 * @see RepositorySystemSession#getConfigProperties() 043 * @since 2.0.15 044 */ 045public final class HttpTransporterUtils { 046 private HttpTransporterUtils() {} 047 048 /** 049 * Getter for {@link ConfigurationProperties#USER_AGENT}. 050 */ 051 public static String getUserAgent(RepositorySystemSession session, RemoteRepository repository) { 052 return ConfigUtils.getString( 053 session, 054 ConfigurationProperties.DEFAULT_USER_AGENT, 055 ConfigurationProperties.USER_AGENT, 056 "aether.connector.userAgent"); 057 } 058 059 /** 060 * Getter for {@link ConfigurationProperties#HTTPS_SECURITY_MODE}. 061 */ 062 public static String getHttpsSecurityMode(RepositorySystemSession session, RemoteRepository repository) { 063 String result = ConfigUtils.getString( 064 session, 065 ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT, 066 ConfigurationProperties.HTTPS_SECURITY_MODE + "." + repository.getId(), 067 ConfigurationProperties.HTTPS_SECURITY_MODE); 068 if (!ConfigurationProperties.HTTPS_SECURITY_MODE_DEFAULT.equals(result) 069 && !ConfigurationProperties.HTTPS_SECURITY_MODE_INSECURE.equals(result)) { 070 throw new IllegalArgumentException("Unsupported '" + result + "' HTTPS security mode."); 071 } 072 return result; 073 } 074 075 /** 076 * Getter for {@link ConfigurationProperties#HTTP_CONNECTION_MAX_TTL}. 077 */ 078 public static int getHttpConnectionMaxTtlSeconds(RepositorySystemSession session, RemoteRepository repository) { 079 int result = ConfigUtils.getInteger( 080 session, 081 ConfigurationProperties.DEFAULT_HTTP_CONNECTION_MAX_TTL, 082 ConfigurationProperties.HTTP_CONNECTION_MAX_TTL + "." + repository.getId(), 083 ConfigurationProperties.HTTP_CONNECTION_MAX_TTL); 084 if (result < 0) { 085 throw new IllegalArgumentException(ConfigurationProperties.HTTP_CONNECTION_MAX_TTL + " value must be >= 0"); 086 } 087 return result; 088 } 089 090 /** 091 * Getter for {@link ConfigurationProperties#HTTP_MAX_CONNECTIONS_PER_ROUTE}. 092 */ 093 public static int getHttpMaxConnectionsPerRoute(RepositorySystemSession session, RemoteRepository repository) { 094 int result = ConfigUtils.getInteger( 095 session, 096 ConfigurationProperties.DEFAULT_HTTP_MAX_CONNECTIONS_PER_ROUTE, 097 ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE + "." + repository.getId(), 098 ConfigurationProperties.HTTP_MAX_CONNECTIONS_PER_ROUTE); 099 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}