/** * Licensed to the Apache Software Foundation (ASF) under one * or more contributor license agreements. See the NOTICE file * distributed with this work for additional information * regarding copyright ownership. The ASF licenses this file * to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance * with the License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package org.apache.hadoop.hdfs.web; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URL; import java.net.URLConnection; import java.security.GeneralSecurityException; import javax.net.ssl.HostnameVerifier; import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.SSLSocketFactory; import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; import org.apache.hadoop.classification.InterfaceAudience; import org.apache.hadoop.classification.InterfaceStability; import org.apache.hadoop.conf.Configuration; import org.apache.hadoop.security.UserGroupInformation; import org.apache.hadoop.security.authentication.client.AuthenticatedURL; import org.apache.hadoop.security.authentication.client.AuthenticationException; import org.apache.hadoop.security.authentication.client.ConnectionConfigurator; import org.apache.hadoop.security.ssl.SSLFactory; import com.google.common.annotations.VisibleForTesting; /** * Utilities for handling URLs */ @InterfaceAudience.LimitedPrivate({ "HDFS" }) @InterfaceStability.Unstable public class URLConnectionFactory { private static final Log LOG = LogFactory.getLog(URLConnectionFactory.class); /** SPNEGO authenticator */ private static final KerberosUgiAuthenticator AUTH = new KerberosUgiAuthenticator(); /** * Timeout for socket connects and reads */ public final static int DEFAULT_SOCKET_TIMEOUT = 1 * 60 * 1000; // 1 minute private final ConnectionConfigurator connConfigurator; private static final ConnectionConfigurator DEFAULT_TIMEOUT_CONN_CONFIGURATOR = new ConnectionConfigurator() { @Override public HttpURLConnection configure(HttpURLConnection conn) throws IOException { URLConnectionFactory.setTimeouts(conn, DEFAULT_SOCKET_TIMEOUT); return conn; } }; /** * The URLConnectionFactory that sets the default timeout and it only trusts * Java's SSL certificates. */ public static final URLConnectionFactory DEFAULT_SYSTEM_CONNECTION_FACTORY = new URLConnectionFactory( DEFAULT_TIMEOUT_CONN_CONFIGURATOR); /** * Construct a new URLConnectionFactory based on the configuration. It will * try to load SSL certificates when it is specified. */ public static URLConnectionFactory newDefaultURLConnectionFactory(Configuration conf) { ConnectionConfigurator conn = null; try { conn = newSslConnConfigurator(DEFAULT_SOCKET_TIMEOUT, conf); } catch (Exception e) { LOG.debug( "Cannot load customized ssl related configuration. Fallback to system-generic settings.", e); conn = DEFAULT_TIMEOUT_CONN_CONFIGURATOR; } return new URLConnectionFactory(conn); } @VisibleForTesting URLConnectionFactory(ConnectionConfigurator connConfigurator) { this.connConfigurator = connConfigurator; } /** * Create a new ConnectionConfigurator for SSL connections */ private static ConnectionConfigurator newSslConnConfigurator(final int timeout, Configuration conf) throws IOException, GeneralSecurityException { final SSLFactory factory; final SSLSocketFactory sf; final HostnameVerifier hv; factory = new SSLFactory(SSLFactory.Mode.CLIENT, conf); factory.init(); sf = factory.createSSLSocketFactory(); hv = factory.getHostnameVerifier(); return new ConnectionConfigurator() { @Override public HttpURLConnection configure(HttpURLConnection conn) throws IOException { if (conn instanceof HttpsURLConnection) { HttpsURLConnection c = (HttpsURLConnection) conn; c.setSSLSocketFactory(sf); c.setHostnameVerifier(hv); } URLConnectionFactory.setTimeouts(conn, timeout); return conn; } }; } /** * Opens a url with read and connect timeouts * * @param url * to open * @return URLConnection * @throws IOException */ public URLConnection openConnection(URL url) throws IOException { try { return openConnection(url, false); } catch (AuthenticationException e) { // Unreachable return null; } } /** * Opens a url with read and connect timeouts * * @param url * URL to open * @param isSpnego * whether the url should be authenticated via SPNEGO * @return URLConnection * @throws IOException * @throws AuthenticationException */ public URLConnection openConnection(URL url, boolean isSpnego) throws IOException, AuthenticationException { if (isSpnego) { if (LOG.isDebugEnabled()) { LOG.debug("open AuthenticatedURL connection" + url); } UserGroupInformation.getCurrentUser().checkTGTAndReloginFromKeytab(); final AuthenticatedURL.Token authToken = new AuthenticatedURL.Token(); return new AuthenticatedURL(AUTH, connConfigurator).openConnection(url, authToken); } else { if (LOG.isDebugEnabled()) { LOG.debug("open URL connection"); } URLConnection connection = url.openConnection(); if (connection instanceof HttpURLConnection) { connConfigurator.configure((HttpURLConnection) connection); } return connection; } } /** * Sets timeout parameters on the given URLConnection. * * @param connection * URLConnection to set * @param socketTimeout * the connection and read timeout of the connection. */ private static void setTimeouts(URLConnection connection, int socketTimeout) { connection.setConnectTimeout(socketTimeout); connection.setReadTimeout(socketTimeout); } }