Directive for extractScheme does not work with https #658

Closed
michael-smith opened this Issue Dec 14, 2016 · 6 comments

Projects

None yet

4 participants

@michael-smith
michael-smith commented Dec 14, 2016 edited

The extractScheme directive always returns "http" even when "https" is being used.

A basic example using an https context results in "Scheme: http" ...

package org.example

import scala.collection.immutable.Seq
import javax.net.ssl._
import java.security.cert.X509Certificate
import akka.actor.ActorSystem
import akka.stream._
import akka.http._
import akka.http.scaladsl.server._
import akka.http.scaladsl._
import akka.http.scaladsl.model._
import akka.http.scaladsl.server._
import akka.http.scaladsl.server.Directives._

object Main {

  private val httpsConnectionContext = ConnectionContext.https(createSslContext)

  def main(args: Array[String]) {
    implicit val system = ActorSystem("my-system")
    implicit val materializer = ActorMaterializer()
    implicit val executionContext = system.dispatcher

    val route =
      extractScheme { scheme =>
        get {
          println("Scheme is: " + scheme)
          complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, s"<h1>Scheme: ${scheme}</h1>"))
        }
      }

    val bindingFuture = Http().bindAndHandle(route, "localhost", 8443, connectionContext = httpsConnectionContext)

    println(s"Server online at https://localhost:8443/\nPress RETURN to stop...")
    scala.io.StdIn.readLine()
    bindingFuture
      .flatMap(_.unbind())
      .onComplete(_ => system.terminate())
  }

  private def createSslContext: SSLContext = {
    val context = SSLContext.getInstance("TLSv1")
    context.init(keyManagers(context), Array.empty, null)
    context
  }

  private final def keyManagers(sslContext: SSLContext): Array[KeyManager] = {
    import java.security._
    val kmf = KeyManagerFactory.getInstance("SunX509")
    val ks = KeyStore.getInstance("JKS")
    val ksPass = "changeit".toCharArray()
    var kms: Array[KeyManager] = null
    val is = getClass.getResourceAsStream("keystore.jks")
    try {
      ks.load(is, ksPass)
      kmf.init(ks, ksPass)
      kms = kmf.getKeyManagers
    } finally { is.close }
    kms
  }
}
@zettelmj
Contributor

Looks like the information if the session is encrypted or not is lost here: https://github.com/akka/akka-http/blob/master/akka-http-core/src/main/scala/akka/http/impl/engine/parsing/HttpMessageParser.scala#L56
from that point on only the byte content is passed on if I am not mistaken.
Passing that information on would allow to guess the right schema afterwards.

@zettelmj
Contributor

As far as i can see the HttpEntity is created by using information from the passed in via the bystream. All additional information known when receiving this byte stream is not being used to create the HttpEntity. This is can be seen with this example. I would assume that other information is also lost due to this behaviour

@jrudolph
Member

The error seems to be done here: https://github.com/akka/akka-http/blob/023fde04fc822fae0a9acd77e03f490f1044aee5/akka-http-core/src/main/scala/akka/http/impl/engine/server/HttpServerBluePrint.scala#L217-L217

The blueprint needs a flag to be passed in from the outside whether it runs on TLS or not and then at the line above the secureConnection flag must be set accordingly.

@jrudolph
Member

No, it should probably be passed from Http.scala.

@ktoso
Member
ktoso commented Jan 5, 2017 edited

Solved 45ce8ad, thanks @zettelmj !

@ktoso ktoso closed this Jan 5, 2017
@ktoso ktoso removed the 1 - triaged label Jan 5, 2017
@ktoso ktoso added this to the 10.0.2 milestone Jan 5, 2017
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment