diff --git a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt index 9c12056315..39b4a2eb31 100644 --- a/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt +++ b/node/src/main/kotlin/net/corda/node/services/network/NetworkMapClient.kt @@ -14,6 +14,7 @@ import net.corda.core.utilities.seconds import net.corda.core.utilities.trace import net.corda.node.services.api.NetworkMapCacheInternal import net.corda.node.utilities.NamedThreadFactory +import net.corda.node.utilities.registration.cacheControl import net.corda.nodeapi.internal.SignedNodeInfo import net.corda.nodeapi.internal.network.NetworkMap import net.corda.nodeapi.internal.network.NetworkParameters @@ -54,7 +55,7 @@ class NetworkMapClient(compatibilityZoneURL: URL, private val trustedRoot: X509C val connection = networkMapUrl.openHttpConnection() val signedNetworkMap = connection.responseAs>() val networkMap = signedNetworkMap.verifiedNetworkMapCert(trustedRoot) - val timeout = CacheControl.parse(Headers.of(connection.headerFields.filterKeys { it != null }.mapValues { it.value[0] })).maxAgeSeconds().seconds + val timeout = connection.cacheControl().maxAgeSeconds().seconds logger.trace { "Fetched network map update from $networkMapUrl successfully, retrieved ${networkMap.nodeInfoHashes.size} node info hashes. Node Info hashes: ${networkMap.nodeInfoHashes.joinToString("\n")}" } return NetworkMapResponse(networkMap, timeout) } diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt index 201cab875d..7f3d0ddf24 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/HTTPNetworkRegistrationService.kt @@ -2,7 +2,10 @@ package net.corda.node.utilities.registration import com.google.common.net.MediaType import net.corda.core.internal.openHttpConnection +import net.corda.core.utilities.seconds import net.corda.nodeapi.internal.crypto.X509CertificateFactory +import okhttp3.CacheControl +import okhttp3.Headers import org.apache.commons.io.IOUtils import org.bouncycastle.pkcs.PKCS10CertificationRequest import java.io.IOException @@ -22,10 +25,13 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL) : NetworkRegistr } @Throws(CertificateRequestException::class) - override fun retrieveCertificates(requestId: String): List? { + override fun retrieveCertificates(requestId: String): CertificateResponse { // Poll server to download the signed certificate once request has been approved. val conn = URL("$registrationURL/$requestId").openHttpConnection() conn.requestMethod = "GET" + val maxAge = conn.cacheControl().maxAgeSeconds() + // Default poll interval to 10 seconds if not specified by the server, for backward compatibility. + val pollInterval = if (maxAge == -1) 10.seconds else maxAge.seconds return when (conn.responseCode) { HTTP_OK -> ZipInputStream(conn.inputStream).use { @@ -34,9 +40,9 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL) : NetworkRegistr while (it.nextEntry != null) { certificates += factory.generateCertificate(it) } - certificates + CertificateResponse(pollInterval, certificates) } - HTTP_NO_CONTENT -> null + HTTP_NO_CONTENT -> CertificateResponse(pollInterval, null) HTTP_UNAUTHORIZED -> throw CertificateRequestException("Certificate signing request has been rejected: ${conn.errorMessage}") else -> throwUnexpectedResponseCode(conn) } @@ -66,3 +72,5 @@ class HTTPNetworkRegistrationService(compatibilityZoneURL: URL) : NetworkRegistr private val HttpURLConnection.errorMessage: String get() = IOUtils.toString(errorStream, charset) } + +fun HttpURLConnection.cacheControl(): CacheControl = CacheControl.parse(Headers.of(headerFields.filterKeys { it != null }.mapValues { it.value[0] })) diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt index a940a28a05..287f4b41c9 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelper.kt @@ -3,7 +3,6 @@ package net.corda.node.utilities.registration import net.corda.core.crypto.Crypto import net.corda.core.identity.CordaX500Name import net.corda.core.internal.* -import net.corda.core.utilities.seconds import net.corda.node.services.config.NodeConfiguration import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509KeyStore @@ -28,7 +27,6 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, networkRootTrustStorePath: Path, networkRootTruststorePassword: String) { private companion object { - val pollInterval = 10.seconds const val SELF_SIGNED_PRIVATE_KEY = "Self Signed Private Key" } @@ -148,17 +146,18 @@ class NetworkRegistrationHelper(private val config: NodeConfiguration, * Poll Certificate Signing Server for approved certificate, * enter a slow polling loop if server return null. * @param requestId Certificate signing request ID. - * @return Map of certificate chain. + * @return List of certificate chain. */ private fun pollServerForCertificates(requestId: String): List { println("Start polling server for certificate signing approval.") // Poll server to download the signed certificate once request has been approved. - var certificates = certService.retrieveCertificates(requestId) - while (certificates == null) { + while (true) { + val (pollInterval, certificates) = certService.retrieveCertificates(requestId) + if (certificates != null) { + return certificates + } Thread.sleep(pollInterval.toMillis()) - certificates = certService.retrieveCertificates(requestId) } - return certificates } /** diff --git a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationService.kt b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationService.kt index beea4635d5..cafc16dd45 100644 --- a/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationService.kt +++ b/node/src/main/kotlin/net/corda/node/utilities/registration/NetworkRegistrationService.kt @@ -4,6 +4,7 @@ import net.corda.core.CordaException import net.corda.core.serialization.CordaSerializable import org.bouncycastle.pkcs.PKCS10CertificationRequest import java.security.cert.X509Certificate +import java.time.Duration interface NetworkRegistrationService { /** Submits a CSR to the signing service and returns an opaque request ID. */ @@ -11,8 +12,10 @@ interface NetworkRegistrationService { /** Poll Certificate Signing Server for the request and returns a chain of certificates if request has been approved, null otherwise. */ @Throws(CertificateRequestException::class) - fun retrieveCertificates(requestId: String): List? + fun retrieveCertificates(requestId: String): CertificateResponse } +data class CertificateResponse(val pollInterval: Duration, val certificates: List?) + @CordaSerializable class CertificateRequestException(message: String) : CordaException(message) diff --git a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt index e7e6775a5e..2371ab4437 100644 --- a/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt +++ b/node/src/test/kotlin/net/corda/node/utilities/registration/NetworkRegistrationHelperTest.kt @@ -12,6 +12,7 @@ import net.corda.core.identity.CordaX500Name import net.corda.core.internal.createDirectories import net.corda.core.internal.div import net.corda.core.internal.x500Name +import net.corda.core.utilities.seconds import net.corda.node.services.config.NodeConfiguration import net.corda.nodeapi.internal.crypto.CertificateType import net.corda.nodeapi.internal.crypto.X509KeyStore @@ -158,7 +159,7 @@ class NetworkRegistrationHelperTest { private fun createRegistrationHelper(response: List): NetworkRegistrationHelper { val certService = rigorousMock().also { doReturn(requestId).whenever(it).submitRequest(any()) - doReturn(response).whenever(it).retrieveCertificates(eq(requestId)) + doReturn(CertificateResponse(5.seconds, response)).whenever(it).retrieveCertificates(eq(requestId)) } return NetworkRegistrationHelper(config, certService, config.certificatesDirectory / networkRootTrustStoreFileName, networkRootTrustStorePassword) }