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 edu.umd.cs.findbugs.annotations.SuppressFBWarnings; 023import org.slf4j.LoggerFactory; 024 025/** 026 * A generic HTTP connector. It creates {@link HttpClient.Builder} and 027 * {@link HttpRequest.Builder} that can be individually customized according to the needs 028 * of the CA. 029 * 030 * @since 3.0.0 031 */ 032public class HttpConnector { 033 private static final String USER_AGENT; 034 035 private final NetworkSettings networkSettings; 036 037 static { 038 var agent = new StringBuilder("acme4j"); 039 040 try (var in = HttpConnector.class.getResourceAsStream("/org/shredzone/acme4j/version.properties")) { 041 var prop = new Properties(); 042 prop.load(in); 043 agent.append('/').append(prop.getProperty("version")); 044 } catch (Exception ex) { 045 // Ignore, just don't use a version 046 LoggerFactory.getLogger(HttpConnector.class).warn("Could not read library version", ex); 047 } 048 049 agent.append(" Java/").append(System.getProperty("java.version")); 050 USER_AGENT = agent.toString(); 051 } 052 053 /** 054 * Returns the default User-Agent to be used. 055 * 056 * @return User-Agent 057 */ 058 public static String defaultUserAgent() { 059 return USER_AGENT; 060 } 061 062 /** 063 * Creates a new {@link HttpConnector} that is using the given 064 * {@link NetworkSettings}. 065 */ 066 @SuppressFBWarnings("EI_EXPOSE_REP2") // behavior is intended 067 public HttpConnector(NetworkSettings networkSettings) { 068 this.networkSettings = networkSettings; 069 } 070 071 /** 072 * Creates a new {@link HttpRequest.Builder} that is preconfigured and bound to the 073 * given URL. Subclasses can override this method to extend the configuration, or 074 * create a different builder. 075 * 076 * @param url 077 * {@link URL} to connect to 078 * @return {@link HttpRequest.Builder} connected to the {@link URL} 079 */ 080 public HttpRequest.Builder createRequestBuilder(URL url) { 081 try { 082 return HttpRequest.newBuilder(url.toURI()) 083 .header("User-Agent", USER_AGENT) 084 .timeout(networkSettings.getTimeout()); 085 } catch (URISyntaxException ex) { 086 throw new IllegalArgumentException("Invalid URL", ex); 087 } 088 } 089 090 /** 091 * Creates a new {@link HttpClient.Builder}. 092 * <p> 093 * The {@link HttpClient.Builder} is already preconfigured with a reasonable timeout, 094 * the proxy settings, authenticator, and that it follows normal redirects. 095 * Subclasses can override this method to extend the configuration, or to create a 096 * different builder. 097 */ 098 public HttpClient.Builder createClientBuilder() { 099 var builder = HttpClient.newBuilder() 100 .followRedirects(HttpClient.Redirect.NORMAL) 101 .connectTimeout(networkSettings.getTimeout()) 102 .proxy(networkSettings.getProxySelector()); 103 104 if (networkSettings.getAuthenticator() != null) { 105 builder.authenticator(networkSettings.getAuthenticator()); 106 } 107 108 return builder; 109 } 110 111}