mirror of https://github.com/shred/acme4j
				
				
				
			Handle HTTP errors when fetching a nonce
The nonce is fetched via HEAD request. Before this fix, if there was a HTTP error, acme4j expected a Problem JSON body, which was not send because of the HEAD request, and lead to an AcmeProtocolException. Now either an AcmeException or AcmeRetryAfterException is thrown.pull/168/head
							parent
							
								
									aeff12088f
								
							
						
					
					
						commit
						6d5da63b8e
					
				| 
						 | 
				
			
			@ -152,6 +152,10 @@ public abstract class AcmeJsonResource extends AcmeResource {
 | 
			
		|||
            retryAfterOpt.ifPresent(instant -> LOG.debug("Retry-After: {}", instant));
 | 
			
		||||
            setRetryAfter(retryAfterOpt.orElse(null));
 | 
			
		||||
            return retryAfterOpt;
 | 
			
		||||
        } catch (AcmeRetryAfterException ex) {
 | 
			
		||||
            LOG.debug("Retry-After while attempting to read the resource", ex);
 | 
			
		||||
            setRetryAfter(ex.getRetryAfter());
 | 
			
		||||
            return Optional.of(ex.getRetryAfter());
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -51,6 +51,7 @@ import org.shredzone.acme4j.exception.AcmeException;
 | 
			
		|||
import org.shredzone.acme4j.exception.AcmeNetworkException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeRateLimitedException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeServerException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeUnauthorizedException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeUserActionRequiredException;
 | 
			
		||||
| 
						 | 
				
			
			@ -132,7 +133,12 @@ public class DefaultConnection implements Connection {
 | 
			
		|||
 | 
			
		||||
            var rc = getResponse().statusCode();
 | 
			
		||||
            if (rc != HTTP_OK && rc != HTTP_NO_CONTENT) {
 | 
			
		||||
                throwAcmeException();
 | 
			
		||||
                var message = "Server responded with HTTP " + rc + " while trying to retrieve a nonce";
 | 
			
		||||
                var retryAfterInstant = getRetryAfter();
 | 
			
		||||
                if (retryAfterInstant.isPresent()) {
 | 
			
		||||
                    throw new AcmeRetryAfterException(message, retryAfterInstant.get());
 | 
			
		||||
                };
 | 
			
		||||
                throw new AcmeException(message);
 | 
			
		||||
            }
 | 
			
		||||
 | 
			
		||||
            session.setNonce(getNonce()
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
| 
						 | 
				
			
			@ -52,6 +52,7 @@ import org.shredzone.acme4j.Session;
 | 
			
		|||
import org.shredzone.acme4j.exception.AcmeException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeProtocolException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeRateLimitedException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeRetryAfterException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeServerException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeUnauthorizedException;
 | 
			
		||||
import org.shredzone.acme4j.exception.AcmeUserActionRequiredException;
 | 
			
		||||
| 
						 | 
				
			
			@ -142,6 +143,57 @@ public class DefaultConnectionTest {
 | 
			
		|||
        verify(getRequestedFor(urlEqualTo(REQUEST_PATH)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test that {@link DefaultConnection#getNonce()} handles a retry-after header
 | 
			
		||||
     * correctly.
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetNonceFromHeaderRetryAfter() {
 | 
			
		||||
        var retryAfter = Instant.now().plusSeconds(30L).truncatedTo(SECONDS);
 | 
			
		||||
 | 
			
		||||
        stubFor(head(urlEqualTo(NEW_NONCE_PATH)).willReturn(aResponse()
 | 
			
		||||
                .withStatus(HttpURLConnection.HTTP_UNAVAILABLE)
 | 
			
		||||
                .withHeader("Content-Type", "application/problem+json")
 | 
			
		||||
                .withHeader("Retry-After", DATE_FORMATTER.format(retryAfter))
 | 
			
		||||
                // do not send a body here because it is a HEAD request!
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        assertThat(session.getNonce()).isNull();
 | 
			
		||||
 | 
			
		||||
        var ex = assertThrows(AcmeRetryAfterException.class, () -> {
 | 
			
		||||
            try (var conn = session.connect()) {
 | 
			
		||||
                conn.resetNonce(session);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        assertThat(ex.getMessage()).isEqualTo("Server responded with HTTP 503 while trying to retrieve a nonce");
 | 
			
		||||
        assertThat(ex.getRetryAfter()).isEqualTo(retryAfter);
 | 
			
		||||
 | 
			
		||||
        verify(headRequestedFor(urlEqualTo(NEW_NONCE_PATH)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test that {@link DefaultConnection#getNonce()} handles a general HTTP error
 | 
			
		||||
     * correctly.
 | 
			
		||||
     */
 | 
			
		||||
    @Test
 | 
			
		||||
    public void testGetNonceFromHeaderHttpError() {
 | 
			
		||||
        stubFor(head(urlEqualTo(NEW_NONCE_PATH)).willReturn(aResponse()
 | 
			
		||||
                .withStatus(HttpURLConnection.HTTP_INTERNAL_ERROR)
 | 
			
		||||
                // do not send a body here because it is a HEAD request!
 | 
			
		||||
        ));
 | 
			
		||||
 | 
			
		||||
        assertThat(session.getNonce()).isNull();
 | 
			
		||||
 | 
			
		||||
        var ex = assertThrows(AcmeException.class, () -> {
 | 
			
		||||
            try (var conn = session.connect()) {
 | 
			
		||||
                conn.resetNonce(session);
 | 
			
		||||
            }
 | 
			
		||||
        });
 | 
			
		||||
        assertThat(ex.getMessage()).isEqualTo("Server responded with HTTP 500 while trying to retrieve a nonce");
 | 
			
		||||
 | 
			
		||||
        verify(headRequestedFor(urlEqualTo(NEW_NONCE_PATH)));
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * Test that {@link DefaultConnection#getNonce()} fails on an invalid
 | 
			
		||||
     * {@code Replay-Nonce} header.
 | 
			
		||||
| 
						 | 
				
			
			
 | 
			
		|||
		Loading…
	
		Reference in New Issue