diff --git a/akka-http-testkit/src/main/scala/akka/http/testkit/RouteTest.scala b/akka-http-testkit/src/main/scala/akka/http/testkit/RouteTest.scala index 69624ceed0..9c565bc1fa 100644 --- a/akka-http-testkit/src/main/scala/akka/http/testkit/RouteTest.scala +++ b/akka-http-testkit/src/main/scala/akka/http/testkit/RouteTest.scala @@ -83,7 +83,7 @@ trait RouteTest extends RequestBuilding with RouteTestResultComponent { case _ ⇒ Nil } - def rejections: List[Rejection] = result.rejections + def rejections: immutable.Seq[Rejection] = result.rejections def rejection: Rejection = { val r = rejections if (r.size == 1) r.head else failTest("Expected a single rejection but got %s (%s)".format(r.size, r)) diff --git a/akka-http-testkit/src/main/scala/akka/http/testkit/RouteTestResultComponent.scala b/akka-http-testkit/src/main/scala/akka/http/testkit/RouteTestResultComponent.scala index 681cf711b5..1636cc818b 100644 --- a/akka-http-testkit/src/main/scala/akka/http/testkit/RouteTestResultComponent.scala +++ b/akka-http-testkit/src/main/scala/akka/http/testkit/RouteTestResultComponent.scala @@ -21,12 +21,12 @@ trait RouteTestResultComponent { * A receptacle for the response or rejections created by a route. */ class RouteTestResult(timeout: FiniteDuration)(implicit fm: FlowMaterializer) { - private[this] var result: Option[Either[List[Rejection], HttpResponse]] = None + private[this] var result: Option[Either[immutable.Seq[Rejection], HttpResponse]] = None private[this] val latch = new CountDownLatch(1) def handled: Boolean = synchronized { result.isDefined && result.get.isRight } - def rejections: List[Rejection] = synchronized { + def rejections: immutable.Seq[Rejection] = synchronized { result match { case Some(Left(rejections)) ⇒ rejections case Some(Right(response)) ⇒ failTest("Request was not rejected, response was " + response) diff --git a/akka-http/src/main/scala/akka/http/server/Directive.scala b/akka-http/src/main/scala/akka/http/server/Directive.scala index 8c05ca6875..4c8bc5087a 100644 --- a/akka-http/src/main/scala/akka/http/server/Directive.scala +++ b/akka-http/src/main/scala/akka/http/server/Directive.scala @@ -7,6 +7,8 @@ package akka.http.server import akka.http.server.directives.RouteDirectives import akka.http.server.util._ +import scala.collection.immutable + trait ConjunctionMagnet[L] { type Out def apply(underlying: Directive[L]): Out @@ -55,7 +57,7 @@ abstract class Directive[L](implicit val ev: Tuple[L]) { self ⇒ def tapply(f: L ⇒ Route): Route def |[R >: L](that: Directive[R]): Directive[R] = - recover(rejections ⇒ directives.BasicDirectives.mapRejections(rejections ::: _) & that)(that.ev) + recover(rejections ⇒ directives.BasicDirectives.mapRejections(rejections ++ _) & that)(that.ev) /** * Joins two directives into one which extracts the concatenation of its base directive extractions. @@ -85,7 +87,7 @@ abstract class Directive[L](implicit val ev: Tuple[L]) { self ⇒ self.tapply { values ⇒ ctx ⇒ if (predicate(values)) f(values)(ctx) else ctx.reject(rejections: _*) } } - def recover[R >: L: Tuple](recovery: List[Rejection] ⇒ Directive[R]): Directive[R] = + def recover[R >: L: Tuple](recovery: immutable.Seq[Rejection] ⇒ Directive[R]): Directive[R] = new Directive[R] { def tapply(f: R ⇒ Route) = { ctx ⇒ @volatile var rejectedFromInnerRoute = false @@ -98,7 +100,7 @@ abstract class Directive[L](implicit val ev: Tuple[L]) { self ⇒ } } - def recoverPF[R >: L: Tuple](recovery: PartialFunction[List[Rejection], Directive[R]]): Directive[R] = + def recoverPF[R >: L: Tuple](recovery: PartialFunction[immutable.Seq[Rejection], Directive[R]]): Directive[R] = recover { rejections ⇒ if (recovery isDefinedAt rejections) recovery(rejections) else RouteDirectives.reject(rejections: _*) diff --git a/akka-http/src/main/scala/akka/http/server/Rejection.scala b/akka-http/src/main/scala/akka/http/server/Rejection.scala index 51ba12697e..6b03411349 100644 --- a/akka-http/src/main/scala/akka/http/server/Rejection.scala +++ b/akka-http/src/main/scala/akka/http/server/Rejection.scala @@ -7,6 +7,8 @@ package akka.http.server import akka.http.model._ import akka.http.model.headers.{ ByteRange, HttpEncoding } +import scala.collection.immutable + /** * A rejection encapsulates a specific reason why a Route was not able to handle a request. Rejections are gathered * up over the course of a Route evaluation and finally converted to [[spray.http.HttpResponse]]s by the @@ -179,7 +181,7 @@ case class ValidationRejection(message: String, cause: Option[Throwable] = None) * MethodRejection added by the ``get`` directive is cancelled by the ``put`` directive (since the HTTP method * did indeed match eventually). */ -case class TransformationRejection(transform: List[Rejection] ⇒ List[Rejection]) extends Rejection +case class TransformationRejection(transform: immutable.Seq[Rejection] ⇒ immutable.Seq[Rejection]) extends Rejection /** * A Throwable wrapping a Rejection. diff --git a/akka-http/src/main/scala/akka/http/server/RejectionHandler.scala b/akka-http/src/main/scala/akka/http/server/RejectionHandler.scala index 7c10f7fc99..130438b5df 100644 --- a/akka-http/src/main/scala/akka/http/server/RejectionHandler.scala +++ b/akka-http/src/main/scala/akka/http/server/RejectionHandler.scala @@ -4,6 +4,7 @@ package akka.http.server +import scala.collection.immutable import scala.concurrent.ExecutionContext import akka.http.model._ import StatusCodes._ @@ -16,102 +17,102 @@ trait RejectionHandler extends RejectionHandler.PF { } object RejectionHandler { - type PF = PartialFunction[List[Rejection], Route] + type PF = PartialFunction[immutable.Seq[Rejection], Route] implicit def apply(pf: PF): RejectionHandler = apply(default = false)(pf) private def apply(default: Boolean)(pf: PF): RejectionHandler = new RejectionHandler { def isDefault = default - def isDefinedAt(rejections: List[Rejection]) = pf.isDefinedAt(rejections) - def apply(rejections: List[Rejection]) = pf(rejections) + def isDefinedAt(rejections: immutable.Seq[Rejection]) = pf.isDefinedAt(rejections) + def apply(rejections: immutable.Seq[Rejection]) = pf(rejections) } def default(implicit ec: ExecutionContext) = apply(default = true) { case Nil ⇒ complete(NotFound, "The requested resource could not be found.") - case AuthenticationFailedRejection(cause, challengeHeaders) :: _ ⇒ + case AuthenticationFailedRejection(cause, challengeHeaders) +: _ ⇒ val rejectionMessage = cause match { case CredentialsMissing ⇒ "The resource requires authentication, which was not supplied with the request" case CredentialsRejected ⇒ "The supplied authentication is invalid" } ctx ⇒ ctx.complete(Unauthorized, challengeHeaders, rejectionMessage) - case AuthorizationFailedRejection :: _ ⇒ + case AuthorizationFailedRejection +: _ ⇒ complete(Forbidden, "The supplied authentication is not authorized to access this resource") - case MalformedFormFieldRejection(name, msg, _) :: _ ⇒ + case MalformedFormFieldRejection(name, msg, _) +: _ ⇒ complete(BadRequest, "The form field '" + name + "' was malformed:\n" + msg) - case MalformedHeaderRejection(headerName, msg, _) :: _ ⇒ + case MalformedHeaderRejection(headerName, msg, _) +: _ ⇒ complete(BadRequest, s"The value of HTTP header '$headerName' was malformed:\n" + msg) - case MalformedQueryParamRejection(name, msg, _) :: _ ⇒ + case MalformedQueryParamRejection(name, msg, _) +: _ ⇒ complete(BadRequest, "The query parameter '" + name + "' was malformed:\n" + msg) - case MalformedRequestContentRejection(msg, _) :: _ ⇒ + case MalformedRequestContentRejection(msg, _) +: _ ⇒ complete(BadRequest, "The request content was malformed:\n" + msg) - case rejections @ (MethodRejection(_) :: _) ⇒ + case rejections @ (MethodRejection(_) +: _) ⇒ val methods = rejections.collect { case MethodRejection(method) ⇒ method } complete(MethodNotAllowed, List(Allow(methods)), "HTTP method not allowed, supported methods: " + methods.mkString(", ")) - case rejections @ (SchemeRejection(_) :: _) ⇒ + case rejections @ (SchemeRejection(_) +: _) ⇒ val schemes = rejections.collect { case SchemeRejection(scheme) ⇒ scheme } complete(BadRequest, "Uri scheme not allowed, supported schemes: " + schemes.mkString(", ")) - case MissingCookieRejection(cookieName) :: _ ⇒ + case MissingCookieRejection(cookieName) +: _ ⇒ complete(BadRequest, "Request is missing required cookie '" + cookieName + '\'') - case MissingFormFieldRejection(fieldName) :: _ ⇒ + case MissingFormFieldRejection(fieldName) +: _ ⇒ complete(BadRequest, "Request is missing required form field '" + fieldName + '\'') - case MissingHeaderRejection(headerName) :: _ ⇒ + case MissingHeaderRejection(headerName) +: _ ⇒ complete(BadRequest, "Request is missing required HTTP header '" + headerName + '\'') - case MissingQueryParamRejection(paramName) :: _ ⇒ + case MissingQueryParamRejection(paramName) +: _ ⇒ complete(NotFound, "Request is missing required query parameter '" + paramName + '\'') - case RequestEntityExpectedRejection :: _ ⇒ + case RequestEntityExpectedRejection +: _ ⇒ complete(BadRequest, "Request entity expected but not supplied") - case TooManyRangesRejection(_) :: _ ⇒ + case TooManyRangesRejection(_) +: _ ⇒ complete(RequestedRangeNotSatisfiable, "Request contains too many ranges.") - case UnsatisfiableRangeRejection(unsatisfiableRanges, actualEntityLength) :: _ ⇒ + case UnsatisfiableRangeRejection(unsatisfiableRanges, actualEntityLength) +: _ ⇒ complete(RequestedRangeNotSatisfiable, List(`Content-Range`(ContentRange.Unsatisfiable(actualEntityLength))), unsatisfiableRanges.mkString("None of the following requested Ranges were satisfiable:\n", "\n", "")) - case rejections @ (UnacceptedResponseContentTypeRejection(_) :: _) ⇒ + case rejections @ (UnacceptedResponseContentTypeRejection(_) +: _) ⇒ val supported = rejections.flatMap { case UnacceptedResponseContentTypeRejection(supported) ⇒ supported case _ ⇒ Nil } complete(NotAcceptable, "Resource representation is only available with these Content-Types:\n" + supported.map(_.value).mkString("\n")) - case rejections @ (UnacceptedResponseEncodingRejection(_) :: _) ⇒ + case rejections @ (UnacceptedResponseEncodingRejection(_) +: _) ⇒ val supported = rejections.collect { case UnacceptedResponseEncodingRejection(supported) ⇒ supported } complete(NotAcceptable, "Resource representation is only available with these Content-Encodings:\n" + supported.map(_.value).mkString("\n")) - case rejections @ (UnsupportedRequestContentTypeRejection(_) :: _) ⇒ + case rejections @ (UnsupportedRequestContentTypeRejection(_) +: _) ⇒ val supported = rejections.collect { case UnsupportedRequestContentTypeRejection(supported) ⇒ supported } complete(UnsupportedMediaType, "There was a problem with the requests Content-Type:\n" + supported.mkString(" or ")) - case rejections @ (UnsupportedRequestEncodingRejection(_) :: _) ⇒ + case rejections @ (UnsupportedRequestEncodingRejection(_) +: _) ⇒ val supported = rejections.collect { case UnsupportedRequestEncodingRejection(supported) ⇒ supported } complete(BadRequest, "The requests Content-Encoding must be one the following:\n" + supported.map(_.value).mkString("\n")) - case ValidationRejection(msg, _) :: _ ⇒ + case ValidationRejection(msg, _) +: _ ⇒ complete(BadRequest, msg) - case x :: _ ⇒ sys.error("Unhandled rejection: " + x) + case x +: _ ⇒ sys.error("Unhandled rejection: " + x) } /** * Filters out all TransformationRejections from the given sequence and applies them (in order) to the * remaining rejections. */ - def applyTransformations(rejections: List[Rejection]): List[Rejection] = { + def applyTransformations(rejections: immutable.Seq[Rejection]): immutable.Seq[Rejection] = { val (transformations, rest) = rejections.partition(_.isInstanceOf[TransformationRejection]) (rest.distinct /: transformations.asInstanceOf[Seq[TransformationRejection]]) { case (remaining, transformation) ⇒ transformation.transform(remaining) diff --git a/akka-http/src/main/scala/akka/http/server/RequestContext.scala b/akka-http/src/main/scala/akka/http/server/RequestContext.scala index 55d0b85935..6606fc2629 100644 --- a/akka-http/src/main/scala/akka/http/server/RequestContext.scala +++ b/akka-http/src/main/scala/akka/http/server/RequestContext.scala @@ -107,12 +107,12 @@ trait RequestContext { /** * Returns a copy of this context with the given rejection transformation function chained into the response chain. */ - def withRejectionsMapped(f: List[Rejection] ⇒ List[Rejection]): RequestContext + def withRejectionsMapped(f: immutable.Seq[Rejection] ⇒ immutable.Seq[Rejection]): RequestContext /** * Returns a copy of this context with the given rejection handling function chained into the response chain. */ - def withRejectionHandling(f: List[Rejection] ⇒ Future[RouteResult]): RequestContext + def withRejectionHandling(f: immutable.Seq[Rejection] ⇒ Future[RouteResult]): RequestContext /** * Returns a copy of this context with the given exception handling function chained into the response chain. diff --git a/akka-http/src/main/scala/akka/http/server/RequestContextImpl.scala b/akka-http/src/main/scala/akka/http/server/RequestContextImpl.scala index 95241c601e..77daccf59c 100644 --- a/akka-http/src/main/scala/akka/http/server/RequestContextImpl.scala +++ b/akka-http/src/main/scala/akka/http/server/RequestContextImpl.scala @@ -38,7 +38,7 @@ private[http] class RequestContextImpl( .fast.flatMap(finish)(executionContext) override def reject(rejections: Rejection*): Future[RouteResult] = - finish(RouteResult.rejected(rejections.toList)) + finish(RouteResult.rejected(rejections.toVector)) override def fail(error: Throwable): Future[RouteResult] = finish(RouteResult.failure(error)) @@ -75,12 +75,12 @@ private[http] class RequestContextImpl( override def withHttpResponseHeadersMapped(f: immutable.Seq[HttpHeader] ⇒ immutable.Seq[HttpHeader]): RequestContext = withHttpResponseMapped(_ mapHeaders f) - override def withRejectionsMapped(f: List[Rejection] ⇒ List[Rejection]): RequestContext = + override def withRejectionsMapped(f: immutable.Seq[Rejection] ⇒ immutable.Seq[Rejection]): RequestContext = withRouteResponseMappedPF { case RouteResult.Rejected(rejs) ⇒ RouteResult.rejected(f(rejs)) } - override def withRejectionHandling(f: List[Rejection] ⇒ Future[RouteResult]): RequestContext = + override def withRejectionHandling(f: immutable.Seq[Rejection] ⇒ Future[RouteResult]): RequestContext = withRouteResponseHandling { case RouteResult.Rejected(rejs) ⇒ // `finish` is *not* chained in here, because the user already applied it when creating the result of f diff --git a/akka-http/src/main/scala/akka/http/server/RouteResult.scala b/akka-http/src/main/scala/akka/http/server/RouteResult.scala index dc7507dbd3..6e23ecadc6 100644 --- a/akka-http/src/main/scala/akka/http/server/RouteResult.scala +++ b/akka-http/src/main/scala/akka/http/server/RouteResult.scala @@ -4,6 +4,8 @@ package akka.http.server +import scala.collection.immutable + import akka.http.model.HttpResponse /** @@ -17,8 +19,8 @@ sealed trait RouteResult object RouteResult { final case class Complete private[RouteResult] (response: HttpResponse) extends RouteResult final case class Failure private[RouteResult] (exception: Throwable) extends RouteResult - final case class Rejected private[RouteResult] (rejections: List[Rejection]) extends RouteResult + final case class Rejected private[RouteResult] (rejections: immutable.Seq[Rejection]) extends RouteResult private[http] def complete(response: HttpResponse) = Complete(response) private[http] def failure(exception: Throwable) = Failure(exception) - private[http] def rejected(rejections: List[Rejection]) = Rejected(rejections) + private[http] def rejected(rejections: immutable.Seq[Rejection]) = Rejected(rejections) } diff --git a/akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala b/akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala index e58c2e4e1b..aa15aff4dd 100644 --- a/akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala +++ b/akka-http/src/main/scala/akka/http/server/directives/BasicDirectives.scala @@ -27,7 +27,7 @@ trait BasicDirectives { def mapRouteResponsePF(f: PartialFunction[RouteResult, RouteResult]): Directive0 = mapRequestContext(_ withRouteResponseMappedPF f) - def mapRejections(f: List[Rejection] ⇒ List[Rejection]): Directive0 = + def mapRejections(f: immutable.Seq[Rejection] ⇒ immutable.Seq[Rejection]): Directive0 = mapRequestContext(_ withRejectionsMapped f) def mapHttpResponse(f: HttpResponse ⇒ HttpResponse): Directive0 =