spring-cloud / spring-cloud/spring-cloud-vault

Customize the http client builder

Open
#675 5 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

type: enhancement
Dominant language
Java
Stars
291
Forks
152
Avg merge
11h 24m
Merged PRs (30d)
3

Description

Hello,

As i have already mentioned the need to configure http client connection TTL for spring cloud vault (https://github.com/spring-cloud/spring-cloud-vault/issues/660), i need to find the good way to do it.

You gave us a way to do it : https://gist.github.com/mp911de/157e6ae14ba6bb3565c36b425d3d83b7
However, even if the class HttpComponents become public, there is no method getBuilder()

Here is the static class HttpComponents in ClientHttpRequestFactoryFactory :

    static class HttpComponents {
        HttpComponents() {
        }

        static ClientHttpRequestFactory usingHttpComponents(ClientOptions options, SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException {
            HttpClientBuilder httpClientBuilder = HttpClients.custom();
            httpClientBuilder.setRoutePlanner(new SystemDefaultRoutePlanner(DefaultSchemePortResolver.INSTANCE, ProxySelector.getDefault()));
            if (ClientHttpRequestFactoryFactory.hasSslConfiguration(sslConfiguration)) {
                SSLContext sslContext = ClientHttpRequestFactoryFactory.getSSLContext(sslConfiguration, ClientHttpRequestFactoryFactory.getTrustManagers(sslConfiguration));
                String[] enabledProtocols = null;
                if (!sslConfiguration.getEnabledProtocols().isEmpty()) {
                    enabledProtocols = (String[])sslConfiguration.getEnabledProtocols().toArray(new String[0]);
                }

                String[] enabledCipherSuites = null;
                if (!sslConfiguration.getEnabledCipherSuites().isEmpty()) {
                    enabledCipherSuites = (String[])sslConfiguration.getEnabledCipherSuites().toArray(new String[0]);
                }

                SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, enabledProtocols, enabledCipherSuites, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
                httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
                httpClientBuilder.setSSLContext(sslContext);
            }

            RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(Math.toIntExact(options.getConnectionTimeout().toMillis())).setSocketTimeout(Math.toIntExact(options.getReadTimeout().toMillis())).setAuthenticationEnabled(true).build();
            httpClientBuilder.setDefaultRequestConfig(requestConfig);
            httpClientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
            return new HttpComponentsClientHttpRequestFactory(httpClientBuilder.build());
        }
    }

The only method available from this static class HttpComponents is usingHttpComponents but this method need some parameters (clientOptions and SslConfiguration) and return ClientHttpRequestFactory
This method does not let configure the HttpClientBuilder and from Spring Boot main method.
How could we get the clientOptions and SslConfiguration which necessitate to inject VaultProperties.

This is a code i have done, do you have a better way before wait for the change apply in new spring vault core version about visibility of HttpComponents ?

Here is the code i have done :

public static void main(String[] args) throws GeneralSecurityException, IOException {

       SpringApplication app = new SpringApplication(StandardsMicroserviceApplicationTest.class);

       // how could we get it ? We need to retreive data from VaultProperties to instanciate these classes
       ClientOptions clientOptions = null;
       SslConfiguration sslConfiguration = null;

       // create http client builder from copied code method usingHttpComponents from ClientHttpRequestFactoryFactory
       HttpClientBuilder builder = customUsingHttpComponents(clientOptions, sslConfiguration);

       HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory(builder.build());

       app.addBootstrapRegistryInitializer(registry -> {
           registry.register(AbstractVaultConfiguration.ClientFactoryWrapper.class,
                   BootstrapRegistry.InstanceSupplier.of(new AbstractVaultConfiguration.ClientFactoryWrapper(requestFactory)));
       });

       app.run(args);
       log.info("Application started");
   }


   // copied code from ClientHttpRequestFactoryFactory class and change it to return HttpClientBuilder instead
   public static HttpClientBuilder customUsingHttpComponents(ClientOptions options, SslConfiguration sslConfiguration) throws GeneralSecurityException, IOException {
       HttpClientBuilder httpClientBuilder = HttpClients.custom();

       //customize http client builder connection TimeToLive
       httpClientBuilder = httpClientBuilder.setConnectionTimeToLive(120, TimeUnit.SECONDS);

       httpClientBuilder.setRoutePlanner(new SystemDefaultRoutePlanner(DefaultSchemePortResolver.INSTANCE, ProxySelector.getDefault()));
       if (ClientHttpRequestFactoryFactory.hasSslConfiguration(sslConfiguration)) {
           SSLContext sslContext = ClientHttpRequestFactoryFactory.getSSLContext(sslConfiguration, ClientHttpRequestFactoryFactory.getTrustManagers(sslConfiguration));
           String[] enabledProtocols = null;
           if (!sslConfiguration.getEnabledProtocols().isEmpty()) {
               enabledProtocols = (String[])sslConfiguration.getEnabledProtocols().toArray(new String[0]);
           }

           String[] enabledCipherSuites = null;
           if (!sslConfiguration.getEnabledCipherSuites().isEmpty()) {
               enabledCipherSuites = (String[])sslConfiguration.getEnabledCipherSuites().toArray(new String[0]);
           }

           SSLConnectionSocketFactory sslSocketFactory = new SSLConnectionSocketFactory(sslContext, enabledProtocols, enabledCipherSuites, SSLConnectionSocketFactory.getDefaultHostnameVerifier());
           httpClientBuilder.setSSLSocketFactory(sslSocketFactory);
           httpClientBuilder.setSSLContext(sslContext);
       }

       RequestConfig requestConfig = RequestConfig.custom().setConnectTimeout(Math.toIntExact(options.getConnectionTimeout().toMillis())).setSocketTimeout(Math.toIntExact(options.getReadTimeout().toMillis())).setAuthenticationEnabled(true).build();
       httpClientBuilder.setDefaultRequestConfig(requestConfig);
       httpClientBuilder.setRedirectStrategy(new LaxRedirectStrategy());
       return httpClientBuilder;
   }


   // copied code from Spring vault core
   static SslConfiguration createSslConfiguration(VaultProperties.Ssl ssl) {
       SslConfiguration.KeyStoreConfiguration keyStore = SslConfiguration.KeyStoreConfiguration.unconfigured();
       SslConfiguration.KeyStoreConfiguration trustStore = SslConfiguration.KeyStoreConfiguration.unconfigured();
       if (ssl.getKeyStore() != null) {
           if (StringUtils.hasText(ssl.getKeyStorePassword())) {
               keyStore = SslConfiguration.KeyStoreConfiguration.of(ssl.getKeyStore(), ssl.getKeyStorePassword().toCharArray());
           } else {
               keyStore = SslConfiguration.KeyStoreConfiguration.of(ssl.getKeyStore());
           }

           if (StringUtils.hasText(ssl.getKeyStoreType())) {
               keyStore = keyStore.withStoreType(ssl.getKeyStoreType());
           }
       }

       if (ssl.getTrustStore() != null) {
           if (StringUtils.hasText(ssl.getTrustStorePassword())) {
               trustStore = SslConfiguration.KeyStoreConfiguration.of(ssl.getTrustStore(), ssl.getTrustStorePassword().toCharArray());
           } else {
               trustStore = SslConfiguration.KeyStoreConfiguration.of(ssl.getTrustStore());
           }

           if (StringUtils.hasText(ssl.getTrustStoreType())) {
               trustStore = trustStore.withStoreType(ssl.getTrustStoreType());
           }
       }

       return new SslConfiguration(keyStore, trustStore, ssl.getEnabledProtocols(), ssl.getEnabledCipherSuites());
   }

there are some missing parts, how inject VaultProperties data to configure the ClientOption and SslConfiguration before run the application as you its done in spring vault ?
I will need to use multiple properties from spring configuration application as value of connectionTimeToLive, retry flag, .. and retrieve these properties does not seem be possible because spring context is not yes defined

Thank you.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with ClientHttpRequestFactoryFactory.HttpComponents and the BootstrapRegistry setup shown in the issue, then trace how VaultProperties supplies ClientOptions and SslConfiguration. Compare the current usingHttpComponents parameters with the requested HttpClientBuilder customization. Done means a supported way to configure options such as connection time-to-live and retry settings before the application context is available.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
25/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.