diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java b/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java index ee2d7fd1..27db3fed 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/AcmeResource.java @@ -14,7 +14,7 @@ package org.shredzone.acme4j; import java.io.Serializable; -import java.net.URI; +import java.net.URL; import java.util.Objects; /** @@ -24,7 +24,7 @@ public abstract class AcmeResource implements Serializable { private static final long serialVersionUID = -7930580802257379731L; private transient Session session; - private URI location; + private URL location; /** * Create a new {@link AcmeResource}. @@ -57,7 +57,7 @@ public abstract class AcmeResource implements Serializable { /** * Sets the resource's location. */ - protected void setLocation(URI location) { + protected void setLocation(URL location) { this.location = Objects.requireNonNull(location, "location"); } @@ -81,7 +81,7 @@ public abstract class AcmeResource implements Serializable { /** * Gets the resource's location. */ - public URI getLocation() { + public URL getLocation() { return location; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java index 0cd7af81..70b7d090 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Authorization.java @@ -17,7 +17,7 @@ import static java.util.stream.Collectors.toList; import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.time.Instant; import java.util.ArrayList; import java.util.Arrays; @@ -49,7 +49,7 @@ public class Authorization extends AcmeResource { private List> combinations; private boolean loaded = false; - protected Authorization(Session session, URI location) { + protected Authorization(Session session, URL location) { super(session); setLocation(location); } @@ -64,7 +64,7 @@ public class Authorization extends AcmeResource { * Location of the Authorization * @return {@link Authorization} bound to the session and location */ - public static Authorization bind(Session session, URI location) { + public static Authorization bind(Session session, URL location) { return new Authorization(session, location); } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java index d7caf6cd..e6314fae 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Certificate.java @@ -14,7 +14,8 @@ package org.shredzone.acme4j; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.MalformedURLException; +import java.net.URL; import java.security.cert.CertificateEncodingException; import java.security.cert.X509Certificate; import java.util.ArrayList; @@ -37,19 +38,19 @@ public class Certificate extends AcmeResource { private static final Logger LOG = LoggerFactory.getLogger(Certificate.class); private static final int MAX_CHAIN_LENGTH = 10; - private URI chainCertUri; + private URL chainCertUrl; private X509Certificate cert = null; private X509Certificate[] chain = null; - protected Certificate(Session session, URI certUri) { + protected Certificate(Session session, URL certUrl) { super(session); - setLocation(certUri); + setLocation(certUrl); } - protected Certificate(Session session, URI certUri, URI chainUri, X509Certificate cert) { + protected Certificate(Session session, URL certUrl, URL chainUrl, X509Certificate cert) { super(session); - setLocation(certUri); - this.chainCertUri = chainUri; + setLocation(certUrl); + this.chainCertUrl = chainUrl; this.cert = cert; } @@ -62,16 +63,16 @@ public class Certificate extends AcmeResource { * Location of the Certificate * @return {@link Certificate} bound to the session and location */ - public static Certificate bind(Session session, URI location) { + public static Certificate bind(Session session, URL location) { return new Certificate(session, location); } /** - * Returns the URI of the certificate chain. {@code null} if not known or not + * Returns the URL of the certificate chain. {@code null} if not known or not * available. */ - public URI getChainLocation() { - return chainCertUri; + public URL getChainLocation() { + return chainCertUrl; } /** @@ -92,7 +93,7 @@ public class Certificate extends AcmeResource { conn.accept(HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_ACCEPTED); conn.handleRetryAfter("certificate is not available for download yet"); - chainCertUri = conn.getLink("up"); + chainCertUrl = conn.getLink("up"); cert = conn.readCertificate(); } } @@ -111,21 +112,21 @@ public class Certificate extends AcmeResource { */ public X509Certificate[] downloadChain() throws AcmeException { if (chain == null) { - if (chainCertUri == null) { + if (chainCertUrl == null) { download(); } - if (chainCertUri == null) { + if (chainCertUrl == null) { throw new AcmeProtocolException("No certificate chain provided"); } LOG.debug("downloadChain"); List certChain = new ArrayList<>(); - URI link = chainCertUri; + URL link = chainCertUrl; while (link != null && certChain.size() < MAX_CHAIN_LENGTH) { try (Connection conn = getSession().provider().connect()) { - conn.sendRequest(chainCertUri, getSession()); + conn.sendRequest(chainCertUrl, getSession()); conn.accept(HttpURLConnection.HTTP_OK); certChain.add(conn.readCertificate()); @@ -159,8 +160,8 @@ public class Certificate extends AcmeResource { */ public void revoke(RevocationReason reason) throws AcmeException { LOG.debug("revoke"); - URI resUri = getSession().resourceUri(Resource.REVOKE_CERT); - if (resUri == null) { + URL resUrl = getSession().resourceUrl(Resource.REVOKE_CERT); + if (resUrl == null) { throw new AcmeProtocolException("CA does not support certificate revocation"); } @@ -176,7 +177,7 @@ public class Certificate extends AcmeResource { claims.put("reason", reason.getReasonCode()); } - conn.sendSignedRequest(resUri, claims, getSession()); + conn.sendSignedRequest(resUrl, claims, getSession()); conn.accept(HttpURLConnection.HTTP_OK); } catch (CertificateEncodingException ex) { throw new AcmeProtocolException("Invalid certificate", ex); @@ -199,7 +200,12 @@ public class Certificate extends AcmeResource { */ public static void revoke(Session session, X509Certificate cert, RevocationReason reason) throws AcmeException { - new Certificate(session, URI.create(""), null, cert).revoke(reason); + try { + URL dummyUrl = new URL("http://"); + new Certificate(session, dummyUrl, null, cert).revoke(reason); + } catch (MalformedURLException ex) { + throw new InternalError(ex); + } } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java index 2a6b7eea..8cf34401 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Registration.java @@ -17,6 +17,7 @@ import static org.shredzone.acme4j.toolbox.AcmeUtils.*; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Instant; @@ -56,17 +57,17 @@ public class Registration extends AcmeResource { private final List contacts = new ArrayList<>(); private URI agreement; - private URI authorizations; - private URI certificates; + private URL authorizations; + private URL certificates; private Status status; private boolean loaded = false; - protected Registration(Session session, URI location) { + protected Registration(Session session, URL location) { super(session); setLocation(location); } - protected Registration(Session session, URI location, URI agreement) { + protected Registration(Session session, URL location, URI agreement) { super(session); setLocation(location); this.agreement = agreement; @@ -78,10 +79,10 @@ public class Registration extends AcmeResource { * @param session * {@link Session} to be used * @param location - * Location URI of the registration + * Location URL of the registration * @return {@link Registration} bound to the session and location */ - public static Registration bind(Session session, URI location) { + public static Registration bind(Session session, URL location) { return new Registration(session, location); } @@ -185,7 +186,7 @@ public class Registration extends AcmeResource { .put("type", "dns") .put("value", toAce(domain)); - conn.sendSignedRequest(getSession().resourceUri(Resource.NEW_AUTHZ), claims, getSession()); + conn.sendSignedRequest(getSession().resourceUrl(Resource.NEW_AUTHZ), claims, getSession()); conn.accept(HttpURLConnection.HTTP_CREATED); JSON json = conn.readJsonResponse(); @@ -240,7 +241,7 @@ public class Registration extends AcmeResource { claims.put("notAfter", notAfter); } - conn.sendSignedRequest(getSession().resourceUri(Resource.NEW_CERT), claims, getSession()); + conn.sendSignedRequest(getSession().resourceUrl(Resource.NEW_CERT), claims, getSession()); int rc = conn.accept(HttpURLConnection.HTTP_CREATED, HttpURLConnection.HTTP_ACCEPTED); X509Certificate cert = null; @@ -252,9 +253,9 @@ public class Registration extends AcmeResource { } } - URI chainCertUri = conn.getLink("up"); + URL chainCertUrl = conn.getLink("up"); - return new Certificate(getSession(), conn.getLocation(), chainCertUri, cert); + return new Certificate(getSession(), conn.getLocation(), chainCertUrl, cert); } } @@ -277,7 +278,7 @@ public class Registration extends AcmeResource { LOG.debug("key-change"); try (Connection conn = getSession().provider().connect()) { - URI keyChangeUri = getSession().resourceUri(Resource.KEY_CHANGE); + URL keyChangeUrl = getSession().resourceUrl(Resource.KEY_CHANGE); PublicJsonWebKey newKeyJwk = PublicJsonWebKey.Factory.newPublicJwk(newKeyPair.getPublic()); JSONBuilder payloadClaim = new JSONBuilder(); @@ -286,7 +287,7 @@ public class Registration extends AcmeResource { JsonWebSignature innerJws = new JsonWebSignature(); innerJws.setPayload(payloadClaim.toString()); - innerJws.getHeaders().setObjectHeaderValue("url", keyChangeUri); + innerJws.getHeaders().setObjectHeaderValue("url", keyChangeUrl); innerJws.getHeaders().setJwkHeaderValue("jwk", newKeyJwk); innerJws.setAlgorithmHeaderValue(keyAlgorithm(newKeyJwk)); innerJws.setKey(newKeyPair.getPrivate()); @@ -298,7 +299,7 @@ public class Registration extends AcmeResource { outerClaim.put("signature", innerJws.getEncodedSignature()); outerClaim.put("payload", innerJws.getEncodedPayload()); - conn.sendSignedRequest(keyChangeUri, outerClaim, getSession()); + conn.sendSignedRequest(keyChangeUrl, outerClaim, getSession()); conn.accept(HttpURLConnection.HTTP_OK); getSession().setKeyPair(newKeyPair); @@ -361,19 +362,19 @@ public class Registration extends AcmeResource { .forEach(contacts::add); } - this.authorizations = json.get(KEY_AUTHORIZATIONS).asURI(); - this.certificates = json.get(KEY_CERTIFICATES).asURI(); + this.authorizations = json.get(KEY_AUTHORIZATIONS).asURL(); + this.certificates = json.get(KEY_CERTIFICATES).asURL(); if (json.contains(KEY_STATUS)) { this.status = Status.parse(json.get(KEY_STATUS).asString()); } - URI location = conn.getLocation(); + URL location = conn.getLocation(); if (location != null) { setLocation(location); } - URI tos = conn.getLink("terms-of-service"); + URI tos = conn.getLinkAsURI("terms-of-service"); if (tos != null) { this.agreement = tos; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java b/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java index 93dceff4..9c8390f8 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/RegistrationBuilder.java @@ -15,6 +15,7 @@ package org.shredzone.acme4j; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.List; @@ -83,11 +84,11 @@ public class RegistrationBuilder { claims.put("contact", contacts); } - conn.sendSignedRequest(session.resourceUri(Resource.NEW_REG), claims, session); + conn.sendSignedRequest(session.resourceUrl(Resource.NEW_REG), claims, session); conn.accept(HttpURLConnection.HTTP_CREATED); - URI location = conn.getLocation(); - URI tos = conn.getLink("terms-of-service"); + URL location = conn.getLocation(); + URI tos = conn.getLinkAsURI("terms-of-service"); return new Registration(session, location, tos); } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java b/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java index 2984be37..f7a8bd0c 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/Session.java @@ -14,6 +14,7 @@ package org.shredzone.acme4j; import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.time.Duration; import java.time.Instant; @@ -40,7 +41,7 @@ import org.shredzone.acme4j.toolbox.JSON; * volatile data. */ public class Session { - private final AtomicReference> resourceMap = new AtomicReference<>(); + private final AtomicReference> resourceMap = new AtomicReference<>(); private final AtomicReference metadata = new AtomicReference<>(); private final URI serverUri; private final AcmeProvider provider; @@ -175,14 +176,14 @@ public class Session { } /** - * Gets the {@link URI} of the given {@link Resource}. This may involve connecting to + * Gets the {@link URL} of the given {@link Resource}. This may involve connecting to * the server and getting a directory. The result is cached. * * @param resource - * {@link Resource} to get the {@link URI} of - * @return {@link URI}, or {@code null} if the server does not offer that resource + * {@link Resource} to get the {@link URL} of + * @return {@link URL}, or {@code null} if the server does not offer that resource */ - public URI resourceUri(Resource resource) throws AcmeException { + public URL resourceUrl(Resource resource) throws AcmeException { readDirectory(); return resourceMap.get().get(Objects.requireNonNull(resource, "resource")); } @@ -219,11 +220,11 @@ public class Session { metadata.set(new Metadata(JSON.empty())); } - Map map = new EnumMap<>(Resource.class); + Map map = new EnumMap<>(Resource.class); for (Resource res : Resource.values()) { - URI uri = directoryJson.get(res.path()).asURI(); - if (uri != null) { - map.put(res, uri); + URL url = directoryJson.get(res.path()).asURL(); + if (url != null) { + map.put(res, url); } } resourceMap.set(map); diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java index 8534174c..85150d43 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/challenge/Challenge.java @@ -14,7 +14,7 @@ package org.shredzone.acme4j.challenge; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.time.Instant; import java.util.Objects; @@ -72,7 +72,7 @@ public class Challenge extends AcmeResource { * @return {@link Challenge} bound to this session and location */ @SuppressWarnings("unchecked") - public static T bind(Session session, URI location) throws AcmeException { + public static T bind(Session session, URL location) throws AcmeException { Objects.requireNonNull(session, "session"); Objects.requireNonNull(location, "location"); @@ -105,11 +105,11 @@ public class Challenge extends AcmeResource { } /** - * Returns the location {@link URI} of the challenge. + * Returns the location {@link URL} of the challenge. */ @Override - public URI getLocation() { - return data.get(KEY_URI).asURI(); + public URL getLocation() { + return data.get(KEY_URI).asURL(); } /** diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java index c1240675..3c67f3dd 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java @@ -14,8 +14,8 @@ package org.shredzone.acme4j.connector; import java.net.URI; +import java.net.URL; import java.security.cert.X509Certificate; -import java.util.Collection; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeException; @@ -31,24 +31,24 @@ public interface Connection extends AutoCloseable { /** * Sends a simple GET request. * - * @param uri - * {@link URI} to send the request to. + * @param url + * {@link URL} to send the request to. * @param session * {@link Session} instance to be used for tracking */ - void sendRequest(URI uri, Session session) throws AcmeException; + void sendRequest(URL url, Session session) throws AcmeException; /** * Sends a signed POST request. * - * @param uri - * {@link URI} to send the request to. + * @param url + * {@link URL} to send the request to. * @param claims * {@link JSONBuilder} containing claims. Must not be {@code null}. * @param session * {@link Session} instance to be used for signing and tracking */ - void sendSignedRequest(URI uri, JSONBuilder claims, Session session) throws AcmeException; + void sendSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException; /** * Checks if the HTTP response status is in the given list of acceptable HTTP states, @@ -96,9 +96,9 @@ public interface Connection extends AutoCloseable { *

* Relative links are resolved against the last request's URL. * - * @return Location {@link URI}, or {@code null} if no Location header was set + * @return Location {@link URL}, or {@code null} if no Location header was set */ - URI getLocation(); + URL getLocation(); /** * Gets a relation link from the header. @@ -108,20 +108,21 @@ public interface Connection extends AutoCloseable { * * @param relation * Link relation - * @return Link, or {@code null} if there was no such relation link + * @return Link {@link URL}, or {@code null} if there was no such relation link */ - URI getLink(String relation); + URL getLink(String relation); /** - * Gets one or more relation link from the header. + * Gets a relation link from the header. *

- * Relative links are resolved against the last request's URL. + * Relative links are resolved against the last request's URL. If there is more than + * one relation, the first one is returned. * * @param relation * Link relation - * @return Collection of links, or {@code null} if there was no such relation link + * @return Link {@link URI}, or {@code null} if there was no such relation link */ - Collection getLinks(String relation); + URI getLinkAsURI(String relation); /** * Closes the {@link Connection}, releasing all resources. diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java index 245893d4..da3b05ec 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/DefaultConnection.java @@ -13,6 +13,7 @@ */ package org.shredzone.acme4j.connector; +import static java.util.stream.Collectors.toList; import static org.shredzone.acme4j.toolbox.AcmeUtils.keyAlgorithm; import java.io.IOException; @@ -91,15 +92,15 @@ public class DefaultConnection implements Connection { } @Override - public void sendRequest(URI uri, Session session) throws AcmeException { - Objects.requireNonNull(uri, "uri"); + public void sendRequest(URL url, Session session) throws AcmeException { + Objects.requireNonNull(url, "url"); Objects.requireNonNull(session, "session"); assertConnectionIsClosed(); - LOG.debug("GET {}", uri); + LOG.debug("GET {}", url); try { - conn = httpConnector.openConnection(uri); + conn = httpConnector.openConnection(url); conn.setRequestMethod("GET"); conn.setRequestProperty(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET); conn.setRequestProperty(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag()); @@ -114,8 +115,8 @@ public class DefaultConnection implements Connection { } @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) throws AcmeException { - Objects.requireNonNull(uri, "uri"); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException { + Objects.requireNonNull(url, "url"); Objects.requireNonNull(claims, "claims"); Objects.requireNonNull(session, "session"); assertConnectionIsClosed(); @@ -124,8 +125,8 @@ public class DefaultConnection implements Connection { KeyPair keypair = session.getKeyPair(); if (session.getNonce() == null) { - LOG.debug("Getting initial nonce, HEAD {}", uri); - conn = httpConnector.openConnection(uri); + LOG.debug("Getting initial nonce, HEAD {}", url); + conn = httpConnector.openConnection(url); conn.setRequestMethod("HEAD"); conn.setRequestProperty(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag()); conn.connect(); @@ -137,9 +138,9 @@ public class DefaultConnection implements Connection { throw new AcmeProtocolException("Server did not provide a nonce"); } - LOG.debug("POST {} with claims: {}", uri, claims); + LOG.debug("POST {} with claims: {}", url, claims); - conn = httpConnector.openConnection(uri); + conn = httpConnector.openConnection(url); conn.setRequestMethod("POST"); conn.setRequestProperty(ACCEPT_HEADER, "application/json"); conn.setRequestProperty(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET); @@ -152,7 +153,7 @@ public class DefaultConnection implements Connection { JsonWebSignature jws = new JsonWebSignature(); jws.setPayload(claims.toString()); jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce())); - jws.getHeaders().setObjectHeaderValue("url", uri); + jws.getHeaders().setObjectHeaderValue("url", url); jws.getHeaders().setJwkHeaderValue("jwk", jwk); jws.setAlgorithmHeaderValue(keyAlgorithm(jwk)); jws.setKey(keypair.getPrivate()); @@ -289,7 +290,7 @@ public class DefaultConnection implements Connection { } @Override - public URI getLocation() { + public URL getLocation() { assertConnectionIsOpen(); String location = conn.getHeaderField(LOCATION_HEADER); @@ -302,24 +303,37 @@ public class DefaultConnection implements Connection { } @Override - public URI getLink(String relation) { - Collection links = getLinks(relation); - if (links == null) { - return null; - } - - if (links.size() > 1) { - LOG.debug("Link: {} - using the first of {}", relation, links.size()); - } - - return links.iterator().next(); + public URL getLink(String relation) { + return getLinks(relation).stream() + .findFirst() + .map(this::resolveRelative) + .orElse(null); } @Override - public Collection getLinks(String relation) { + public URI getLinkAsURI(String relation) { + return getLinks(relation).stream() + .findFirst() + .map(this::resolveRelativeAsURI) + .orElse(null); + } + + @Override + public void close() { + conn = null; + } + + /** + * Returns the link headers of the given relation. The link URIs are unresolved. + * + * @param relation + * Relation name + * @return Link headers + */ + private Collection getLinks(String relation) { assertConnectionIsOpen(); - List result = new ArrayList<>(); + List result = new ArrayList<>(); List links = conn.getHeaderFields().get(LINK_HEADER); if (links != null) { @@ -329,17 +343,12 @@ public class DefaultConnection implements Connection { if (m.matches()) { String location = m.group(1); LOG.debug("Link: {} -> {}", relation, location); - result.add(resolveRelative(location)); + result.add(location); } } } - return !result.isEmpty() ? result : null; - } - - @Override - public void close() { - conn = null; + return result; } /** @@ -389,14 +398,16 @@ public class DefaultConnection implements Connection { } if ("agreementRequired".equals(error)) { - URI instance = resolveRelative(json.get("instance").asString()); - URI tos = getLink("terms-of-service"); + URI instance = resolveRelativeAsURI(json.get("instance").asString()); + URI tos = getLinkAsURI("terms-of-service"); return new AcmeAgreementRequiredException(type, detail, tos, instance); } if ("rateLimited".equals(error)) { Optional retryAfter = getRetryAfterHeader(); - Collection rateLimits = getLinks("rate-limit"); + Collection rateLimits = getLinks("rate-limit").stream() + .map(this::resolveRelativeAsURI) + .collect(toList()); return new AcmeRateLimitExceededException(type, detail, retryAfter.orElse(null), rateLimits); } @@ -437,7 +448,29 @@ public class DefaultConnection implements Connection { } /** - * Resolves a relative link against the connection's last URI. + * Resolves a relative link against the connection's last URL. + * + * @param link + * Link to resolve. Absolute links are just converted to an URL. May be + * {@code null}. + * @return Absolute URL of the given link, or {@code null} if the link was + * {@code null}. + */ + private URL resolveRelative(String link) { + if (link == null) { + return null; + } + + assertConnectionIsOpen(); + try { + return new URL(conn.getURL(), link); + } catch (MalformedURLException ex) { + throw new AcmeProtocolException("Cannot resolve relative link: " + link, ex); + } + } + + /** + * Resolves a relative link against the connection's last URL. * * @param link * Link to resolve. Absolute links are just converted to an URI. May be @@ -445,15 +478,15 @@ public class DefaultConnection implements Connection { * @return Absolute URI of the given link, or {@code null} if the link was * {@code null}. */ - private URI resolveRelative(String link) { + private URI resolveRelativeAsURI(String link) { if (link == null) { return null; } assertConnectionIsOpen(); try { - return new URL(conn.getURL(), link).toURI(); - } catch (MalformedURLException | URISyntaxException ex) { + return conn.getURL().toURI().resolve(link); + } catch (URISyntaxException ex) { throw new AcmeProtocolException("Cannot resolve relative link: " + link, ex); } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java index 5e543176..ecfe6fea 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/HttpConnector.java @@ -16,7 +16,7 @@ package org.shredzone.acme4j.connector; import java.io.IOException; import java.io.InputStream; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.util.Properties; import org.slf4j.LoggerFactory; @@ -59,14 +59,14 @@ public class HttpConnector { } /** - * Opens a {@link HttpURLConnection} to the given {@link URI}. + * Opens a {@link HttpURLConnection} to the given {@link URL}. * - * @param uri - * {@link URI} to connect to - * @return {@link HttpURLConnection} connected to the {@link URI} + * @param url + * {@link URL} to connect to + * @return {@link HttpURLConnection} connected to the {@link URL} */ - public HttpURLConnection openConnection(URI uri) throws IOException { - HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); + public HttpURLConnection openConnection(URL url) throws IOException { + HttpURLConnection conn = (HttpURLConnection) url.openConnection(); configure(conn); return conn; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java index 71f37f54..0799d3d3 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/ResourceIterator.java @@ -14,7 +14,7 @@ package org.shredzone.acme4j.connector; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.util.ArrayDeque; import java.util.Deque; import java.util.Iterator; @@ -29,7 +29,7 @@ import org.shredzone.acme4j.exception.AcmeProtocolException; import org.shredzone.acme4j.toolbox.JSON; /** - * An {@link Iterator} that fetches a batch of URIs from the ACME server, and generates + * An {@link Iterator} that fetches a batch of URLs from the ACME server, and generates * {@link AcmeResource} instances. * * @param @@ -39,10 +39,10 @@ public class ResourceIterator implements Iterator { private final Session session; private final String field; - private final Deque uriList = new ArrayDeque<>(); - private final BiFunction creator; + private final Deque urlList = new ArrayDeque<>(); + private final BiFunction creator; private boolean eol = false; - private URI nextUri; + private URL nextUrl; /** * Creates a new {@link ResourceIterator}. @@ -52,15 +52,15 @@ public class ResourceIterator implements Iterator { * @param field * Field name to be used in the JSON response * @param start - * URI of the first JSON array, may be {@code null} for an empty iterator + * URL of the first JSON array, may be {@code null} for an empty iterator * @param creator * Creator for an {@link AcmeResource} that is bound to the given - * {@link Session} and {@link URI}. + * {@link Session} and {@link URL}. */ - public ResourceIterator(Session session, String field, URI start, BiFunction creator) { + public ResourceIterator(Session session, String field, URL start, BiFunction creator) { this.session = Objects.requireNonNull(session, "session"); this.field = Objects.requireNonNull(field, "field"); - this.nextUri = start; + this.nextUrl = start; this.creator = Objects.requireNonNull(creator, "creator"); } @@ -68,7 +68,7 @@ public class ResourceIterator implements Iterator { * Checks if there is another object in the result. * * @throws AcmeProtocolException - * if the next batch of URIs could not be fetched from the server + * if the next batch of URLs could not be fetched from the server */ @Override public boolean hasNext() { @@ -76,32 +76,32 @@ public class ResourceIterator implements Iterator { return false; } - if (uriList.isEmpty()) { + if (urlList.isEmpty()) { fetch(); } - if (uriList.isEmpty()) { + if (urlList.isEmpty()) { eol = true; } - return !uriList.isEmpty(); + return !urlList.isEmpty(); } /** * Returns the next object of the result. * * @throws AcmeProtocolException - * if the next batch of URIs could not be fetched from the server + * if the next batch of URLs could not be fetched from the server * @throws NoSuchElementException * if there are no more entries */ @Override public T next() { - if (!eol && uriList.isEmpty()) { + if (!eol && urlList.isEmpty()) { fetch(); } - URI next = uriList.poll(); + URL next = urlList.poll(); if (next == null) { eol = true; throw new NoSuchElementException("no more " + field); @@ -119,11 +119,11 @@ public class ResourceIterator implements Iterator { } /** - * Fetches the next batch of URIs. Handles exceptions. Does nothing if there is no - * URI of the next batch. + * Fetches the next batch of URLs. Handles exceptions. Does nothing if there is no + * URL of the next batch. */ private void fetch() { - if (nextUri == null) { + if (nextUrl == null) { return; } @@ -135,34 +135,34 @@ public class ResourceIterator implements Iterator { } /** - * Reads the next batch of URIs from the server, and fills the queue with the URIs. If - * there is a "next" header, it is used for the next batch of URIs. + * Reads the next batch of URLs from the server, and fills the queue with the URLs. If + * there is a "next" header, it is used for the next batch of URLs. */ private void readAndQueue() throws AcmeException { try (Connection conn = session.provider().connect()) { - conn.sendRequest(nextUri, session); + conn.sendRequest(nextUrl, session); conn.accept(HttpURLConnection.HTTP_OK); JSON json = conn.readJsonResponse(); - fillUriList(json); + fillUrlList(json); - nextUri = conn.getLink("next"); + nextUrl = conn.getLink("next"); } } /** - * Fills the uri list with the URIs found in the desired field. + * Fills the url list with the URLs found in the desired field. * * @param json * JSON map to read from */ - private void fillUriList(JSON json) { + private void fillUrlList(JSON json) { JSON.Array array = json.get(field).asArray(); if (array == null) { return; } - array.stream().map(JSON.Value::asURI).forEach(uriList::add); + array.stream().map(JSON.Value::asURL).forEach(urlList::add); } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeConflictException.java b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeConflictException.java index 7926bead..b276441d 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeConflictException.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/exception/AcmeConflictException.java @@ -13,7 +13,7 @@ */ package org.shredzone.acme4j.exception; -import java.net.URI; +import java.net.URL; import java.util.Objects; /** @@ -23,7 +23,7 @@ import java.util.Objects; public class AcmeConflictException extends AcmeException { private static final long serialVersionUID = 7454201988845449591L; - private final URI location; + private final URL location; /** * Creates a new {@link AcmeConflictException}. @@ -31,9 +31,9 @@ public class AcmeConflictException extends AcmeException { * @param msg * Details about the conflicting resource * @param location - * {@link URI} of the conflicting resource + * {@link URL} of the conflicting resource */ - public AcmeConflictException(String msg, URI location) { + public AcmeConflictException(String msg, URL location) { super(msg); this.location = Objects.requireNonNull(location, "location"); } @@ -41,7 +41,7 @@ public class AcmeConflictException extends AcmeException { /** * Location of the conflicting resource. */ - public URI getLocation() { + public URL getLocation() { return location; } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java index c563ad2a..8b8be1e6 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AcmeProvider.java @@ -14,6 +14,7 @@ package org.shredzone.acme4j.provider; import java.net.URI; +import java.net.URL; import java.util.ServiceLoader; import org.shredzone.acme4j.Session; @@ -41,15 +42,15 @@ public interface AcmeProvider { boolean accepts(URI serverUri); /** - * Resolves the server URI and returns the matching directory URI. + * Resolves the server URI and returns the matching directory URL. * * @param serverUri * Server {@link URI} - * @return Resolved directory {@link URI} + * @return Resolved directory {@link URL} * @throws IllegalArgumentException * if the server {@link URI} is not accepted */ - URI resolve(URI serverUri); + URL resolve(URI serverUri); /** * Creates a {@link Connection} for communication with the ACME server. diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java index 0ce2990d..5ab59c73 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/GenericAcmeProvider.java @@ -13,7 +13,9 @@ */ package org.shredzone.acme4j.provider; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URL; /** * A generic {@link AcmeProvider}. It should be working for all ACME servers complying to @@ -30,8 +32,12 @@ public class GenericAcmeProvider extends AbstractAcmeProvider { } @Override - public URI resolve(URI serverUri) { - return serverUri; + public URL resolve(URI serverUri) { + try { + return serverUri.toURL(); + } catch (MalformedURLException ex) { + throw new IllegalArgumentException("Invalid server URI", ex); + } } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java index d2dee002..e75f7746 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProvider.java @@ -13,8 +13,9 @@ */ package org.shredzone.acme4j.provider.letsencrypt; +import java.net.MalformedURLException; import java.net.URI; -import java.net.URISyntaxException; +import java.net.URL; import org.shredzone.acme4j.connector.HttpConnector; import org.shredzone.acme4j.exception.AcmeProtocolException; @@ -37,8 +38,8 @@ public class LetsEncryptAcmeProvider extends AbstractAcmeProvider { private static final Logger LOG = LoggerFactory.getLogger(LetsEncryptAcmeProvider.class); - private static final String V01_DIRECTORY_URI = "https://acme-v01.api.letsencrypt.org/directory"; - private static final String STAGING_DIRECTORY_URI = "https://acme-staging.api.letsencrypt.org/directory"; + private static final String V01_DIRECTORY_URL = "https://acme-v01.api.letsencrypt.org/directory"; + private static final String STAGING_DIRECTORY_URL = "https://acme-staging.api.letsencrypt.org/directory"; @Override public boolean accepts(URI serverUri) { @@ -47,21 +48,21 @@ public class LetsEncryptAcmeProvider extends AbstractAcmeProvider { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { String path = serverUri.getPath(); - String directoryUri; + String directoryUrl; if (path == null || "".equals(path) || "/".equals(path) || "/v01".equals(path)) { - directoryUri = V01_DIRECTORY_URI; + directoryUrl = V01_DIRECTORY_URL; } else if ("/staging".equals(path)) { - directoryUri = STAGING_DIRECTORY_URI; + directoryUrl = STAGING_DIRECTORY_URL; } else { throw new IllegalArgumentException("Unknown URI " + serverUri); } try { - return new URI(directoryUri); - } catch (URISyntaxException ex) { - throw new AcmeProtocolException(directoryUri, ex); + return new URL(directoryUrl); + } catch (MalformedURLException ex) { + throw new AcmeProtocolException(directoryUrl, ex); } } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java index 1f719561..72085727 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnector.java @@ -15,7 +15,7 @@ package org.shredzone.acme4j.provider.letsencrypt; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.security.KeyManagementException; import java.security.KeyStore; import java.security.KeyStoreException; @@ -42,8 +42,8 @@ public class LetsEncryptHttpConnector extends HttpConnector { private static SSLSocketFactory sslSocketFactory; @Override - public HttpURLConnection openConnection(URI uri) throws IOException { - HttpURLConnection conn = super.openConnection(uri); + public HttpURLConnection openConnection(URL url) throws IOException { + HttpURLConnection conn = super.openConnection(url); if (conn instanceof HttpsURLConnection) { ((HttpsURLConnection) conn).setSSLSocketFactory(createSocketFactory()); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java index 27c32987..16497629 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AcmeResourceTest.java @@ -20,7 +20,7 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.ObjectInputStream; import java.io.ObjectOutputStream; -import java.net.URI; +import java.net.URL; import org.junit.Test; import org.shredzone.acme4j.toolbox.TestUtils; @@ -36,7 +36,7 @@ public class AcmeResourceTest { @Test public void testConstructor() throws Exception { Session session = TestUtils.session(); - URI location = new URI("http://example.com/acme/resource"); + URL location = new URL("http://example.com/acme/resource"); try { new DummyResource(null); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java index 182b7597..9dcbcdfa 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/AuthorizationTest.java @@ -20,7 +20,7 @@ import static org.shredzone.acme4j.toolbox.TestUtils.*; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.time.Duration; import java.time.Instant; import java.util.Collection; @@ -44,7 +44,7 @@ public class AuthorizationTest { private static final String SNAILMAIL_TYPE = "snail-01"; // a non-existent challenge - private URI locationUri = URI.create("http://example.com/acme/registration");; + private URL locationUrl = url("http://example.com/acme/registration"); /** * Test that {@link Authorization#findChallenge(String)} does only find standalone @@ -121,8 +121,8 @@ public class AuthorizationTest { public void testUpdate() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -150,13 +150,13 @@ public class AuthorizationTest { provider.putTestChallenge("http-01", httpChallenge); provider.putTestChallenge("dns-01", dnsChallenge); - Authorization auth = new Authorization(session, locationUri); + Authorization auth = new Authorization(session, locationUrl); auth.update(); assertThat(auth.getDomain(), is("example.org")); assertThat(auth.getStatus(), is(Status.VALID)); assertThat(auth.getExpires(), is(parseTimestamp("2016-01-02T17:12:40Z"))); - assertThat(auth.getLocation(), is(locationUri)); + assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getChallenges(), containsInAnyOrder( (Challenge) httpChallenge, (Challenge) dnsChallenge)); @@ -179,9 +179,9 @@ public class AuthorizationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { + public void sendRequest(URL url, Session session) { requestWasSent.set(true); - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); } @Override @@ -207,7 +207,7 @@ public class AuthorizationTest { provider.putTestChallenge("http-01", new Http01Challenge(session)); provider.putTestChallenge("dns-01", new Dns01Challenge(session)); - Authorization auth = new Authorization(session, locationUri); + Authorization auth = new Authorization(session, locationUrl); // Lazy loading assertThat(requestWasSent.get(), is(false)); @@ -233,8 +233,8 @@ public class AuthorizationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -262,7 +262,7 @@ public class AuthorizationTest { provider.putTestChallenge("http-01", httpChallenge); provider.putTestChallenge("dns-01", dnsChallenge); - Authorization auth = new Authorization(session, locationUri); + Authorization auth = new Authorization(session, locationUrl); try { auth.update(); @@ -274,7 +274,7 @@ public class AuthorizationTest { assertThat(auth.getDomain(), is("example.org")); assertThat(auth.getStatus(), is(Status.VALID)); assertThat(auth.getExpires(), is(parseTimestamp("2016-01-02T17:12:40Z"))); - assertThat(auth.getLocation(), is(locationUri)); + assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getChallenges(), containsInAnyOrder( (Challenge) httpChallenge, (Challenge) dnsChallenge)); @@ -295,11 +295,11 @@ public class AuthorizationTest { public void testDeactivate() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { JSON json = claims.toJSON(); assertThat(json.get("resource").asString(), is("authz")); assertThat(json.get("status").asString(), is("deactivated")); - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); assertThat(session, is(notNullValue())); } @@ -311,7 +311,7 @@ public class AuthorizationTest { } }; - Authorization auth = new Authorization(provider.createSession(), locationUri); + Authorization auth = new Authorization(provider.createSession(), locationUrl); auth.deactivate(); provider.close(); @@ -328,7 +328,7 @@ public class AuthorizationTest { provider.putTestChallenge(Dns01Challenge.TYPE, new Dns01Challenge(session)); provider.putTestChallenge(TlsSni02Challenge.TYPE, new TlsSni02Challenge(session)); - Authorization authorization = new Authorization(session, locationUri); + Authorization authorization = new Authorization(session, locationUrl); authorization.unmarshalAuthorization(getJsonAsObject("authorizationChallenges")); return authorization; } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java index 7d391f1f..92954836 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/CertificateTest.java @@ -20,7 +20,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Duration; @@ -39,9 +39,9 @@ import org.shredzone.acme4j.toolbox.TestUtils; */ public class CertificateTest { - private URI resourceUri = URI.create("http://example.com/acme/resource"); - private URI locationUri = URI.create("http://example.com/acme/certificate"); - private URI chainUri = URI.create("http://example.com/acme/chain"); + private URL resourceUrl = url("http://example.com/acme/resource"); + private URL locationUrl = url("http://example.com/acme/certificate"); + private URL chainUrl = url("http://example.com/acme/chain"); /** * Test that a certificate can be downloaded. @@ -51,17 +51,17 @@ public class CertificateTest { final X509Certificate originalCert = TestUtils.createCertificate(); TestableConnectionProvider provider = new TestableConnectionProvider() { - private boolean isLocationUri; + private boolean isLocationUrl; @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, isOneOf(locationUri, chainUri)); - isLocationUri = uri.equals(locationUri); + public void sendRequest(URL url, Session session) { + assertThat(url, isOneOf(locationUrl, chainUrl)); + isLocationUrl = url.equals(locationUrl); } @Override public int accept(int... httpStatus) throws AcmeException { - if (isLocationUri) { + if (isLocationUrl) { // The leaf certificate, might be asynchronous assertThat(httpStatus, isIntArrayContainingInAnyOrder( HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_ACCEPTED)); @@ -84,18 +84,18 @@ public class CertificateTest { } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { switch(relation) { - case "up": return (isLocationUri ? chainUri : null); + case "up": return (isLocationUrl ? chainUrl : null); default: return null; } } }; - Certificate cert = new Certificate(provider.createSession(), locationUri); + Certificate cert = new Certificate(provider.createSession(), locationUrl); X509Certificate downloadedCert = cert.download(); assertThat(downloadedCert, is(sameInstance(originalCert))); - assertThat(cert.getChainLocation(), is(chainUri)); + assertThat(cert.getChainLocation(), is(chainUrl)); X509Certificate[] downloadedChain = cert.downloadChain(); assertThat(downloadedChain.length, is(1)); @@ -113,8 +113,8 @@ public class CertificateTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -131,7 +131,7 @@ public class CertificateTest { } }; - Certificate cert = new Certificate(provider.createSession(), locationUri); + Certificate cert = new Certificate(provider.createSession(), locationUrl); try { cert.download(); @@ -152,8 +152,8 @@ public class CertificateTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateRequest"))); assertThat(session, is(notNullValue())); } @@ -165,9 +165,9 @@ public class CertificateTest { } }; - provider.putTestResource(Resource.REVOKE_CERT, resourceUri); + provider.putTestResource(Resource.REVOKE_CERT, resourceUrl); - Certificate cert = new Certificate(provider.createSession(), locationUri, null, originalCert); + Certificate cert = new Certificate(provider.createSession(), locationUrl, null, originalCert); cert.revoke(); provider.close(); @@ -182,8 +182,8 @@ public class CertificateTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest"))); assertThat(session, is(notNullValue())); } @@ -195,9 +195,9 @@ public class CertificateTest { } }; - provider.putTestResource(Resource.REVOKE_CERT, resourceUri); + provider.putTestResource(Resource.REVOKE_CERT, resourceUrl); - Certificate cert = new Certificate(provider.createSession(), locationUri, null, originalCert); + Certificate cert = new Certificate(provider.createSession(), locationUrl, null, originalCert); cert.revoke(RevocationReason.KEY_COMPROMISE); provider.close(); @@ -213,8 +213,8 @@ public class CertificateTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest"))); assertThat(session, is(notNullValue())); assertThat(session.getKeyPair(), is(certKeyPair)); @@ -227,7 +227,7 @@ public class CertificateTest { } }; - provider.putTestResource(Resource.REVOKE_CERT, resourceUri); + provider.putTestResource(Resource.REVOKE_CERT, resourceUrl); Session session = provider.createSession(); session.setKeyPair(certKeyPair); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java index e60fbb0c..10a66719 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationBuilderTest.java @@ -20,6 +20,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import org.junit.Test; import org.shredzone.acme4j.connector.Resource; @@ -32,8 +33,8 @@ import org.shredzone.acme4j.toolbox.JSONBuilder; */ public class RegistrationBuilderTest { - private URI resourceUri = URI.create("http://example.com/acme/resource");; - private URI locationUri = URI.create("http://example.com/acme/registration");; + private URL resourceUrl = url("http://example.com/acme/resource"); + private URL locationUrl = url("http://example.com/acme/registration"); private URI agreementUri = URI.create("http://example.com/agreement.pdf");; /** @@ -43,8 +44,8 @@ public class RegistrationBuilderTest { public void testRegistration() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("newRegistration"))); assertThat(session, is(notNullValue())); } @@ -56,12 +57,12 @@ public class RegistrationBuilderTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URI getLinkAsURI(String relation) { switch(relation) { case "terms-of-service": return agreementUri; default: return null; @@ -69,14 +70,14 @@ public class RegistrationBuilderTest { } }; - provider.putTestResource(Resource.NEW_REG, resourceUri); + provider.putTestResource(Resource.NEW_REG, resourceUrl); RegistrationBuilder builder = new RegistrationBuilder(); builder.addContact("mailto:foo@example.com"); Registration registration = builder.create(provider.createSession()); - assertThat(registration.getLocation(), is(locationUri)); + assertThat(registration.getLocation(), is(locationUrl)); assertThat(registration.getAgreement(), is(agreementUri)); provider.close(); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java index 5663218c..06366fe3 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/RegistrationTest.java @@ -21,6 +21,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.IOException; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.security.cert.X509Certificate; import java.time.Instant; @@ -51,10 +52,10 @@ import org.shredzone.acme4j.toolbox.TestUtils; */ public class RegistrationTest { - private URI resourceUri = URI.create("http://example.com/acme/resource"); - private URI locationUri = URI.create("http://example.com/acme/registration"); + private URL resourceUrl = url("http://example.com/acme/resource"); + private URL locationUrl = url("http://example.com/acme/registration"); + private URL chainUrl = url("http://example.com/acme/chain"); private URI agreementUri = URI.create("http://example.com/agreement.pdf"); - private URI chainUri = URI.create("http://example.com/acme/chain"); /** * Test that a registration can be updated. @@ -66,8 +67,8 @@ public class RegistrationTest { private Integer response; @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(locationUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(locationUrl)); assertThat(claims.toString(), sameJSONAs(getJson("updateRegistration"))); assertThat(session, is(notNullValue())); jsonResponse = getJsonAsObject("updateRegistrationResponse"); @@ -75,8 +76,8 @@ public class RegistrationTest { } @Override - public void sendRequest(URI uri, Session session) { - if (URI.create("https://example.com/acme/reg/1/authz").equals(uri)) { + public void sendRequest(URL url, Session session) { + if (url("https://example.com/acme/reg/1/authz").equals(url)) { jsonResponse = new JSONBuilder() .array("authorizations", "https://example.com/acme/auth/1") .toJSON(); @@ -84,7 +85,7 @@ public class RegistrationTest { return; } - if (URI.create("https://example.com/acme/reg/1/cert").equals(uri)) { + if (url("https://example.com/acme/reg/1/cert").equals(url)) { jsonResponse = new JSONBuilder() .array("certificates", "https://example.com/acme/cert/1") .toJSON(); @@ -107,24 +108,28 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { + return null; + } + + @Override + public URI getLinkAsURI(String relation) { switch(relation) { case "terms-of-service": return agreementUri; - case "next": return null; default: return null; } } }; - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); registration.update(); - assertThat(registration.getLocation(), is(locationUri)); + assertThat(registration.getLocation(), is(locationUrl)); assertThat(registration.getAgreement(), is(agreementUri)); assertThat(registration.getContacts(), hasSize(1)); assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com"))); @@ -132,14 +137,12 @@ public class RegistrationTest { Iterator authIt = registration.getAuthorizations(); assertThat(authIt, not(nullValue())); - assertThat(authIt.next().getLocation(), - is(URI.create("https://example.com/acme/auth/1"))); + assertThat(authIt.next().getLocation(), is(url("https://example.com/acme/auth/1"))); assertThat(authIt.hasNext(), is(false)); Iterator certIt = registration.getCertificates(); assertThat(certIt, not(nullValue())); - assertThat(certIt.next().getLocation(), - is(URI.create("https://example.com/acme/cert/1"))); + assertThat(certIt.next().getLocation(), is(url("https://example.com/acme/cert/1"))); assertThat(certIt.hasNext(), is(false)); provider.close(); @@ -154,9 +157,9 @@ public class RegistrationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { requestWasSent.set(true); - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); } @Override @@ -172,12 +175,12 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URI getLinkAsURI(String relation) { switch(relation) { case "terms-of-service": return agreementUri; default: return null; @@ -185,7 +188,7 @@ public class RegistrationTest { } }; - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); // Lazy loading assertThat(requestWasSent.get(), is(false)); @@ -208,8 +211,8 @@ public class RegistrationTest { public void testAuthorizeDomain() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("newAuthorizationRequest"))); assertThat(session, is(notNullValue())); } @@ -226,8 +229,8 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } }; @@ -236,19 +239,19 @@ public class RegistrationTest { Http01Challenge httpChallenge = new Http01Challenge(session); Dns01Challenge dnsChallenge = new Dns01Challenge(session); - provider.putTestResource(Resource.NEW_AUTHZ, resourceUri); + provider.putTestResource(Resource.NEW_AUTHZ, resourceUrl); provider.putTestChallenge(Http01Challenge.TYPE, httpChallenge); provider.putTestChallenge(Dns01Challenge.TYPE, dnsChallenge); String domainName = "example.org"; - Registration registration = new Registration(session, locationUri); + Registration registration = new Registration(session, locationUrl); Authorization auth = registration.authorizeDomain(domainName); assertThat(auth.getDomain(), is(domainName)); assertThat(auth.getStatus(), is(Status.PENDING)); assertThat(auth.getExpires(), is(nullValue())); - assertThat(auth.getLocation(), is(locationUri)); + assertThat(auth.getLocation(), is(locationUrl)); assertThat(auth.getChallenges(), containsInAnyOrder( (Challenge) httpChallenge, (Challenge) dnsChallenge)); @@ -269,7 +272,7 @@ public class RegistrationTest { public void testAuthorizeBadDomain() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider(); Session session = provider.createSession(); - Registration registration = Registration.bind(session, locationUri); + Registration registration = Registration.bind(session, locationUrl); try { registration.authorizeDomain(null); @@ -297,13 +300,13 @@ public class RegistrationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { + public void sendRequest(URL url, Session session) { fail("Attempted to download the certificate. Should be downloaded already!"); } @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequestWithDate"))); assertThat(session, is(notNullValue())); } @@ -321,32 +324,32 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { switch(relation) { - case "up": return chainUri; + case "up": return chainUrl; default: return null; } } }; - provider.putTestResource(Resource.NEW_CERT, resourceUri); + provider.putTestResource(Resource.NEW_CERT, resourceUrl); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); ZoneId utc = ZoneId.of("UTC"); Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant(); Instant notAfter = LocalDate.of(2016, 1, 8).atStartOfDay(utc).toInstant(); - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); Certificate cert = registration.requestCertificate(csr, notBefore, notAfter); assertThat(cert.download(), is(originalCert)); - assertThat(cert.getLocation(), is(locationUri)); - assertThat(cert.getChainLocation(), is(chainUri)); + assertThat(cert.getLocation(), is(locationUrl)); + assertThat(cert.getChainLocation(), is(chainUrl)); provider.close(); } @@ -358,8 +361,8 @@ public class RegistrationTest { public void testRequestCertificateAsync() throws AcmeException, IOException { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequest"))); assertThat(session, is(notNullValue())); } @@ -372,28 +375,28 @@ public class RegistrationTest { } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { switch(relation) { - case "up": return chainUri; + case "up": return chainUrl; default: return null; } } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } }; - provider.putTestResource(Resource.NEW_CERT, resourceUri); + provider.putTestResource(Resource.NEW_CERT, resourceUrl); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); Certificate cert = registration.requestCertificate(csr); - assertThat(cert.getLocation(), is(locationUri)); - assertThat(cert.getChainLocation(), is(chainUri)); + assertThat(cert.getLocation(), is(locationUrl)); + assertThat(cert.getChainLocation(), is(chainUrl)); provider.close(); } @@ -406,8 +409,8 @@ public class RegistrationTest { public void testRequestCertificateBrokenSync() throws AcmeException, IOException { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequestWithDate"))); assertThat(session, is(notNullValue())); } @@ -425,31 +428,31 @@ public class RegistrationTest { } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { switch(relation) { - case "up": return chainUri; + case "up": return chainUrl; default: return null; } } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } }; - provider.putTestResource(Resource.NEW_CERT, resourceUri); + provider.putTestResource(Resource.NEW_CERT, resourceUrl); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); ZoneId utc = ZoneId.of("UTC"); Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant(); Instant notAfter = LocalDate.of(2016, 1, 8).atStartOfDay(utc).toInstant(); - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); Certificate cert = registration.requestCertificate(csr, notBefore, notAfter); - assertThat(cert.getLocation(), is(locationUri)); - assertThat(cert.getChainLocation(), is(chainUri)); + assertThat(cert.getLocation(), is(locationUrl)); + assertThat(cert.getChainLocation(), is(chainUrl)); provider.close(); } @@ -464,9 +467,9 @@ public class RegistrationTest { final TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder payload, Session session) { + public void sendSignedRequest(URL url, JSONBuilder payload, Session session) { try { - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); assertThat(session, is(notNullValue())); assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair))); @@ -487,7 +490,7 @@ public class RegistrationTest { StringBuilder expectedPayload = new StringBuilder(); expectedPayload.append('{'); - expectedPayload.append("\"account\":\"").append(resourceUri).append("\","); + expectedPayload.append("\"account\":\"").append(resourceUrl).append("\","); expectedPayload.append("\"newKey\":{"); expectedPayload.append("\"kty\":\"").append(TestUtils.D_KTY).append("\","); expectedPayload.append("\"e\":\"").append(TestUtils.D_E).append("\","); @@ -506,12 +509,12 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return resourceUri; + public URL getLocation() { + return resourceUrl; } }; - provider.putTestResource(Resource.KEY_CHANGE, locationUri); + provider.putTestResource(Resource.KEY_CHANGE, locationUrl); Session session = new Session(new URI(TestUtils.ACME_SERVER_URI), oldKeyPair) { @Override @@ -522,7 +525,7 @@ public class RegistrationTest { assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair))); - Registration registration = new Registration(session, resourceUri); + Registration registration = new Registration(session, resourceUrl); registration.changeKey(newKeyPair); assertThat(session.getKeyPair(), is(sameInstance(newKeyPair))); @@ -536,7 +539,7 @@ public class RegistrationTest { TestableConnectionProvider provider = new TestableConnectionProvider(); Session session = provider.createSession(); - Registration registration = new Registration(session, locationUri); + Registration registration = new Registration(session, locationUrl); registration.changeKey(session.getKeyPair()); provider.close(); @@ -549,11 +552,11 @@ public class RegistrationTest { public void testDeactivate() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { JSON json = claims.toJSON(); assertThat(json.get("resource").asString(), is("reg")); assertThat(json.get("status").asString(), is("deactivated")); - assertThat(uri, is(locationUri)); + assertThat(url, is(locationUrl)); assertThat(session, is(notNullValue())); } @@ -565,7 +568,7 @@ public class RegistrationTest { } }; - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); registration.deactivate(); provider.close(); @@ -580,8 +583,8 @@ public class RegistrationTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(locationUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(locationUrl)); assertThat(claims.toString(), sameJSONAs(getJson("modifyRegistration"))); assertThat(session, is(notNullValue())); } @@ -598,12 +601,12 @@ public class RegistrationTest { } @Override - public URI getLocation() { - return locationUri; + public URL getLocation() { + return locationUrl; } @Override - public URI getLink(String relation) { + public URI getLinkAsURI(String relation) { switch(relation) { case "terms-of-service": return agreementUri; default: return null; @@ -611,7 +614,7 @@ public class RegistrationTest { } }; - Registration registration = new Registration(provider.createSession(), locationUri); + Registration registration = new Registration(provider.createSession(), locationUrl); EditableRegistration editable = registration.modify(); assertThat(editable, notNullValue()); @@ -621,7 +624,7 @@ public class RegistrationTest { editable.getContacts().add(URI.create("mailto:foo3@example.com")); editable.commit(); - assertThat(registration.getLocation(), is(locationUri)); + assertThat(registration.getLocation(), is(locationUrl)); assertThat(registration.getAgreement(), is(agreementUri)); assertThat(registration.getContacts().size(), is(2)); assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com"))); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java index 02591044..5b16793c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/SessionTest.java @@ -16,7 +16,7 @@ package org.shredzone.acme4j; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; -import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import java.io.IOException; import java.net.URI; @@ -203,13 +203,13 @@ public class SessionTest { }; }; - assertThat(session.resourceUri(Resource.NEW_REG), - is(URI.create("https://example.com/acme/new-reg"))); - assertThat(session.resourceUri(Resource.NEW_AUTHZ), - is(URI.create("https://example.com/acme/new-authz"))); - assertThat(session.resourceUri(Resource.NEW_CERT), - is(URI.create("https://example.com/acme/new-cert"))); - assertThat(session.resourceUri(Resource.REVOKE_CERT), + assertThat(session.resourceUrl(Resource.NEW_REG), + is(url("https://example.com/acme/new-reg"))); + assertThat(session.resourceUrl(Resource.NEW_AUTHZ), + is(url("https://example.com/acme/new-authz"))); + assertThat(session.resourceUrl(Resource.NEW_CERT), + is(url("https://example.com/acme/new-cert"))); + assertThat(session.resourceUrl(Resource.REVOKE_CERT), is(nullValue())); Metadata meta = session.getMetadata(); @@ -227,13 +227,13 @@ public class SessionTest { * {@link Session} to assert */ private void assertSession(Session session) throws AcmeException { - assertThat(session.resourceUri(Resource.NEW_REG), - is(URI.create("https://example.com/acme/new-reg"))); - assertThat(session.resourceUri(Resource.NEW_AUTHZ), - is(URI.create("https://example.com/acme/new-authz"))); - assertThat(session.resourceUri(Resource.NEW_CERT), - is(URI.create("https://example.com/acme/new-cert"))); - assertThat(session.resourceUri(Resource.REVOKE_CERT), + assertThat(session.resourceUrl(Resource.NEW_REG), + is(url("https://example.com/acme/new-reg"))); + assertThat(session.resourceUrl(Resource.NEW_AUTHZ), + is(url("https://example.com/acme/new-authz"))); + assertThat(session.resourceUrl(Resource.NEW_CERT), + is(url("https://example.com/acme/new-cert"))); + assertThat(session.resourceUrl(Resource.REVOKE_CERT), is(nullValue())); Metadata meta = session.getMetadata(); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java index 5c6ec946..bdcd9c9a 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/challenge/ChallengeTest.java @@ -46,8 +46,8 @@ import org.shredzone.acme4j.toolbox.TestUtils; */ public class ChallengeTest { private Session session; - private URI resourceUri = URI.create("https://example.com/acme/some-resource"); - private URI locationUri = URI.create("https://example.com/acme/some-location"); + private URL resourceUrl = url("https://example.com/acme/some-resource"); + private URL locationUrl = url("https://example.com/acme/some-location"); @Before public void setup() throws IOException { @@ -61,8 +61,8 @@ public class ChallengeTest { public void testChallenge() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -82,11 +82,11 @@ public class ChallengeTest { provider.putTestChallenge(Http01Challenge.TYPE, new Http01Challenge(session)); - Http01Challenge challenge = Challenge.bind(session, locationUri); + Http01Challenge challenge = Challenge.bind(session, locationUrl); assertThat(challenge.getType(), is(Http01Challenge.TYPE)); assertThat(challenge.getStatus(), is(Status.VALID)); - assertThat(challenge.getLocation(), is(locationUri)); + assertThat(challenge.getLocation(), is(locationUrl)); assertThat(challenge.getToken(), is("IlirfxKKXAsHtmzK29Pj8A")); provider.close(); @@ -111,7 +111,7 @@ public class ChallengeTest { // Test unmarshalled values assertThat(challenge.getType(), is("generic-01")); assertThat(challenge.getStatus(), is(Status.VALID)); - assertThat(challenge.getLocation(), is(new URI("http://example.com/challenge/123"))); + assertThat(challenge.getLocation(), is(url("http://example.com/challenge/123"))); assertThat(challenge.getValidated(), is(parseTimestamp("2015-12-12T17:19:36.336785823Z"))); assertThat(challenge.getError(), is(notNullValue())); assertThat(challenge.getError().getType(), is(URI.create("urn:ietf:params:acme:error:connection"))); @@ -154,8 +154,8 @@ public class ChallengeTest { public void testTrigger() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { - assertThat(uri, is(resourceUri)); + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { + assertThat(url, is(resourceUrl)); assertThat(claims.toString(), sameJSONAs(getJson("triggerHttpChallengeRequest"))); assertThat(session, is(notNullValue())); } @@ -181,7 +181,7 @@ public class ChallengeTest { challenge.trigger(); assertThat(challenge.getStatus(), is(Status.PENDING)); - assertThat(challenge.getLocation(), is(locationUri)); + assertThat(challenge.getLocation(), is(locationUrl)); provider.close(); } @@ -193,8 +193,8 @@ public class ChallengeTest { public void testUpdate() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -223,7 +223,7 @@ public class ChallengeTest { challenge.update(); assertThat(challenge.getStatus(), is(Status.VALID)); - assertThat(challenge.getLocation(), is(locationUri)); + assertThat(challenge.getLocation(), is(locationUrl)); provider.close(); } @@ -237,8 +237,8 @@ public class ChallengeTest { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -273,7 +273,7 @@ public class ChallengeTest { } assertThat(challenge.getStatus(), is(Status.VALID)); - assertThat(challenge.getLocation(), is(locationUri)); + assertThat(challenge.getLocation(), is(locationUrl)); provider.close(); } @@ -291,7 +291,7 @@ public class ChallengeTest { } try { - Challenge.bind(null, locationUri); + Challenge.bind(null, locationUrl); fail("session accepts null"); } catch (NullPointerException ex) { // expected @@ -305,8 +305,8 @@ public class ChallengeTest { public void testBadBind() throws Exception { TestableConnectionProvider provider = new TestableConnectionProvider() { @Override - public void sendRequest(URI uri, Session session) { - assertThat(uri, is(locationUri)); + public void sendRequest(URL url, Session session) { + assertThat(url, is(locationUrl)); } @Override @@ -323,7 +323,7 @@ public class ChallengeTest { }; Session session = provider.createSession(); - Challenge.bind(session, locationUri); + Challenge.bind(session, locationUrl); provider.close(); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java index 13ba9c3a..2f62140d 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DefaultConnectionTest.java @@ -16,6 +16,7 @@ package org.shredzone.acme4j.connector; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.Mockito.*; +import static org.shredzone.acme4j.toolbox.TestUtils.url; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.io.ByteArrayInputStream; @@ -29,7 +30,6 @@ import java.time.Duration; import java.time.Instant; import java.time.temporal.ChronoUnit; import java.util.Arrays; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -56,7 +56,7 @@ import org.shredzone.acme4j.toolbox.TestUtils; */ public class DefaultConnectionTest { - private URI requestUri = URI.create("http://example.com/acme/");; + private URL requestUrl = url("http://example.com/acme/"); private HttpURLConnection mockUrlConnection; private HttpConnector mockHttpConnection; private Session session; @@ -66,7 +66,7 @@ public class DefaultConnectionTest { mockUrlConnection = mock(HttpURLConnection.class); mockHttpConnection = mock(HttpConnector.class); - when(mockHttpConnection.openConnection(requestUri)).thenReturn(mockUrlConnection); + when(mockHttpConnection.openConnection(requestUrl)).thenReturn(mockUrlConnection); session = TestUtils.session(); session.setLocale(Locale.JAPAN); @@ -144,8 +144,8 @@ public class DefaultConnectionTest { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { conn.conn = mockUrlConnection; - URI location = conn.getLocation(); - assertThat(location, is(new URI("https://example.com/otherlocation"))); + URL location = conn.getLocation(); + assertThat(location, is(url("https://example.com/otherlocation"))); } verify(mockUrlConnection).getHeaderField("Location"); @@ -163,8 +163,8 @@ public class DefaultConnectionTest { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { conn.conn = mockUrlConnection; - URI location = conn.getLocation(); - assertThat(location, is(new URI("https://example.org/otherlocation"))); + URL location = conn.getLocation(); + assertThat(location, is(url("https://example.org/otherlocation"))); } verify(mockUrlConnection).getHeaderField("Location"); @@ -191,48 +191,15 @@ public class DefaultConnectionTest { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { conn.conn = mockUrlConnection; - assertThat(conn.getLink("next"), is(new URI("https://example.com/acme/new-authz"))); - assertThat(conn.getLink("recover"), is(new URI("https://example.org/recover-reg"))); - assertThat(conn.getLink("terms-of-service"), is(new URI("https://example.com/acme/terms"))); + assertThat(conn.getLink("next"), is(url("https://example.com/acme/new-authz"))); + assertThat(conn.getLink("recover"), is(url("https://example.org/recover-reg"))); + assertThat(conn.getLink("terms-of-service"), is(url("https://example.com/acme/terms"))); assertThat(conn.getLink("secret-stuff"), is(nullValue())); - } - } - /** - * Test that multiple link headers are evaluated. - */ - @Test - public void testGetMultiLink() { - Map> headers = new HashMap<>(); - headers.put("Link", Arrays.asList( - "; rel=\"terms-of-service\"", - "; rel=\"terms-of-service\"", - "; rel=\"terms-of-service\"" - )); - - when(mockUrlConnection.getHeaderFields()).thenReturn(headers); - - try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { - conn.conn = mockUrlConnection; - assertThat(conn.getLinks("terms-of-service"), containsInAnyOrder( - URI.create("https://example.com/acme/terms1"), - URI.create("https://example.com/acme/terms2"), - URI.create("https://example.com/acme/terms3") - )); - } - } - - /** - * Test that no link headers are properly handled. - */ - @Test - public void testGetNoLink() { - Map> headers = Collections.emptyMap(); - when(mockUrlConnection.getHeaderFields()).thenReturn(headers); - - try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { - conn.conn = mockUrlConnection; - assertThat(conn.getLinks("something"), is(nullValue())); + assertThat(conn.getLinkAsURI("next"), is(new URI("https://example.com/acme/new-authz"))); + assertThat(conn.getLinkAsURI("recover"), is(new URI("https://example.org/recover-reg"))); + assertThat(conn.getLinkAsURI("terms-of-service"), is(new URI("https://example.com/acme/terms"))); + assertThat(conn.getLinkAsURI("secret-stuff"), is(nullValue())); } } @@ -243,7 +210,7 @@ public class DefaultConnectionTest { public void testNoLocation() throws Exception { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { conn.conn = mockUrlConnection; - URI location = conn.getLocation(); + URL location = conn.getLocation(); assertThat(location, is(nullValue())); } @@ -488,7 +455,7 @@ public class DefaultConnectionTest { @Test public void testSendRequest() throws Exception { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { - conn.sendRequest(requestUri, session); + conn.sendRequest(requestUrl, session); } verify(mockUrlConnection).setRequestMethod("GET"); @@ -526,7 +493,7 @@ public class DefaultConnectionTest { }) { JSONBuilder cb = new JSONBuilder(); cb.put("foo", 123).put("bar", "a-string"); - conn.sendSignedRequest(requestUri, cb, DefaultConnectionTest.this.session); + conn.sendSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session); } verify(mockUrlConnection).setRequestMethod("HEAD"); @@ -551,7 +518,7 @@ public class DefaultConnectionTest { StringBuilder expectedHeader = new StringBuilder(); expectedHeader.append('{'); expectedHeader.append("\"nonce\":\"").append(Base64Url.encode(nonce1)).append("\","); - expectedHeader.append("\"url\":\"").append(requestUri).append("\","); + expectedHeader.append("\"url\":\"").append(requestUrl).append("\","); expectedHeader.append("\"alg\":\"RS256\","); expectedHeader.append("\"jwk\":{"); expectedHeader.append("\"kty\":\"").append(TestUtils.KTY).append("\","); @@ -576,7 +543,7 @@ public class DefaultConnectionTest { public void testSendSignedRequestNoNonce() throws Exception { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { JSONBuilder cb = new JSONBuilder(); - conn.sendSignedRequest(requestUri, cb, DefaultConnectionTest.this.session); + conn.sendSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session); } } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java index e58574d7..23a811bf 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/DummyConnection.java @@ -14,8 +14,8 @@ package org.shredzone.acme4j.connector; import java.net.URI; +import java.net.URL; import java.security.cert.X509Certificate; -import java.util.Collection; import org.shredzone.acme4j.Session; import org.shredzone.acme4j.exception.AcmeException; @@ -29,12 +29,12 @@ import org.shredzone.acme4j.toolbox.JSONBuilder; public class DummyConnection implements Connection { @Override - public void sendRequest(URI uri, Session session) { + public void sendRequest(URL url, Session session) { throw new UnsupportedOperationException(); } @Override - public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { + public void sendSignedRequest(URL url, JSONBuilder claims, Session session) { throw new UnsupportedOperationException(); } @@ -64,17 +64,17 @@ public class DummyConnection implements Connection { } @Override - public URI getLocation() { + public URL getLocation() { throw new UnsupportedOperationException(); } @Override - public URI getLink(String relation) { + public URL getLink(String relation) { throw new UnsupportedOperationException(); } @Override - public Collection getLinks(String relation) { + public URI getLinkAsURI(String relation) { throw new UnsupportedOperationException(); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java index 990a8fdc..1b372730 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/HttpConnectorTest.java @@ -17,10 +17,10 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.Mockito.*; +import static org.shredzone.acme4j.toolbox.TestUtils.url; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import org.junit.Test; @@ -59,7 +59,7 @@ public class HttpConnectorTest { @Category(HttpURLConnection.class) public void testOpenConnection() throws IOException, URISyntaxException { HttpConnector connector = new HttpConnector(); - HttpURLConnection conn = connector.openConnection(new URI("http://example.com")); + HttpURLConnection conn = connector.openConnection(url("http://example.com")); assertThat(conn, not(nullValue())); conn.connect(); assertThat(conn.getResponseCode(), is(HttpURLConnection.HTTP_OK)); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java index 1bb6059f..afb50fe6 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ResourceIteratorTest.java @@ -15,11 +15,11 @@ package org.shredzone.acme4j.connector; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; -import static org.shredzone.acme4j.toolbox.TestUtils.isIntArrayContainingInAnyOrder; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; +import java.net.URL; import java.util.ArrayList; import java.util.Iterator; import java.util.List; @@ -43,19 +43,19 @@ public class ResourceIteratorTest { private final int RESOURCES_PER_PAGE = 5; private final String TYPE = "authorizations"; - private List resourceURIs = new ArrayList<>(PAGES * RESOURCES_PER_PAGE); - private List pageURIs = new ArrayList<>(PAGES); + private List resourceURLs = new ArrayList<>(PAGES * RESOURCES_PER_PAGE); + private List pageURLs = new ArrayList<>(PAGES); @Before public void setup() { - resourceURIs.clear(); + resourceURLs.clear(); for (int ix = 0; ix < RESOURCES_PER_PAGE * PAGES; ix++) { - resourceURIs.add(URI.create("https://example.com/acme/auth/" + ix)); + resourceURLs.add(url("https://example.com/acme/auth/" + ix)); } - pageURIs.clear(); + pageURLs.clear(); for (int ix = 0; ix < PAGES; ix++) { - pageURIs.add(URI.create("https://example.com/acme/batch/" + ix)); + pageURLs.add(url("https://example.com/acme/batch/" + ix)); } } @@ -76,14 +76,14 @@ public class ResourceIteratorTest { */ @Test public void iteratorTest() throws IOException { - List result = new ArrayList<>(); + List result = new ArrayList<>(); - Iterator it = createIterator(pageURIs.get(0)); + Iterator it = createIterator(pageURLs.get(0)); while (it.hasNext()) { result.add(it.next().getLocation()); } - assertThat(result, is(equalTo(resourceURIs))); + assertThat(result, is(equalTo(resourceURLs))); } /** @@ -91,9 +91,9 @@ public class ResourceIteratorTest { */ @Test public void nextHasNextTest() throws IOException { - List result = new ArrayList<>(); + List result = new ArrayList<>(); - Iterator it = createIterator(pageURIs.get(0)); + Iterator it = createIterator(pageURLs.get(0)); assertThat(it.hasNext(), is(true)); assertThat(it.hasNext(), is(true)); @@ -107,7 +107,7 @@ public class ResourceIteratorTest { assertThat(it.hasNext(), is(false)); } - assertThat(result, is(equalTo(resourceURIs))); + assertThat(result, is(equalTo(resourceURLs))); } /** @@ -115,7 +115,7 @@ public class ResourceIteratorTest { */ @Test(expected = UnsupportedOperationException.class) public void removeTest() throws IOException { - Iterator it = createIterator(pageURIs.get(0)); + Iterator it = createIterator(pageURLs.get(0)); it.next(); it.remove(); // throws UnsupportedOperationException } @@ -127,13 +127,13 @@ public class ResourceIteratorTest { * URI of the first page * @return Created {@link Iterator} */ - private Iterator createIterator(URI first) throws IOException { + private Iterator createIterator(URL first) throws IOException { TestableConnectionProvider provider = new TestableConnectionProvider() { private int ix; @Override - public void sendRequest(URI uri, Session session) { - ix = pageURIs.indexOf(uri); + public void sendRequest(URL url, Session session) { + ix = pageURLs.indexOf(url); assertThat(ix, is(greaterThanOrEqualTo(0))); } @@ -149,15 +149,15 @@ public class ResourceIteratorTest { int end = (ix + 1) * RESOURCES_PER_PAGE; JSONBuilder cb = new JSONBuilder(); - cb.array(TYPE, resourceURIs.subList(start, end).toArray()); + cb.array(TYPE, resourceURLs.subList(start, end).toArray()); return JSON.parse(cb.toString()); } @Override - public URI getLink(String relation) { - if ("next".equals(relation) && (ix + 1 < pageURIs.size())) { - return pageURIs.get(ix + 1); + public URL getLink(String relation) { + if ("next".equals(relation) && (ix + 1 < pageURLs.size())) { + return pageURLs.get(ix + 1); } return null; } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java index 694af22d..0c6e8004 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/connector/SessionProviderTest.java @@ -18,6 +18,7 @@ import static org.junit.Assert.assertThat; import java.io.IOException; import java.net.URI; +import java.net.URL; import java.security.KeyPair; import java.util.ServiceLoader; @@ -92,7 +93,7 @@ public class SessionProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } @@ -120,7 +121,7 @@ public class SessionProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeConflictExceptionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeConflictExceptionTest.java index dd8ec815..4e1aef7b 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeConflictExceptionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/exception/AcmeConflictExceptionTest.java @@ -15,8 +15,9 @@ package org.shredzone.acme4j.exception; import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; +import static org.shredzone.acme4j.toolbox.TestUtils.url; -import java.net.URI; +import java.net.URL; import org.junit.Test; @@ -28,13 +29,13 @@ public class AcmeConflictExceptionTest { @Test public void testAcmeConflictException() { String msg = "Account already exists"; - URI locationUri = URI.create("http://example.com/location/123"); + URL locationUrl = url("http://example.com/location/123"); AcmeConflictException ex - = new AcmeConflictException(msg, locationUri); + = new AcmeConflictException(msg, locationUrl); assertThat(ex.getMessage(), is(msg)); - assertThat(ex.getLocation(), is(locationUri)); + assertThat(ex.getLocation(), is(locationUrl)); } } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java index b233d4c1..444f8111 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/AbstractAcmeProviderTest.java @@ -17,11 +17,12 @@ import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.*; -import static org.shredzone.acme4j.toolbox.TestUtils.getJsonAsObject; +import static org.shredzone.acme4j.toolbox.TestUtils.*; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import java.net.HttpURLConnection; import java.net.URI; +import java.net.URL; import java.util.concurrent.atomic.AtomicBoolean; import org.junit.Test; @@ -56,7 +57,7 @@ public class AbstractAcmeProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } @@ -79,7 +80,7 @@ public class AbstractAcmeProviderTest { @Test public void testResources() throws Exception { final URI testServerUri = new URI("http://example.com/acme"); - final URI testResolvedUri = new URI("http://example.com/acme/directory"); + final URL testResolvedUrl = url("http://example.com/acme/directory"); final Connection connection = mock(Connection.class); final Session session = mock(Session.class); @@ -99,16 +100,16 @@ public class AbstractAcmeProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { assertThat(serverUri, is(testServerUri)); - return testResolvedUri; + return testResolvedUrl; } }; JSON map = provider.directory(session, testServerUri); assertThat(map.toString(), sameJSONAs(TestUtils.getJson("directory"))); - verify(connection).sendRequest(testResolvedUri, session); + verify(connection).sendRequest(testResolvedUrl, session); verify(connection).accept(any(Integer.class)); verify(connection).updateSession(any(Session.class)); verify(connection).readJsonResponse(); @@ -131,7 +132,7 @@ public class AbstractAcmeProviderTest { } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } }; diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java index 257b1e58..b91f700d 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/GenericAcmeProviderTest.java @@ -16,8 +16,10 @@ package org.shredzone.acme4j.provider; import static org.hamcrest.Matchers.*; import static org.junit.Assert.assertThat; +import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; +import java.net.URL; import org.junit.Test; @@ -42,13 +44,13 @@ public class GenericAcmeProviderTest { * Test if the provider resolves the URI correctly. */ @Test - public void testResolve() throws URISyntaxException { + public void testResolve() throws URISyntaxException, MalformedURLException { URI serverUri = new URI("http://example.com/acme"); GenericAcmeProvider provider = new GenericAcmeProvider(); - URI resolvedUri = provider.resolve(serverUri); - assertThat(resolvedUri, is(equalTo(serverUri))); + URL resolvedUrl = provider.resolve(serverUri); + assertThat(resolvedUrl, is(equalTo(serverUri.toURL()))); } } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java index f0d7566b..b80f2117 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/TestableConnectionProvider.java @@ -15,6 +15,7 @@ package org.shredzone.acme4j.provider; import java.io.IOException; import java.net.URI; +import java.net.URL; import java.util.HashMap; import java.util.Map; @@ -42,9 +43,9 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP * @param r * {@link Resource} to be mapped * @param u - * {@link URI} to be returned + * {@link URL} to be returned */ - public void putTestResource(Resource r, URI u) { + public void putTestResource(Resource r, URL u) { directory.put(r.path(), u); } @@ -75,7 +76,7 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP } @Override - public URI resolve(URI serverUri) { + public URL resolve(URI serverUri) { throw new UnsupportedOperationException(); } diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java index 3454b5d2..2bff358c 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptAcmeProviderTest.java @@ -15,6 +15,7 @@ package org.shredzone.acme4j.provider.letsencrypt; import static org.hamcrest.Matchers.is; import static org.junit.Assert.*; +import static org.shredzone.acme4j.toolbox.TestUtils.url; import java.net.URI; import java.net.URISyntaxException; @@ -52,10 +53,10 @@ public class LetsEncryptAcmeProviderTest { public void testResolve() throws URISyntaxException { LetsEncryptAcmeProvider provider = new LetsEncryptAcmeProvider(); - assertThat(provider.resolve(new URI("acme://letsencrypt.org")), is(new URI(V01_DIRECTORY_URI))); - assertThat(provider.resolve(new URI("acme://letsencrypt.org/")), is(new URI(V01_DIRECTORY_URI))); - assertThat(provider.resolve(new URI("acme://letsencrypt.org/v01")), is(new URI(V01_DIRECTORY_URI))); - assertThat(provider.resolve(new URI("acme://letsencrypt.org/staging")), is(new URI(STAGING_DIRECTORY_URI))); + assertThat(provider.resolve(new URI("acme://letsencrypt.org")), is(url(V01_DIRECTORY_URI))); + assertThat(provider.resolve(new URI("acme://letsencrypt.org/")), is(url(V01_DIRECTORY_URI))); + assertThat(provider.resolve(new URI("acme://letsencrypt.org/v01")), is(url(V01_DIRECTORY_URI))); + assertThat(provider.resolve(new URI("acme://letsencrypt.org/staging")), is(url(STAGING_DIRECTORY_URI))); try { provider.resolve(new URI("acme://letsencrypt.org/v99")); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java index 09a17288..2adbebaf 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/provider/letsencrypt/LetsEncryptHttpConnectorTest.java @@ -15,10 +15,10 @@ package org.shredzone.acme4j.provider.letsencrypt; import static org.hamcrest.Matchers.*; import static org.junit.Assert.*; +import static org.shredzone.acme4j.toolbox.TestUtils.url; import java.io.IOException; import java.net.HttpURLConnection; -import java.net.URI; import java.net.URISyntaxException; import javax.net.ssl.HttpsURLConnection; @@ -46,7 +46,7 @@ public class LetsEncryptHttpConnectorTest { try { HttpURLConnection goodConn = connector.openConnection( - new URI("https://acme-staging.api.letsencrypt.org/directory")); + url("https://acme-staging.api.letsencrypt.org/directory")); assertThat(goodConn, is(instanceOf(HttpsURLConnection.class))); goodConn.connect(); } catch (SSLHandshakeException ex) { @@ -55,7 +55,7 @@ public class LetsEncryptHttpConnectorTest { try { HttpURLConnection badConn = connector.openConnection( - new URI("https://www.google.com")); + url("https://www.google.com")); assertThat(badConn, is(instanceOf(HttpsURLConnection.class))); badConn.connect(); fail("Connection accepts foreign certificate"); diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java index 560a600c..e1e00ad3 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/toolbox/TestUtils.java @@ -17,7 +17,9 @@ import java.io.ByteArrayOutputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; +import java.net.MalformedURLException; import java.net.URI; +import java.net.URL; import java.security.InvalidAlgorithmParameterException; import java.security.KeyFactory; import java.security.KeyPair; @@ -112,6 +114,22 @@ public final class TestUtils { return JSON.parse(getJson(key)); } + /** + * Creates an {@link URL} from a String. Only throws a runtime exception if the URL is + * malformed. + * + * @param url + * URL to use + * @return {@link URL} object + */ + public static URL url(String url) { + try { + return new URL(url); + } catch (MalformedURLException ex) { + throw new IllegalArgumentException(url, ex); + } + } + /** * Creates a {@link Session} instance. It uses {@link #ACME_SERVER_URI} as server URI. */ diff --git a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java index a812b9b0..3aa56192 100644 --- a/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java +++ b/acme4j-example/src/main/java/org/shredzone/acme4j/ClientTest.java @@ -110,7 +110,7 @@ public class ClientTest { Certificate certificate = reg.requestCertificate(csrb.getEncoded()); LOG.info("Success! The certificate for domains " + domains + " has been generated!"); - LOG.info("Certificate URI: " + certificate.getLocation()); + LOG.info("Certificate URL: " + certificate.getLocation()); // Download the leaf certificate and certificate chain. X509Certificate cert = certificate.download(); @@ -151,9 +151,9 @@ public class ClientTest { * created. *

* This is a simple way of finding your {@link Registration}. A better way is to get - * the URI of your new registration with {@link Registration#getLocation()} and store + * the URL of your new registration with {@link Registration#getLocation()} and store * it somewhere. If you need to get access to your account later, reconnect to it via - * {@link Registration#bind(Session, URI)} by using the stored location. + * {@link Registration#bind(Session, URL)} by using the stored location. * * @param session * {@link Session} to bind with @@ -165,7 +165,7 @@ public class ClientTest { try { // Try to create a new Registration. reg = new RegistrationBuilder().create(session); - LOG.info("Registered a new user, URI: " + reg.getLocation()); + LOG.info("Registered a new user, URL: " + reg.getLocation()); // This is a new account. Let the user accept the Terms of Service. // We won't be able to authorize domains until the ToS is accepted. @@ -177,7 +177,7 @@ public class ClientTest { // The Key Pair is already registered. getLocation() contains the // URL of the existing registration's location. Bind it to the session. reg = Registration.bind(session, ex.getLocation()); - LOG.info("Account does already exist, URI: " + reg.getLocation(), ex); + LOG.info("Account does already exist, URL: " + reg.getLocation(), ex); } return reg; diff --git a/src/site/markdown/migration.md b/src/site/markdown/migration.md index 5e0ed548..5ee6ef8d 100644 --- a/src/site/markdown/migration.md +++ b/src/site/markdown/migration.md @@ -2,6 +2,10 @@ This document will help you migrate your code to the latest _acme4j_ version. +## Migration to Version 0.14 + +All resource locations are now `URL` objects (previously they were `URI`s). This should affect your source only in a minimum way, if any. + ## Migration to Version 0.13 In the `acme4j-client` module, the `org.shredzone.acme4j.util` package has been renamed in order to fix a split package in Java 9. This package is meant to be internal, so this change should not break your code. The same named public package in the `acme4j-utils` module is unchanged. diff --git a/src/site/markdown/provider.md b/src/site/markdown/provider.md index b813d21d..b3a2c673 100644 --- a/src/site/markdown/provider.md +++ b/src/site/markdown/provider.md @@ -21,11 +21,11 @@ However, it is also possible to adapt the behavior of wide parts of _acme4j_ to A client provider implements the [`AcmeProvider`](./apidocs/org/shredzone/acme4j/provider/AcmeProvider.html) interface, but usually it is easier to extend [`AbstractAcmeProvider`](./apidocs/org/shredzone/acme4j/provider/AbstractAcmeProvider.html) and implement only these two methods: * `accepts(URI)` checks if the client provider is accepting the provided URI. Usually it would be an URI like `acme://example.com`. Note that the `http` and `https` schemes are reserved for the generic provider and cannot be used by other providers. -* `resolve(URI)` parses that URI and returns the corresponding URI of the directory service. +* `resolve(URI)` parses that URI and returns the corresponding URL of the directory service. The `AcmeProvider` implementation needs to be registered with Java's `ServiceLoader`. In the `META-INF/services` path of your project, create a file `org.shredzone.acme4j.provider.AcmeProvider` and write the fully qualified class name of your implementation into that file. -When _acme4j_ tries to connect to an acme URI, it first invokes the `accepts(URI)` method of all registered `AcmeProvider`s. Only one of the providers must return `true` for a successful connection. _acme4j_ then invokes the `resolve(URI)` method of that provider, and connects to the directory URI that is returned. +When _acme4j_ tries to connect to an acme URI, it first invokes the `accepts(URI)` method of all registered `AcmeProvider`s. Only one of the providers must return `true` for a successful connection. _acme4j_ then invokes the `resolve(URI)` method of that provider, and connects to the directory URL that is returned. The connection fails if none or more than one `AcmeProvider` implementations `accept` the acme URI. @@ -43,6 +43,6 @@ In your `AcmeProvider` implementation, override the `createChallenge(Session, St ## No directory service -An ACME server may not provide a directory service, for example when fixed URIs are to be used. +An ACME server may not provide a directory service, for example when fixed URLs are to be used. -In this case, override the `resources(Session, URI)` method, and return a `Map` of all available resources and their respective URI. +In this case, override the `resources(Session, URI)` method, and return a `Map` of all available resources and their respective URL. diff --git a/src/site/markdown/usage/authorization.md b/src/site/markdown/usage/authorization.md index da5f9c3a..ca0995e8 100644 --- a/src/site/markdown/usage/authorization.md +++ b/src/site/markdown/usage/authorization.md @@ -56,12 +56,12 @@ If your final certificate will contain further domains or subdomains, repeat the ## Update an Authorization -The server also provides an authorization URI. It can be retrieved from `Authorization.getLocation()`. You can recreate the `Authorization` object at a later time just by binding it to your `Session`: +The server also provides an authorization URL. It can be retrieved from `Authorization.getLocation()`. You can recreate the `Authorization` object at a later time just by binding it to your `Session`: ```java -URI authUri = ... // Authorization URI +URL authUrl = ... // Authorization URL -Authorization auth = Authorization.bind(session, authUri); +Authorization auth = Authorization.bind(session, authUrl); ``` As soon as you invoke a getter, the `Authorization` object lazily loads the current server state of your authorization, including the domain name, the overall status, and an expiry date. @@ -84,14 +84,14 @@ To recreate a `Challenge` object at a later time, all you need is to store the o ```java Challenge originalChallenge = ... // some Challenge instance -URI challengeUri = originalChallenge.getLocation(); +URL challengeUrl = originalChallenge.getLocation(); ``` Later, you restore the `Challenge` object by invoking `Challenge.bind()`. ```java -URI challengeUri = ... // challenge URI -Challenge restoredChallenge = Challenge.bind(session, challengeUri); +URL challengeUrl = ... // challenge URL +Challenge restoredChallenge = Challenge.bind(session, challengeUrl); ``` The `restoredChallenge` already reflects the current state of the challenge. diff --git a/src/site/markdown/usage/certificate.md b/src/site/markdown/usage/certificate.md index 7fff510a..c1c87183 100644 --- a/src/site/markdown/usage/certificate.md +++ b/src/site/markdown/usage/certificate.md @@ -34,7 +34,7 @@ Now all you need to do is to pass in a binary representation of the CSR and requ Certificate cert = registration.requestCertificate(csr); ``` -`cert.getLocation()` returns an URI where the signed certificate can be downloaded from. Optionally (if delivered by the ACME server) `cert.getChainLocation()` returns the URI of the first part of the CA chain. +`cert.getLocation()` returns an URL where the signed certificate can be downloaded from. Optionally (if delivered by the ACME server) `cert.getChainLocation()` returns the URL of the first part of the CA chain. The `Certificate` object offers methods to download the certificate and the certificate chain. @@ -50,8 +50,8 @@ Congratulations! You have just created your first certificate via _acme4j_. To recreate a `Certificate` object from the location, just bind it: ```java -URI locationUri = ... // location URI from cert.getLocation() -Certificate cert = Certificate.bind(session, locationUri); +URL locationUrl = ... // location URL from cert.getLocation() +Certificate cert = Certificate.bind(session, locationUrl); ``` ### Saving Certificates diff --git a/src/site/markdown/usage/register.md b/src/site/markdown/usage/register.md index 3f3ba1dd..5ea3ca54 100644 --- a/src/site/markdown/usage/register.md +++ b/src/site/markdown/usage/register.md @@ -2,7 +2,7 @@ If it is the first time you connect to the ACME server, you need to register your account key. -To do so, create a `RegistrationBuilder`, optionally add some contact information, then invoke `create()`. If the account was successfully created, you will get a `Registration` object in return. Invoking its `getLocation()` method will return the location URI of your account. You should store it somewhere, because you will need it later. Unlike your key pair, the location is a public information that does not need security precautions. +To do so, create a `RegistrationBuilder`, optionally add some contact information, then invoke `create()`. If the account was successfully created, you will get a `Registration` object in return. Invoking its `getLocation()` method will return the location URL of your account. You should store it somewhere, because you will need it later. Unlike your key pair, the location is a public information that does not need security precautions. ```java RegistrationBuilder builder = new RegistrationBuilder(); @@ -10,10 +10,10 @@ builder.addContact("mailto:acme@example.com"); Registration registration = builder.create(session); -URI accountLocationUri = registration.getLocation(); +URL accountLocationUrl = registration.getLocation(); ``` -`create()` will fail and throw an `AcmeConflictException` if your key was already registered with the CA. The `AcmeConflictException` contains the location of the registration. This may be helpful if you forgot your account URI and need to recover it. +`create()` will fail and throw an `AcmeConflictException` if your key was already registered with the CA. The `AcmeConflictException` contains the location of the registration. This may be helpful if you forgot your account URL and need to recover it. The following example will create a new `Registration` and restore an existing `Registration`. diff --git a/src/site/markdown/usage/session.md b/src/site/markdown/usage/session.md index db415b35..39cce1b7 100644 --- a/src/site/markdown/usage/session.md +++ b/src/site/markdown/usage/session.md @@ -31,12 +31,12 @@ Instead of a generic provider, this call uses a special _Let's Encrypt_ provider Now that you have a `Session` object, you can use it to bind ACME resource objects. For example, this is the way to get a `Registration` object to an existing registration: ```java -URI accountLocationUri = ... // your account's URI, as returned by Registration.getLocation() +URL accountLocationUrl = ... // your account's URL, as returned by Registration.getLocation() -Registration registration = Registration.bind(session, accountLocationUri); +Registration registration = Registration.bind(session, accountLocationUrl); ``` -You can create any of the resource objects `Registration`, `Authorization`, `Challenge` and `Certificate` like that, as long as you know the corresponding resource URI. To get the resource URI, use the `getLocation()` method. +You can create any of the resource objects `Registration`, `Authorization`, `Challenge` and `Certificate` like that, as long as you know the corresponding resource URL. To get the resource URL, use the `getLocation()` method. ## Serialization