Use URL for locations

Fixes issue #40
pull/45/head
Richard Körber 2017-11-04 12:49:49 +01:00
parent 1289e2f5e8
commit 04a40c83e0
42 changed files with 526 additions and 475 deletions

View File

@ -14,7 +14,7 @@
package org.shredzone.acme4j; package org.shredzone.acme4j;
import java.io.Serializable; import java.io.Serializable;
import java.net.URI; import java.net.URL;
import java.util.Objects; import java.util.Objects;
/** /**
@ -24,7 +24,7 @@ public abstract class AcmeResource implements Serializable {
private static final long serialVersionUID = -7930580802257379731L; private static final long serialVersionUID = -7930580802257379731L;
private transient Session session; private transient Session session;
private URI location; private URL location;
/** /**
* Create a new {@link AcmeResource}. * Create a new {@link AcmeResource}.
@ -57,7 +57,7 @@ public abstract class AcmeResource implements Serializable {
/** /**
* Sets the resource's location. * Sets the resource's location.
*/ */
protected void setLocation(URI location) { protected void setLocation(URL location) {
this.location = Objects.requireNonNull(location, "location"); this.location = Objects.requireNonNull(location, "location");
} }
@ -81,7 +81,7 @@ public abstract class AcmeResource implements Serializable {
/** /**
* Gets the resource's location. * Gets the resource's location.
*/ */
public URI getLocation() { public URL getLocation() {
return location; return location;
} }

View File

@ -17,7 +17,7 @@ import static java.util.stream.Collectors.toList;
import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp; import static org.shredzone.acme4j.toolbox.AcmeUtils.parseTimestamp;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URL;
import java.time.Instant; import java.time.Instant;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
@ -49,7 +49,7 @@ public class Authorization extends AcmeResource {
private List<List<Challenge>> combinations; private List<List<Challenge>> combinations;
private boolean loaded = false; private boolean loaded = false;
protected Authorization(Session session, URI location) { protected Authorization(Session session, URL location) {
super(session); super(session);
setLocation(location); setLocation(location);
} }
@ -64,7 +64,7 @@ public class Authorization extends AcmeResource {
* Location of the Authorization * Location of the Authorization
* @return {@link Authorization} bound to the session and location * @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); return new Authorization(session, location);
} }

View File

