Future assigned to a `val` allows entry to multiple concatenated directives.
#2,420 opened on Feb 12, 2019
Repository metrics
- Stars
- (1,311 stars)
- PR merge metrics
- (Avg merge 1d 10h) (2 merged PRs in 30d)
Description
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.