akka/akka-http

Future assigned to a `val` allows entry to multiple concatenated directives.

Open

#2420 aperta il 12 feb 2019

Vedi su GitHub
 (3 commenti) (0 reazioni) (0 assegnatari)Scala (598 fork)batch import
1 - triagedhelp wantedt:docst:routing

Metriche repository

Star
 (1311 star)
Metriche merge PR
 (Merge medio 1g 10h) (2 PR mergiate in 30 g)

Descrizione

Hi, I seem to have an issue where directives are behaving differently depending if a val assignment is present in a function called behind the directives... In the assignment to val setup the function will pass both directives, executing downstream code twice. Without assignment to val the downstream code is executed a single time for the first directive.

In tests for the below code I've mocked out the Library class and counted the invocations. In my actual application there is an actor system downstream messaged using ask ? and multiple messages are sent to it.

small reproduction: https://github.com/jwymah/akka-http-possible-bug

class Library {
    def getAFuture(n: Int): Future[String] = {
        println(s"Library.getAFuture() called with $n")
        Future.successful(s"'Library.getAFuture() called with: $n")
    }
}

class Routes(library: Library) extends MyDirectives {
    implicit val timeout = Timeout(5.seconds)

    def unexpected(n: Int): Route = {
        val future = library.getAFuture(n).mapTo[String]
        onSuccess(future) {
            //      println(s"-----future successful from the Unexpected. $n") // this gets printed twice when called with 10
            complete(_)
        }
    }

    def asExpected(n: Int): Route = {
        onSuccess(library.getAFuture(n).mapTo[String]) {
            //      println(s"-----future successful the Expected. $n")  // this also gets printed twice when called when 10 but the library is only called once
            complete(_)
        }
    }

    val route: Route = {
        pathPrefix("unexpected") {
            path(IntNumber) { number =>
                get {
                    is10orHigher(number) {
                        unexpected(10)
                    } ~
                    is5orHigher(number) {
                        unexpected(5)
                    }
                }
            }
        } ~
        pathPrefix("asexpected") {
            path(IntNumber) { number =>
                get {
                    is10orHigher(number) {
                        asExpected(10)
                    } ~
                    is5orHigher(number) {
                        asExpected(5)
                    }
                }
            }
        }
    }
}

trait MyDirectives extends Directives {
    def is10orHigher(n: Int): Directive0 = {
        if (n >= 10)
            pass
        else
            reject
    }

    def is5orHigher(n: Int): Directive0 = {
        if (n >= 5)
            pass
        else
            reject
    }
}

calling http://localhost:8080/unexpected/10 will print two lines that are behind different directives

Library.getAFuture() called with 10
Library.getAFuture() called with 5

I'm not sure if this is a bug in akka, a bug in scala, or a feature. But as far as I can tell the two functions above should behave the same.

Guida contributor