001/* 002 * acme4j - Java ACME client 003 * 004 * Copyright (C) 2015 Richard "Shred" Körber 005 * http://acme4j.shredzone.org 006 * 007 * Licensed under the Apache License, Version 2.0 (the "License"); 008 * you may not use this file except in compliance with the License. 009 * 010 * This program is distributed in the hope that it will be useful, 011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. 013 */ 014package org.shredzone.acme4j.connector; 015 016import java.net.URISyntaxException; 017import java.net.URL; 018import java.net.http.HttpClient; 019import java.net.http.HttpRequest; 020import java.util.Properties; 021 022import org.slf4j.LoggerFactory; 023 024/** 025 * A generic HTTP connector. It creates {@link HttpClient.Builder} and 026 * {@link HttpRequest.Builder} that can be individually customized according to the needs 027 * of the CA. 028 * 029 * @since 3.0.0 030 */ 031public class HttpConnector { 032 private static final String USER_AGENT; 033 034 private final NetworkSettings networkSettings; 035 036 static { 037 var agent = new StringBuilder("acme4j"); 038 039 try (var in = HttpConnector.class.getResourceAsStream("/org/shredzone/acme4j/version.properties")) { 040 var prop = new Properties(); 041 prop.load(in); 042 agent.append('/').append(prop.getProperty("version")); 043 } catch (Exception ex) { 044 // Ignore, just don't use a version 045 LoggerFactory.getLogger(HttpConnector.class).warn("Could not read library version", ex); 046 } 047 048 agent.append(" Java/").append(System.getProperty("java.version")); 049 USER_AGENT = agent.toString(); 050 } 051 052 /** 053 * Returns the default User-Agent to be used. 054 * 055 * @return User-Agent 056 */ 057 public static String defaultUserAgent() { 058 return USER_AGENT; 059 } 060 061 /** 062 * Creates a new {@link HttpConnector} that is using the given 063 * {@link NetworkSettings}. 064 */ 065 public HttpConnector(NetworkSettings networkSettings) { 066 this.networkSettings = networkSettings; 067 } 068 069 /** 070 * Creates a new {@link HttpRequest.Builder} that is preconfigured and bound to the 071 * given URL. Subclasses can override this method to extend the configuration, or 072 * create a different builder. 073 * 074 * @param url 075 * {@link URL} to connect to 076 * @return {@link HttpRequest.Builder} connected to the {@link URL} 077 */ 078 public HttpRequest.Builder createRequestBuilder(URL url) { 079 try { 080 return HttpRequest.newBuilder(url.toURI()) 081 .header("User-Agent", USER_AGENT) 082 .timeout(networkSettings.getTimeout()); 083 } catch (URISyntaxException ex) { 084 throw new IllegalArgumentException("Invalid URL", ex); 085 } 086 } 087 088 /** 089 * Creates a new {@link HttpClient.Builder}. 090 * <p> 091 * The {@link HttpClient.Builder} is already preconfigured with a reasonable timeout, 092 * the proxy settings, authenticator, and that it follows normal redirects. 093 * Subclasses can override this method to extend the configuration, or to create a 094 * different builder. 095 */ 096 public HttpClient.Builder createClientBuilder() { 097 var builder = HttpClient.newBuilder() 098 .followRedirects(HttpClient.Redirect.NORMAL) 099 .connectTimeout(networkSettings.getTimeout()) 100 .proxy(networkSettings.getProxySelector()); 101 102 if (networkSettings.getAuthenticator() != null) { 103 builder.authenticator(networkSettings.getAuthenticator()); 104 } 105 106 return builder; 107 } 108 109}