HADOOP-17337. S3A NetworkBinding has a runtime dependency on shaded httpclient. (#2599)

Contributed by Steve Loughran.

Change-Id: I0471322fc88d8bc3896ac439aefb31e6a856936c
This commit is contained in:
Steve Loughran 2021-02-03 14:29:56 +00:00
parent 99337a4dd0
commit 70411cb1f1
No known key found for this signature in database
GPG Key ID: D22CF846DBB162A0
2 changed files with 74 additions and 25 deletions

View File

@ -0,0 +1,47 @@
/*
* 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.fs.s3a.impl;
import javax.net.ssl.HostnameVerifier;
import java.io.IOException;
import com.amazonaws.ClientConfiguration;
import com.amazonaws.thirdparty.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.hadoop.security.ssl.DelegatingSSLSocketFactory;
/**
* This interacts with the Shaded httpclient library used in the full
* AWS SDK. If the S3A client is used with the unshaded SDK, this
* class will not link.
*/
public class ConfigureShadedAWSSocketFactory implements
NetworkBinding.ConfigureAWSSocketFactory {
@Override
public void configureSocketFactory(final ClientConfiguration awsConf,
final DelegatingSSLSocketFactory.SSLChannelMode channelMode)
throws IOException {
DelegatingSSLSocketFactory.initializeDefaultFactory(channelMode);
awsConf.getApacheHttpClientConfig().setSslSocketFactory(
new SSLConnectionSocketFactory(
DelegatingSSLSocketFactory.getDefaultFactory(),
(HostnameVerifier) null));
}
}

View File

@ -19,14 +19,10 @@
package org.apache.hadoop.fs.s3a.impl; package org.apache.hadoop.fs.s3a.impl;
import java.io.IOException; import java.io.IOException;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException; import java.lang.reflect.InvocationTargetException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLSocketFactory;
import com.amazonaws.ClientConfiguration; import com.amazonaws.ClientConfiguration;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@ -43,20 +39,21 @@
/** /**
* Configures network settings when communicating with AWS services. * Configures network settings when communicating with AWS services.
*/ */
public class NetworkBinding { public final class NetworkBinding {
private static final Logger LOG = private static final Logger LOG =
LoggerFactory.getLogger(NetworkBinding.class); LoggerFactory.getLogger(NetworkBinding.class);
private static final String AWS_SOCKET_FACTORY_CLASSNAME = "com.amazonaws" + private static final String BINDING_CLASSNAME = "org.apache.hadoop.fs.s3a.impl.ConfigureShadedAWSSocketFactory";
".thirdparty.apache.http.conn.ssl.SSLConnectionSocketFactory";
private NetworkBinding() {
}
/** /**
* Configures the {@code SSLConnectionSocketFactory} used by the AWS SDK. * Configures the {@code SSLConnectionSocketFactory} used by the AWS SDK.
* A custom Socket Factory can be set using the method * A custom Socket Factory can be set using the method
* {@code setSslSocketFactory()}. * {@code setSslSocketFactory()}.
* If {@code SSLConnectionSocketFactory} cannot be found on the classpath, the value * Uses reflection to do this via {@link ConfigureShadedAWSSocketFactory}
* of {@link org.apache.hadoop.fs.s3a.Constants#SSL_CHANNEL_MODE} is ignored. * so as to avoid
*
* @param conf the {@link Configuration} used to get the client specified * @param conf the {@link Configuration} used to get the client specified
* value of {@code SSL_CHANNEL_MODE} * value of {@code SSL_CHANNEL_MODE}
* @param awsConf the {@code ClientConfiguration} to set the * @param awsConf the {@code ClientConfiguration} to set the
@ -84,28 +81,33 @@ public static void bindSSLChannelMode(Configuration conf,
DelegatingSSLSocketFactory.initializeDefaultFactory(channelMode); DelegatingSSLSocketFactory.initializeDefaultFactory(channelMode);
try { try {
// Look for AWS_SOCKET_FACTORY_CLASSNAME on the classpath and instantiate // use reflection to load in our own binding class.
// an instance using the DelegatingSSLSocketFactory as the // this is *probably* overkill, but it is how we can be fully confident
// SSLSocketFactory. // that no attempt will be made to load/link to the AWS Shaded SDK except
Class<?> sslConnectionSocketFactory = Class.forName( // within this try/catch block
AWS_SOCKET_FACTORY_CLASSNAME); Class<? extends ConfigureAWSSocketFactory> clazz =
Constructor<?> factoryConstructor = (Class<? extends ConfigureAWSSocketFactory>) Class.forName(BINDING_CLASSNAME);
sslConnectionSocketFactory.getDeclaredConstructor( clazz.getConstructor()
SSLSocketFactory.class, HostnameVerifier.class); .newInstance()
awsConf.getApacheHttpClientConfig().setSslSocketFactory( .configureSocketFactory(awsConf, channelMode);
(com.amazonaws.thirdparty.apache.http.conn.ssl.
SSLConnectionSocketFactory) factoryConstructor
.newInstance(DelegatingSSLSocketFactory
.getDefaultFactory(),
(HostnameVerifier) null));
} catch (ClassNotFoundException | NoSuchMethodException | } catch (ClassNotFoundException | NoSuchMethodException |
IllegalAccessException | InstantiationException | IllegalAccessException | InstantiationException |
InvocationTargetException | LinkageError e) { InvocationTargetException | LinkageError e) {
LOG.debug("Unable to create class {}, value of {} will be ignored", LOG.debug("Unable to create class {}, value of {} will be ignored",
AWS_SOCKET_FACTORY_CLASSNAME, SSL_CHANNEL_MODE, e); BINDING_CLASSNAME, SSL_CHANNEL_MODE, e);
} }
} }
/**
* Interface used to bind to the socket factory, allows the code which
* works with the shaded AWS libraries to exist in their own class.
*/
interface ConfigureAWSSocketFactory {
void configureSocketFactory(ClientConfiguration awsConf,
DelegatingSSLSocketFactory.SSLChannelMode channelMode)
throws IOException;
}
/** /**
* Given an S3 bucket region as returned by a bucket location query, * Given an S3 bucket region as returned by a bucket location query,
* fix it into a form which can be used by other AWS commands. * fix it into a form which can be used by other AWS commands.