All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.shredzone.acme4j.provider.AbstractAcmeProvider Maven / Gradle / Ivy

The newest version!
/*
 * acme4j - Java ACME client
 *
 * Copyright (C) 2015 Richard "Shred" Körber
 *   http://acme4j.shredzone.org
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 */
package org.shredzone.acme4j.provider;

import java.net.URI;
import java.time.ZonedDateTime;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.ServiceLoader;

import org.shredzone.acme4j.Login;
import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.challenge.Challenge;
import org.shredzone.acme4j.challenge.Dns01Challenge;
import org.shredzone.acme4j.challenge.Http01Challenge;
import org.shredzone.acme4j.challenge.TlsAlpn01Challenge;
import org.shredzone.acme4j.challenge.TokenChallenge;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.DefaultConnection;
import org.shredzone.acme4j.connector.HttpConnector;
import org.shredzone.acme4j.connector.NetworkSettings;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.toolbox.JSON;

/**
 * Abstract implementation of {@link AcmeProvider}. It consists of a challenge
 * registry and a standard {@link HttpConnector}.
 * 

* Implementing classes must implement at least {@link AcmeProvider#accepts(URI)} * and {@link AbstractAcmeProvider#resolve(URI)}. */ public abstract class AbstractAcmeProvider implements AcmeProvider { private static final int HTTP_NOT_MODIFIED = 304; private static final Map CHALLENGES = challengeMap(); @Override public Connection connect(URI serverUri, NetworkSettings networkSettings) { return new DefaultConnection(createHttpConnector(networkSettings)); } @Override public JSON directory(Session session, URI serverUri) throws AcmeException { var expires = session.getDirectoryExpires(); if (expires != null && expires.isAfter(ZonedDateTime.now())) { // The cached directory is still valid return null; } try (var conn = connect(serverUri, session.networkSettings())) { var lastModified = session.getDirectoryLastModified(); var rc = conn.sendRequest(resolve(serverUri), session, lastModified); if (lastModified != null && rc == HTTP_NOT_MODIFIED) { // The server has not been modified since return null; } // evaluate caching headers session.setDirectoryLastModified(conn.getLastModified().orElse(null)); session.setDirectoryExpires(conn.getExpiration().orElse(null)); // use nonce header if there is one, saves a HEAD request... conn.getNonce().ifPresent(session::setNonce); return conn.readJsonResponse(); } } private static Map challengeMap() { var map = new HashMap(); map.put(Dns01Challenge.TYPE, Dns01Challenge::new); map.put(Http01Challenge.TYPE, Http01Challenge::new); map.put(TlsAlpn01Challenge.TYPE, TlsAlpn01Challenge::new); for (var provider : ServiceLoader.load(ChallengeProvider.class)) { var typeAnno = provider.getClass().getAnnotation(ChallengeType.class); if (typeAnno == null) { throw new IllegalStateException("ChallengeProvider " + provider.getClass().getName() + " has no @ChallengeType annotation"); } var type = typeAnno.value(); if (type == null || type.trim().isEmpty()) { throw new IllegalStateException("ChallengeProvider " + provider.getClass().getName() + ": type must not be null or empty"); } if (map.containsKey(type)) { throw new IllegalStateException("ChallengeProvider " + provider.getClass().getName() + ": there is already a provider for challenge type " + type); } map.put(type, provider); } return Collections.unmodifiableMap(map); } /** * {@inheritDoc} *

* This implementation handles the standard challenge types. For unknown types, * generic {@link Challenge} or {@link TokenChallenge} instances are created. *

* Custom provider implementations may override this method to provide challenges that * are proprietary to the provider. */ @Override public Challenge createChallenge(Login login, JSON data) { Objects.requireNonNull(login, "login"); Objects.requireNonNull(data, "data"); var type = data.get("type").asString(); var constructor = CHALLENGES.get(type); if (constructor != null) { return constructor.create(login, data); } if (data.contains("token")) { return new TokenChallenge(login, data); } else { return new Challenge(login, data); } } /** * Creates a {@link HttpConnector}. *

* Subclasses may override this method to configure the {@link HttpConnector}. */ protected HttpConnector createHttpConnector(NetworkSettings settings) { return new HttpConnector(settings); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy