Split Connection into interface and implementation

pull/17/merge
Richard Körber 2015-12-16 00:46:16 +01:00
parent a775cf868f
commit 045968a423
4 changed files with 328 additions and 261 deletions

View File

@ -13,56 +13,20 @@
*/
package org.shredzone.acme4j.connector;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyPair;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.EnumMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.jose4j.base64url.Base64Url;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.lang.JoseException;
import org.shredzone.acme4j.Account;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeServerException;
import org.shredzone.acme4j.util.ClaimBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Connects to the ACME server and offers different methods for invoking the API.
*
* @author Richard "Shred" Körber
*/
public class Connection implements AutoCloseable {
private static final Logger LOG = LoggerFactory.getLogger(Connection.class);
private static final Pattern BASE64URL_PATTERN = Pattern.compile("[0-9A-Za-z_-]+");
protected final HttpConnector httpConnector;
protected HttpURLConnection conn;
public Connection(HttpConnector httpConnector) {
this.httpConnector = httpConnector;
}
@Override
public void close() {
conn = null;
}
public interface Connection extends AutoCloseable {
/**
* Forcedly starts a new {@link Session}. Usually this method is not required, as a
@ -73,18 +37,7 @@ public class Connection implements AutoCloseable {
* @param session
* {@link Session} instance to be used for tracking
*/
public void startSession(URI uri, Session session) throws AcmeException {
try {
LOG.debug("Initial replay nonce from {}", uri);
HttpURLConnection localConn = httpConnector.openConnection(uri);
localConn.setRequestMethod("HEAD");
localConn.connect();
session.setNonce(getNonceFromHeader(localConn));
} catch (IOException ex) {
throw new AcmeException("Failed to request a nonce", ex);
}
}
void startSession(URI uri, Session session) throws AcmeException;
/**
* Sends a simple GET request.
@ -93,24 +46,7 @@ public class Connection implements AutoCloseable {
* {@link URI} to send the request to.
* @return HTTP response code
*/
public int sendRequest(URI uri) throws AcmeException {
try {
LOG.debug("GET {}", uri);
conn = httpConnector.openConnection(uri);
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setDoOutput(false);
conn.connect();
throwException();
return conn.getResponseCode();
} catch (IOException ex) {
throw new AcmeException("API access failed", ex);
}
}
int sendRequest(URI uri) throws AcmeException;
/**
* Sends a signed POST request.
@ -125,209 +61,40 @@ public class Connection implements AutoCloseable {
* {@link Account} to be used for signing the request
* @return HTTP response code
*/
public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session, Account account) throws AcmeException {
try {
KeyPair keypair = account.getKeyPair();
if (session.getNonce() == null) {
startSession(uri, session);
}
if (session.getNonce() == null) {
throw new AcmeException("No nonce available");
}
LOG.debug("POST {} with claims: {}", uri, claims);
conn = httpConnector.openConnection(uri);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
final JsonWebKey jwk = JsonWebKey.Factory.newJwk(keypair.getPublic());
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toString());
jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce()));
jws.getHeaders().setJwkHeaderValue("jwk", jwk);
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
jws.setKey(keypair.getPrivate());
byte[] outputData = jws.getCompactSerialization().getBytes("utf-8");
conn.setFixedLengthStreamingMode(outputData.length);
conn.connect();
try (OutputStream out = conn.getOutputStream()) {
out.write(outputData);
}
session.setNonce(getNonceFromHeader(conn));
throwException();
return conn.getResponseCode();
} catch (JoseException | IOException ex) {
throw new AcmeException("Failed to send request to " + uri, ex);
}
}
int sendSignedRequest(URI uri, ClaimBuilder claims, Session session, Account account) throws AcmeException;
/**
* Reads a server response as JSON data.
*
* @return Map containing the parsed JSON data
*/
public Map<String, Object> readJsonResponse() throws AcmeException {
if (conn == null) {
throw new IllegalStateException("Not connected");
}
String contentType = conn.getHeaderField("Content-Type");
if (!("application/json".equals(contentType)
|| "application/problem+json".equals(contentType))) {
throw new AcmeException("Unexpected content type: " + contentType);
}
StringBuilder sb = new StringBuilder();
Map<String, Object> result = null;
try {
InputStream in = (conn.getResponseCode() < 400 ? conn.getInputStream() : conn.getErrorStream());
if (in != null) {
try (BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"))) {
sb.append(r.readLine());
}
result = JsonUtil.parseJson(sb.toString());
LOG.debug("Result JSON: {}", sb);
}
} catch (JoseException | IOException ex) {
throw new AcmeException("Failed to parse response: " + sb, ex);
}
return result;
}
Map<String, Object> readJsonResponse() throws AcmeException;
/**
* Reads a certificate.
*
* @return {@link X509Certificate} that was read.
*/
public X509Certificate readCertificate() throws AcmeException {
if (conn == null) {
throw new IllegalStateException("Not connected");
}
String contentType = conn.getHeaderField("Content-Type");
if (!("application/pkix-cert".equals(contentType))) {
throw new AcmeException("Unexpected content type: " + contentType);
}
try (InputStream in = conn.getInputStream()) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(in);
} catch (IOException ex) {
throw new AcmeException("Failed to read certificate", ex);
} catch (CertificateException ex) {
throw new AcmeException("Error while generating the X.509 certificate", ex);
}
}
X509Certificate readCertificate() throws AcmeException;
/**
* Reads a resource directory.
*
* @return Map of {@link Resource} and the respective {@link URI} to invoke
*/
public Map<Resource, URI> readDirectory() throws AcmeException {
String contentType = conn.getHeaderField("Content-Type");
if (!("application/json".equals(contentType))) {
throw new AcmeException("Unexpected content type: " + contentType);
}
EnumMap<Resource, URI> resourceMap = new EnumMap<>(Resource.class);
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) {
sb.append(reader.readLine());
} catch (IOException ex) {
throw new AcmeException("Could not read resource map", ex);
}
try {
Map<String, Object> result = JsonUtil.parseJson(sb.toString());
for (Map.Entry<String, Object> entry : result.entrySet()) {
Resource res = Resource.parse(entry.getKey());
if (res != null) {
URI uri = new URI(entry.getValue().toString());
resourceMap.put(res, uri);
}
}
LOG.debug("Resource directory: {}", resourceMap);
} catch (JoseException | URISyntaxException ex) {
throw new AcmeException("Could not parse resource map: " + sb, ex);
}
return resourceMap;
}
Map<Resource, URI> readDirectory() throws AcmeException;
/**
* Gets a location from the {@code Location} header.
*
* @return Location {@link URI}, or {@code null} if no Location header was set
*/
public URI getLocation() throws AcmeException {
String location = conn.getHeaderField("Location");
if (location == null) {
return null;
}
try {
LOG.debug("Location: {}", location);
return new URI(location);
} catch (URISyntaxException ex) {
throw new AcmeException("Bad Location header: " + location);
}
}
URI getLocation() throws AcmeException;
/**
* Checks if the server returned an error, and if so, throws a {@link AcmeException}.
*
* @throws AcmeException
* if the server returned a JSON problem
* Closes the {@link Connection}, releasing all resources.
*/
protected void throwException() throws AcmeException {
if ("application/problem+json".equals(conn.getHeaderField("Content-Type"))) {
Map<String, Object> map = readJsonResponse();
String type = (String) map.get("type");
String detail = (String) map.get("detail");
throw new AcmeServerException(type, detail);
}
}
/**
* Extracts a nonce from the header.
*
* @param localConn
* {@link HttpURLConnection} to get the nonce from
* @return Nonce
* @throws AcmeException
* if there was no {@code Replay-Nonce} header, or the nonce was invalid
*/
protected byte[] getNonceFromHeader(HttpURLConnection localConn) throws AcmeException {
String nonceHeader = localConn.getHeaderField("Replay-Nonce");
if (nonceHeader == null || nonceHeader.trim().isEmpty()) {
throw new AcmeException("No replay nonce");
}
if (!BASE64URL_PATTERN.matcher(nonceHeader).matches()) {
throw new AcmeException("Invalid replay nonce: " + nonceHeader);
}
LOG.debug("Replay Nonce: {}", nonceHeader);
return Base64Url.decode(nonceHeader);
}
@Override
void close();
}

