botorabi/HomieCenter

View on GitHub
src/main/java/net/vrfun/homiecenter/fritzbox/Requests.java

Summary

Maintainability
A
1 hr
Test Coverage
A
100%
/*
 * Copyright (c) 2018 - 2021 by Botorabi. All rights reserved.
 * https://github.com/botorabi/HomieCenter
 *
 * License: MIT License (MIT), read the LICENSE text in
 *          main directory for more details.
 */
package net.vrfun.homiecenter.fritzbox;

import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.ssl.TrustStrategy;
import org.springframework.http.*;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.http.converter.StringHttpMessageConverter;
import org.springframework.lang.NonNull;
import org.springframework.lang.Nullable;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import javax.net.ssl.SSLContext;
import java.nio.charset.Charset;
import java.security.*;
import java.security.cert.X509Certificate;
import java.util.Map;


/**
 * Utilities for requesting FRITZ!Box via http or https.
 *
 * @author          boto
 * Creation Date    7th June 2018
 */
public class Requests {

    /**
     * Request using GET method.
     */
    public ResponseEntity<String> get(@NonNull final String url, @Nullable final Map<String, String> parameters) throws Exception {
        String finalUrl = url;
        if (parameters != null) {
            String params = "";
            for (Map.Entry<String, String> entry : parameters.entrySet()) {
                if (!params.isEmpty()) {
                    params += "&";
                }
                params += entry.getKey() + "=" + entry.getValue();
            }
            if (!params.isEmpty()) {
                finalUrl += "?" + params;
            }
        }

        if (url.startsWith("https")) {
            return createRestTemplateWithoutCertificateValidation().getForEntity(finalUrl, String.class);
        }

        return createRestTemplate().getForEntity(finalUrl, String.class);
    }

    /**
     * Request using POST method.
     */
    public ResponseEntity<String> post(@NonNull final String url, @Nullable final Map<String, String> parameters) throws Exception {
        HttpHeaders headers = new HttpHeaders();
        headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);

        MultiValueMap<String, String> postData = new LinkedMultiValueMap<>();
        if (parameters != null) {
            for (Map.Entry<String, String> entry : parameters.entrySet()) {
                postData.add(entry.getKey(), entry.getValue());
            }
        }

        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<>(postData, headers);

        if (url.startsWith("https")) {
            return createRestTemplateWithoutCertificateValidation().postForEntity(url, request, String.class);
        }

        return createRestTemplate().postForEntity(url, request, String.class);
    }

    /**
     * Create a REST template which supports UTF-8 character set in TEXT response handling.
     */
    @NonNull
    protected RestTemplate createRestTemplate() {
        RestTemplate restTemplate = new RestTemplate();

        addUTF8Support(restTemplate);

        return restTemplate;
    }

    /**
     * Create a RestTemplate like createRestTemplate(), but with a proper https validation mechanism.
     * As the certificate of the FRITZ!Box cannot be validated, it is simply ignored during HTTPS requests.
     */
    @NonNull
    protected RestTemplate createRestTemplateWithoutCertificateValidation() throws KeyStoreException, NoSuchAlgorithmException, KeyManagementException {
        final TrustStrategy TRUSTING_STRATEGY = (X509Certificate[] chain, String authType) -> true;
        SSLContext sslContext = org.apache.http.ssl.SSLContexts.custom()
                .loadTrustMaterial(null, TRUSTING_STRATEGY)
                .build();

        SSLConnectionSocketFactory socketFactory = new SSLConnectionSocketFactory(sslContext);

        CloseableHttpClient httpClient = HttpClients.custom()
                .setSSLSocketFactory(socketFactory)
                .build();

        HttpComponentsClientHttpRequestFactory requestFactory = new HttpComponentsClientHttpRequestFactory();
        requestFactory.setHttpClient(httpClient);

        RestTemplate restTemplate = new RestTemplate(requestFactory);

        addUTF8Support(restTemplate);

        return restTemplate;
    }

    private void addUTF8Support(RestTemplate restTemplate) {
        restTemplate.getMessageConverters().stream().forEach(converter -> {
            if (converter instanceof StringHttpMessageConverter) {
                ((StringHttpMessageConverter)converter).setDefaultCharset(Charset.forName("UTF-8"));
            }
        });
    }
}