001package io.freefair.spring.okhttp.autoconfigure;
002
003import io.freefair.spring.okhttp.ApplicationInterceptor;
004import io.freefair.spring.okhttp.NetworkInterceptor;
005import io.freefair.spring.okhttp.OkHttp3Configurer;
006import jakarta.annotation.PreDestroy;
007import okhttp3.*;
008import org.springframework.beans.factory.ObjectProvider;
009import org.springframework.beans.factory.annotation.Autowired;
010import org.springframework.boot.autoconfigure.AutoConfiguration;
011import org.springframework.boot.autoconfigure.condition.ConditionalOnClass;
012import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
013import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
014import org.springframework.boot.context.properties.EnableConfigurationProperties;
015import org.springframework.context.annotation.Bean;
016import org.springframework.util.CollectionUtils;
017import org.springframework.util.FileSystemUtils;
018import javax.net.ssl.HostnameVerifier;
019import java.io.File;
020import java.io.IOException;
021import java.nio.file.Files;
022import java.time.Duration;
023import java.util.concurrent.TimeUnit;
024
025/**
026 * @author Lars Grefer
027 */
028@SuppressWarnings("SpringJavaAutowiredFieldsWarningInspection")
029@AutoConfiguration
030@ConditionalOnClass(OkHttpClient.class)
031@EnableConfigurationProperties(OkHttpProperties.class)
032public class OkHttp3AutoConfiguration {
033    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(OkHttp3AutoConfiguration.class);
034    @Autowired
035    private OkHttpProperties okHttpProperties;
036    @Autowired
037    private ObjectProvider<OkHttp3Configurer> configurers;
038    @Autowired
039    @ApplicationInterceptor
040    private ObjectProvider<Interceptor> applicationInterceptors;
041    @Autowired
042    @NetworkInterceptor
043    private ObjectProvider<Interceptor> networkInterceptors;
044    private File tempDirCache = null;
045
046    @Bean
047    @ConditionalOnMissingBean
048    public OkHttpClient okHttp3Client(ObjectProvider<Cache> cache, ObjectProvider<CookieJar> cookieJar, ObjectProvider<Dns> dns, ObjectProvider<HostnameVerifier> hostnameVerifier, ObjectProvider<CertificatePinner> certificatePinner, ConnectionPool connectionPool, ObjectProvider<EventListener> eventListener) {
049        OkHttpClient.Builder builder = new OkHttpClient.Builder();
050        cache.ifUnique(builder::cache);
051        eventListener.ifUnique(builder::eventListener);
052        builder.connectTimeout(okHttpProperties.getConnectTimeout());
053        builder.readTimeout(okHttpProperties.getReadTimeout());
054        builder.writeTimeout(okHttpProperties.getWriteTimeout());
055        builder.pingInterval(okHttpProperties.getPingInterval());
056        cookieJar.ifUnique(builder::cookieJar);
057        dns.ifUnique(builder::dns);
058        hostnameVerifier.ifUnique(builder::hostnameVerifier);
059        certificatePinner.ifUnique(builder::certificatePinner);
060        builder.connectionPool(connectionPool);
061        builder.followRedirects(okHttpProperties.isFollowRedirects());
062        builder.followSslRedirects(okHttpProperties.isFollowSslRedirects());
063        builder.retryOnConnectionFailure(okHttpProperties.isRetryOnConnectionFailure());
064        if (!CollectionUtils.isEmpty(okHttpProperties.getProtocols())) {
065            builder.protocols(okHttpProperties.getProtocols());
066        }
067        applicationInterceptors.forEach(builder::addInterceptor);
068        networkInterceptors.forEach(builder::addNetworkInterceptor);
069        configurers.forEach(configurer -> configurer.configure(builder));
070        return builder.build();
071    }
072
073    @Bean
074    @ConditionalOnMissingBean
075    public ConnectionPool okHttp3ConnectionPool() {
076        int maxIdleConnections = okHttpProperties.getConnectionPool().getMaxIdleConnections();
077        Duration keepAliveDuration = okHttpProperties.getConnectionPool().getKeepAliveDuration();
078        return new ConnectionPool(maxIdleConnections, keepAliveDuration.toNanos(), TimeUnit.NANOSECONDS);
079    }
080
081    @Bean
082    @ConditionalOnMissingBean
083    @ConditionalOnProperty(value = "okhttp.cache.enabled", havingValue = "true", matchIfMissing = true)
084    public Cache okHttp3Cache() throws IOException {
085        File directory = okHttpProperties.getCache().getDirectory();
086        if (directory == null) {
087            tempDirCache = Files.createTempDirectory("okhttp-cache").toFile();
088            directory = tempDirCache;
089        }
090        return new Cache(directory, okHttpProperties.getCache().getMaxSize().toBytes());
091    }
092
093    @PreDestroy
094    public void deleteTempCache() {
095        if (tempDirCache != null) {
096            log.debug("Deleting the temporary OkHttp Cache at {}", tempDirCache.getAbsolutePath());
097            try {
098                FileSystemUtils.deleteRecursively(tempDirCache);
099            } catch (Exception e) {
100                log.warn("Failed to delete the temporary OkHttp Cache at {}", tempDirCache.getAbsolutePath());
101            }
102        }
103    }
104}