View File

@ -0,0 +1,295 @@
/*
* acme4j - Java ACME client
*
* Copyright (C) 2015 Richard "Shred" Körber
* http://acme4j.shredzone.org
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.impl;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URISyntaxException;
import java.security.KeyPair;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.EnumMap;
import java.util.Map;
import java.util.regex.Pattern;
import org.jose4j.base64url.Base64Url;
import org.jose4j.json.JsonUtil;
import org.jose4j.jwk.JsonWebKey;
import org.jose4j.jws.AlgorithmIdentifiers;
import org.jose4j.jws.JsonWebSignature;
import org.jose4j.lang.JoseException;
import org.shredzone.acme4j.Account;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.HttpConnector;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.connector.Session;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeServerException;
import org.shredzone.acme4j.util.ClaimBuilder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Default implementation of {@link Connection}.
*
* @author Richard "Shred" Körber
*/
public class DefaultConnection implements Connection {
private static final Logger LOG = LoggerFactory.getLogger(DefaultConnection.class);
private static final Pattern BASE64URL_PATTERN = Pattern.compile("[0-9A-Za-z_-]+");
protected final HttpConnector httpConnector;
protected HttpURLConnection conn;
public DefaultConnection(HttpConnector httpConnector) {
this.httpConnector = httpConnector;
}
@Override
public void startSession(URI uri, Session session) throws AcmeException {
try {
LOG.debug("Initial replay nonce from {}", uri);
HttpURLConnection localConn = httpConnector.openConnection(uri);
localConn.setRequestMethod("HEAD");
localConn.connect();
session.setNonce(getNonceFromHeader(localConn));
} catch (IOException ex) {
throw new AcmeException("Failed to request a nonce", ex);
}
}
@Override
public int sendRequest(URI uri) throws AcmeException {
try {
LOG.debug("GET {}", uri);
conn = httpConnector.openConnection(uri);
conn.setRequestMethod("GET");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setDoOutput(false);
conn.connect();
throwException();
return conn.getResponseCode();
} catch (IOException ex) {
throw new AcmeException("API access failed", ex);
}
}
@Override
public int sendSignedRequest(URI uri, ClaimBuilder claims, Session session, Account account) throws AcmeException {
try {
KeyPair keypair = account.getKeyPair();
if (session.getNonce() == null) {
startSession(uri, session);
}
if (session.getNonce() == null) {
throw new AcmeException("No nonce available");
}
LOG.debug("POST {} with claims: {}", uri, claims);
conn = httpConnector.openConnection(uri);
conn.setRequestMethod("POST");
conn.setRequestProperty("Accept", "application/json");
conn.setRequestProperty("Accept-Charset", "utf-8");
conn.setRequestProperty("Content-Type", "application/json");
conn.setDoOutput(true);
final JsonWebKey jwk = JsonWebKey.Factory.newJwk(keypair.getPublic());
JsonWebSignature jws = new JsonWebSignature();
jws.setPayload(claims.toString());
jws.getHeaders().setObjectHeaderValue("nonce", Base64Url.encode(session.getNonce()));
jws.getHeaders().setJwkHeaderValue("jwk", jwk);
jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
jws.setKey(keypair.getPrivate());
byte[] outputData = jws.getCompactSerialization().getBytes("utf-8");
conn.setFixedLengthStreamingMode(outputData.length);
conn.connect();
try (OutputStream out = conn.getOutputStream()) {
out.write(outputData);
}
session.setNonce(getNonceFromHeader(conn));
throwException();
return conn.getResponseCode();
} catch (JoseException | IOException ex) {
throw new AcmeException("Failed to send request to " + uri, ex);
}
}
@Override
public Map<String, Object> readJsonResponse() throws AcmeException {
if (conn == null) {
throw new IllegalStateException("Not connected");
}
String contentType = conn.getHeaderField("Content-Type");
if (!("application/json".equals(contentType)
|| "application/problem+json".equals(contentType))) {
throw new AcmeException("Unexpected content type: " + contentType);
}
StringBuilder sb = new StringBuilder();
Map<String, Object> result = null;
try {
InputStream in = (conn.getResponseCode() < 400 ? conn.getInputStream() : conn.getErrorStream());
if (in != null) {
try (BufferedReader r = new BufferedReader(new InputStreamReader(in, "utf-8"))) {
sb.append(r.readLine());
}
result = JsonUtil.parseJson(sb.toString());
LOG.debug("Result JSON: {}", sb);
}
} catch (JoseException | IOException ex) {
throw new AcmeException("Failed to parse response: " + sb, ex);
}
return result;
}
@Override
public X509Certificate readCertificate() throws AcmeException {
if (conn == null) {
throw new IllegalStateException("Not connected");
}
String contentType = conn.getHeaderField("Content-Type");
if (!("application/pkix-cert".equals(contentType))) {
throw new AcmeException("Unexpected content type: " + contentType);
}
try (InputStream in = conn.getInputStream()) {
CertificateFactory cf = CertificateFactory.getInstance("X.509");
return (X509Certificate) cf.generateCertificate(in);
} catch (IOException ex) {
throw new AcmeException("Failed to read certificate", ex);
} catch (CertificateException ex) {
throw new AcmeException("Error while generating the X.509 certificate", ex);
}
}
@Override
public Map<Resource, URI> readDirectory() throws AcmeException {
String contentType = conn.getHeaderField("Content-Type");
if (!("application/json".equals(contentType))) {
throw new AcmeException("Unexpected content type: " + contentType);
}
EnumMap<Resource, URI> resourceMap = new EnumMap<>(Resource.class);
StringBuilder sb = new StringBuilder();
try (BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "utf-8"))) {
sb.append(reader.readLine());
} catch (IOException ex) {
throw new AcmeException("Could not read resource map", ex);
}
try {
Map<String, Object> result = JsonUtil.parseJson(sb.toString());
for (Map.Entry<String, Object> entry : result.entrySet()) {
Resource res = Resource.parse(entry.getKey());
if (res != null) {
URI uri = new URI(entry.getValue().toString());
resourceMap.put(res, uri);
}
}
LOG.debug("Resource directory: {}", resourceMap);
} catch (JoseException | URISyntaxException ex) {
throw new AcmeException("Could not parse resource map: " + sb, ex);
}
return resourceMap;
}
@Override
public URI getLocation() throws AcmeException {
String location = conn.getHeaderField("Location");
if (location == null) {
return null;
}
try {
LOG.debug("Location: {}", location);
return new URI(location);
} catch (URISyntaxException ex) {
throw new AcmeException("Bad Location header: " + location);
}
}
@Override
public void close() {
conn = null;
}
/**
* Checks if the server returned an error, and if so, throws a {@link AcmeException}.
*
* @throws AcmeException
* if the server returned a JSON problem
*/
protected void throwException() throws AcmeException {
if ("application/problem+json".equals(conn.getHeaderField("Content-Type"))) {
Map<String, Object> map = readJsonResponse();
String type = (String) map.get("type");
String detail = (String) map.get("detail");
throw new AcmeServerException(type, detail);
}
}
/**
* Extracts a nonce from the header.
*
* @param localConn
* {@link HttpURLConnection} to get the nonce from
* @return Nonce
* @throws AcmeException
* if there was no {@code Replay-Nonce} header, or the nonce was invalid
*/
protected byte[] getNonceFromHeader(HttpURLConnection localConn) throws AcmeException {
String nonceHeader = localConn.getHeaderField("Replay-Nonce");
if (nonceHeader == null || nonceHeader.trim().isEmpty()) {
throw new AcmeException("No replay nonce");
}
if (!BASE64URL_PATTERN.matcher(nonceHeader).matches()) {
throw new AcmeException("Invalid replay nonce: " + nonceHeader);
}
LOG.debug("Replay Nonce: {}", nonceHeader);
return Base64Url.decode(nonceHeader);
}
}

