001/* 002 * The contents of this file are subject to the terms of the Common Development and 003 * Distribution License (the License). You may not use this file except in compliance with the 004 * License. 005 * 006 * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the 007 * specific language governing permission and limitations under the License. 008 * 009 * When distributing Covered Software, include this CDDL Header Notice in each file and include 010 * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL 011 * Header, with the fields enclosed by brackets [] replaced by your own identifying 012 * information: "Portions Copyright [year] [name of copyright owner]". 013 * 014 * Copyright 2014-2015 ForgeRock AS. 015 */ 016 017package org.forgerock.http.handler; 018 019import static org.forgerock.util.time.Duration.duration; 020 021import java.io.Closeable; 022import java.io.IOException; 023import java.util.Collections; 024import java.util.List; 025 026import javax.net.ssl.KeyManager; 027import javax.net.ssl.TrustManager; 028 029import org.forgerock.services.context.Context; 030import org.forgerock.http.Handler; 031import org.forgerock.http.HttpApplicationException; 032import org.forgerock.http.io.Buffer; 033import org.forgerock.http.io.IO; 034import org.forgerock.http.protocol.Request; 035import org.forgerock.http.protocol.Response; 036import org.forgerock.http.spi.HttpClient; 037import org.forgerock.http.spi.HttpClientProvider; 038import org.forgerock.http.spi.Loader; 039import org.forgerock.util.Factory; 040import org.forgerock.util.Option; 041import org.forgerock.util.Options; 042import org.forgerock.util.Reject; 043import org.forgerock.util.promise.NeverThrowsException; 044import org.forgerock.util.promise.Promise; 045import org.forgerock.util.time.Duration; 046 047/** 048 * An HTTP client for sending requests to remote servers. 049 */ 050public final class HttpClientHandler implements Handler, Closeable { 051 052 /** 053 * The TCP connect timeout for new HTTP connections. The default timeout is 054 * 10 seconds. 055 */ 056 public static final Option<Duration> OPTION_CONNECT_TIMEOUT = Option.withDefault(duration("10 seconds")); 057 058 /** 059 * The TCP socket timeout when waiting for HTTP responses. The default 060 * timeout is 10 seconds. 061 */ 062 public static final Option<Duration> OPTION_SO_TIMEOUT = Option.withDefault(duration("10 seconds")); 063 064 /** 065 * Specifies whether HTTP connections should be kept alive an reused for 066 * additional requests. By default, connections will be reused if possible. 067 */ 068 public static final Option<Boolean> OPTION_REUSE_CONNECTIONS = Option.withDefault(true); 069 070 /** 071 * Specifies whether requests should be retried if a failure is detected. By 072 * default requests will be retried. 073 */ 074 public static final Option<Boolean> OPTION_RETRY_REQUESTS = Option.withDefault(true); 075 076 /** 077 * Specifies the list of key managers that should be used when configuring 078 * SSL/TLS connections. By default the system key manager(s) will be used. 079 */ 080 public static final Option<KeyManager[]> OPTION_KEY_MANAGERS = Option.of(KeyManager[].class, null); 081 082 /** 083 * The strategy which should be used for loading the 084 * {@link HttpClientProvider}. By default, the provider will be loaded using 085 * a {@code ServiceLoader}. 086 * 087 * @see Loader#SERVICE_LOADER 088 */ 089 public static final Option<Loader> OPTION_LOADER = Option.of(Loader.class, Loader.SERVICE_LOADER); 090 091 /** 092 * Specifies the maximum number of connections that should be pooled by the 093 * HTTP client. At most 64 connections will be cached by default. 094 */ 095 public static final Option<Integer> OPTION_MAX_CONNECTIONS = Option.withDefault(64); 096 097 /** 098 * Specifies the temporary storage that should be used for storing HTTP 099 * responses. By default {@link IO#newTemporaryStorage()} is used. 100 */ 101 @SuppressWarnings({ "unchecked", "rawtypes" }) 102 public static final Option<Factory<Buffer>> OPTION_TEMPORARY_STORAGE = (Option) Option.of( 103 Factory.class, IO.newTemporaryStorage()); 104 105 /** 106 * Specifies the list of trust managers that should be used when configuring 107 * SSL/TLS connections. By default the system trust manager(s) will be used. 108 */ 109 public static final Option<TrustManager[]> OPTION_TRUST_MANAGERS = Option.of(TrustManager[].class, null); 110 111 /** The client implementation. */ 112 private final HttpClient httpClient; 113 114 /** 115 * SSL host name verification policies. 116 */ 117 public enum HostnameVerifier { 118 /** 119 * Accepts any host name (disables host name verification). 120 */ 121 ALLOW_ALL, 122 123 /** 124 * Requires that the host name matches the host name presented in the 125 * certificate. Wild-cards only match a single domain. 126 */ 127 STRICT; 128 } 129 130 /** 131 * Specifies the SSL host name verification policy. The default is to allow 132 * all host names. 133 */ 134 public static final Option<HostnameVerifier> OPTION_HOSTNAME_VERIFIER = Option.of( 135 HostnameVerifier.class, HostnameVerifier.ALLOW_ALL); 136 137 /** 138 * SSLContext algorithm to be used when making SSL/TLS connections. 139 */ 140 public static final Option<String> OPTION_SSLCONTEXT_ALGORITHM = Option.withDefault("TLS"); 141 142 /** 143 * List of SSL protocols to be enabled on the HttpClient. 144 * Defaults to the list of SSL protocols supported by the Java runtime. 145 * 146 * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#jssenames"> 147 * JDK 7 Supported Protocols</a> 148 */ 149 @SuppressWarnings({ "rawtypes", "unchecked" }) 150 public static final Option<List<String>> OPTION_SSL_ENABLED_PROTOCOLS = 151 (Option) Option.of(List.class, Collections.<String>emptyList()); 152 153 /** 154 * List of JSSE ciphers to be enabled on the HttpClient. 155 * Defaults to the list of ciphers supported by the Java runtime. 156 * 157 * @see <a href="http://docs.oracle.com/javase/7/docs/technotes/guides/security/StandardNames.html#ciphersuites"> 158 * JDK 7 Cipher Suite Names</a> 159 */ 160 @SuppressWarnings({ "rawtypes", "unchecked" }) 161 public static final Option<List<String>> OPTION_SSL_CIPHER_SUITES = 162 (Option) Option.of(List.class, Collections.<String>emptyList()); 163 164 /** 165 * Creates a new HTTP client using default client options. The returned 166 * client must be closed when it is no longer needed by the application. 167 * 168 * @throws HttpApplicationException 169 * If no client provider could be found. 170 */ 171 public HttpClientHandler() throws HttpApplicationException { 172 this(Options.unmodifiableDefaultOptions()); 173 } 174 175 /** 176 * Creates a new HTTP client using the provided client options. The returned 177 * client must be closed when it is no longer needed by the application. 178 * 179 * @param options 180 * The options which will be used to configure the client. 181 * @throws HttpApplicationException 182 * If no client provider could be found, or if the client could 183 * not be configured using the provided set of options. 184 * @throws NullPointerException 185 * If {@code options} was {@code null}. 186 */ 187 public HttpClientHandler(final Options options) throws HttpApplicationException { 188 Reject.ifNull(options); 189 final Loader loader = options.get(OPTION_LOADER); 190 final HttpClientProvider factory = loader.load(HttpClientProvider.class, options); 191 if (factory == null) { 192 throw new HttpApplicationException("No HTTP client provider found"); 193 } 194 this.httpClient = factory.newHttpClient(options); 195 } 196 197 /** 198 * Completes all pending requests and release resources associated with 199 * underlying implementation. 200 * 201 * @throws IOException 202 * if an I/O error occurs 203 */ 204 @Override 205 public void close() throws IOException { 206 httpClient.close(); 207 } 208 209 /** 210 * Sends an HTTP request to a remote server and returns a {@code Promise} 211 * representing the asynchronous response. 212 * <p> 213 * {@inheritDoc} 214 */ 215 @Override 216 public Promise<Response, NeverThrowsException> handle(Context context, Request request) { 217 return httpClient.sendAsync(request); 218 } 219}