@ -14,7 +14,8 @@
package org.shredzone.acme4j; package org.shredzone.acme4j;
import java.net.HttpURLConnection; 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.CertificateEncodingException;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.ArrayList; 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 Logger LOG = LoggerFactory.getLogger(Certificate.class);
private static final int MAX_CHAIN_LENGTH = 10; private static final int MAX_CHAIN_LENGTH = 10;
private URI chainCertUri; private URL chainCertUrl;
private X509Certificate cert = null; private X509Certificate cert = null;
private X509Certificate[] chain = null; private X509Certificate[] chain = null;
protected Certificate(Session session, URI certUri) { protected Certificate(Session session, URL certUrl) {
super(session); 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); super(session);
setLocation(certUri); setLocation(certUrl);
this.chainCertUri = chainUri; this.chainCertUrl = chainUrl;
this.cert = cert; this.cert = cert;
} }
@ -62,16 +63,16 @@ public class Certificate extends AcmeResource {
* Location of the Certificate * Location of the Certificate
* @return {@link Certificate} bound to the session and location * @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); 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. * available.
*/ */
public URI getChainLocation() { public URL getChainLocation() {
return chainCertUri; return chainCertUrl;
} }
/** /**
@ -92,7 +93,7 @@ public class Certificate extends AcmeResource {
conn.accept(HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_ACCEPTED); conn.accept(HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_ACCEPTED);
conn.handleRetryAfter("certificate is not available for download yet"); conn.handleRetryAfter("certificate is not available for download yet");
chainCertUri = conn.getLink("up"); chainCertUrl = conn.getLink("up");
cert = conn.readCertificate(); cert = conn.readCertificate();
} }
} }
@ -111,21 +112,21 @@ public class Certificate extends AcmeResource {
*/ */
public X509Certificate[] downloadChain() throws AcmeException { public X509Certificate[] downloadChain() throws AcmeException {
if (chain == null) { if (chain == null) {
if (chainCertUri == null) { if (chainCertUrl == null) {
download(); download();
} }
if (chainCertUri == null) { if (chainCertUrl == null) {
throw new AcmeProtocolException("No certificate chain provided"); throw new AcmeProtocolException("No certificate chain provided");
} }
LOG.debug("downloadChain"); LOG.debug("downloadChain");
List<X509Certificate> certChain = new ArrayList<>(); List<X509Certificate> certChain = new ArrayList<>();
URI link = chainCertUri; URL link = chainCertUrl;
while (link != null && certChain.size() < MAX_CHAIN_LENGTH) { while (link != null && certChain.size() < MAX_CHAIN_LENGTH) {
try (Connection conn = getSession().provider().connect()) { try (Connection conn = getSession().provider().connect()) {
conn.sendRequest(chainCertUri, getSession()); conn.sendRequest(chainCertUrl, getSession());
conn.accept(HttpURLConnection.HTTP_OK); conn.accept(HttpURLConnection.HTTP_OK);
certChain.add(conn.readCertificate()); certChain.add(conn.readCertificate());
@ -159,8 +160,8 @@ public class Certificate extends AcmeResource {
*/ */
public void revoke(RevocationReason reason) throws AcmeException { public void revoke(RevocationReason reason) throws AcmeException {
LOG.debug("revoke"); LOG.debug("revoke");
URI resUri = getSession().resourceUri(Resource.REVOKE_CERT); URL resUrl = getSession().resourceUrl(Resource.REVOKE_CERT);
if (resUri == null) { if (resUrl == null) {
throw new AcmeProtocolException("CA does not support certificate revocation"); throw new AcmeProtocolException("CA does not support certificate revocation");
} }
@ -176,7 +177,7 @@ public class Certificate extends AcmeResource {
claims.put("reason", reason.getReasonCode()); claims.put("reason", reason.getReasonCode());
} }
conn.sendSignedRequest(resUri, claims, getSession()); conn.sendSignedRequest(resUrl, claims, getSession());
conn.accept(HttpURLConnection.HTTP_OK); conn.accept(HttpURLConnection.HTTP_OK);
} catch (CertificateEncodingException ex) { } catch (CertificateEncodingException ex) {
throw new AcmeProtocolException("Invalid certificate", ex); throw new AcmeProtocolException("Invalid certificate", ex);
@ -199,7 +200,12 @@ public class Certificate extends AcmeResource {
*/ */
public static void revoke(Session session, X509Certificate cert, public static void revoke(Session session, X509Certificate cert,
RevocationReason reason) throws AcmeException { 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);
}
} }
} }

View File

@ -17,6 +17,7 @@ import static org.shredzone.acme4j.toolbox.AcmeUtils.*;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Instant; import java.time.Instant;
@ -56,17 +57,17 @@ public class Registration extends AcmeResource {
private final List<URI> contacts = new ArrayList<>(); private final List<URI> contacts = new ArrayList<>();
private URI agreement; private URI agreement;
private URI authorizations; private URL authorizations;
private URI certificates; private URL certificates;
private Status status; private Status status;
private boolean loaded = false; private boolean loaded = false;
protected Registration(Session session, URI location) { protected Registration(Session session, URL location) {
super(session); super(session);
setLocation(location); setLocation(location);
} }
protected Registration(Session session, URI location, URI agreement) { protected Registration(Session session, URL location, URI agreement) {
super(session); super(session);
setLocation(location); setLocation(location);
this.agreement = agreement; this.agreement = agreement;
@ -78,10 +79,10 @@ public class Registration extends AcmeResource {
* @param session * @param session
* {@link Session} to be used * {@link Session} to be used
* @param location * @param location
* Location URI of the registration * Location URL of the registration
* @return {@link Registration} bound to the session and location * @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); return new Registration(session, location);
} }
@ -185,7 +186,7 @@ public class Registration extends AcmeResource {
.put("type", "dns") .put("type", "dns")
.put("value", toAce(domain)); .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); conn.accept(HttpURLConnection.HTTP_CREATED);
JSON json = conn.readJsonResponse(); JSON json = conn.readJsonResponse();
@ -240,7 +241,7 @@ public class Registration extends AcmeResource {
claims.put("notAfter", notAfter); 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); int rc = conn.accept(HttpURLConnection.HTTP_CREATED, HttpURLConnection.HTTP_ACCEPTED);
X509Certificate cert = null; 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"); LOG.debug("key-change");
try (Connection conn = getSession().provider().connect()) { 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()); PublicJsonWebKey newKeyJwk = PublicJsonWebKey.Factory.newPublicJwk(newKeyPair.getPublic());
JSONBuilder payloadClaim = new JSONBuilder(); JSONBuilder payloadClaim = new JSONBuilder();
@ -286,7 +287,7 @@ public class Registration extends AcmeResource {
JsonWebSignature innerJws = new JsonWebSignature(); JsonWebSignature innerJws = new JsonWebSignature();
innerJws.setPayload(payloadClaim.toString()); innerJws.setPayload(payloadClaim.toString());
innerJws.getHeaders().setObjectHeaderValue("url", keyChangeUri); innerJws.getHeaders().setObjectHeaderValue("url", keyChangeUrl);
innerJws.getHeaders().setJwkHeaderValue("jwk", newKeyJwk); innerJws.getHeaders().setJwkHeaderValue("jwk", newKeyJwk);
innerJws.setAlgorithmHeaderValue(keyAlgorithm(newKeyJwk)); innerJws.setAlgorithmHeaderValue(keyAlgorithm(newKeyJwk));
innerJws.setKey(newKeyPair.getPrivate()); innerJws.setKey(newKeyPair.getPrivate());
@ -298,7 +299,7 @@ public class Registration extends AcmeResource {
outerClaim.put("signature", innerJws.getEncodedSignature()); outerClaim.put("signature", innerJws.getEncodedSignature());
outerClaim.put("payload", innerJws.getEncodedPayload()); outerClaim.put("payload", innerJws.getEncodedPayload());
conn.sendSignedRequest(keyChangeUri, outerClaim, getSession()); conn.sendSignedRequest(keyChangeUrl, outerClaim, getSession());
conn.accept(HttpURLConnection.HTTP_OK); conn.accept(HttpURLConnection.HTTP_OK);
getSession().setKeyPair(newKeyPair); getSession().setKeyPair(newKeyPair);
@ -361,19 +362,19 @@ public class Registration extends AcmeResource {
.forEach(contacts::add); .forEach(contacts::add);
} }
this.authorizations = json.get(KEY_AUTHORIZATIONS).asURI(); this.authorizations = json.get(KEY_AUTHORIZATIONS).asURL();
this.certificates = json.get(KEY_CERTIFICATES).asURI(); this.certificates = json.get(KEY_CERTIFICATES).asURL();
if (json.contains(KEY_STATUS)) { if (json.contains(KEY_STATUS)) {
this.status = Status.parse(json.get(KEY_STATUS).asString()); this.status = Status.parse(json.get(KEY_STATUS).asString());
} }
URI location = conn.getLocation(); URL location = conn.getLocation();
if (location != null) { if (location != null) {
setLocation(location); setLocation(location);
} }
URI tos = conn.getLink("terms-of-service"); URI tos = conn.getLinkAsURI("terms-of-service");
if (tos != null) { if (tos != null) {
this.agreement = tos; this.agreement = tos;
} }

View File

@ -15,6 +15,7 @@ package org.shredzone.acme4j;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.List; import java.util.List;
@ -83,11 +84,11 @@ public class RegistrationBuilder {
claims.put("contact", contacts); 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); conn.accept(HttpURLConnection.HTTP_CREATED);
URI location = conn.getLocation(); URL location = conn.getLocation();
URI tos = conn.getLink("terms-of-service"); URI tos = conn.getLinkAsURI("terms-of-service");
return new Registration(session, location, tos); return new Registration(session, location, tos);
} }

View File

@ -14,6 +14,7 @@
package org.shredzone.acme4j; package org.shredzone.acme4j;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.security.KeyPair; import java.security.KeyPair;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
@ -40,7 +41,7 @@ import org.shredzone.acme4j.toolbox.JSON;
* volatile data. * volatile data.
*/ */
public class Session { public class Session {
private final AtomicReference<Map<Resource, URI>> resourceMap = new AtomicReference<>(); private final AtomicReference<Map<Resource, URL>> resourceMap = new AtomicReference<>();
private final AtomicReference<Metadata> metadata = new AtomicReference<>(); private final AtomicReference<Metadata> metadata = new AtomicReference<>();
private final URI serverUri; private final URI serverUri;
private final AcmeProvider provider; 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. * the server and getting a directory. The result is cached.
* *
* @param resource * @param resource
* {@link Resource} to get the {@link URI} of * {@link Resource} to get the {@link URL} of
* @return {@link URI}, or {@code null} if the server does not offer that resource * @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(); readDirectory();
return resourceMap.get().get(Objects.requireNonNull(resource, "resource")); return resourceMap.get().get(Objects.requireNonNull(resource, "resource"));
} }
@ -219,11 +220,11 @@ public class Session {
metadata.set(new Metadata(JSON.empty())); metadata.set(new Metadata(JSON.empty()));
} }
Map<Resource, URI> map = new EnumMap<>(Resource.class); Map<Resource, URL> map = new EnumMap<>(Resource.class);
for (Resource res : Resource.values()) { for (Resource res : Resource.values()) {
URI uri = directoryJson.get(res.path()).asURI(); URL url = directoryJson.get(res.path()).asURL();
if (uri != null) { if (url != null) {
map.put(res, uri); map.put(res, url);
} }
} }
resourceMap.set(map); resourceMap.set(map);

View File

@ -14,7 +14,7 @@
package org.shredzone.acme4j.challenge; package org.shredzone.acme4j.challenge;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URL;
import java.time.Instant; import java.time.Instant;
import java.util.Objects; import java.util.Objects;
@ -72,7 +72,7 @@ public class Challenge extends AcmeResource {
* @return {@link Challenge} bound to this session and location * @return {@link Challenge} bound to this session and location
*/ */
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public static <T extends Challenge> T bind(Session session, URI location) throws AcmeException { public static <T extends Challenge> T bind(Session session, URL location) throws AcmeException {
Objects.requireNonNull(session, "session"); Objects.requireNonNull(session, "session");
Objects.requireNonNull(location, "location"); 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 @Override
public URI getLocation() { public URL getLocation() {
return data.get(KEY_URI).asURI(); return data.get(KEY_URI).asURL();
} }
/** /**

View File

@ -14,8 +14,8 @@
package org.shredzone.acme4j.connector; package org.shredzone.acme4j.connector;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Collection;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeException;
@ -31,24 +31,24 @@ public interface Connection extends AutoCloseable {
/** /**
* Sends a simple GET request. * Sends a simple GET request.
* *
* @param uri * @param url
* {@link URI} to send the request to. * {@link URL} to send the request to.
* @param session * @param session
* {@link Session} instance to be used for tracking * {@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. * Sends a signed POST request.
* *
* @param uri * @param url
* {@link URI} to send the request to. * {@link URL} to send the request to.
* @param claims * @param claims
* {@link JSONBuilder} containing claims. Must not be {@code null}. * {@link JSONBuilder} containing claims. Must not be {@code null}.
* @param session * @param session
* {@link Session} instance to be used for signing and tracking * {@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, * Checks if the HTTP response status is in the given list of acceptable HTTP states,
@ -96,9 +96,9 @@ public interface Connection extends AutoCloseable {
* <p> * <p>
* Relative links are resolved against the last request's URL. * 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. * Gets a relation link from the header.
@ -108,20 +108,21 @@ public interface Connection extends AutoCloseable {
* *
* @param relation * @param relation
* Link 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.
* <p> * <p>
* 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 * @param relation
* Link 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<URI> getLinks(String relation); URI getLinkAsURI(String relation);
/** /**
* Closes the {@link Connection}, releasing all resources. * Closes the {@link Connection}, releasing all resources.

View File

@ -13,6 +13,7 @@
*/ */
package org.shredzone.acme4j.connector; package org.shredzone.acme4j.connector;
import static java.util.stream.Collectors.toList;
import static org.shredzone.acme4j.toolbox.AcmeUtils.keyAlgorithm; import static org.shredzone.acme4j.toolbox.AcmeUtils.keyAlgorithm;
import java.io.IOException; import java.io.IOException;
@ -91,15 +92,15 @@ public class DefaultConnection implements Connection {
} }
@Override @Override
public void sendRequest(URI uri, Session session) throws AcmeException { public void sendRequest(URL url, Session session) throws AcmeException {
Objects.requireNonNull(uri, "uri"); Objects.requireNonNull(url, "url");
Objects.requireNonNull(session, "session"); Objects.requireNonNull(session, "session");
assertConnectionIsClosed(); assertConnectionIsClosed();
LOG.debug("GET {}", uri); LOG.debug("GET {}", url);
try { try {
conn = httpConnector.openConnection(uri); conn = httpConnector.openConnection(url);
conn.setRequestMethod("GET"); conn.setRequestMethod("GET");
conn.setRequestProperty(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET); conn.setRequestProperty(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET);
conn.setRequestProperty(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag()); conn.setRequestProperty(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag());
@ -114,8 +115,8 @@ public class DefaultConnection implements Connection {
} }
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) throws AcmeException { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) throws AcmeException {
Objects.requireNonNull(uri, "uri"); Objects.requireNonNull(url, "url");
Objects.requireNonNull(claims, "claims"); Objects.requireNonNull(claims, "claims");
Objects.requireNonNull(session, "session"); Objects.requireNonNull(session, "session");
assertConnectionIsClosed(); assertConnectionIsClosed();
@ -124,8 +125,8 @@ public class DefaultConnection implements Connection {
KeyPair keypair = session.getKeyPair(); KeyPair keypair = session.getKeyPair();
if (session.getNonce() == null) { if (session.getNonce() == null) {
LOG.debug("Getting initial nonce, HEAD {}", uri); LOG.debug("Getting initial nonce, HEAD {}", url);
conn = httpConnector.openConnection(uri); conn = httpConnector.openConnection(url);
conn.setRequestMethod("HEAD"); conn.setRequestMethod("HEAD");
conn.setRequestProperty(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag()); conn.setRequestProperty(ACCEPT_LANGUAGE_HEADER, session.getLocale().toLanguageTag());
conn.connect(); conn.connect();
@ -137,9 +138,9 @@ public class DefaultConnection implements Connection {
throw new AcmeProtocolException("Server did not provide a nonce"); 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.setRequestMethod("POST");
conn.setRequestProperty(ACCEPT_HEADER, "application/json"); conn.setRequestProperty(ACCEPT_HEADER, "application/json");
conn.setRequestProperty(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET); conn.setRequestProperty(ACCEPT_CHARSET_HEADER, DEFAULT_CHARSET);
@ -152,7 +153,7 @@ public class DefaultConnection implements Connection {
JsonWebSignature jws = new JsonWebSignature(); JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toString()); jws.setPayload(claims.toString());
jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce())); jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce()));
jws.getHeaders().setObjectHeaderValue("url", uri); jws.getHeaders().setObjectHeaderValue("url", url);
jws.getHeaders().setJwkHeaderValue("jwk", jwk); jws.getHeaders().setJwkHeaderValue("jwk", jwk);
jws.setAlgorithmHeaderValue(keyAlgorithm(jwk)); jws.setAlgorithmHeaderValue(keyAlgorithm(jwk));
jws.setKey(keypair.getPrivate()); jws.setKey(keypair.getPrivate());
@ -289,7 +290,7 @@ public class DefaultConnection implements Connection {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
assertConnectionIsOpen(); assertConnectionIsOpen();
String location = conn.getHeaderField(LOCATION_HEADER); String location = conn.getHeaderField(LOCATION_HEADER);
@ -302,24 +303,37 @@ public class DefaultConnection implements Connection {
} }
@Override @Override
public URI getLink(String relation) { public URL getLink(String relation) {
Collection<URI> links = getLinks(relation); return getLinks(relation).stream()
if (links == null) { .findFirst()
return null; .map(this::resolveRelative)
} .orElse(null);
if (links.size() > 1) {
LOG.debug("Link: {} - using the first of {}", relation, links.size());
}
return links.iterator().next();
} }
@Override @Override
public Collection<URI> 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<String> getLinks(String relation) {
assertConnectionIsOpen(); assertConnectionIsOpen();
List<URI> result = new ArrayList<>(); List<String> result = new ArrayList<>();
List<String> links = conn.getHeaderFields().get(LINK_HEADER); List<String> links = conn.getHeaderFields().get(LINK_HEADER);
if (links != null) { if (links != null) {
@ -329,17 +343,12 @@ public class DefaultConnection implements Connection {
if (m.matches()) { if (m.matches()) {
String location = m.group(1); String location = m.group(1);
LOG.debug("Link: {} -> {}", relation, location); LOG.debug("Link: {} -> {}", relation, location);
result.add(resolveRelative(location)); result.add(location);
} }
} }
} }
return !result.isEmpty() ? result : null; return result;
}
@Override
public void close() {
conn = null;
} }
/** /**
@ -389,14 +398,16 @@ public class DefaultConnection implements Connection {
} }
if ("agreementRequired".equals(error)) { if ("agreementRequired".equals(error)) {
URI instance = resolveRelative(json.get("instance").asString()); URI instance = resolveRelativeAsURI(json.get("instance").asString());
URI tos = getLink("terms-of-service"); URI tos = getLinkAsURI("terms-of-service");
return new AcmeAgreementRequiredException(type, detail, tos, instance); return new AcmeAgreementRequiredException(type, detail, tos, instance);
} }
if ("rateLimited".equals(error)) { if ("rateLimited".equals(error)) {
Optional<Instant> retryAfter = getRetryAfterHeader(); Optional<Instant> retryAfter = getRetryAfterHeader();
Collection<URI> rateLimits = getLinks("rate-limit"); Collection<URI> rateLimits = getLinks("rate-limit").stream()
.map(this::resolveRelativeAsURI)
.collect(toList());
return new AcmeRateLimitExceededException(type, detail, retryAfter.orElse(null), rateLimits); 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 * @param link
* Link to resolve. Absolute links are just converted to an URI. May be * 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 * @return Absolute URI of the given link, or {@code null} if the link was
* {@code null}. * {@code null}.
*/ */
private URI resolveRelative(String link) { private URI resolveRelativeAsURI(String link) {
if (link == null) { if (link == null) {
return null; return null;
} }
assertConnectionIsOpen(); assertConnectionIsOpen();
try { try {
return new URL(conn.getURL(), link).toURI(); return conn.getURL().toURI().resolve(link);
} catch (MalformedURLException | URISyntaxException ex) { } catch (URISyntaxException ex) {
throw new AcmeProtocolException("Cannot resolve relative link: " + link, ex); throw new AcmeProtocolException("Cannot resolve relative link: " + link, ex);
} }
} }

View File

@ -16,7 +16,7 @@ package org.shredzone.acme4j.connector;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URL;
import java.util.Properties; import java.util.Properties;
import org.slf4j.LoggerFactory; 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 * @param url
* {@link URI} to connect to * {@link URL} to connect to
* @return {@link HttpURLConnection} connected to the {@link URI} * @return {@link HttpURLConnection} connected to the {@link URL}
*/ */
public HttpURLConnection openConnection(URI uri) throws IOException { public HttpURLConnection openConnection(URL url) throws IOException {
HttpURLConnection conn = (HttpURLConnection) uri.toURL().openConnection(); HttpURLConnection conn = (HttpURLConnection) url.openConnection();
configure(conn); configure(conn);
return conn; return conn;
} }

View File

@ -14,7 +14,7 @@
package org.shredzone.acme4j.connector; package org.shredzone.acme4j.connector;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URL;
import java.util.ArrayDeque; import java.util.ArrayDeque;
import java.util.Deque; import java.util.Deque;
import java.util.Iterator; import java.util.Iterator;
@ -29,7 +29,7 @@ import org.shredzone.acme4j.exception.AcmeProtocolException;
import org.shredzone.acme4j.toolbox.JSON; 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. * {@link AcmeResource} instances.
* *
* @param <T> * @param <T>
@ -39,10 +39,10 @@ public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
private final Session session; private final Session session;
private final String field; private final String field;
private final Deque<URI> uriList = new ArrayDeque<>(); private final Deque<URL> urlList = new ArrayDeque<>();
private final BiFunction<Session, URI, T> creator; private final BiFunction<Session, URL, T> creator;
private boolean eol = false; private boolean eol = false;
private URI nextUri; private URL nextUrl;
/** /**
* Creates a new {@link ResourceIterator}. * Creates a new {@link ResourceIterator}.
@ -52,15 +52,15 @@ public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
* @param field * @param field
* Field name to be used in the JSON response * Field name to be used in the JSON response
* @param start * @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 * @param creator
* Creator for an {@link AcmeResource} that is bound to the given * 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<Session, URI, T> creator) { public ResourceIterator(Session session, String field, URL start, BiFunction<Session, URL, T> creator) {
this.session = Objects.requireNonNull(session, "session"); this.session = Objects.requireNonNull(session, "session");
this.field = Objects.requireNonNull(field, "field"); this.field = Objects.requireNonNull(field, "field");
this.nextUri = start; this.nextUrl = start;
this.creator = Objects.requireNonNull(creator, "creator"); this.creator = Objects.requireNonNull(creator, "creator");
} }
@ -68,7 +68,7 @@ public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
* Checks if there is another object in the result. * Checks if there is another object in the result.
* *
* @throws AcmeProtocolException * @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 @Override
public boolean hasNext() { public boolean hasNext() {
@ -76,32 +76,32 @@ public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
return false; return false;
} }
if (uriList.isEmpty()) { if (urlList.isEmpty()) {
fetch(); fetch();
} }
if (uriList.isEmpty()) { if (urlList.isEmpty()) {
eol = true; eol = true;
} }
return !uriList.isEmpty(); return !urlList.isEmpty();
} }
/** /**
* Returns the next object of the result. * Returns the next object of the result.
* *
* @throws AcmeProtocolException * @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 * @throws NoSuchElementException
* if there are no more entries * if there are no more entries
*/ */
@Override @Override
public T next() { public T next() {
if (!eol && uriList.isEmpty()) { if (!eol && urlList.isEmpty()) {
fetch(); fetch();
} }
URI next = uriList.poll(); URL next = urlList.poll();
if (next == null) { if (next == null) {
eol = true; eol = true;
throw new NoSuchElementException("no more " + field); throw new NoSuchElementException("no more " + field);
@ -119,11 +119,11 @@ public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
} }
/** /**
* Fetches the next batch of URIs. Handles exceptions. Does nothing if there is no * Fetches the next batch of URLs. Handles exceptions. Does nothing if there is no
* URI of the next batch. * URL of the next batch.
*/ */
private void fetch() { private void fetch() {
if (nextUri == null) { if (nextUrl == null) {
return; return;
} }
@ -135,34 +135,34 @@ public class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
} }
/** /**
* Reads the next batch of URIs from the server, and fills the queue with the URIs. If * 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 URIs. * there is a "next" header, it is used for the next batch of URLs.
*/ */
private void readAndQueue() throws AcmeException { private void readAndQueue() throws AcmeException {
try (Connection conn = session.provider().connect()) { try (Connection conn = session.provider().connect()) {
conn.sendRequest(nextUri, session); conn.sendRequest(nextUrl, session);
conn.accept(HttpURLConnection.HTTP_OK); conn.accept(HttpURLConnection.HTTP_OK);
JSON json = conn.readJsonResponse(); 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 * @param json
* JSON map to read from * JSON map to read from
*/ */
private void fillUriList(JSON json) { private void fillUrlList(JSON json) {
JSON.Array array = json.get(field).asArray(); JSON.Array array = json.get(field).asArray();
if (array == null) { if (array == null) {
return; return;
} }
array.stream().map(JSON.Value::asURI).forEach(uriList::add); array.stream().map(JSON.Value::asURL).forEach(urlList::add);
} }
} }

View File

@ -13,7 +13,7 @@
*/ */
package org.shredzone.acme4j.exception; package org.shredzone.acme4j.exception;
import java.net.URI; import java.net.URL;
import java.util.Objects; import java.util.Objects;
/** /**
@ -23,7 +23,7 @@ import java.util.Objects;
public class AcmeConflictException extends AcmeException { public class AcmeConflictException extends AcmeException {
private static final long serialVersionUID = 7454201988845449591L; private static final long serialVersionUID = 7454201988845449591L;
private final URI location; private final URL location;
/** /**
* Creates a new {@link AcmeConflictException}. * Creates a new {@link AcmeConflictException}.
@ -31,9 +31,9 @@ public class AcmeConflictException extends AcmeException {
* @param msg * @param msg
* Details about the conflicting resource * Details about the conflicting resource
* @param location * @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); super(msg);
this.location = Objects.requireNonNull(location, "location"); this.location = Objects.requireNonNull(location, "location");
} }
@ -41,7 +41,7 @@ public class AcmeConflictException extends AcmeException {
/** /**
* Location of the conflicting resource. * Location of the conflicting resource.
*/ */
public URI getLocation() { public URL getLocation() {
return location; return location;
} }

View File

@ -14,6 +14,7 @@
package org.shredzone.acme4j.provider; package org.shredzone.acme4j.provider;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.util.ServiceLoader; import java.util.ServiceLoader;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
@ -41,15 +42,15 @@ public interface AcmeProvider {
boolean accepts(URI serverUri); 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 * @param serverUri
* Server {@link URI} * Server {@link URI}
* @return Resolved directory {@link URI} * @return Resolved directory {@link URL}
* @throws IllegalArgumentException * @throws IllegalArgumentException
* if the server {@link URI} is not accepted * 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. * Creates a {@link Connection} for communication with the ACME server.

View File

@ -13,7 +13,9 @@
*/ */
package org.shredzone.acme4j.provider; package org.shredzone.acme4j.provider;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL;
/** /**
* A generic {@link AcmeProvider}. It should be working for all ACME servers complying to * A generic {@link AcmeProvider}. It should be working for all ACME servers complying to
@ -30,8 +32,12 @@ public class GenericAcmeProvider extends AbstractAcmeProvider {
} }
@Override @Override
public URI resolve(URI serverUri) { public URL resolve(URI serverUri) {
return serverUri; try {
return serverUri.toURL();
} catch (MalformedURLException ex) {
throw new IllegalArgumentException("Invalid server URI", ex);
}
} }
} }

View File

@ -13,8 +13,9 @@
*/ */
package org.shredzone.acme4j.provider.letsencrypt; package org.shredzone.acme4j.provider.letsencrypt;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URL;
import org.shredzone.acme4j.connector.HttpConnector; import org.shredzone.acme4j.connector.HttpConnector;
import org.shredzone.acme4j.exception.AcmeProtocolException; 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 Logger LOG = LoggerFactory.getLogger(LetsEncryptAcmeProvider.class);
private static final String V01_DIRECTORY_URI = "https://acme-v01.api.letsencrypt.org/directory"; private static final String V01_DIRECTORY_URL = "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 STAGING_DIRECTORY_URL = "https://acme-staging.api.letsencrypt.org/directory";
@Override @Override
public boolean accepts(URI serverUri) { public boolean accepts(URI serverUri) {
@ -47,21 +48,21 @@ public class LetsEncryptAcmeProvider extends AbstractAcmeProvider {
} }
@Override @Override
public URI resolve(URI serverUri) { public URL resolve(URI serverUri) {
String path = serverUri.getPath(); String path = serverUri.getPath();
String directoryUri; String directoryUrl;
if (path == null || "".equals(path) || "/".equals(path) || "/v01".equals(path)) { if (path == null || "".equals(path) || "/".equals(path) || "/v01".equals(path)) {
directoryUri = V01_DIRECTORY_URI; directoryUrl = V01_DIRECTORY_URL;
} else if ("/staging".equals(path)) { } else if ("/staging".equals(path)) {
directoryUri = STAGING_DIRECTORY_URI; directoryUrl = STAGING_DIRECTORY_URL;
} else { } else {
throw new IllegalArgumentException("Unknown URI " + serverUri); throw new IllegalArgumentException("Unknown URI " + serverUri);
} }
try { try {
return new URI(directoryUri); return new URL(directoryUrl);
} catch (URISyntaxException ex) { } catch (MalformedURLException ex) {
throw new AcmeProtocolException(directoryUri, ex); throw new AcmeProtocolException(directoryUrl, ex);
} }
} }

View File

@ -15,7 +15,7 @@ package org.shredzone.acme4j.provider.letsencrypt;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URL;
import java.security.KeyManagementException; import java.security.KeyManagementException;
import java.security.KeyStore; import java.security.KeyStore;
import java.security.KeyStoreException; import java.security.KeyStoreException;
@ -42,8 +42,8 @@ public class LetsEncryptHttpConnector extends HttpConnector {
private static SSLSocketFactory sslSocketFactory; private static SSLSocketFactory sslSocketFactory;
@Override @Override
public HttpURLConnection openConnection(URI uri) throws IOException { public HttpURLConnection openConnection(URL url) throws IOException {
HttpURLConnection conn = super.openConnection(uri); HttpURLConnection conn = super.openConnection(url);
if (conn instanceof HttpsURLConnection) { if (conn instanceof HttpsURLConnection) {
((HttpsURLConnection) conn).setSSLSocketFactory(createSocketFactory()); ((HttpsURLConnection) conn).setSSLSocketFactory(createSocketFactory());
} }

View File

@ -20,7 +20,7 @@ import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream; import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream; import java.io.ObjectInputStream;
import java.io.ObjectOutputStream; import java.io.ObjectOutputStream;
import java.net.URI; import java.net.URL;
import org.junit.Test; import org.junit.Test;
import org.shredzone.acme4j.toolbox.TestUtils; import org.shredzone.acme4j.toolbox.TestUtils;
@ -36,7 +36,7 @@ public class AcmeResourceTest {
@Test @Test
public void testConstructor() throws Exception { public void testConstructor() throws Exception {
Session session = TestUtils.session(); Session session = TestUtils.session();
URI location = new URI("http://example.com/acme/resource"); URL location = new URL("http://example.com/acme/resource");
try { try {
new DummyResource(null); new DummyResource(null);

View File

@ -20,7 +20,7 @@ import static org.shredzone.acme4j.toolbox.TestUtils.*;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URL;
import java.time.Duration; import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.util.Collection; import java.util.Collection;
@ -44,7 +44,7 @@ public class AuthorizationTest {
private static final String SNAILMAIL_TYPE = "snail-01"; // a non-existent challenge 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 * Test that {@link Authorization#findChallenge(String)} does only find standalone
@ -121,8 +121,8 @@ public class AuthorizationTest {
public void testUpdate() throws Exception { public void testUpdate() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -150,13 +150,13 @@ public class AuthorizationTest {
provider.putTestChallenge("http-01", httpChallenge); provider.putTestChallenge("http-01", httpChallenge);
provider.putTestChallenge("dns-01", dnsChallenge); provider.putTestChallenge("dns-01", dnsChallenge);
Authorization auth = new Authorization(session, locationUri); Authorization auth = new Authorization(session, locationUrl);
auth.update(); auth.update();
assertThat(auth.getDomain(), is("example.org")); assertThat(auth.getDomain(), is("example.org"));
assertThat(auth.getStatus(), is(Status.VALID)); assertThat(auth.getStatus(), is(Status.VALID));
assertThat(auth.getExpires(), is(parseTimestamp("2016-01-02T17:12:40Z"))); assertThat(auth.getExpires(), is(parseTimestamp("2016-01-02T17:12:40Z")));
assertThat(auth.getLocation(), is(locationUri)); assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder( assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge)); (Challenge) httpChallenge, (Challenge) dnsChallenge));
@ -179,9 +179,9 @@ public class AuthorizationTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
requestWasSent.set(true); requestWasSent.set(true);
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -207,7 +207,7 @@ public class AuthorizationTest {
provider.putTestChallenge("http-01", new Http01Challenge(session)); provider.putTestChallenge("http-01", new Http01Challenge(session));
provider.putTestChallenge("dns-01", new Dns01Challenge(session)); provider.putTestChallenge("dns-01", new Dns01Challenge(session));
Authorization auth = new Authorization(session, locationUri); Authorization auth = new Authorization(session, locationUrl);
// Lazy loading // Lazy loading
assertThat(requestWasSent.get(), is(false)); assertThat(requestWasSent.get(), is(false));
@ -233,8 +233,8 @@ public class AuthorizationTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -262,7 +262,7 @@ public class AuthorizationTest {
provider.putTestChallenge("http-01", httpChallenge); provider.putTestChallenge("http-01", httpChallenge);
provider.putTestChallenge("dns-01", dnsChallenge); provider.putTestChallenge("dns-01", dnsChallenge);
Authorization auth = new Authorization(session, locationUri); Authorization auth = new Authorization(session, locationUrl);
try { try {
auth.update(); auth.update();
@ -274,7 +274,7 @@ public class AuthorizationTest {
assertThat(auth.getDomain(), is("example.org")); assertThat(auth.getDomain(), is("example.org"));
assertThat(auth.getStatus(), is(Status.VALID)); assertThat(auth.getStatus(), is(Status.VALID));
assertThat(auth.getExpires(), is(parseTimestamp("2016-01-02T17:12:40Z"))); assertThat(auth.getExpires(), is(parseTimestamp("2016-01-02T17:12:40Z")));
assertThat(auth.getLocation(), is(locationUri)); assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder( assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge)); (Challenge) httpChallenge, (Challenge) dnsChallenge));
@ -295,11 +295,11 @@ public class AuthorizationTest {
public void testDeactivate() throws Exception { public void testDeactivate() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
JSON json = claims.toJSON(); JSON json = claims.toJSON();
assertThat(json.get("resource").asString(), is("authz")); assertThat(json.get("resource").asString(), is("authz"));
assertThat(json.get("status").asString(), is("deactivated")); assertThat(json.get("status").asString(), is("deactivated"));
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
assertThat(session, is(notNullValue())); 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(); auth.deactivate();
provider.close(); provider.close();
@ -328,7 +328,7 @@ public class AuthorizationTest {
provider.putTestChallenge(Dns01Challenge.TYPE, new Dns01Challenge(session)); provider.putTestChallenge(Dns01Challenge.TYPE, new Dns01Challenge(session));
provider.putTestChallenge(TlsSni02Challenge.TYPE, new TlsSni02Challenge(session)); provider.putTestChallenge(TlsSni02Challenge.TYPE, new TlsSni02Challenge(session));
Authorization authorization = new Authorization(session, locationUri); Authorization authorization = new Authorization(session, locationUrl);
authorization.unmarshalAuthorization(getJsonAsObject("authorizationChallenges")); authorization.unmarshalAuthorization(getJsonAsObject("authorizationChallenges"));
return authorization; return authorization;
} }

View File

@ -20,7 +20,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URL;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Duration; import java.time.Duration;
@ -39,9 +39,9 @@ import org.shredzone.acme4j.toolbox.TestUtils;
*/ */
public class CertificateTest { public class CertificateTest {
private URI resourceUri = URI.create("http://example.com/acme/resource"); private URL resourceUrl = url("http://example.com/acme/resource");
private URI locationUri = URI.create("http://example.com/acme/certificate"); private URL locationUrl = url("http://example.com/acme/certificate");
private URI chainUri = URI.create("http://example.com/acme/chain"); private URL chainUrl = url("http://example.com/acme/chain");
/** /**
* Test that a certificate can be downloaded. * Test that a certificate can be downloaded.
@ -51,17 +51,17 @@ public class CertificateTest {
final X509Certificate originalCert = TestUtils.createCertificate(); final X509Certificate originalCert = TestUtils.createCertificate();
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
private boolean isLocationUri; private boolean isLocationUrl;
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
assertThat(uri, isOneOf(locationUri, chainUri)); assertThat(url, isOneOf(locationUrl, chainUrl));
isLocationUri = uri.equals(locationUri); isLocationUrl = url.equals(locationUrl);
} }
@Override @Override
public int accept(int... httpStatus) throws AcmeException { public int accept(int... httpStatus) throws AcmeException {
if (isLocationUri) { if (isLocationUrl) {
// The leaf certificate, might be asynchronous // The leaf certificate, might be asynchronous
assertThat(httpStatus, isIntArrayContainingInAnyOrder( assertThat(httpStatus, isIntArrayContainingInAnyOrder(
HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_ACCEPTED)); HttpURLConnection.HTTP_OK, HttpURLConnection.HTTP_ACCEPTED));
@ -84,18 +84,18 @@ public class CertificateTest {
} }
@Override @Override
public URI getLink(String relation) { public URL getLink(String relation) {
switch(relation) { switch(relation) {
case "up": return (isLocationUri ? chainUri : null); case "up": return (isLocationUrl ? chainUrl : null);
default: return null; default: return null;
} }
} }
}; };
Certificate cert = new Certificate(provider.createSession(), locationUri); Certificate cert = new Certificate(provider.createSession(), locationUrl);
X509Certificate downloadedCert = cert.download(); X509Certificate downloadedCert = cert.download();
assertThat(downloadedCert, is(sameInstance(originalCert))); assertThat(downloadedCert, is(sameInstance(originalCert)));
assertThat(cert.getChainLocation(), is(chainUri)); assertThat(cert.getChainLocation(), is(chainUrl));
X509Certificate[] downloadedChain = cert.downloadChain(); X509Certificate[] downloadedChain = cert.downloadChain();
assertThat(downloadedChain.length, is(1)); assertThat(downloadedChain.length, is(1));
@ -113,8 +113,8 @@ public class CertificateTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -131,7 +131,7 @@ public class CertificateTest {
} }
}; };
Certificate cert = new Certificate(provider.createSession(), locationUri); Certificate cert = new Certificate(provider.createSession(), locationUrl);
try { try {
cert.download(); cert.download();
@ -152,8 +152,8 @@ public class CertificateTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateRequest"))); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateRequest")));
assertThat(session, is(notNullValue())); 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(); cert.revoke();
provider.close(); provider.close();
@ -182,8 +182,8 @@ public class CertificateTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest"))); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest")));
assertThat(session, is(notNullValue())); 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); cert.revoke(RevocationReason.KEY_COMPROMISE);
provider.close(); provider.close();
@ -213,8 +213,8 @@ public class CertificateTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest"))); assertThat(claims.toString(), sameJSONAs(getJson("revokeCertificateWithReasonRequest")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
assertThat(session.getKeyPair(), is(certKeyPair)); 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 session = provider.createSession();
session.setKeyPair(certKeyPair); session.setKeyPair(certKeyPair);

View File

@ -20,6 +20,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URL;
import org.junit.Test; import org.junit.Test;
import org.shredzone.acme4j.connector.Resource; import org.shredzone.acme4j.connector.Resource;
@ -32,8 +33,8 @@ import org.shredzone.acme4j.toolbox.JSONBuilder;
*/ */
public class RegistrationBuilderTest { public class RegistrationBuilderTest {
private URI resourceUri = URI.create("http://example.com/acme/resource");; private URL resourceUrl = url("http://example.com/acme/resource");
private URI locationUri = URI.create("http://example.com/acme/registration");; private URL locationUrl = url("http://example.com/acme/registration");
private URI agreementUri = URI.create("http://example.com/agreement.pdf");; private URI agreementUri = URI.create("http://example.com/agreement.pdf");;
/** /**
@ -43,8 +44,8 @@ public class RegistrationBuilderTest {
public void testRegistration() throws Exception { public void testRegistration() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("newRegistration"))); assertThat(claims.toString(), sameJSONAs(getJson("newRegistration")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
} }
@ -56,12 +57,12 @@ public class RegistrationBuilderTest {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return locationUri; return locationUrl;
} }
@Override @Override
public URI getLink(String relation) { public URI getLinkAsURI(String relation) {
switch(relation) { switch(relation) {
case "terms-of-service": return agreementUri; case "terms-of-service": return agreementUri;
default: return null; 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(); RegistrationBuilder builder = new RegistrationBuilder();
builder.addContact("mailto:foo@example.com"); builder.addContact("mailto:foo@example.com");
Registration registration = builder.create(provider.createSession()); Registration registration = builder.create(provider.createSession());
assertThat(registration.getLocation(), is(locationUri)); assertThat(registration.getLocation(), is(locationUrl));
assertThat(registration.getAgreement(), is(agreementUri)); assertThat(registration.getAgreement(), is(agreementUri));
provider.close(); provider.close();

View File

@ -21,6 +21,7 @@ import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.security.KeyPair; import java.security.KeyPair;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.time.Instant; import java.time.Instant;
@ -51,10 +52,10 @@ import org.shredzone.acme4j.toolbox.TestUtils;
*/ */
public class RegistrationTest { public class RegistrationTest {
private URI resourceUri = URI.create("http://example.com/acme/resource"); private URL resourceUrl = url("http://example.com/acme/resource");
private URI locationUri = URI.create("http://example.com/acme/registration"); 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 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. * Test that a registration can be updated.
@ -66,8 +67,8 @@ public class RegistrationTest {
private Integer response; private Integer response;
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
assertThat(claims.toString(), sameJSONAs(getJson("updateRegistration"))); assertThat(claims.toString(), sameJSONAs(getJson("updateRegistration")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
jsonResponse = getJsonAsObject("updateRegistrationResponse"); jsonResponse = getJsonAsObject("updateRegistrationResponse");
@ -75,8 +76,8 @@ public class RegistrationTest {
} }
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
if (URI.create("https://example.com/acme/reg/1/authz").equals(uri)) { if (url("https://example.com/acme/reg/1/authz").equals(url)) {
jsonResponse = new JSONBuilder() jsonResponse = new JSONBuilder()
.array("authorizations", "https://example.com/acme/auth/1") .array("authorizations", "https://example.com/acme/auth/1")
.toJSON(); .toJSON();
@ -84,7 +85,7 @@ public class RegistrationTest {
return; 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() jsonResponse = new JSONBuilder()
.array("certificates", "https://example.com/acme/cert/1") .array("certificates", "https://example.com/acme/cert/1")
.toJSON(); .toJSON();
@ -107,24 +108,28 @@ public class RegistrationTest {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return locationUri; return locationUrl;
} }
@Override @Override
public URI getLink(String relation) { public URL getLink(String relation) {
return null;
}
@Override
public URI getLinkAsURI(String relation) {
switch(relation) { switch(relation) {
case "terms-of-service": return agreementUri; case "terms-of-service": return agreementUri;
case "next": return null;
default: return null; default: return null;
} }
} }
}; };
Registration registration = new Registration(provider.createSession(), locationUri); Registration registration = new Registration(provider.createSession(), locationUrl);
registration.update(); registration.update();
assertThat(registration.getLocation(), is(locationUri)); assertThat(registration.getLocation(), is(locationUrl));
assertThat(registration.getAgreement(), is(agreementUri)); assertThat(registration.getAgreement(), is(agreementUri));
assertThat(registration.getContacts(), hasSize(1)); assertThat(registration.getContacts(), hasSize(1));
assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com"))); assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com")));
@ -132,14 +137,12 @@ public class RegistrationTest {
Iterator<Authorization> authIt = registration.getAuthorizations(); Iterator<Authorization> authIt = registration.getAuthorizations();
assertThat(authIt, not(nullValue())); assertThat(authIt, not(nullValue()));
assertThat(authIt.next().getLocation(), assertThat(authIt.next().getLocation(), is(url("https://example.com/acme/auth/1")));
is(URI.create("https://example.com/acme/auth/1")));
assertThat(authIt.hasNext(), is(false)); assertThat(authIt.hasNext(), is(false));
Iterator<Certificate> certIt = registration.getCertificates(); Iterator<Certificate> certIt = registration.getCertificates();
assertThat(certIt, not(nullValue())); assertThat(certIt, not(nullValue()));
assertThat(certIt.next().getLocation(), assertThat(certIt.next().getLocation(), is(url("https://example.com/acme/cert/1")));
is(URI.create("https://example.com/acme/cert/1")));
assertThat(certIt.hasNext(), is(false)); assertThat(certIt.hasNext(), is(false));
provider.close(); provider.close();
@ -154,9 +157,9 @@ public class RegistrationTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
requestWasSent.set(true); requestWasSent.set(true);
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -172,12 +175,12 @@ public class RegistrationTest {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return locationUri; return locationUrl;
} }
@Override @Override
public URI getLink(String relation) { public URI getLinkAsURI(String relation) {
switch(relation) { switch(relation) {
case "terms-of-service": return agreementUri; case "terms-of-service": return agreementUri;
default: return null; 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 // Lazy loading
assertThat(requestWasSent.get(), is(false)); assertThat(requestWasSent.get(), is(false));
@ -208,8 +211,8 @@ public class RegistrationTest {
public void testAuthorizeDomain() throws Exception { public void testAuthorizeDomain() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("newAuthorizationRequest"))); assertThat(claims.toString(), sameJSONAs(getJson("newAuthorizationRequest")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
} }
@ -226,8 +229,8 @@ public class RegistrationTest {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return locationUri; return locationUrl;
} }
}; };
@ -236,19 +239,19 @@ public class RegistrationTest {
Http01Challenge httpChallenge = new Http01Challenge(session); Http01Challenge httpChallenge = new Http01Challenge(session);
Dns01Challenge dnsChallenge = new Dns01Challenge(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(Http01Challenge.TYPE, httpChallenge);
provider.putTestChallenge(Dns01Challenge.TYPE, dnsChallenge); provider.putTestChallenge(Dns01Challenge.TYPE, dnsChallenge);
String domainName = "example.org"; String domainName = "example.org";
Registration registration = new Registration(session, locationUri); Registration registration = new Registration(session, locationUrl);
Authorization auth = registration.authorizeDomain(domainName); Authorization auth = registration.authorizeDomain(domainName);
assertThat(auth.getDomain(), is(domainName)); assertThat(auth.getDomain(), is(domainName));
assertThat(auth.getStatus(), is(Status.PENDING)); assertThat(auth.getStatus(), is(Status.PENDING));
assertThat(auth.getExpires(), is(nullValue())); assertThat(auth.getExpires(), is(nullValue()));
assertThat(auth.getLocation(), is(locationUri)); assertThat(auth.getLocation(), is(locationUrl));
assertThat(auth.getChallenges(), containsInAnyOrder( assertThat(auth.getChallenges(), containsInAnyOrder(
(Challenge) httpChallenge, (Challenge) dnsChallenge)); (Challenge) httpChallenge, (Challenge) dnsChallenge));
@ -269,7 +272,7 @@ public class RegistrationTest {
public void testAuthorizeBadDomain() throws Exception { public void testAuthorizeBadDomain() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider(); TestableConnectionProvider provider = new TestableConnectionProvider();
Session session = provider.createSession(); Session session = provider.createSession();
Registration registration = Registration.bind(session, locationUri); Registration registration = Registration.bind(session, locationUrl);
try { try {
registration.authorizeDomain(null); registration.authorizeDomain(null);
@ -297,13 +300,13 @@ public class RegistrationTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @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!"); fail("Attempted to download the certificate. Should be downloaded already!");
} }
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequestWithDate"))); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequestWithDate")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
} }
@ -321,32 +324,32 @@ public class RegistrationTest {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return locationUri; return locationUrl;
} }
@Override @Override
public URI getLink(String relation) { public URL getLink(String relation) {
switch(relation) { switch(relation) {
case "up": return chainUri; case "up": return chainUrl;
default: return null; default: return null;
} }
} }
}; };
provider.putTestResource(Resource.NEW_CERT, resourceUri); provider.putTestResource(Resource.NEW_CERT, resourceUrl);
byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der");
ZoneId utc = ZoneId.of("UTC"); ZoneId utc = ZoneId.of("UTC");
Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant(); Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant();
Instant notAfter = LocalDate.of(2016, 1, 8).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); Certificate cert = registration.requestCertificate(csr, notBefore, notAfter);
assertThat(cert.download(), is(originalCert)); assertThat(cert.download(), is(originalCert));
assertThat(cert.getLocation(), is(locationUri)); assertThat(cert.getLocation(), is(locationUrl));
assertThat(cert.getChainLocation(), is(chainUri)); assertThat(cert.getChainLocation(), is(chainUrl));
provider.close(); provider.close();
} }
@ -358,8 +361,8 @@ public class RegistrationTest {
public void testRequestCertificateAsync() throws AcmeException, IOException { public void testRequestCertificateAsync() throws AcmeException, IOException {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequest"))); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequest")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
} }
@ -372,28 +375,28 @@ public class RegistrationTest {
} }
@Override @Override
public URI getLink(String relation) { public URL getLink(String relation) {
switch(relation) { switch(relation) {
case "up": return chainUri; case "up": return chainUrl;
default: return null; default: return null;
} }
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return locationUri; return locationUrl;
} }
}; };
provider.putTestResource(Resource.NEW_CERT, resourceUri); provider.putTestResource(Resource.NEW_CERT, resourceUrl);
byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); 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); Certificate cert = registration.requestCertificate(csr);
assertThat(cert.getLocation(), is(locationUri)); assertThat(cert.getLocation(), is(locationUrl));
assertThat(cert.getChainLocation(), is(chainUri)); assertThat(cert.getChainLocation(), is(chainUrl));
provider.close(); provider.close();
} }
@ -406,8 +409,8 @@ public class RegistrationTest {
public void testRequestCertificateBrokenSync() throws AcmeException, IOException { public void testRequestCertificateBrokenSync() throws AcmeException, IOException {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequestWithDate"))); assertThat(claims.toString(), sameJSONAs(getJson("requestCertificateRequestWithDate")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
} }
@ -425,31 +428,31 @@ public class RegistrationTest {
} }
@Override @Override
public URI getLink(String relation) { public URL getLink(String relation) {
switch(relation) { switch(relation) {
case "up": return chainUri; case "up": return chainUrl;
default: return null; default: return null;
} }
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return locationUri; return locationUrl;
} }
}; };
provider.putTestResource(Resource.NEW_CERT, resourceUri); provider.putTestResource(Resource.NEW_CERT, resourceUrl);
byte[] csr = TestUtils.getResourceAsByteArray("/csr.der"); byte[] csr = TestUtils.getResourceAsByteArray("/csr.der");
ZoneId utc = ZoneId.of("UTC"); ZoneId utc = ZoneId.of("UTC");
Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant(); Instant notBefore = LocalDate.of(2016, 1, 1).atStartOfDay(utc).toInstant();
Instant notAfter = LocalDate.of(2016, 1, 8).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); Certificate cert = registration.requestCertificate(csr, notBefore, notAfter);
assertThat(cert.getLocation(), is(locationUri)); assertThat(cert.getLocation(), is(locationUrl));
assertThat(cert.getChainLocation(), is(chainUri)); assertThat(cert.getChainLocation(), is(chainUrl));
provider.close(); provider.close();
} }
@ -464,9 +467,9 @@ public class RegistrationTest {
final TestableConnectionProvider provider = new TestableConnectionProvider() { final TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder payload, Session session) { public void sendSignedRequest(URL url, JSONBuilder payload, Session session) {
try { try {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair))); assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair)));
@ -487,7 +490,7 @@ public class RegistrationTest {
StringBuilder expectedPayload = new StringBuilder(); StringBuilder expectedPayload = new StringBuilder();
expectedPayload.append('{'); expectedPayload.append('{');
expectedPayload.append("\"account\":\"").append(resourceUri).append("\","); expectedPayload.append("\"account\":\"").append(resourceUrl).append("\",");
expectedPayload.append("\"newKey\":{"); expectedPayload.append("\"newKey\":{");
expectedPayload.append("\"kty\":\"").append(TestUtils.D_KTY).append("\","); expectedPayload.append("\"kty\":\"").append(TestUtils.D_KTY).append("\",");
expectedPayload.append("\"e\":\"").append(TestUtils.D_E).append("\","); expectedPayload.append("\"e\":\"").append(TestUtils.D_E).append("\",");
@ -506,12 +509,12 @@ public class RegistrationTest {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return resourceUri; 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) { Session session = new Session(new URI(TestUtils.ACME_SERVER_URI), oldKeyPair) {
@Override @Override
@ -522,7 +525,7 @@ public class RegistrationTest {
assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair))); assertThat(session.getKeyPair(), is(sameInstance(oldKeyPair)));
Registration registration = new Registration(session, resourceUri); Registration registration = new Registration(session, resourceUrl);
registration.changeKey(newKeyPair); registration.changeKey(newKeyPair);
assertThat(session.getKeyPair(), is(sameInstance(newKeyPair))); assertThat(session.getKeyPair(), is(sameInstance(newKeyPair)));
@ -536,7 +539,7 @@ public class RegistrationTest {
TestableConnectionProvider provider = new TestableConnectionProvider(); TestableConnectionProvider provider = new TestableConnectionProvider();
Session session = provider.createSession(); Session session = provider.createSession();
Registration registration = new Registration(session, locationUri); Registration registration = new Registration(session, locationUrl);
registration.changeKey(session.getKeyPair()); registration.changeKey(session.getKeyPair());
provider.close(); provider.close();
@ -549,11 +552,11 @@ public class RegistrationTest {
public void testDeactivate() throws Exception { public void testDeactivate() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
JSON json = claims.toJSON(); JSON json = claims.toJSON();
assertThat(json.get("resource").asString(), is("reg")); assertThat(json.get("resource").asString(), is("reg"));
assertThat(json.get("status").asString(), is("deactivated")); assertThat(json.get("status").asString(), is("deactivated"));
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
assertThat(session, is(notNullValue())); 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(); registration.deactivate();
provider.close(); provider.close();
@ -580,8 +583,8 @@ public class RegistrationTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
assertThat(claims.toString(), sameJSONAs(getJson("modifyRegistration"))); assertThat(claims.toString(), sameJSONAs(getJson("modifyRegistration")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
} }
@ -598,12 +601,12 @@ public class RegistrationTest {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
return locationUri; return locationUrl;
} }
@Override @Override
public URI getLink(String relation) { public URI getLinkAsURI(String relation) {
switch(relation) { switch(relation) {
case "terms-of-service": return agreementUri; case "terms-of-service": return agreementUri;
default: return null; 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(); EditableRegistration editable = registration.modify();
assertThat(editable, notNullValue()); assertThat(editable, notNullValue());
@ -621,7 +624,7 @@ public class RegistrationTest {
editable.getContacts().add(URI.create("mailto:foo3@example.com")); editable.getContacts().add(URI.create("mailto:foo3@example.com"));
editable.commit(); editable.commit();
assertThat(registration.getLocation(), is(locationUri)); assertThat(registration.getLocation(), is(locationUrl));
assertThat(registration.getAgreement(), is(agreementUri)); assertThat(registration.getAgreement(), is(agreementUri));
assertThat(registration.getContacts().size(), is(2)); assertThat(registration.getContacts().size(), is(2));
assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com"))); assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com")));

View File

@ -16,7 +16,7 @@ package org.shredzone.acme4j;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Mockito.*; 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.io.IOException;
import java.net.URI; import java.net.URI;
@ -203,13 +203,13 @@ public class SessionTest {
}; };
}; };
assertThat(session.resourceUri(Resource.NEW_REG), assertThat(session.resourceUrl(Resource.NEW_REG),
is(URI.create("https://example.com/acme/new-reg"))); is(url("https://example.com/acme/new-reg")));
assertThat(session.resourceUri(Resource.NEW_AUTHZ), assertThat(session.resourceUrl(Resource.NEW_AUTHZ),
is(URI.create("https://example.com/acme/new-authz"))); is(url("https://example.com/acme/new-authz")));
assertThat(session.resourceUri(Resource.NEW_CERT), assertThat(session.resourceUrl(Resource.NEW_CERT),
is(URI.create("https://example.com/acme/new-cert"))); is(url("https://example.com/acme/new-cert")));
assertThat(session.resourceUri(Resource.REVOKE_CERT), assertThat(session.resourceUrl(Resource.REVOKE_CERT),
is(nullValue())); is(nullValue()));
Metadata meta = session.getMetadata(); Metadata meta = session.getMetadata();
@ -227,13 +227,13 @@ public class SessionTest {
* {@link Session} to assert * {@link Session} to assert
*/ */
private void assertSession(Session session) throws AcmeException { private void assertSession(Session session) throws AcmeException {
assertThat(session.resourceUri(Resource.NEW_REG), assertThat(session.resourceUrl(Resource.NEW_REG),
is(URI.create("https://example.com/acme/new-reg"))); is(url("https://example.com/acme/new-reg")));
assertThat(session.resourceUri(Resource.NEW_AUTHZ), assertThat(session.resourceUrl(Resource.NEW_AUTHZ),
is(URI.create("https://example.com/acme/new-authz"))); is(url("https://example.com/acme/new-authz")));
assertThat(session.resourceUri(Resource.NEW_CERT), assertThat(session.resourceUrl(Resource.NEW_CERT),
is(URI.create("https://example.com/acme/new-cert"))); is(url("https://example.com/acme/new-cert")));
assertThat(session.resourceUri(Resource.REVOKE_CERT), assertThat(session.resourceUrl(Resource.REVOKE_CERT),
is(nullValue())); is(nullValue()));
Metadata meta = session.getMetadata(); Metadata meta = session.getMetadata();

View File

@ -46,8 +46,8 @@ import org.shredzone.acme4j.toolbox.TestUtils;
*/ */
public class ChallengeTest { public class ChallengeTest {
private Session session; private Session session;
private URI resourceUri = URI.create("https://example.com/acme/some-resource"); private URL resourceUrl = url("https://example.com/acme/some-resource");
private URI locationUri = URI.create("https://example.com/acme/some-location"); private URL locationUrl = url("https://example.com/acme/some-location");
@Before @Before
public void setup() throws IOException { public void setup() throws IOException {
@ -61,8 +61,8 @@ public class ChallengeTest {
public void testChallenge() throws Exception { public void testChallenge() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -82,11 +82,11 @@ public class ChallengeTest {
provider.putTestChallenge(Http01Challenge.TYPE, new Http01Challenge(session)); 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.getType(), is(Http01Challenge.TYPE));
assertThat(challenge.getStatus(), is(Status.VALID)); assertThat(challenge.getStatus(), is(Status.VALID));
assertThat(challenge.getLocation(), is(locationUri)); assertThat(challenge.getLocation(), is(locationUrl));
assertThat(challenge.getToken(), is("IlirfxKKXAsHtmzK29Pj8A")); assertThat(challenge.getToken(), is("IlirfxKKXAsHtmzK29Pj8A"));
provider.close(); provider.close();
@ -111,7 +111,7 @@ public class ChallengeTest {
// Test unmarshalled values // Test unmarshalled values
assertThat(challenge.getType(), is("generic-01")); assertThat(challenge.getType(), is("generic-01"));
assertThat(challenge.getStatus(), is(Status.VALID)); 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.getValidated(), is(parseTimestamp("2015-12-12T17:19:36.336785823Z")));
assertThat(challenge.getError(), is(notNullValue())); assertThat(challenge.getError(), is(notNullValue()));
assertThat(challenge.getError().getType(), is(URI.create("urn:ietf:params:acme:error:connection"))); 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 { public void testTrigger() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
assertThat(uri, is(resourceUri)); assertThat(url, is(resourceUrl));
assertThat(claims.toString(), sameJSONAs(getJson("triggerHttpChallengeRequest"))); assertThat(claims.toString(), sameJSONAs(getJson("triggerHttpChallengeRequest")));
assertThat(session, is(notNullValue())); assertThat(session, is(notNullValue()));
} }
@ -181,7 +181,7 @@ public class ChallengeTest {
challenge.trigger(); challenge.trigger();
assertThat(challenge.getStatus(), is(Status.PENDING)); assertThat(challenge.getStatus(), is(Status.PENDING));
assertThat(challenge.getLocation(), is(locationUri)); assertThat(challenge.getLocation(), is(locationUrl));
provider.close(); provider.close();
} }
@ -193,8 +193,8 @@ public class ChallengeTest {
public void testUpdate() throws Exception { public void testUpdate() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -223,7 +223,7 @@ public class ChallengeTest {
challenge.update(); challenge.update();
assertThat(challenge.getStatus(), is(Status.VALID)); assertThat(challenge.getStatus(), is(Status.VALID));
assertThat(challenge.getLocation(), is(locationUri)); assertThat(challenge.getLocation(), is(locationUrl));
provider.close(); provider.close();
} }
@ -237,8 +237,8 @@ public class ChallengeTest {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -273,7 +273,7 @@ public class ChallengeTest {
} }
assertThat(challenge.getStatus(), is(Status.VALID)); assertThat(challenge.getStatus(), is(Status.VALID));
assertThat(challenge.getLocation(), is(locationUri)); assertThat(challenge.getLocation(), is(locationUrl));
provider.close(); provider.close();
} }
@ -291,7 +291,7 @@ public class ChallengeTest {
} }
try { try {
Challenge.bind(null, locationUri); Challenge.bind(null, locationUrl);
fail("session accepts null"); fail("session accepts null");
} catch (NullPointerException ex) { } catch (NullPointerException ex) {
// expected // expected
@ -305,8 +305,8 @@ public class ChallengeTest {
public void testBadBind() throws Exception { public void testBadBind() throws Exception {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
assertThat(uri, is(locationUri)); assertThat(url, is(locationUrl));
} }
@Override @Override
@ -323,7 +323,7 @@ public class ChallengeTest {
}; };
Session session = provider.createSession(); Session session = provider.createSession();
Challenge.bind(session, locationUri); Challenge.bind(session, locationUrl);
provider.close(); provider.close();
} }

View File

@ -16,6 +16,7 @@ package org.shredzone.acme4j.connector;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static org.shredzone.acme4j.toolbox.TestUtils.url;
import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs; import static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.io.ByteArrayInputStream; import java.io.ByteArrayInputStream;
@ -29,7 +30,6 @@ import java.time.Duration;
import java.time.Instant; import java.time.Instant;
import java.time.temporal.ChronoUnit; import java.time.temporal.ChronoUnit;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
@ -56,7 +56,7 @@ import org.shredzone.acme4j.toolbox.TestUtils;
*/ */
public class DefaultConnectionTest { public class DefaultConnectionTest {
private URI requestUri = URI.create("http://example.com/acme/");; private URL requestUrl = url("http://example.com/acme/");
private HttpURLConnection mockUrlConnection; private HttpURLConnection mockUrlConnection;
private HttpConnector mockHttpConnection; private HttpConnector mockHttpConnection;
private Session session; private Session session;
@ -66,7 +66,7 @@ public class DefaultConnectionTest {
mockUrlConnection = mock(HttpURLConnection.class); mockUrlConnection = mock(HttpURLConnection.class);
mockHttpConnection = mock(HttpConnector.class); mockHttpConnection = mock(HttpConnector.class);
when(mockHttpConnection.openConnection(requestUri)).thenReturn(mockUrlConnection); when(mockHttpConnection.openConnection(requestUrl)).thenReturn(mockUrlConnection);
session = TestUtils.session(); session = TestUtils.session();
session.setLocale(Locale.JAPAN); session.setLocale(Locale.JAPAN);
@ -144,8 +144,8 @@ public class DefaultConnectionTest {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection; conn.conn = mockUrlConnection;
URI location = conn.getLocation(); URL location = conn.getLocation();
assertThat(location, is(new URI("https://example.com/otherlocation"))); assertThat(location, is(url("https://example.com/otherlocation")));
} }
verify(mockUrlConnection).getHeaderField("Location"); verify(mockUrlConnection).getHeaderField("Location");
@ -163,8 +163,8 @@ public class DefaultConnectionTest {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection; conn.conn = mockUrlConnection;
URI location = conn.getLocation(); URL location = conn.getLocation();
assertThat(location, is(new URI("https://example.org/otherlocation"))); assertThat(location, is(url("https://example.org/otherlocation")));
} }
verify(mockUrlConnection).getHeaderField("Location"); verify(mockUrlConnection).getHeaderField("Location");
@ -191,48 +191,15 @@ public class DefaultConnectionTest {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection; conn.conn = mockUrlConnection;
assertThat(conn.getLink("next"), is(new URI("https://example.com/acme/new-authz"))); assertThat(conn.getLink("next"), is(url("https://example.com/acme/new-authz")));
assertThat(conn.getLink("recover"), is(new URI("https://example.org/recover-reg"))); assertThat(conn.getLink("recover"), is(url("https://example.org/recover-reg")));
assertThat(conn.getLink("terms-of-service"), is(new URI("https://example.com/acme/terms"))); assertThat(conn.getLink("terms-of-service"), is(url("https://example.com/acme/terms")));
assertThat(conn.getLink("secret-stuff"), is(nullValue())); assertThat(conn.getLink("secret-stuff"), is(nullValue()));
}
}
/** assertThat(conn.getLinkAsURI("next"), is(new URI("https://example.com/acme/new-authz")));
* Test that multiple link headers are evaluated. 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")));
@Test assertThat(conn.getLinkAsURI("secret-stuff"), is(nullValue()));
public void testGetMultiLink() {
Map<String, List<String>> headers = new HashMap<>();
headers.put("Link", Arrays.asList(
"<https://example.com/acme/terms1>; rel=\"terms-of-service\"",
"<https://example.com/acme/terms2>; rel=\"terms-of-service\"",
"<https://example.com/acme/terms3>; 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<String, List<String>> headers = Collections.emptyMap();
when(mockUrlConnection.getHeaderFields()).thenReturn(headers);
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection;
assertThat(conn.getLinks("something"), is(nullValue()));
} }
} }
@ -243,7 +210,7 @@ public class DefaultConnectionTest {
public void testNoLocation() throws Exception { public void testNoLocation() throws Exception {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection; conn.conn = mockUrlConnection;
URI location = conn.getLocation(); URL location = conn.getLocation();
assertThat(location, is(nullValue())); assertThat(location, is(nullValue()));
} }
@ -488,7 +455,7 @@ public class DefaultConnectionTest {
@Test @Test
public void testSendRequest() throws Exception { public void testSendRequest() throws Exception {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.sendRequest(requestUri, session); conn.sendRequest(requestUrl, session);
} }
verify(mockUrlConnection).setRequestMethod("GET"); verify(mockUrlConnection).setRequestMethod("GET");
@ -526,7 +493,7 @@ public class DefaultConnectionTest {
}) { }) {
JSONBuilder cb = new JSONBuilder(); JSONBuilder cb = new JSONBuilder();
cb.put("foo", 123).put("bar", "a-string"); 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"); verify(mockUrlConnection).setRequestMethod("HEAD");
@ -551,7 +518,7 @@ public class DefaultConnectionTest {
StringBuilder expectedHeader = new StringBuilder(); StringBuilder expectedHeader = new StringBuilder();
expectedHeader.append('{'); expectedHeader.append('{');
expectedHeader.append("\"nonce\":\"").append(Base64Url.encode(nonce1)).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("\"alg\":\"RS256\",");
expectedHeader.append("\"jwk\":{"); expectedHeader.append("\"jwk\":{");
expectedHeader.append("\"kty\":\"").append(TestUtils.KTY).append("\","); expectedHeader.append("\"kty\":\"").append(TestUtils.KTY).append("\",");
@ -576,7 +543,7 @@ public class DefaultConnectionTest {
public void testSendSignedRequestNoNonce() throws Exception { public void testSendSignedRequestNoNonce() throws Exception {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) { try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
JSONBuilder cb = new JSONBuilder(); JSONBuilder cb = new JSONBuilder();
conn.sendSignedRequest(requestUri, cb, DefaultConnectionTest.this.session); conn.sendSignedRequest(requestUrl, cb, DefaultConnectionTest.this.session);
} }
} }

View File

@ -14,8 +14,8 @@
package org.shredzone.acme4j.connector; package org.shredzone.acme4j.connector;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.security.cert.X509Certificate; import java.security.cert.X509Certificate;
import java.util.Collection;
import org.shredzone.acme4j.Session; import org.shredzone.acme4j.Session;
import org.shredzone.acme4j.exception.AcmeException; import org.shredzone.acme4j.exception.AcmeException;
@ -29,12 +29,12 @@ import org.shredzone.acme4j.toolbox.JSONBuilder;
public class DummyConnection implements Connection { public class DummyConnection implements Connection {
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public void sendSignedRequest(URI uri, JSONBuilder claims, Session session) { public void sendSignedRequest(URL url, JSONBuilder claims, Session session) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -64,17 +64,17 @@ public class DummyConnection implements Connection {
} }
@Override @Override
public URI getLocation() { public URL getLocation() {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public URI getLink(String relation) { public URL getLink(String relation) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@Override @Override
public Collection<URI> getLinks(String relation) { public URI getLinkAsURI(String relation) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -17,10 +17,10 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import static org.mockito.ArgumentMatchers.anyInt; import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.*; import static org.mockito.Mockito.*;
import static org.shredzone.acme4j.toolbox.TestUtils.url;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import org.junit.Test; import org.junit.Test;
@ -59,7 +59,7 @@ public class HttpConnectorTest {
@Category(HttpURLConnection.class) @Category(HttpURLConnection.class)
public void testOpenConnection() throws IOException, URISyntaxException { public void testOpenConnection() throws IOException, URISyntaxException {
HttpConnector connector = new HttpConnector(); 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())); assertThat(conn, not(nullValue()));
conn.connect(); conn.connect();
assertThat(conn.getResponseCode(), is(HttpURLConnection.HTTP_OK)); assertThat(conn.getResponseCode(), is(HttpURLConnection.HTTP_OK));

View File

@ -15,11 +15,11 @@ package org.shredzone.acme4j.connector;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat; 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.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URL;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
@ -43,19 +43,19 @@ public class ResourceIteratorTest {
private final int RESOURCES_PER_PAGE = 5; private final int RESOURCES_PER_PAGE = 5;
private final String TYPE = "authorizations"; private final String TYPE = "authorizations";
private List<URI> resourceURIs = new ArrayList<>(PAGES * RESOURCES_PER_PAGE); private List<URL> resourceURLs = new ArrayList<>(PAGES * RESOURCES_PER_PAGE);
private List<URI> pageURIs = new ArrayList<>(PAGES); private List<URL> pageURLs = new ArrayList<>(PAGES);
@Before @Before
public void setup() { public void setup() {
resourceURIs.clear(); resourceURLs.clear();
for (int ix = 0; ix < RESOURCES_PER_PAGE * PAGES; ix++) { 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++) { 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 @Test
public void iteratorTest() throws IOException { public void iteratorTest() throws IOException {
List<URI> result = new ArrayList<>(); List<URL> result = new ArrayList<>();
Iterator<Authorization> it = createIterator(pageURIs.get(0)); Iterator<Authorization> it = createIterator(pageURLs.get(0));
while (it.hasNext()) { while (it.hasNext()) {
result.add(it.next().getLocation()); result.add(it.next().getLocation());
} }
assertThat(result, is(equalTo(resourceURIs))); assertThat(result, is(equalTo(resourceURLs)));
} }
/** /**
@ -91,9 +91,9 @@ public class ResourceIteratorTest {
*/ */
@Test @Test
public void nextHasNextTest() throws IOException { public void nextHasNextTest() throws IOException {
List<URI> result = new ArrayList<>(); List<URL> result = new ArrayList<>();
Iterator<Authorization> it = createIterator(pageURIs.get(0)); Iterator<Authorization> it = createIterator(pageURLs.get(0));
assertThat(it.hasNext(), is(true)); assertThat(it.hasNext(), is(true));
assertThat(it.hasNext(), is(true)); assertThat(it.hasNext(), is(true));
@ -107,7 +107,7 @@ public class ResourceIteratorTest {
assertThat(it.hasNext(), is(false)); 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) @Test(expected = UnsupportedOperationException.class)
public void removeTest() throws IOException { public void removeTest() throws IOException {
Iterator<Authorization> it = createIterator(pageURIs.get(0)); Iterator<Authorization> it = createIterator(pageURLs.get(0));
it.next(); it.next();
it.remove(); // throws UnsupportedOperationException it.remove(); // throws UnsupportedOperationException
} }
@ -127,13 +127,13 @@ public class ResourceIteratorTest {
* URI of the first page * URI of the first page
* @return Created {@link Iterator} * @return Created {@link Iterator}
*/ */
private Iterator<Authorization> createIterator(URI first) throws IOException { private Iterator<Authorization> createIterator(URL first) throws IOException {
TestableConnectionProvider provider = new TestableConnectionProvider() { TestableConnectionProvider provider = new TestableConnectionProvider() {
private int ix; private int ix;
@Override @Override
public void sendRequest(URI uri, Session session) { public void sendRequest(URL url, Session session) {
ix = pageURIs.indexOf(uri); ix = pageURLs.indexOf(url);
assertThat(ix, is(greaterThanOrEqualTo(0))); assertThat(ix, is(greaterThanOrEqualTo(0)));
} }
@ -149,15 +149,15 @@ public class ResourceIteratorTest {
int end = (ix + 1) * RESOURCES_PER_PAGE; int end = (ix + 1) * RESOURCES_PER_PAGE;
JSONBuilder cb = new JSONBuilder(); 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()); return JSON.parse(cb.toString());
} }
@Override @Override
public URI getLink(String relation) { public URL getLink(String relation) {
if ("next".equals(relation) && (ix + 1 < pageURIs.size())) { if ("next".equals(relation) && (ix + 1 < pageURLs.size())) {
return pageURIs.get(ix + 1); return pageURLs.get(ix + 1);
} }
return null; return null;
} }

View File

@ -18,6 +18,7 @@ import static org.junit.Assert.assertThat;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.security.KeyPair; import java.security.KeyPair;
import java.util.ServiceLoader; import java.util.ServiceLoader;
@ -92,7 +93,7 @@ public class SessionProviderTest {
} }
@Override @Override
public URI resolve(URI serverUri) { public URL resolve(URI serverUri) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -120,7 +121,7 @@ public class SessionProviderTest {
} }
@Override @Override
public URI resolve(URI serverUri) { public URL resolve(URI serverUri) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -15,8 +15,9 @@ package org.shredzone.acme4j.exception;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat; 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; import org.junit.Test;
@ -28,13 +29,13 @@ public class AcmeConflictExceptionTest {
@Test @Test
public void testAcmeConflictException() { public void testAcmeConflictException() {
String msg = "Account already exists"; String msg = "Account already exists";
URI locationUri = URI.create("http://example.com/location/123"); URL locationUrl = url("http://example.com/location/123");
AcmeConflictException ex AcmeConflictException ex
= new AcmeConflictException(msg, locationUri); = new AcmeConflictException(msg, locationUrl);
assertThat(ex.getMessage(), is(msg)); assertThat(ex.getMessage(), is(msg));
assertThat(ex.getLocation(), is(locationUri)); assertThat(ex.getLocation(), is(locationUrl));
} }
} }

View File

@ -17,11 +17,12 @@ import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.*; 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 static uk.co.datumedge.hamcrest.json.SameJSONAs.sameJSONAs;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicBoolean;
import org.junit.Test; import org.junit.Test;
@ -56,7 +57,7 @@ public class AbstractAcmeProviderTest {
} }
@Override @Override
public URI resolve(URI serverUri) { public URL resolve(URI serverUri) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
@ -79,7 +80,7 @@ public class AbstractAcmeProviderTest {
@Test @Test
public void testResources() throws Exception { public void testResources() throws Exception {
final URI testServerUri = new URI("http://example.com/acme"); 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 Connection connection = mock(Connection.class);
final Session session = mock(Session.class); final Session session = mock(Session.class);
@ -99,16 +100,16 @@ public class AbstractAcmeProviderTest {
} }
@Override @Override
public URI resolve(URI serverUri) { public URL resolve(URI serverUri) {
assertThat(serverUri, is(testServerUri)); assertThat(serverUri, is(testServerUri));
return testResolvedUri; return testResolvedUrl;
} }
}; };
JSON map = provider.directory(session, testServerUri); JSON map = provider.directory(session, testServerUri);
assertThat(map.toString(), sameJSONAs(TestUtils.getJson("directory"))); 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).accept(any(Integer.class));
verify(connection).updateSession(any(Session.class)); verify(connection).updateSession(any(Session.class));
verify(connection).readJsonResponse(); verify(connection).readJsonResponse();
@ -131,7 +132,7 @@ public class AbstractAcmeProviderTest {
} }
@Override @Override
public URI resolve(URI serverUri) { public URL resolve(URI serverUri) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }
}; };

View File

@ -16,8 +16,10 @@ package org.shredzone.acme4j.provider;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.assertThat; import static org.junit.Assert.assertThat;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import java.net.URL;
import org.junit.Test; import org.junit.Test;
@ -42,13 +44,13 @@ public class GenericAcmeProviderTest {
* Test if the provider resolves the URI correctly. * Test if the provider resolves the URI correctly.
*/ */
@Test @Test
public void testResolve() throws URISyntaxException { public void testResolve() throws URISyntaxException, MalformedURLException {
URI serverUri = new URI("http://example.com/acme"); URI serverUri = new URI("http://example.com/acme");
GenericAcmeProvider provider = new GenericAcmeProvider(); GenericAcmeProvider provider = new GenericAcmeProvider();
URI resolvedUri = provider.resolve(serverUri); URL resolvedUrl = provider.resolve(serverUri);
assertThat(resolvedUri, is(equalTo(serverUri))); assertThat(resolvedUrl, is(equalTo(serverUri.toURL())));
} }
} }

View File

@ -15,6 +15,7 @@ package org.shredzone.acme4j.provider;
import java.io.IOException; import java.io.IOException;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.util.HashMap; import java.util.HashMap;
import java.util.Map; import java.util.Map;
@ -42,9 +43,9 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
* @param r * @param r
* {@link Resource} to be mapped * {@link Resource} to be mapped
* @param u * @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); directory.put(r.path(), u);
} }
@ -75,7 +76,7 @@ public class TestableConnectionProvider extends DummyConnection implements AcmeP
} }
@Override @Override
public URI resolve(URI serverUri) { public URL resolve(URI serverUri) {
throw new UnsupportedOperationException(); throw new UnsupportedOperationException();
} }

View File

@ -15,6 +15,7 @@ package org.shredzone.acme4j.provider.letsencrypt;
import static org.hamcrest.Matchers.is; import static org.hamcrest.Matchers.is;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.shredzone.acme4j.toolbox.TestUtils.url;
import java.net.URI; import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
@ -52,10 +53,10 @@ public class LetsEncryptAcmeProviderTest {
public void testResolve() throws URISyntaxException { public void testResolve() throws URISyntaxException {
LetsEncryptAcmeProvider provider = new LetsEncryptAcmeProvider(); 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(url(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/")), is(url(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/v01")), is(url(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/staging")), is(url(STAGING_DIRECTORY_URI)));
try { try {
provider.resolve(new URI("acme://letsencrypt.org/v99")); provider.resolve(new URI("acme://letsencrypt.org/v99"));

View File

@ -15,10 +15,10 @@ package org.shredzone.acme4j.provider.letsencrypt;
import static org.hamcrest.Matchers.*; import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*; import static org.junit.Assert.*;
import static org.shredzone.acme4j.toolbox.TestUtils.url;
import java.io.IOException; import java.io.IOException;
import java.net.HttpURLConnection; import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException; import java.net.URISyntaxException;
import javax.net.ssl.HttpsURLConnection; import javax.net.ssl.HttpsURLConnection;
@ -46,7 +46,7 @@ public class LetsEncryptHttpConnectorTest {
try { try {
HttpURLConnection goodConn = connector.openConnection( 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))); assertThat(goodConn, is(instanceOf(HttpsURLConnection.class)));
goodConn.connect(); goodConn.connect();
} catch (SSLHandshakeException ex) { } catch (SSLHandshakeException ex) {
@ -55,7 +55,7 @@ public class LetsEncryptHttpConnectorTest {
try { try {
HttpURLConnection badConn = connector.openConnection( HttpURLConnection badConn = connector.openConnection(
new URI("https://www.google.com")); url("https://www.google.com"));
assertThat(badConn, is(instanceOf(HttpsURLConnection.class))); assertThat(badConn, is(instanceOf(HttpsURLConnection.class)));
badConn.connect(); badConn.connect();
fail("Connection accepts foreign certificate"); fail("Connection accepts foreign certificate");

View File

@ -17,7 +17,9 @@ import java.io.ByteArrayOutputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URI; import java.net.URI;
import java.net.URL;
import java.security.InvalidAlgorithmParameterException; import java.security.InvalidAlgorithmParameterException;
import java.security.KeyFactory; import java.security.KeyFactory;
import java.security.KeyPair; import java.security.KeyPair;
@ -112,6 +114,22 @@ public final class TestUtils {
return JSON.parse(getJson(key)); 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. * Creates a {@link Session} instance. It uses {@link #ACME_SERVER_URI} as server URI.
*/ */

View File

@ -110,7 +110,7 @@ public class ClientTest {
Certificate certificate = reg.requestCertificate(csrb.getEncoded()); Certificate certificate = reg.requestCertificate(csrb.getEncoded());
LOG.info("Success! The certificate for domains " + domains + " has been generated!"); 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. // Download the leaf certificate and certificate chain.
X509Certificate cert = certificate.download(); X509Certificate cert = certificate.download();
@ -151,9 +151,9 @@ public class ClientTest {
* created. * created.
* <p> * <p>
* This is a simple way of finding your {@link Registration}. A better way is to get * 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 * 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 * @param session
* {@link Session} to bind with * {@link Session} to bind with
@ -165,7 +165,7 @@ public class ClientTest {
try { try {
// Try to create a new Registration. // Try to create a new Registration.
reg = new RegistrationBuilder().create(session); 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. // 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. // 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 // The Key Pair is already registered. getLocation() contains the
// URL of the existing registration's location. Bind it to the session. // URL of the existing registration's location. Bind it to the session.
reg = Registration.bind(session, ex.getLocation()); 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; return reg;

View File

@ -2,6 +2,10 @@
This document will help you migrate your code to the latest _acme4j_ version. 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 ## 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. 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.

View File

@ -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: 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. * `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. 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. 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 ## 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.

View File

@ -56,12 +56,12 @@ If your final certificate will contain further domains or subdomains, repeat the
## Update an Authorization ## 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 ```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. 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 ```java
Challenge originalChallenge = ... // some Challenge instance Challenge originalChallenge = ... // some Challenge instance
URI challengeUri = originalChallenge.getLocation(); URL challengeUrl = originalChallenge.getLocation();
``` ```
Later, you restore the `Challenge` object by invoking `Challenge.bind()`. Later, you restore the `Challenge` object by invoking `Challenge.bind()`.
```java ```java
URI challengeUri = ... // challenge URI URL challengeUrl = ... // challenge URL
Challenge restoredChallenge = Challenge.bind(session, challengeUri); Challenge restoredChallenge = Challenge.bind(session, challengeUrl);
``` ```
The `restoredChallenge` already reflects the current state of the challenge. The `restoredChallenge` already reflects the current state of the challenge.

View File

@ -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); 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. 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: To recreate a `Certificate` object from the location, just bind it:
```java ```java
URI locationUri = ... // location URI from cert.getLocation() URL locationUrl = ... // location URL from cert.getLocation()
Certificate cert = Certificate.bind(session, locationUri); Certificate cert = Certificate.bind(session, locationUrl);
``` ```
### Saving Certificates ### Saving Certificates

View File

@ -2,7 +2,7 @@
If it is the first time you connect to the ACME server, you need to register your account key. 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 ```java
RegistrationBuilder builder = new RegistrationBuilder(); RegistrationBuilder builder = new RegistrationBuilder();
@ -10,10 +10,10 @@ builder.addContact("mailto:acme@example.com");
Registration registration = builder.create(session); 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`. The following example will create a new `Registration` and restore an existing `Registration`.

View File

@ -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: 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 ```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 ## Serialization