From 045968a42328f7c1edd593f0e635dd67bf7f178c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Richard=20K=C3=B6rber?= Date: Wed, 16 Dec 2015 00:46:16 +0100 Subject: [PATCH] Split Connection into interface and implementation --- .../acme4j/connector/Connection.java | 255 +-------------- .../acme4j/impl/DefaultConnection.java | 295 ++++++++++++++++++ .../provider/AbstractAcmeClientProvider.java | 3 +- .../DefaultConnectionTest.java} | 36 ++- 4 files changed, 328 insertions(+), 261 deletions(-) create mode 100644 acme4j-client/src/main/java/org/shredzone/acme4j/impl/DefaultConnection.java rename acme4j-client/src/test/java/org/shredzone/acme4j/{connector/ConnectionTest.java => impl/DefaultConnectionTest.java} (91%) diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java index e0420a4c..82752426 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/connector/Connection.java @@ -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 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 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 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 readDirectory() throws AcmeException { - String contentType = conn.getHeaderField("Content-Type"); - if (!("application/json".equals(contentType))) { - throw new AcmeException("Unexpected content type: " + contentType); - } - - EnumMap 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 result = JsonUtil.parseJson(sb.toString()); - for (Map.Entry 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 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 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(); } diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/impl/DefaultConnection.java b/acme4j-client/src/main/java/org/shredzone/acme4j/impl/DefaultConnection.java new file mode 100644 index 00000000..6539cb3e --- /dev/null +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/impl/DefaultConnection.java @@ -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 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 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 readDirectory() throws AcmeException { + String contentType = conn.getHeaderField("Content-Type"); + if (!("application/json".equals(contentType))) { + throw new AcmeException("Unexpected content type: " + contentType); + } + + EnumMap 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 result = JsonUtil.parseJson(sb.toString()); + for (Map.Entry 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 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); + } + +} diff --git a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeClientProvider.java b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeClientProvider.java index 8fac0d51..d3da809b 100644 --- a/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeClientProvider.java +++ b/acme4j-client/src/main/java/org/shredzone/acme4j/provider/AbstractAcmeClientProvider.java @@ -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()); } /** diff --git a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ConnectionTest.java b/acme4j-client/src/test/java/org/shredzone/acme4j/impl/DefaultConnectionTest.java similarity index 91% rename from acme4j-client/src/test/java/org/shredzone/acme4j/connector/ConnectionTest.java rename to acme4j-client/src/test/java/org/shredzone/acme4j/impl/DefaultConnectionTest.java index 0b9832e5..bb8a1e8b 100644 --- a/acme4j-client/src/test/java/org/shredzone/acme4j/connector/ConnectionTest.java +++ b/acme4j-client/src/test/java/org/shredzone/acme4j/impl/DefaultConnectionTest.java @@ -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 readJsonResponse() throws AcmeException { Map result = new HashMap(); @@ -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 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 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 result = conn.readDirectory(); assertThat(result.keySet(), hasSize(2));