View File

@ -24,6 +24,7 @@ import org.shredzone.acme4j.challenge.ProofOfPossessionChallenge;
import org.shredzone.acme4j.challenge.TlsSniChallenge;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.HttpConnector;
import org.shredzone.acme4j.impl.DefaultConnection;
import org.shredzone.acme4j.impl.GenericAcmeClient;
/**
@ -75,7 +76,7 @@ public abstract class AbstractAcmeClientProvider implements AcmeClientProvider {
@Override
public Connection createConnection() {
return new Connection(createHttpConnector());
return new DefaultConnection(createHttpConnector());
}
/**

View File

@ -11,7 +11,7 @@
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
*/
package org.shredzone.acme4j.connector;
package org.shredzone.acme4j.impl;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
@ -39,6 +39,10 @@ import org.jose4j.jwx.CompactSerializer;
import org.junit.Before;
import org.junit.Test;
import org.shredzone.acme4j.Account;
import org.shredzone.acme4j.connector.Connection;
import org.shredzone.acme4j.connector.HttpConnector;
import org.shredzone.acme4j.connector.Resource;
import org.shredzone.acme4j.connector.Session;
import org.shredzone.acme4j.exception.AcmeException;
import org.shredzone.acme4j.exception.AcmeServerException;
import org.shredzone.acme4j.util.ClaimBuilder;
@ -49,7 +53,7 @@ import org.shredzone.acme4j.util.TestUtils;
*
* @author Richard "Shred" Körber
*/
public class ConnectionTest {
public class DefaultConnectionTest {
private URI requestUri;
private HttpURLConnection mockUrlConnection;
@ -73,7 +77,7 @@ public class ConnectionTest {
public void testNoNonceFromHeader() throws AcmeException {
when(mockUrlConnection.getHeaderField("Replay-Nonce")).thenReturn(null);
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.getNonceFromHeader(mockUrlConnection);
fail("Expected to fail");
} catch (AcmeException ex) {
@ -95,7 +99,7 @@ public class ConnectionTest {
when(mockUrlConnection.getHeaderField("Replay-Nonce"))
.thenReturn(Base64Url.encode(nonce));
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
byte[] nonceFromHeader = conn.getNonceFromHeader(mockUrlConnection);
assertThat(nonceFromHeader, is(nonce));
}
@ -114,7 +118,7 @@ public class ConnectionTest {
when(mockUrlConnection.getHeaderField("Replay-Nonce")).thenReturn(badNonce);
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.getNonceFromHeader(mockUrlConnection);
fail("Expected to fail");
} catch (AcmeException ex) {
@ -132,7 +136,7 @@ public class ConnectionTest {
public void testGetLocation() throws Exception {
when(mockUrlConnection.getHeaderField("Location")).thenReturn("http://example.com/otherlocation");
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection;
URI location = conn.getLocation();
assertThat(location, is(new URI("http://example.com/otherlocation")));
@ -147,7 +151,7 @@ public class ConnectionTest {
*/
@Test
public void testNoLocation() throws Exception {
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection;
URI location = conn.getLocation();
assertThat(location, is(nullValue()));
@ -164,7 +168,7 @@ public class ConnectionTest {
public void testNoThrowException() throws AcmeException {
when(mockUrlConnection.getHeaderField("Content-Type")).thenReturn("application/json");
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection;
conn.throwException();
}
@ -184,7 +188,7 @@ public class ConnectionTest {
when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_FORBIDDEN);
when(mockUrlConnection.getErrorStream()).thenReturn(new ByteArrayInputStream(jsonData.getBytes("utf-8")));
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection;
conn.throwException();
fail("Expected to fail");
@ -210,7 +214,7 @@ public class ConnectionTest {
when(mockUrlConnection.getHeaderField("Content-Type"))
.thenReturn("application/problem+json");
try (Connection conn = new Connection(mockHttpConnection) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection) {
@Override
public Map<String,Object> readJsonResponse() throws AcmeException {
Map<String, Object> result = new HashMap<String, Object>();
@ -245,7 +249,7 @@ public class ConnectionTest {
.thenReturn(Base64Url.encode(nonce));
Session session = new Session();
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.startSession(requestUri, session);
}
assertThat(session.getNonce(), is(nonce));
@ -263,7 +267,7 @@ public class ConnectionTest {
public void testSendRequest() throws Exception {
final Set<String> invoked = new HashSet<>();
try (Connection conn = new Connection(mockHttpConnection) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection) {
@Override
protected void throwException() throws AcmeException {
invoked.add("throwException");
@ -295,7 +299,7 @@ public class ConnectionTest {
when(mockUrlConnection.getOutputStream()).thenReturn(outputStream);
when(mockUrlConnection.getHeaderField("Replay-Nonce")).thenReturn(Base64Url.encode(nonce2));
try (Connection conn = new Connection(mockHttpConnection) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection) {
@Override
protected void throwException() throws AcmeException {
invoked.add("throwException");
@ -363,7 +367,7 @@ public class ConnectionTest {
when(mockUrlConnection.getResponseCode()).thenReturn(HttpURLConnection.HTTP_OK);
when(mockUrlConnection.getInputStream()).thenReturn(new ByteArrayInputStream(jsonData.getBytes("utf-8")));
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection;
Map<String, Object> result = conn.readJsonResponse();
assertThat(result.keySet(), hasSize(2));
@ -392,7 +396,7 @@ public class ConnectionTest {
when(mockUrlConnection.getInputStream()).thenReturn(new ByteArrayInputStream(original.getEncoded()));
X509Certificate downloaded;
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection;
downloaded = conn.readCertificate();
}
@ -421,7 +425,7 @@ public class ConnectionTest {
when(mockUrlConnection.getHeaderField("Content-Type")).thenReturn("application/json");
when(mockUrlConnection.getInputStream()).thenReturn(new ByteArrayInputStream(jsonData.toString().getBytes("utf-8")));
try (Connection conn = new Connection(mockHttpConnection)) {
try (DefaultConnection conn = new DefaultConnection(mockHttpConnection)) {
conn.conn = mockUrlConnection;
Map<Resource, URI> result = conn.readDirectory();
assertThat(result.keySet(), hasSize(2));