mirror of https://github.com/shred/acme4j
Offer iterators of authorizations and certificates
parent
68b7560f2f
commit
cef5984f81
|
@ -23,6 +23,7 @@ import java.util.ArrayList;
|
||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -31,6 +32,7 @@ import org.jose4j.jws.JsonWebSignature;
|
||||||
import org.jose4j.lang.JoseException;
|
import org.jose4j.lang.JoseException;
|
||||||
import org.shredzone.acme4j.connector.Connection;
|
import org.shredzone.acme4j.connector.Connection;
|
||||||
import org.shredzone.acme4j.connector.Resource;
|
import org.shredzone.acme4j.connector.Resource;
|
||||||
|
import org.shredzone.acme4j.connector.ResourceIterator;
|
||||||
import org.shredzone.acme4j.exception.AcmeException;
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
import org.shredzone.acme4j.exception.AcmeNetworkException;
|
import org.shredzone.acme4j.exception.AcmeNetworkException;
|
||||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
|
@ -50,6 +52,8 @@ 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 URI certificates;
|
||||||
private Status status;
|
private Status status;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -96,6 +100,48 @@ public class Registration extends AcmeResource {
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link Iterator} of all {@link Authorization} belonging to this
|
||||||
|
* {@link Registration}.
|
||||||
|
* <p>
|
||||||
|
* Using the iterator will initiate one or more requests to the ACME server.
|
||||||
|
*
|
||||||
|
* @return {@link Iterator} instance that returns {@link Authorization} objects.
|
||||||
|
* {@link Iterator#hasNext()} and {@link Iterator#next()} may throw
|
||||||
|
* {@link AcmeProtocolException} if a batch of authorization URIs could not be
|
||||||
|
* fetched from the server.
|
||||||
|
*/
|
||||||
|
public Iterator<Authorization> getAuthorizations() throws AcmeException {
|
||||||
|
LOG.debug("getAuthorizations");
|
||||||
|
return new ResourceIterator<Authorization>(getSession(), "authorizations", authorizations) {
|
||||||
|
@Override
|
||||||
|
protected Authorization create(Session session, URI uri) {
|
||||||
|
return Authorization.bind(session, uri);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns an {@link Iterator} of all {@link Certificate} belonging to this
|
||||||
|
* {@link Registration}.
|
||||||
|
* <p>
|
||||||
|
* Using the iterator will initiate one or more requests to the ACME server.
|
||||||
|
*
|
||||||
|
* @return {@link Iterator} instance that returns {@link Certificate} objects.
|
||||||
|
* {@link Iterator#hasNext()} and {@link Iterator#next()} may throw
|
||||||
|
* {@link AcmeProtocolException} if a batch of certificate URIs could not be
|
||||||
|
* fetched from the server.
|
||||||
|
*/
|
||||||
|
public Iterator<Certificate> getCertificates() throws AcmeException {
|
||||||
|
LOG.debug("getCertificates");
|
||||||
|
return new ResourceIterator<Certificate>(getSession(), "certificates", certificates) {
|
||||||
|
@Override
|
||||||
|
protected Certificate create(Session session, URI uri) {
|
||||||
|
return Certificate.bind(session, uri);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Updates the registration to the current account status.
|
* Updates the registration to the current account status.
|
||||||
*/
|
*/
|
||||||
|
@ -294,6 +340,26 @@ public class Registration extends AcmeResource {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (json.containsKey("authorizations")) {
|
||||||
|
try {
|
||||||
|
this.authorizations = new URI((String) json.get("authorizations"));
|
||||||
|
} catch (ClassCastException | URISyntaxException ex) {
|
||||||
|
throw new AcmeProtocolException("Illegal authorizations URI", ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.authorizations = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (json.containsKey("certificates")) {
|
||||||
|
try {
|
||||||
|
this.certificates = new URI((String) json.get("certificates"));
|
||||||
|
} catch (ClassCastException | URISyntaxException ex) {
|
||||||
|
throw new AcmeProtocolException("Illegal certificates URI", ex);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this.certificates = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (json.containsKey("status")) {
|
if (json.containsKey("status")) {
|
||||||
this.status = Status.parse((String) json.get("status"));
|
this.status = Status.parse((String) json.get("status"));
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,177 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Richard "Shred" Körber
|
||||||
|
* http://acme4j.shredzone.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
package org.shredzone.acme4j.connector;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.net.URISyntaxException;
|
||||||
|
import java.util.ArrayDeque;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.Deque;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import org.shredzone.acme4j.AcmeResource;
|
||||||
|
import org.shredzone.acme4j.Session;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeException;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeNetworkException;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An {@link Iterator} that fetches a batch of URIs from the ACME server, and
|
||||||
|
* generates {@link AcmeResource} instances.
|
||||||
|
*
|
||||||
|
* @author Richard "Shred" Körber
|
||||||
|
*/
|
||||||
|
public abstract class ResourceIterator<T extends AcmeResource> implements Iterator<T> {
|
||||||
|
|
||||||
|
private final Session session;
|
||||||
|
private final String field;
|
||||||
|
private final Deque<URI> uriList = new ArrayDeque<>();
|
||||||
|
private boolean eol = false;
|
||||||
|
private URI nextUri;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link ResourceIterator}.
|
||||||
|
*
|
||||||
|
* @param session
|
||||||
|
* {@link Session} to bind this iterator to
|
||||||
|
* @param field
|
||||||
|
* Field name to be used in the JSON response
|
||||||
|
* @param start
|
||||||
|
* URI of the first JSON array, may be {@code null} for an empty iterator
|
||||||
|
*/
|
||||||
|
public ResourceIterator(Session session, String field, URI start) {
|
||||||
|
this.session = session;
|
||||||
|
this.field = field;
|
||||||
|
this.nextUri = start;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if there is another object in the result.
|
||||||
|
*
|
||||||
|
* @throws AcmeProtocolException
|
||||||
|
* if the next batch of URIs could not be fetched from the server
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public boolean hasNext() {
|
||||||
|
if (eol) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uriList.isEmpty()) {
|
||||||
|
fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (uriList.isEmpty()) {
|
||||||
|
eol = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return !uriList.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the next object of the result.
|
||||||
|
*
|
||||||
|
* @throws AcmeProtocolException
|
||||||
|
* if the next batch of URIs could not be fetched from the server
|
||||||
|
* @throws NoSuchElementException
|
||||||
|
* if there are no more entries
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public T next() {
|
||||||
|
if (!eol && uriList.isEmpty()) {
|
||||||
|
fetch();
|
||||||
|
}
|
||||||
|
|
||||||
|
URI next = uriList.poll();
|
||||||
|
if (next == null) {
|
||||||
|
eol = true;
|
||||||
|
throw new NoSuchElementException("no more " + field);
|
||||||
|
}
|
||||||
|
|
||||||
|
return create(session, next);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unsupported operation, only here to satisfy the {@link Iterator} interface.
|
||||||
|
*/
|
||||||
|
@Override
|
||||||
|
public void remove() {
|
||||||
|
throw new UnsupportedOperationException("cannot remove " + field);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link AcmeResource} object by binding it to the {@link Session} and
|
||||||
|
* using the given {@link URI}.
|
||||||
|
*
|
||||||
|
* @param session
|
||||||
|
* {@link Session} to bind the object to
|
||||||
|
* @param uri
|
||||||
|
* {@link URI} of the resource
|
||||||
|
* @return Created object
|
||||||
|
*/
|
||||||
|
protected abstract T create(Session session, URI uri);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Fetches the next batch of URIs. Handles exceptions. Does nothing if there is no
|
||||||
|
* URI of the next batch.
|
||||||
|
*/
|
||||||
|
private void fetch() {
|
||||||
|
if (nextUri == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
readAndQueue();
|
||||||
|
} catch (AcmeException ex) {
|
||||||
|
throw new AcmeProtocolException("failed to read next set of " + field, ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reads the next batch of URIs from the server, and fills the queue with the URIs. If
|
||||||
|
* there is a "next" header, it is used for the next batch of URIs.
|
||||||
|
*/
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
private void readAndQueue() throws AcmeException {
|
||||||
|
try (Connection conn = session.provider().connect()) {
|
||||||
|
int rc = conn.sendRequest(nextUri);
|
||||||
|
if (rc != HttpURLConnection.HTTP_OK) {
|
||||||
|
conn.throwAcmeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
Map<String, Object> json = conn.readJsonResponse();
|
||||||
|
try {
|
||||||
|
Collection<String> array = (Collection<String>) json.get(field);
|
||||||
|
if (array != null) {
|
||||||
|
for (String uri : array) {
|
||||||
|
uriList.add(new URI(uri));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (ClassCastException ex) {
|
||||||
|
throw new AcmeProtocolException("Expected an array");
|
||||||
|
} catch (URISyntaxException ex) {
|
||||||
|
throw new AcmeProtocolException("Invalid URI", ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
nextUri = conn.getLink("next");
|
||||||
|
} catch (IOException ex) {
|
||||||
|
throw new AcmeNetworkException(ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -24,6 +24,9 @@ import java.net.URI;
|
||||||
import java.net.URISyntaxException;
|
import java.net.URISyntaxException;
|
||||||
import java.security.KeyPair;
|
import java.security.KeyPair;
|
||||||
import java.security.cert.X509Certificate;
|
import java.security.cert.X509Certificate;
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Iterator;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import org.jose4j.jws.JsonWebSignature;
|
import org.jose4j.jws.JsonWebSignature;
|
||||||
|
@ -77,17 +80,39 @@ public class RegistrationTest {
|
||||||
@Test
|
@Test
|
||||||
public void testUpdateRegistration() throws AcmeException, IOException {
|
public void testUpdateRegistration() throws AcmeException, IOException {
|
||||||
TestableConnectionProvider provider = new TestableConnectionProvider() {
|
TestableConnectionProvider provider = new TestableConnectionProvider() {
|
||||||
|
private Map<String, Object> jsonResponse;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session) {
|
public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session) {
|
||||||
assertThat(uri, is(locationUri));
|
assertThat(uri, is(locationUri));
|
||||||
assertThat(claims.toString(), sameJSONAs(getJson("updateRegistration")));
|
assertThat(claims.toString(), sameJSONAs(getJson("updateRegistration")));
|
||||||
assertThat(session, is(notNullValue()));
|
assertThat(session, is(notNullValue()));
|
||||||
|
jsonResponse = getJsonAsMap("updateRegistrationResponse");
|
||||||
return HttpURLConnection.HTTP_ACCEPTED;
|
return HttpURLConnection.HTTP_ACCEPTED;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendRequest(URI uri) {
|
||||||
|
if (URI.create("https://example.com/acme/reg/1/authz").equals(uri)) {
|
||||||
|
jsonResponse = new HashMap<>();
|
||||||
|
jsonResponse.put("authorizations",
|
||||||
|
Arrays.asList("https://example.com/acme/auth/1"));
|
||||||
|
return HttpURLConnection.HTTP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (URI.create("https://example.com/acme/reg/1/cert").equals(uri)) {
|
||||||
|
jsonResponse = new HashMap<>();
|
||||||
|
jsonResponse.put("certificates",
|
||||||
|
Arrays.asList("https://example.com/acme/cert/1"));
|
||||||
|
return HttpURLConnection.HTTP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
return HttpURLConnection.HTTP_NOT_FOUND;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Map<String, Object> readJsonResponse() {
|
public Map<String, Object> readJsonResponse() {
|
||||||
return getJsonAsMap("updateRegistrationResponse");
|
return jsonResponse;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@ -99,6 +124,7 @@ public class RegistrationTest {
|
||||||
public URI getLink(String relation) {
|
public URI getLink(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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -113,6 +139,18 @@ public class RegistrationTest {
|
||||||
assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com")));
|
assertThat(registration.getContacts().get(0), is(URI.create("mailto:foo2@example.com")));
|
||||||
assertThat(registration.getStatus(), is(Status.GOOD));
|
assertThat(registration.getStatus(), is(Status.GOOD));
|
||||||
|
|
||||||
|
Iterator<Authorization> authIt = registration.getAuthorizations();
|
||||||
|
assertThat(authIt, not(nullValue()));
|
||||||
|
assertThat(authIt.next().getLocation(),
|
||||||
|
is(URI.create("https://example.com/acme/auth/1")));
|
||||||
|
assertThat(authIt.hasNext(), is(false));
|
||||||
|
|
||||||
|
Iterator<Certificate> certIt = registration.getCertificates();
|
||||||
|
assertThat(certIt, not(nullValue()));
|
||||||
|
assertThat(certIt.next().getLocation(),
|
||||||
|
is(URI.create("https://example.com/acme/cert/1")));
|
||||||
|
assertThat(certIt.hasNext(), is(false));
|
||||||
|
|
||||||
provider.close();
|
provider.close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -193,8 +193,8 @@ public class SessionTest {
|
||||||
|
|
||||||
final AcmeProvider mockProvider = mock(AcmeProvider.class);
|
final AcmeProvider mockProvider = mock(AcmeProvider.class);
|
||||||
when(mockProvider.directory(
|
when(mockProvider.directory(
|
||||||
org.mockito.Matchers.any(Session.class),
|
ArgumentMatchers.any(Session.class),
|
||||||
org.mockito.Matchers.eq(serverUri)))
|
ArgumentMatchers.eq(serverUri)))
|
||||||
.thenReturn(TestUtils.getJsonAsMap("directoryNoMeta"));
|
.thenReturn(TestUtils.getJsonAsMap("directoryNoMeta"));
|
||||||
|
|
||||||
Session session = new Session(serverUri, keyPair) {
|
Session session = new Session(serverUri, keyPair) {
|
||||||
|
|
|
@ -0,0 +1,181 @@
|
||||||
|
/*
|
||||||
|
* acme4j - Java ACME client
|
||||||
|
*
|
||||||
|
* Copyright (C) 2016 Richard "Shred" Körber
|
||||||
|
* http://acme4j.shredzone.org
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
|
||||||
|
*/
|
||||||
|
package org.shredzone.acme4j.connector;
|
||||||
|
|
||||||
|
import static org.hamcrest.Matchers.*;
|
||||||
|
import static org.junit.Assert.assertThat;
|
||||||
|
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.HttpURLConnection;
|
||||||
|
import java.net.URI;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.NoSuchElementException;
|
||||||
|
|
||||||
|
import org.jose4j.json.JsonUtil;
|
||||||
|
import org.jose4j.lang.JoseException;
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.shredzone.acme4j.Authorization;
|
||||||
|
import org.shredzone.acme4j.Session;
|
||||||
|
import org.shredzone.acme4j.exception.AcmeProtocolException;
|
||||||
|
import org.shredzone.acme4j.provider.TestableConnectionProvider;
|
||||||
|
import org.shredzone.acme4j.util.ClaimBuilder;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Unit test for {@link ResourceIterator}.
|
||||||
|
*
|
||||||
|
* @author Richard "Shred" Körber
|
||||||
|
*/
|
||||||
|
public class ResourceIteratorTest {
|
||||||
|
|
||||||
|
private final int PAGES = 4;
|
||||||
|
private final int RESOURCES_PER_PAGE = 5;
|
||||||
|
private final String TYPE = "authorizations";
|
||||||
|
|
||||||
|
private List<URI> resourceURIs = new ArrayList<>(PAGES * RESOURCES_PER_PAGE);
|
||||||
|
private List<URI> pageURIs = new ArrayList<>(PAGES);
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
resourceURIs.clear();
|
||||||
|
for (int ix = 0; ix < RESOURCES_PER_PAGE * PAGES; ix++) {
|
||||||
|
resourceURIs.add(URI.create("https://example.com/acme/auth/" + ix));
|
||||||
|
}
|
||||||
|
|
||||||
|
pageURIs.clear();
|
||||||
|
for (int ix = 0; ix < PAGES; ix++) {
|
||||||
|
pageURIs.add(URI.create("https://example.com/acme/batch/" + ix));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if the {@link ResourceIterator} handles a {@code null} start URI.
|
||||||
|
*/
|
||||||
|
@Test(expected = NoSuchElementException.class)
|
||||||
|
public void nullTest() throws IOException {
|
||||||
|
Iterator<Authorization> it = createIterator(null);
|
||||||
|
|
||||||
|
assertThat(it, not(nullValue()));
|
||||||
|
assertThat(it.hasNext(), is(false));
|
||||||
|
it.next(); // throws NoSuchElementException
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test if the {@link ResourceIterator} returns all objects in the correct order.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void iteratorTest() throws IOException {
|
||||||
|
List<URI> result = new ArrayList<>();
|
||||||
|
|
||||||
|
Iterator<Authorization> it = createIterator(pageURIs.get(0));
|
||||||
|
while (it.hasNext()) {
|
||||||
|
result.add(it.next().getLocation());
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(result, is(equalTo(resourceURIs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test unusual {@link Iterator#next()} and {@link Iterator#hasNext()} usage.
|
||||||
|
*/
|
||||||
|
@Test
|
||||||
|
public void nextHasNextTest() throws IOException {
|
||||||
|
List<URI> result = new ArrayList<>();
|
||||||
|
|
||||||
|
Iterator<Authorization> it = createIterator(pageURIs.get(0));
|
||||||
|
assertThat(it.hasNext(), is(true));
|
||||||
|
assertThat(it.hasNext(), is(true));
|
||||||
|
|
||||||
|
// don't try this at home, kids...
|
||||||
|
try {
|
||||||
|
for (;;) {
|
||||||
|
result.add(it.next().getLocation());
|
||||||
|
}
|
||||||
|
} catch (NoSuchElementException ex) {
|
||||||
|
assertThat(it.hasNext(), is(false));
|
||||||
|
assertThat(it.hasNext(), is(false));
|
||||||
|
}
|
||||||
|
|
||||||
|
assertThat(result, is(equalTo(resourceURIs)));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test that {@link Iterator#remove()} fails.
|
||||||
|
*/
|
||||||
|
@Test(expected = UnsupportedOperationException.class)
|
||||||
|
public void removeTest() throws IOException {
|
||||||
|
Iterator<Authorization> it = createIterator(pageURIs.get(0));
|
||||||
|
it.next();
|
||||||
|
it.remove(); // throws UnsupportedOperationException
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new {@link Iterator} of {@link Authorization} objects.
|
||||||
|
*
|
||||||
|
* @param first
|
||||||
|
* URI of the first page
|
||||||
|
* @return Created {@link Iterator}
|
||||||
|
*/
|
||||||
|
private Iterator<Authorization> createIterator(URI first) throws IOException {
|
||||||
|
TestableConnectionProvider provider = new TestableConnectionProvider() {
|
||||||
|
private int ix;
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int sendRequest(URI uri) {
|
||||||
|
ix = pageURIs.indexOf(uri);
|
||||||
|
assertThat(ix, is(greaterThanOrEqualTo(0)));
|
||||||
|
return HttpURLConnection.HTTP_OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Map<String, Object> readJsonResponse() {
|
||||||
|
try {
|
||||||
|
int start = ix * RESOURCES_PER_PAGE;
|
||||||
|
int end = (ix + 1) * RESOURCES_PER_PAGE;
|
||||||
|
|
||||||
|
ClaimBuilder cb = new ClaimBuilder();
|
||||||
|
cb.array(TYPE, resourceURIs.subList(start, end).toArray());
|
||||||
|
|
||||||
|
// Make sure to use the JSON parser
|
||||||
|
return JsonUtil.parseJson(cb.toString());
|
||||||
|
} catch (JoseException ex) {
|
||||||
|
throw new AcmeProtocolException("Invalid JSON", ex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public URI getLink(String relation) {
|
||||||
|
if ("next".equals(relation) && (ix + 1 < pageURIs.size())) {
|
||||||
|
return pageURIs.get(ix + 1);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Session session = provider.createSession();
|
||||||
|
|
||||||
|
provider.close();
|
||||||
|
|
||||||
|
return new ResourceIterator<Authorization>(session, TYPE, first) {
|
||||||
|
@Override
|
||||||
|
protected Authorization create(Session session, URI uri) {
|
||||||
|
return Authorization.bind(session, uri);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -54,7 +54,9 @@ updateRegistration = \
|
||||||
updateRegistrationResponse = \
|
updateRegistrationResponse = \
|
||||||
{"agreement":"http://example.com/agreement.pdf",\
|
{"agreement":"http://example.com/agreement.pdf",\
|
||||||
"contact":["mailto:foo2@example.com"],\
|
"contact":["mailto:foo2@example.com"],\
|
||||||
"status":"good"}
|
"status":"good",\
|
||||||
|
"authorizations":"https://example.com/acme/reg/1/authz",\
|
||||||
|
"certificates":"https://example.com/acme/reg/1/cert"}
|
||||||
|
|
||||||
newAuthorizationRequest = \
|
newAuthorizationRequest = \
|
||||||
{"resource":"new-authz",\
|
{"resource":"new-authz",\
|
||||||
|
|
Loading…
Reference in New Issue