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.provider; 015 016import java.net.HttpURLConnection; 017import java.net.URI; 018import java.util.Collections; 019import java.util.HashMap; 020import java.util.Map; 021import java.util.Objects; 022import java.util.function.Function; 023 024import org.shredzone.acme4j.Session; 025import org.shredzone.acme4j.challenge.Challenge; 026import org.shredzone.acme4j.challenge.Dns01Challenge; 027import org.shredzone.acme4j.challenge.Http01Challenge; 028import org.shredzone.acme4j.connector.Connection; 029import org.shredzone.acme4j.connector.DefaultConnection; 030import org.shredzone.acme4j.connector.HttpConnector; 031import org.shredzone.acme4j.exception.AcmeException; 032import org.shredzone.acme4j.toolbox.JSON; 033 034/** 035 * Abstract implementation of {@link AcmeProvider}. It consists of a challenge 036 * registry and a standard {@link HttpConnector}. 037 * <p> 038 * Implementing classes must implement at least {@link AcmeProvider#accepts(URI)} 039 * and {@link AbstractAcmeProvider#resolve(URI)}. 040 */ 041public abstract class AbstractAcmeProvider implements AcmeProvider { 042 043 private static final Map<String, Function<Session, Challenge>> CHALLENGES = challengeMap(); 044 045 @Override 046 public Connection connect() { 047 return new DefaultConnection(createHttpConnector()); 048 } 049 050 @Override 051 public JSON directory(Session session, URI serverUri) throws AcmeException { 052 try (Connection conn = connect()) { 053 conn.sendRequest(resolve(serverUri), session); 054 conn.accept(HttpURLConnection.HTTP_OK); 055 056 // use nonce header if there is one, saves a HEAD request... 057 conn.updateSession(session); 058 059 return conn.readJsonResponse(); 060 } 061 } 062 063 private static Map<String, Function<Session, Challenge>> challengeMap() { 064 Map<String, Function<Session, Challenge>> map = new HashMap<>(); 065 066 map.put(Dns01Challenge.TYPE, Dns01Challenge::new); 067 map.put(Http01Challenge.TYPE, Http01Challenge::new); 068 069 return Collections.unmodifiableMap(map); 070 } 071 072 @Override 073 public Challenge createChallenge(Session session, String type) { 074 Objects.requireNonNull(session, "session"); 075 Objects.requireNonNull(type, "type"); 076 077 Function<Session, Challenge> constructor = CHALLENGES.get(type); 078 if (constructor == null) { 079 return null; 080 } 081 082 return constructor.apply(session); 083 } 084 085 /** 086 * Creates a {@link HttpConnector}. 087 * <p> 088 * Subclasses may override this method to configure the {@link HttpConnector}. 089 */ 090 protected HttpConnector createHttpConnector() { 091 return new HttpConnector(); 092 } 093 094}