Reload certificates server at runtime - scala

Is it possible to reload at runtime certificates in a web server using API of Akka-HTTP? I would that the HttpsConnectionContext is reloaded without shutdown of server. Is there a way to do that? I've already implemented a mechanism that read the renew of certificate but my problem is to reload the context at runtime.
Below I show how my server is started:
log.info("HTTPS ENABLED!")
val https: HttpsConnectionContext = newHttpsConnectionContext()
val (host, port, connectionContext) = ("0.0.0.0", 8080, https)
log.debug(s" Binding RESTful Web Services ... https://$host:$port/")
val bindingFuture =
Http().bindAndHandle(
endpoints,
host,
port,
connectionContext
)
bindingFuture.onComplete {
case Success(bind) =>
log.info(s"HTTPS server binding $bind")
binding = Some(bind)
log.warn(
s" Service online at https://$host:$port/"
)
case Failure(ex) =>
log.error(
s" Failed to bind to https://$host:$port/ - Error : ${ex.getMessage}"
)
close()
}
}
def newHttpsConnectionContext(): HttpsConnectionContext = {
import myService.TlsSettings
log.debug(
s"Creating a new HTTPS Connection Context between my Service (Server) and Clients..."
)
val sslParameters: Option[SSLParameters] = None
val sslConnectionContext: HttpsConnectionContext =
ConnectionContext.https(
TlsSettings(
ApplicationProperties.clientCert,
ApplicationProperties.clientPrivKey
).sslContext,
None,
Some(SSL_CIPHER_SUITES),
Some(SSL_PROTOCOLS),
Some(TLSClientAuth.Need),
sslParameters
)
log.info(
s"HTTPS Connection Context my Service <--> Clients created! $sslConnectionContext"
)
sslConnectionContext
}

Related

Grpc parallel Stream communication leads to error:AkkaNettyGrpcClientGraphStage

