-
Notifications
You must be signed in to change notification settings - Fork 75
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'feature/jsonRpcHttpsSupport' into phase/daedalus
- Loading branch information
Showing
9 changed files
with
238 additions
and
52 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
28 changes: 28 additions & 0 deletions
28
src/main/scala/io/iohk/ethereum/jsonrpc/server/JsonRpcHttpServer.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
package io.iohk.ethereum.jsonrpc.server | ||
|
||
import akka.actor.ActorSystem | ||
import akka.http.scaladsl.Http | ||
import akka.stream.ActorMaterializer | ||
import io.iohk.ethereum.jsonrpc._ | ||
import io.iohk.ethereum.jsonrpc.server.JsonRpcServer.JsonRpcServerConfig | ||
import io.iohk.ethereum.utils.Logger | ||
|
||
import scala.concurrent.ExecutionContext.Implicits.global | ||
import scala.util.{Failure, Success} | ||
|
||
class JsonRpcHttpServer(val jsonRpcController: JsonRpcController, config: JsonRpcServerConfig) | ||
(implicit val actorSystem: ActorSystem) | ||
extends JsonRpcServer with Logger { | ||
|
||
def run(): Unit = { | ||
implicit val materializer = ActorMaterializer() | ||
|
||
val bindingResultF = Http(actorSystem).bindAndHandle(route, config.interface, config.port) | ||
|
||
bindingResultF onComplete { | ||
case Success(serverBinding) => log.info(s"JSON RPC HTTP server listening on ${serverBinding.localAddress}") | ||
case Failure(ex) => log.error("Cannot start JSON HTTP RPC server", ex) | ||
} | ||
} | ||
|
||
} |
112 changes: 112 additions & 0 deletions
112
src/main/scala/io/iohk/ethereum/jsonrpc/server/JsonRpcHttpsServer.scala
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,112 @@ | ||
package io.iohk.ethereum.jsonrpc.server | ||
|
||
import java.io.{File, FileInputStream} | ||
import java.security.{KeyStore, SecureRandom} | ||
import javax.net.ssl.{KeyManagerFactory, SSLContext, TrustManagerFactory} | ||
|
||
import akka.actor.ActorSystem | ||
import akka.http.scaladsl.{ConnectionContext, Http} | ||
import akka.stream.ActorMaterializer | ||
import io.iohk.ethereum.jsonrpc.JsonRpcController | ||
import io.iohk.ethereum.jsonrpc.server.JsonRpcHttpsServer.HttpsSetupResult | ||
import io.iohk.ethereum.jsonrpc.server.JsonRpcServer.JsonRpcServerConfig | ||
import io.iohk.ethereum.utils.Logger | ||
|
||
import scala.concurrent.ExecutionContext.Implicits.global | ||
import scala.io.Source | ||
import scala.util.{Failure, Success, Try} | ||
|
||
class JsonRpcHttpsServer(val jsonRpcController: JsonRpcController, config: JsonRpcServerConfig, | ||
secureRandom: SecureRandom)(implicit val actorSystem: ActorSystem) | ||
extends JsonRpcServer with Logger { | ||
|
||
def run(): Unit = { | ||
implicit val materializer = ActorMaterializer() | ||
|
||
val maybeSslContext = validateCertificateFiles(config.certificateKeyStorePath, config.certificatePasswordFile).flatMap{ | ||
case (certificatePath, passwordFile) => | ||
val passwordReader = Source.fromFile(passwordFile) | ||
try { | ||
val password = passwordReader.getLines().mkString | ||
obtainSSLContext(certificatePath, password) | ||
} finally { | ||
passwordReader.close() | ||
} | ||
} | ||
|
||
val maybeHttpsContext = maybeSslContext.map(sslContext => ConnectionContext.https(sslContext)) | ||
|
||
maybeHttpsContext match { | ||
case Right(httpsContext) => | ||
Http().setDefaultServerHttpContext(httpsContext) | ||
val bindingResultF = Http().bindAndHandle(route, config.interface, config.port, connectionContext = httpsContext) | ||
|
||
bindingResultF onComplete { | ||
case Success(serverBinding) => log.info(s"JSON RPC HTTPS server listening on ${serverBinding.localAddress}") | ||
case Failure(ex) => log.error("Cannot start JSON HTTPS RPC server", ex) | ||
} | ||
case Left(error) => log.error(s"Cannot start JSON HTTPS RPC server due to: $error") | ||
} | ||
} | ||
|
||
/** | ||
* Constructs the SSL context given a certificate | ||
* | ||
* @param certificateKeyStorePath, path to the keystore where the certificate is stored | ||
* @param password for accessing the keystore with the certificate | ||
* @return the SSL context with the obtained certificate or an error if any happened | ||
*/ | ||
private def obtainSSLContext(certificateKeyStorePath: String, password: String): HttpsSetupResult[SSLContext] = { | ||
val passwordCharArray: Array[Char] = password.toCharArray | ||
|
||
val ks: KeyStore = KeyStore.getInstance("JKS") | ||
val keyStoreInitResult: HttpsSetupResult[Unit] = Option(new FileInputStream(certificateKeyStorePath)) | ||
.toRight("Certificate keystore creation failed") | ||
.flatMap { keyStore => | ||
Try(ks.load(keyStore, passwordCharArray)).toEither.left.map( _.getMessage) } | ||
|
||
keyStoreInitResult.map { _ => | ||
val keyManagerFactory: KeyManagerFactory = KeyManagerFactory.getInstance("SunX509") | ||
keyManagerFactory.init(ks, passwordCharArray) | ||
|
||
val tmf: TrustManagerFactory = TrustManagerFactory.getInstance("SunX509") | ||
tmf.init(ks) | ||
|
||
val sslContext: SSLContext = SSLContext.getInstance("TLS") | ||
sslContext.init(keyManagerFactory.getKeyManagers, tmf.getTrustManagers, secureRandom) | ||
sslContext | ||
} | ||
|
||
} | ||
|
||
/** | ||
* Validates that the keystore certificate file and password file were configured and that the files exists | ||
* | ||
* @param maybeCertificatePath, with the path to the keystore certificate if it was configured | ||
* @param maybePasswordFile, with the path to the password file if it was configured | ||
* @return the certificate path and password file or the error detected | ||
*/ | ||
private def validateCertificateFiles(maybeCertificatePath: Option[String], | ||
maybePasswordFile: Option[String]): HttpsSetupResult[(String, String)] = | ||
(maybeCertificatePath, maybePasswordFile) match { | ||
case (Some(certificatePath), Some(passwordFile)) => | ||
val certificateFileMissing = !new File(certificatePath).exists() | ||
val passwordFileMissing = !new File(passwordFile).exists() | ||
if(certificateFileMissing && passwordFileMissing) | ||
Left("Certificate keystore path and password file configured but files are missing") | ||
else if(certificateFileMissing) | ||
Left("Certificate keystore path configured but file is missing") | ||
else if(passwordFileMissing) | ||
Left("Certificate password file configured but file is missing") | ||
else | ||
Right(certificatePath -> passwordFile) | ||
case (None, None) => Left("Certificate keystore path and password file configuration required") | ||
case (None, _) => Left("Certificate keystore path configuration required") | ||
case (_, None) => Left("Certificate password file configuration required") | ||
} | ||
|
||
} | ||
|
||
object JsonRpcHttpsServer { | ||
type HttpsSetupResult[T] = Either[String, T] | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.