I have two services: one that sends stream data and the second one receives it using akka-grpc for communication. When source data is provided Service one is called to process and send it to service two via grpc client. It's possible that multiple instances of server one runs at the same time when multiple source data are provided at the same time.In long running test of my application. I see below error in service one:
ERROR i.a.g.application.actors.DbActor - GraphStage [akka.grpc.internal.AkkaNettyGrpcClientGraphStage$$anon$1#59d40805] terminated abruptly, caused by for example materializer or act
akka.stream.AbruptStageTerminationException: GraphStage [akka.grpc.internal.AkkaNettyGrpcClientGraphStage$$anon$1#59d40805] terminated abruptly, caused by for example materializer or actor system termination.
I have never shutdown actor systems but only kill actors after doing their job. Also I used proto3 and http2 for request binding. Here is a piece of my code in service one:
////////////////////server http binding /////////
val service: HttpRequest => Future[HttpResponse] =
ServiceOneServiceHandler(new ServiceOneServiceImpl(system))
val bound = Http().bindAndHandleAsync(
service,
interface = config.getString("akka.grpc.server.interface"),
port = config.getString("akka.grpc.server.default-http-port").toInt,
connectionContext = HttpConnectionContext(http2 = Always))
bound.foreach { binding =>
logger.info(s"gRPC server bound to: ${binding.localAddress}")
}
////////////////////client /////////
def send2Server[A](data: ListBuffer[A]): Future[ResponseDTO] = {
val reply = {
val thisClient = interface.initialize()
interface.call(client = thisClient, req = data.asInstanceOf[ListBuffer[StoreRequest]].toList)
}
reply
}
///////////////// grpc communication //////////
def send2GrpcServer[A](data: ListBuffer[A]): Unit = {
val reply = send2Server(data)
Await.ready(reply, Duration.Inf) onComplete {
case util.Success(response: ResponseDTO) =>
logger.info(s"got reply message: ${res.description}")
//////check response content and stop application if desired result not found in response
}
case util.Failure(exp) =>
//////stop application
throw exp.getCause
}
}
Error occurred exactly after waiting for service 2 response :
Await.ready(reply, Duration.Inf)
I can't catch the cause of error.
UPDATE
I found that some stream is missed such that service one sends an stream an indefinitely wait for the response and service two does not receive any thing to reply to service one but still don't know why stream is missed
I also updated akka grpc plugin but has no sense:
addSbtPlugin("com.lightbend.akka.grpc" % "sbt-akka-grpc" % "0.6.1")
addSbtPlugin("com.lightbend.sbt" % "sbt-javaagent" % "0.1.4")

How to setup HTTP and HTTPS on the same port with akka-http

I have a Scala app that runs an akka-http webserver on a custom port, let's say 8000.
Until a while ago, it would only handle http:// requests, but recently I switched to https://.
Some of the clients have the link bookmarked and keep getting the no connection error because they try the address with http:// instead of https:// and they keep forgetting why it happens.
I tried binding two services to the same port but failed because only the first one gets binded.
Http().bind(interface = "0.0.0.0", port = Global.settings.restPort, connectionContext = httpsContext)
Http().bind(interface = "0.0.0.0", port = Global.settings.restPort)
All I need from the http:// server is to return a 301 code and redirect to the same address, but with https protocol.
How can I achieve that?
As others have commented, you can't bind the HTTP and HTTPS servers to the same port. You can have both servers running on separate ports and redirect all HTTP traffic to the HTTPS server using Akka-http's scheme() and redirect():
val hostName = "www.example.com"
val portHttp = 8080
val portHttps = 8443
val route =
scheme("http") {
extract(_.request.uri) { uri =>
redirect( uri.withScheme("https").withAuthority(hostName, portHttps),
StatusCodes.MovedPermanently
)
}
} ~
pathSingleSlash {
get {
complete( HttpEntity( ContentTypes.`text/html(UTF-8)`,
"Welcome to Akka-HTTP!"
) )
}
}
Http().bindAndHandle(route, hostName, portHttp)
Http().bindAndHandle(route, hostName, portHttps, connectionContext = httpsContext)
Note that there is no need for applying withAuthority() if you're using standard HTTP and HTTPS ports (i.e. 80 and 443).

Using Finagle Http client for https requests

I am trying to get some data from a REST web service. So far I can get the data correctly if I don't use HTTPS with this code working as expected -
val client = Http.client.newService(s"$host:80")
val r = http.Request(http.Method.Post, "/api/search/")
r.host(host)
r.content = queryBuf
r.headerMap.add(Fields.ContentLength, queryBuf.length.toString)
r.headerMap.add("Content-Type", "application/json;charset=UTF-8")
val response: Future[http.Response] = client(r)
But when I am trying to get the same data from https request (Following this link)
val client = Http.client.withTls(host).newService(s"$host:443")
val r = http.Request(http.Method.Post, "/api/search/")
r.headerMap.add("Cookie", s"_elfowl=${authToken.elfowlToken}; dc=$dc")
r.host(host)
r.content = queryBuf
r.headerMap.add(Fields.ContentLength, queryBuf.length.toString)
r.headerMap.add("Content-Type", "application/json;charset=UTF-8")
r.headerMap.add("User-Agent", authToken.userAgent)
val response: Future[http.Response] = client(r)
I get the error
Remote Info: Not Available at remote address: searchservice.com/10.59.201.29:443. Remote Info: Not Available, flags=0x08
I can curl the same endpoint with 443 port and it returns the right result. Can anyone please help me troubleshoot the issue ?
Few things to check:
withTls(host)
needs to be the host name that is in the certificate of server (as opposed to the the ip for instance)
you can try:
Http.client.withTlsWithoutValidation
to verify the above.
Also you might want to verify if the server checks that the host header is set, and if so, you might want to include it:
val withHeader = new SimpleFilter[http.Request, http.Response] {
override def apply(request: http.Request, service: HttpService): Future[http.Response] = {
request.host_=(host)
service(request)
}
}
withHeader.andThen(client)
more info on host header:
What is http host header?

akka-http how to accept invalid URI [will not solve]

I am developing a service that is called on one path with different query parameters. I have bind a Route to Http:
val route: Route = {
get {
pathPrefix("myRoute"){
parameterMap{ params =>
complete(
MyHandler.genExternResponse(params)
)
}
}
}
val bindingFuture = Http().bindAndHandleAsync(Route.asyncHandler(new myEndpoint().route), "localhost", 8081)
Since i have no influence on what parameters are used, i can't eliminate the calls that contain not-encoded special chars like German umlauts or trademark signs.
for example
www.myhost.com/myRoute?param1=asd&param2=adäöü
I know that those URLs are not valid But one of the requirements is that even requests with those chars are accepted und that i handle them with URL encoding.
The problem is that when i call the service like above, akka-http rejects the request with the Response-status 400 without even handing it to my code.
Is there a way that i can catch those request and handle them by myself or let akka-http URL-encode the special chars for me.
edit:
will not solve
Try This
val route: Route = {
get {
pathPrefix("myRoute"){
parameters('params)
{ params =>
complete(
MyHandler.genExternResponse(params)
)
}
}
}
val bindingFuture = Http().bindAndHandleAsync(Route.asyncHandler(new myEndpoint().route), "localhost", 8081)

Javascript WebSocket not connecting to Scala server

I am trying to implement a real-time game server in Scala, but I have a problem connecting to it with javascript's WebSocket. I tryed connecting with websocket.org/echo.html using either ws://localhost:8888, ws://127.0.0.1:8888 or ws://[my ip]:8888 .
Here's my server code, whch is pretty much the example from Akka's doc:
class MyServer(port: Int) extends Actor with ActorLogging{
var address: InetSocketAddress = new InetSocketAddress("", port)
var socketServer: ServerHandle = IOManager(context.system).listen(address)
override def receive = {
//Fired when the server is listening
case Listening(server, add) =>
log.info(s"The server is now listening on $add.")
//When a new client connect to the server...
case NewClient(server) =>
log.info("A new client just connected !")
server.accept()
//When a message is received by the server
case Read(socket, bytes) =>
log.info("The server just received some data.")
log.info(bytes.decodeString("US-ASCII"))
//When a connection with a client is closed
case Closed(socket, cause) =>
log.info(s"A client just disconnected : $cause")
}
}
object GemsApplication extends App{
println("Starting Gems application !")
val port = Option(System.getenv("PORT")).map(_.toInt).getOrElse(8888)
println(s"Selected port is $port.")
ActorSystem().actorOf(Props( new MyServer(port) ))
}
The odd thing is that I can connect to my server using Python sockets. Since they are may more low-level I'm guessing that I'll have to implement the handshake response to make it work with WebSocket, but shouldn't I see the message from the web socket asking for upgrade ? or even just connecting ?
Thanks for any insight you might have
Robin