You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@openwhisk.apache.org by ra...@apache.org on 2017/08/12 14:38:57 UTC

[incubator-openwhisk] branch master updated: Port Controller from Spray to Akka (#2218)

This is an automated email from the ASF dual-hosted git repository.

rabbah pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-openwhisk.git


The following commit(s) were added to refs/heads/master by this push:
     new d564ea9  Port Controller from Spray to Akka (#2218)
d564ea9 is described below

commit d564ea98743625d56df5d8c060574ba5b10d7f3b
Author: James Dubee <jw...@us.ibm.com>
AuthorDate: Sat Aug 12 10:38:55 2017 -0400

    Port Controller from Spray to Akka (#2218)
    
    * Port Controller from Spray to Akka
    * Increase max-connections and Update JSON Unmarshaller
---
 common/scala/build.gradle                          |   7 +-
 common/scala/src/main/resources/application.conf   |   2 +-
 .../src/main/scala/whisk/common/HttpClient.scala   |  43 ---
 .../scala/whisk/core/entity/ActivationResult.scala |   4 +-
 .../main/scala/whisk/http/BasicHttpService.scala   | 140 +++++----
 .../main/scala/whisk/http/BasicRasService.scala    |   2 +-
 .../src/main/scala/whisk/http/ErrorResponse.scala  |  33 +--
 .../controller/src/main/resources/application.conf |  77 +++--
 .../main/scala/whisk/core/controller/Actions.scala |  47 +--
 .../scala/whisk/core/controller/Activations.scala  |  47 ++-
 .../scala/whisk/core/controller/ApiUtils.scala     |  22 +-
 .../scala/whisk/core/controller/Authenticate.scala |  22 +-
 .../whisk/core/controller/AuthenticatedRoute.scala |  29 +-
 .../controller/AuthorizedRouteDispatcher.scala     |  22 +-
 .../scala/whisk/core/controller/Controller.scala   |  81 ++---
 .../scala/whisk/core/controller/Entities.scala     |  36 ++-
 .../scala/whisk/core/controller/Namespaces.scala   |  14 +-
 .../scala/whisk/core/controller/Packages.scala     |  14 +-
 .../scala/whisk/core/controller/RestAPIs.scala     | 327 +++++++++++----------
 .../main/scala/whisk/core/controller/Rules.scala   |  15 +-
 .../scala/whisk/core/controller/Triggers.scala     |  74 ++---
 .../scala/whisk/core/controller/WebActions.scala   | 115 ++++----
 .../controller/actions/PostActionActivation.scala  |   4 +-
 .../core/controller/actions/SequenceActions.scala  |   1 +
 .../whisk/core/entitlement/ActionCollection.scala  |   3 +-
 .../scala/whisk/core/entitlement/Collection.scala  |  14 +-
 .../scala/whisk/core/entitlement/Entitlement.scala |  11 +-
 .../whisk/core/entitlement/LocalEntitlement.scala  |   3 +-
 .../whisk/core/entitlement/PackageCollection.scala |   5 +-
 .../main/scala/whisk/core/invoker/Invoker.scala    |   7 +-
 .../scala/whisk/core/invoker/InvokerServer.scala   |  15 +-
 tests/build.gradle                                 |   5 +-
 tests/src/test/scala/services/HeadersTests.scala   | 106 +++----
 .../test/scala/system/basic/WskSequenceTests.scala |   5 +-
 .../whisk/core/cli/test/WskWebActionsTests.scala   |   2 +-
 .../core/controller/test/ActionsApiTests.scala     | 139 ++++-----
 .../core/controller/test/ActivationsApiTests.scala |  58 ++--
 .../core/controller/test/AuthenticateTests.scala   |  62 +---
 .../core/controller/test/AuthenticateV2Tests.scala |   5 +-
 .../controller/test/ControllerTestCommon.scala     |   9 +-
 .../controller/test/EntitlementProviderTests.scala |   3 +-
 .../core/controller/test/NamespacesApiTests.scala  |  28 +-
 .../controller/test/PackageActionsApiTests.scala   |  92 +++---
 .../core/controller/test/PackagesApiTests.scala    | 106 +++----
 .../controller/test/RespondWithHeadersTests.scala  |  18 +-
 .../whisk/core/controller/test/RulesApiTests.scala | 114 +++----
 .../core/controller/test/SequenceApiTests.scala    |  47 +--
 .../core/controller/test/SwaggerRoutesTests.scala  |  14 +-
 .../core/controller/test/TriggersApiTests.scala    |  64 ++--
 .../core/controller/test/WebActionsApiTests.scala  | 181 ++++++------
 .../SequenceActionApiMigrationTests.scala          |  20 +-
 51 files changed, 1155 insertions(+), 1159 deletions(-)

diff --git a/common/scala/build.gradle b/common/scala/build.gradle
index fe17c58..e1d0535 100644
--- a/common/scala/build.gradle
+++ b/common/scala/build.gradle
@@ -13,15 +13,10 @@ dependencies {
 
     compile 'io.spray:spray-caching_2.11:1.3.4'
     compile 'io.spray:spray-json_2.11:1.3.3'
-    compile 'io.spray:spray-can_2.11:1.3.4'
-    compile 'io.spray:spray-client_2.11:1.3.4'
-    compile 'io.spray:spray-httpx_2.11:1.3.4'
-    compile 'io.spray:spray-io_2.11:1.3.4'
-    compile 'io.spray:spray-routing_2.11:1.3.4'
 
     compile 'com.typesafe.akka:akka-actor_2.11:2.4.16'
     compile 'com.typesafe.akka:akka-slf4j_2.11:2.4.16'
-    compile 'com.typesafe.akka:akka-http-core_2.11:10.0.2'
+    compile 'com.typesafe.akka:akka-http-core_2.11:10.0.9'
     compile 'com.typesafe.akka:akka-http-spray-json_2.11:10.0.2'
 
     compile 'log4j:log4j:1.2.16'
diff --git a/common/scala/src/main/resources/application.conf b/common/scala/src/main/resources/application.conf
index c3d8686..d704334 100644
--- a/common/scala/src/main/resources/application.conf
+++ b/common/scala/src/main/resources/application.conf
@@ -1,4 +1,4 @@
-# default application configuration file for spray/akka
+# default application configuration file for akka
 include "logging"
 
 akka.http {
diff --git a/common/scala/src/main/scala/whisk/common/HttpClient.scala b/common/scala/src/main/scala/whisk/common/HttpClient.scala
deleted file mode 100644
index f7cd2a9..0000000
--- a/common/scala/src/main/scala/whisk/common/HttpClient.scala
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package whisk.common
-
-import scala.concurrent.ExecutionContext
-import scala.concurrent.Future
-
-import akka.actor.ActorSystem
-import akka.io.IO
-import akka.pattern.ask
-import akka.util.Timeout
-import spray.can.Http
-import spray.client.pipelining.SendReceive
-import spray.client.pipelining.sendReceive
-
-/**
- * Trait which implements a method to make an HTTP Request.
- */
-trait HttpClient {
-    /** Creates HTTP request client. */
-    protected def httpRequest(host: String, port: Int, timeout: Timeout)(
-        implicit as: ActorSystem, ec: ExecutionContext): Future[SendReceive] = {
-        implicit val duration = timeout
-        for (
-            Http.HostConnectorInfo(connector, _) <- IO(Http) ? Http.HostConnectorSetup(host, port)
-        ) yield sendReceive(connector)
-    }
-}
diff --git a/common/scala/src/main/scala/whisk/core/entity/ActivationResult.scala b/common/scala/src/main/scala/whisk/core/entity/ActivationResult.scala
index aef644e..705e722 100644
--- a/common/scala/src/main/scala/whisk/core/entity/ActivationResult.scala
+++ b/common/scala/src/main/scala/whisk/core/entity/ActivationResult.scala
@@ -19,9 +19,11 @@ package whisk.core.entity
 
 import scala.util.Try
 
-import spray.http.StatusCodes.OK
+import akka.http.scaladsl.model.StatusCodes.OK
+
 import spray.json._
 import spray.json.DefaultJsonProtocol
+
 import whisk.common.Logging
 import whisk.http.Messages._
 
diff --git a/common/scala/src/main/scala/whisk/http/BasicHttpService.scala b/common/scala/src/main/scala/whisk/http/BasicHttpService.scala
index 7b1ee7d..6ca0fe5 100644
--- a/common/scala/src/main/scala/whisk/http/BasicHttpService.scala
+++ b/common/scala/src/main/scala/whisk/http/BasicHttpService.scala
@@ -19,56 +19,56 @@ package whisk.http
 
 import scala.concurrent.duration.DurationInt
 import scala.language.postfixOps
+import scala.collection.immutable.Seq
+import scala.concurrent.Future
 
 import akka.actor.Actor
-import akka.actor.ActorContext
 import akka.actor.ActorSystem
 import akka.actor.Props
 import akka.event.Logging
-import akka.io.IO
 import akka.japi.Creator
-import akka.pattern.ask
 import akka.util.Timeout
-import spray.can.Http
-import spray.http.ContentType
-import spray.http.HttpEntity
-import spray.http.HttpRequest
-import spray.http.HttpResponse
-import spray.http.MediaTypes.`text/plain`
-import spray.httpx.SprayJsonSupport.sprayJsonMarshaller
-import spray.httpx.marshalling
-import spray.httpx.marshalling.ToResponseMarshallable.isMarshallable
-import spray.routing.AuthenticationFailedRejection
-import spray.routing.Directive.pimpApply
-import spray.routing.Directives
-import spray.routing.HttpService
-import spray.routing.RejectionHandler
-import spray.routing.Route
-import spray.routing.directives.DebuggingDirectives
-import spray.routing.directives.LogEntry
-import spray.routing.directives.LoggingMagnet.forMessageFromFullShow
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.server.directives.DebuggingDirectives
+import akka.http.scaladsl.server.directives.LogEntry
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.model.HttpRequest
+import akka.http.scaladsl.model._
+import akka.http.scaladsl.server.RejectionHandler
+import akka.http.scaladsl.server.UnacceptedResponseContentTypeRejection
+import akka.http.scaladsl.server.RouteResult.Rejected
+import akka.http.scaladsl.Http
+
+import spray.json._
+
 import whisk.common.LogMarker
 import whisk.common.LogMarkerToken
 import whisk.common.Logging
 import whisk.common.LoggingMarkers
 import whisk.common.TransactionCounter
 import whisk.common.TransactionId
+import akka.stream.ActorMaterializer
 
 /**
- * This trait extends the spray HttpService trait with logging and transaction counting
+ * This trait extends the Akka Directives and Actor with logging and transaction counting
  * facilities common to all OpenWhisk REST services.
  */
-trait BasicHttpService extends HttpService with TransactionCounter {
-
-    /**
-     * Gets the actor context.
-     */
-    implicit def actorRefFactory: ActorContext
-
-    /**
-     * Gets the logging
-     */
+trait BasicHttpService extends Directives with Actor with TransactionCounter {
     implicit def logging: Logging
+    implicit val materializer = ActorMaterializer()
+    implicit val actorSystem = context.system
+    implicit val executionContext = actorSystem.dispatcher
+
+    val port: Int
+
+    /** Rejection handler to terminate connection on a bad request. Delegates to Akka handler. */
+    implicit def customRejectionHandler(implicit transid: TransactionId) =
+        RejectionHandler.default.mapRejectionResponse {
+                case res @ HttpResponse(_, _, ent: HttpEntity.Strict, _) =>
+                    val error = ErrorResponse(ent.data.utf8String, transid).toJson
+                    res.copy(entity = HttpEntity(ContentTypes.`application/json`, error.compactPrint))
+                case x => x
+            }
 
     /**
      * Gets the routes implemented by the HTTP service.
@@ -86,39 +86,47 @@ trait BasicHttpService extends HttpService with TransactionCounter {
      */
     def loglevelForRoute(route: String): Logging.LogLevel = Logging.InfoLevel
 
+    /** Rejection handler to terminate connection on a bad request. Delegates to Akka handler. */
+    val prioritizeRejections = recoverRejections { rejections =>
+        val priorityRejection = rejections.find {
+            case rejection: UnacceptedResponseContentTypeRejection => true
+            case _ => false
+        }
+
+        priorityRejection.map(rejection => Rejected(Seq(rejection))).getOrElse(Rejected(rejections))
+    }
+
     /**
      * Receives a message and runs the router.
      */
-    def receive = runRoute(
+    def route: Route = {
         assignId { implicit transid =>
-            DebuggingDirectives.logRequest(logRequestInfo _) {
-                DebuggingDirectives.logRequestResponse(logResponseInfo _) {
-                    routes
+            handleRejections(customRejectionHandler) {
+                prioritizeRejections {
+                    DebuggingDirectives.logRequest(logRequestInfo _) {
+                        DebuggingDirectives.logRequestResult(logResponseInfo _) {
+                            toStrictEntity(1.second) {
+                                routes
+                            }
+                        }
+                    }
                 }
             }
-        })
+        }
+    }
+
+    def receive = {
+        case _ =>
+    }
 
     /** Assigns transaction id to every request. */
     protected val assignId = extract(_ => transid())
 
-    /** Rejection handler to terminate connection on a bad request. Delegates to Spray handler. */
-
-    protected def customRejectionHandler(implicit transid: TransactionId) = RejectionHandler {
-        case rejections => {
-            logging.info(this, s"[REJECT] $rejections")
-            rejections match {
-                case AuthenticationFailedRejection(cause, challengeHeaders) :: _ =>
-                    BasicHttpService.customRejectionHandler.apply(rejections.takeRight(1))
-                case _ => BasicHttpService.customRejectionHandler.apply(rejections)
-            }
-        }
-    }
-
     /** Generates log entry for every request. */
     protected def logRequestInfo(req: HttpRequest)(implicit tid: TransactionId): LogEntry = {
-        val m = req.method.toString
+        val m = req.method.name.toString
         val p = req.uri.path.toString
-        val q = req.uri.query.toString
+        val q = req.uri.query().toString
         val l = loglevelForRoute(p)
         LogEntry(s"[$tid] $m $p $q", l)
     }
@@ -137,29 +145,19 @@ trait BasicHttpService extends HttpService with TransactionCounter {
             Some(LogEntry(s"[$tid] [$name] $marker", l))
         case _ => None // other kind of responses
     }
+
+    val bindingFuture = {
+        Http().bindAndHandle(route, "0.0.0.0", port)
+    }
+
+    def shutdown(): Future[Unit] = {
+        bindingFuture.flatMap(_.unbind()).map(_ => ())
+    }
 }
 
 object BasicHttpService extends Directives {
-    def startService[T <: Actor](system: ActorSystem, name: String, interface: String, port: Integer, service: Creator[T]) = {
+    def startService[T <: Actor](system: ActorSystem, name: String, interface: String, service: Creator[T]) = {
         val actor = system.actorOf(Props.create(service), s"$name-service")
-
         implicit val timeout = Timeout(5 seconds)
-        IO(Http)(system) ? Http.Bind(actor, interface, port)
-    }
-
-    /** Rejection handler to terminate connection on a bad request. Delegates to Spray handler. */
-    def customRejectionHandler(implicit transid: TransactionId) = RejectionHandler {
-        // get default rejection message, package it as an ErrorResponse instance
-        // which gets serialized into a Json object
-        case r if RejectionHandler.Default.isDefinedAt(r) => {
-            ctx =>
-                RejectionHandler.Default(r) {
-                    ctx.withHttpResponseMapped {
-                        case resp @ HttpResponse(_, HttpEntity.NonEmpty(ContentType(`text/plain`, _), msg), _, _) =>
-                            resp.withEntity(marshalling.marshalUnsafe(ErrorResponse(msg.asString, transid)))
-                    }
-                }
-        }
-        case CustomRejection(status, cause) :: _ => complete(status, ErrorResponse(cause, transid))
     }
 }
diff --git a/common/scala/src/main/scala/whisk/http/BasicRasService.scala b/common/scala/src/main/scala/whisk/http/BasicRasService.scala
index f7d0034..4c773d0 100644
--- a/common/scala/src/main/scala/whisk/http/BasicRasService.scala
+++ b/common/scala/src/main/scala/whisk/http/BasicRasService.scala
@@ -18,7 +18,7 @@
 package whisk.http
 
 import akka.event.Logging
-import spray.httpx.SprayJsonSupport._
+
 import whisk.common.Logging
 import whisk.common.TransactionId
 
diff --git a/common/scala/src/main/scala/whisk/http/ErrorResponse.scala b/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
index 525fdeb..1687630 100644
--- a/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
+++ b/common/scala/src/main/scala/whisk/http/ErrorResponse.scala
@@ -21,17 +21,16 @@ import scala.concurrent.duration.Duration
 import scala.concurrent.duration.FiniteDuration
 import scala.util.Try
 
-import spray.http.MediaType
-import spray.http.StatusCode
-import spray.http.StatusCodes.Forbidden
-import spray.http.StatusCodes.NotFound
-import spray.httpx.SprayJsonSupport.sprayJsonMarshaller
-import spray.httpx.marshalling.ToResponseMarshallable.isMarshallable
+import akka.http.scaladsl.model.StatusCode
+import akka.http.scaladsl.model.StatusCodes.Forbidden
+import akka.http.scaladsl.model.StatusCodes.NotFound
+import akka.http.scaladsl.model.MediaType
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonMarshaller
+import akka.http.scaladsl.server.StandardRoute
+
 import spray.json._
-import spray.json.DefaultJsonProtocol._
-import spray.routing.Directives
-import spray.routing.Rejection
-import spray.routing.StandardRoute
+
 import whisk.common.TransactionId
 import whisk.core.entity.SizeError
 import whisk.core.entity.ByteSize
@@ -107,6 +106,9 @@ object Messages {
     /** Error messages for activations. */
     val abnormalInitialization = "The action did not initialize and exited unexpectedly."
     val abnormalRun = "The action did not produce a valid response and exited unexpectedly."
+    def badEntityName(value: String) = s"Parameter is not a valid value for a entity name: $value"
+    def badNamespace(value: String) = s"Parameter is not a valid value for a namespace: $value"
+    def badEpoch(value: String) = s"Parameter is not a valid value for epoch seconds: $value"
 
     /** Error message for size conformance. */
     def entityTooBig(error: SizeError) = {
@@ -165,10 +167,7 @@ object Messages {
 /** Replaces rejections with Json object containing cause and transaction id. */
 case class ErrorResponse(error: String, code: TransactionId)
 
-/** Custom rejection, wraps status code for response and a cause. */
-case class CustomRejection private (status: StatusCode, cause: String) extends Rejection
-
-object ErrorResponse extends Directives {
+object ErrorResponse extends Directives with DefaultJsonProtocol {
 
     def terminate(status: StatusCode, error: String)(implicit transid: TransactionId): StandardRoute = {
         terminate(status, Option(error) filter { _.trim.nonEmpty } map {
@@ -207,9 +206,3 @@ object ErrorResponse extends Directives {
     }
 
 }
-
-object CustomRejection {
-    def apply(status: StatusCode): CustomRejection = {
-        CustomRejection(status, status.defaultMessage)
-    }
-}
diff --git a/core/controller/src/main/resources/application.conf b/core/controller/src/main/resources/application.conf
index 74afb48..d783df4 100644
--- a/core/controller/src/main/resources/application.conf
+++ b/core/controller/src/main/resources/application.conf
@@ -1,40 +1,57 @@
 # common logging configuration see common scala
 include "logging"
+include "akka-http-version"
 
-# see http://spray.io/documentation/spray-can/configuration/
+# http://doc.akka.io/docs/akka-http/current/scala/http/configuration.html
 # descriptions inlined below for convenience
-spray.can.server {
-   # Description:
-   # If a request hasn't been responded to after the time period set here
-   # a `spray.http.Timedout` message will be sent to the timeout handler.
-   # Set to `infinite` to completely disable request timeouts.
-   #
-   # Explaining the set value:
-   # The controller holds connections up to 60s for blocking invokes, and
-   # all other operations are expected to complete quickly. We allow a grace
-   # period in addition to the blocking invoke timeout.
-  request-timeout = 90s
+akka.http {
+  server {
+    # Description:
+    # If a request hasn't been responded to after the time period set here
+    # a `akka.http.Timedout` message will be sent to the timeout handler.
+    # Set to `infinite` to completely disable request timeouts.
+    #
+    # Explaining the set value:
+    # The controller holds connections up to 60s for blocking invokes, and
+    # all other operations are expected to complete quickly. We allow a grace
+    # period in addition to the blocking invoke timeout.
+    request-timeout = 90s
 
-  # Description:
-  # Enables/disables support for statistics collection and querying.
-  # Even though stats keeping overhead is small,
-  # for maximum performance switch off when not needed.
-  stats-support = off
+    # The maximum number of concurrently accepted connections when using the
+    # `Http().bindAndHandle` methods.
+    #
+    # This setting doesn't apply to the `Http().bind` method which will still
+    # deliver an unlimited backpressured stream of incoming connections.
+    #
+    # Note, that this setting limits the number of the connections on a best-effort basis.
+    # It does *not* strictly guarantee that the number of established TCP connections will never
+    # exceed the limit (but it will be approximately correct) because connection termination happens
+    # asynchronously. It also does *not* guarantee that the number of concurrently active handler
+    # flow materializations will never exceed the limit for the reason that it is impossible to reliably
+    # detect when a materialization has ended.
+    max-connections = 8192
 
-  # Description:
-  # The time after which an idle connection will be automatically closed.
-  # Set to `infinite` to completely disable idle connection timeouts.
-  #
-  # Explaining the set value:
-  # This must be greater than the request timeout.
-  idle-timeout = 120s
+    # Description:
+    # Enables/disables support for statistics collection and querying.
+    # Even though stats keeping overhead is small,
+    # for maximum performance switch off when not needed.
+    stats-support = off
 
-  parsing {
-    # This indirectly puts a bound on the name of entities
-    # 8k matches nginx default
-    max-uri-length = 8k
+    # Description:
+    # The time after which an idle connection will be automatically closed.
+    # Set to `infinite` to completely disable idle connection timeouts.
+    #
+    # Explaining the set value:
+    # This must be greater than the request timeout.
+    idle-timeout = 120s
 
-    # This is 50MB to allow action attachments
-    max-content-length = 50m
+    parsing {
+      # This indirectly puts a bound on the name of entities
+      # 8k matches nginx default
+      max-uri-length = 8k
+
+      # This is 50MB to allow action attachments
+      max-content-length = 50m
+    }
   }
 }
diff --git a/core/controller/src/main/scala/whisk/core/controller/Actions.scala b/core/controller/src/main/scala/whisk/core/controller/Actions.scala
index eeb0b6a..e7d812c 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Actions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Actions.scala
@@ -20,32 +20,37 @@ package whisk.core.controller
 import scala.concurrent.Future
 import scala.concurrent.duration._
 import scala.language.postfixOps
-import scala.util.Failure
-import scala.util.Success
-import scala.util.Try
+import scala.util.{ Failure, Success, Try }
 
 import org.apache.kafka.common.errors.RecordTooLargeException
 
 import akka.actor.ActorSystem
-import spray.http.HttpMethod
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
-import spray.httpx.unmarshalling._
+import akka.http.scaladsl.model.HttpMethod
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.server.RequestContext
+import akka.http.scaladsl.server.RouteResult
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonMarshaller
+import akka.http.scaladsl.unmarshalling._
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
-import spray.routing.RequestContext
+
 import whisk.common.TransactionId
 import whisk.core.WhiskConfig
 import whisk.core.controller.actions.PostActionActivation
 import whisk.core.database.NoDocumentException
 import whisk.core.entitlement._
-import whisk.core.entitlement.Privilege._
 import whisk.core.entity._
 import whisk.core.entity.types.ActivationStore
 import whisk.core.entity.types.EntityStore
 import whisk.http.ErrorResponse.terminate
 import whisk.http.Messages
 import whisk.http.Messages._
+import whisk.core.entitlement.Resource
+import whisk.core.entitlement.Collection
+import whisk.core.entitlement.Privilege.Privilege
+import whisk.core.entitlement.Privilege._
 
 /**
  * A singleton object which defines the properties that must be present in a configuration
@@ -82,6 +87,8 @@ trait WhiskActionsApi
     /** Database service to get activations. */
     protected val activationStore: ActivationStore
 
+    import RestApiCommons.emptyEntityToJsObject
+
     /**
      * Handles operations on action resources, which encompass these cases:
      *
@@ -208,7 +215,7 @@ trait WhiskActionsApi
      * - 500 Internal Server Error
      */
     override def activate(user: Identity, entityName: FullyQualifiedEntityName, env: Option[Parameters])(implicit transid: TransactionId) = {
-        parameter('blocking ? false, 'result ? false, 'timeout ? WhiskActionsApi.maxWaitForBlockingActivation) { (blocking, result, waitOverride) =>
+        parameter('blocking ? false, 'result ? false, 'timeout.as[FiniteDuration] ? WhiskActionsApi.maxWaitForBlockingActivation) { (blocking, result, waitOverride) =>
             entity(as[Option[JsObject]]) { payload =>
                 getEntity(WhiskAction, entityStore, entityName.toDocId, Some {
                     act: WhiskAction =>
@@ -499,7 +506,7 @@ trait WhiskActionsApi
      * namespace.
      */
     private def mergeActionWithPackageAndDispatch(method: HttpMethod, user: Identity, action: EntityName, ref: Option[WhiskPackage] = None)(wp: WhiskPackage)(
-        implicit transid: TransactionId): RequestContext => Unit = {
+        implicit transid: TransactionId): RequestContext => Future[RouteResult] = {
         wp.binding map {
             case b: Binding =>
                 val docid = b.fullyQualifiedName.toDocId
@@ -616,18 +623,16 @@ trait WhiskActionsApi
         Parameters(WhiskAction.execFieldName, exec.kind)
     }
 
-    /** Max atomic action count allowed for sequences */
+    /** Max atomic action count allowed for sequences. */
     private lazy val actionSequenceLimit = whiskConfig.actionSequenceLimit.toInt
 
-    /** Custom deserializer for timeout query parameter. */
-    private implicit val stringToTimeoutDeserializer = new FromStringDeserializer[FiniteDuration] {
-        val max = WhiskActionsApi.maxWaitForBlockingActivation.toMillis
-        def apply(msecs: String): Either[DeserializationError, FiniteDuration] = {
-            Try { msecs.toInt } match {
-                case Success(i) if i > 0 && i <= max => Right(i.milliseconds)
-                case _ => Left {
-                    MalformedContent(Messages.invalidTimeout(WhiskActionsApi.maxWaitForBlockingActivation))
-                }
+    implicit val stringToFiniteDuration: Unmarshaller[String, FiniteDuration] = {
+        Unmarshaller.strict[String, FiniteDuration] { value =>
+            val max = WhiskActionsApi.maxWaitForBlockingActivation.toMillis
+
+            Try { value.toInt } match {
+                case Success(i) if i > 0 && i <= max => i.milliseconds
+                case _                               => throw new IllegalArgumentException(Messages.invalidTimeout(WhiskActionsApi.maxWaitForBlockingActivation))
             }
         }
     }
diff --git a/core/controller/src/main/scala/whisk/core/controller/Activations.scala b/core/controller/src/main/scala/whisk/core/controller/Activations.scala
index 9c46853..e643b13 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Activations.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Activations.scala
@@ -24,14 +24,14 @@ import scala.util.Failure
 import scala.util.Success
 import scala.util.Try
 
-import spray.httpx.SprayJsonSupport.sprayJsonMarshaller
-import spray.httpx.unmarshalling.DeserializationError
-import spray.httpx.unmarshalling.FromStringDeserializer
-import spray.httpx.unmarshalling.MalformedContent
-import spray.routing.Directives
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonMarshaller
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.unmarshalling._
+import akka.http.scaladsl.model.StatusCodes.BadRequest
+
+import spray.json.DeserializationException
 import spray.json.DefaultJsonProtocol.RootJsObjectFormat
 import spray.json._
-import spray.http.StatusCodes.BadRequest
 
 import whisk.common.TransactionId
 import whisk.core.entitlement.Collection
@@ -184,33 +184,30 @@ trait WhiskActivationsApi
             (activation: WhiskActivation) => activation.logs.toJsonObject)
     }
 
-    /** Custom deserializer for query parameters "name" into valid entity name. */
-    private implicit val stringToEntityName = new FromStringDeserializer[EntityName] {
-        def apply(value: String): Either[DeserializationError, EntityName] = {
+    /** Custom unmarshaller for query parameters "name" into valid entity name. */
+    private implicit val stringToEntityName: Unmarshaller[String, EntityName] =
+        Unmarshaller.strict[String, EntityName] { value =>
             Try { EntityName(value) } match {
-                case Success(e) => Right(e)
-                case Failure(t) => Left(MalformedContent(t.getMessage))
+                case Success(e) => e
+                case Failure(t) => throw new IllegalArgumentException(Messages.badEntityName(value))
             }
         }
-    }
 
-    /** Custom deserializer for query parameters "name" into valid namespace. */
-    private implicit val stringToNamespace = new FromStringDeserializer[EntityPath] {
-        def apply(value: String): Either[DeserializationError, EntityPath] = {
+    /** Custom unmarshaller for query parameters "name" into valid namespace. */
+    private implicit val stringToNamespace: Unmarshaller[String, EntityPath] =
+        Unmarshaller.strict[String, EntityPath] { value =>
             Try { EntityPath(value) } match {
-                case Success(e) => Right(e)
-                case Failure(t) => Left(MalformedContent(t.getMessage))
+                case Success(e) => e
+                case Failure(t) => throw new IllegalArgumentException(Messages.badNamespace(value))
             }
         }
-    }
 
-    /** Custom deserializer for query parameters "since" and "upto" into a valid Instant. */
-    private implicit val stringToInstantDeserializer = new FromStringDeserializer[Instant] {
-        def apply(secs: String): Either[DeserializationError, Instant] = {
-            Try { Instant.ofEpochMilli(secs.toLong) } match {
-                case Success(i) => Right(i)
-                case Failure(t) => Left(MalformedContent(s"Parameter is not a valid value for epoch seconds: $secs", Some(t)))
+    /** Custom unmarshaller for query parameters "since" and "upto" into a valid Instant. */
+    private implicit val stringToInstantDeserializer: Unmarshaller[String, Instant] =
+        Unmarshaller.strict[String, Instant] { value =>
+            Try { Instant.ofEpochMilli(value.toLong) } match {
+                case Success(e) => e
+                case Failure(t) => throw new IllegalArgumentException(Messages.badEpoch(value))
             }
         }
-    }
 }
diff --git a/core/controller/src/main/scala/whisk/core/controller/ApiUtils.scala b/core/controller/src/main/scala/whisk/core/controller/ApiUtils.scala
index ba692b6..54f424c 100644
--- a/core/controller/src/main/scala/whisk/core/controller/ApiUtils.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/ApiUtils.scala
@@ -22,20 +22,22 @@ import scala.concurrent.Future
 import scala.util.Failure
 import scala.util.Success
 
-import spray.http.StatusCode
-import spray.http.StatusCodes.Conflict
-import spray.http.StatusCodes.InternalServerError
-import spray.http.StatusCodes.NotFound
-import spray.http.StatusCodes.OK
-import spray.httpx.SprayJsonSupport._
-import spray.httpx.marshalling.ToResponseMarshallable.isMarshallable
+import akka.http.scaladsl.model.StatusCode
+import akka.http.scaladsl.model.StatusCodes.Conflict
+import akka.http.scaladsl.model.StatusCodes.InternalServerError
+import akka.http.scaladsl.model.StatusCodes.NotFound
+import akka.http.scaladsl.model.StatusCodes.OK
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.server.RequestContext
+import akka.http.scaladsl.server.RouteResult
+
 import spray.json.DefaultJsonProtocol._
 import spray.json.JsBoolean
 import spray.json.JsObject
 import spray.json.JsValue
 import spray.json.RootJsonFormat
-import spray.routing.Directives
-import spray.routing.RequestContext
+
 import whisk.common.Logging
 import whisk.common.TransactionId
 import whisk.core.controller.PostProcess.PostProcessEntity
@@ -105,7 +107,7 @@ protected[controller] object FilterEntityList {
  * on an operation and terminate the HTTP request.
  */
 package object PostProcess {
-    type PostProcessEntity[A] = A => RequestContext => Unit
+    type PostProcessEntity[A] = A => RequestContext => Future[RouteResult]
 }
 
 /** A trait for REST APIs that read entities from a datastore */
diff --git a/core/controller/src/main/scala/whisk/core/controller/Authenticate.scala b/core/controller/src/main/scala/whisk/core/controller/Authenticate.scala
index 34d7161..ea3db36 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Authenticate.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Authenticate.scala
@@ -17,20 +17,23 @@
 
 package whisk.core.controller
 
+import akka.http.scaladsl.model.headers.BasicHttpCredentials
+import akka.http.scaladsl.model.headers._
+
 import scala.concurrent.ExecutionContext
 import scala.concurrent.Future
 import scala.util.Try
 
-import spray.routing.authentication.UserPass
 import whisk.common.Logging
 import whisk.common.TransactionId
 import whisk.core.database.NoDocumentException
+import whisk.core.entity.UUID
+import whisk.core.entity.types.AuthStore
+import whisk.core.entity.WhiskAuthStore
 import whisk.core.entity.AuthKey
 import whisk.core.entity.Identity
 import whisk.core.entity.Secret
 import whisk.core.entity.UUID
-import whisk.core.entity.WhiskAuthStore
-import whisk.core.entity.types.AuthStore
 
 object Authenticate {
     /** Required properties for this component */
@@ -39,24 +42,21 @@ object Authenticate {
 
 /** A trait to validate basic auth credentials */
 trait Authenticate {
-
-    /** An execution context for futures */
     protected implicit val executionContext: ExecutionContext
+    protected implicit val logging: Logging
 
     /** Database service to lookup credentials */
     protected val authStore: AuthStore
 
-    protected implicit val logging: Logging
-
     /**
      * Validates credentials against the authentication database; may be used in
      * authentication directive.
      */
-    def validateCredentials(userpass: Option[UserPass])(implicit transid: TransactionId): Future[Option[Identity]] = {
-        userpass flatMap { pw =>
+    def validateCredentials(credentials: Option[BasicHttpCredentials])(implicit transid: TransactionId): Future[Option[Identity]] = {
+        credentials flatMap { pw =>
             Try {
                 // authkey deserialization is wrapped in a try to guard against malformed values
-                val authkey = AuthKey(UUID(pw.user), Secret(pw.pass))
+                val authkey = AuthKey(UUID(pw.username), Secret(pw.password))
                 logging.info(this, s"authenticate: ${authkey.uuid}")
                 val future = Identity.get(authStore, authkey) map { result =>
                     if (authkey == result.authkey) {
@@ -75,7 +75,7 @@ trait Authenticate {
                 future
             }.toOption
         } getOrElse {
-            userpass.foreach(_ => logging.info(this, s"credentials are malformed"))
+            credentials.foreach(_ => logging.info(this, s"credentials are malformed"))
             Future.successful(None)
         }
     }
diff --git a/core/controller/src/main/scala/whisk/core/controller/AuthenticatedRoute.scala b/core/controller/src/main/scala/whisk/core/controller/AuthenticatedRoute.scala
index fad7060..e9b481f 100644
--- a/core/controller/src/main/scala/whisk/core/controller/AuthenticatedRoute.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/AuthenticatedRoute.scala
@@ -17,19 +17,18 @@
 
 package whisk.core.controller
 
-import scala.Left
 import scala.concurrent.ExecutionContext
 import scala.concurrent.Future
 
-import spray.http.StatusCodes.InternalServerError
-import spray.http.StatusCodes.ServiceUnavailable
-import spray.routing.RequestContext
-import spray.routing.Route
-import spray.routing.authentication.BasicHttpAuthenticator
-import spray.routing.authentication.UserPass
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.server.Directives._
+import akka.http.scaladsl.model.headers.BasicHttpCredentials
+import akka.http.scaladsl.server.directives._
+import akka.http.scaladsl.server.directives.AuthenticationResult
+import akka.http.scaladsl.model.headers._
+
 import whisk.common.TransactionId
 import whisk.core.entity.Identity
-import whisk.http.CustomRejection
 
 /** A common trait for secured routes */
 trait AuthenticatedRoute {
@@ -38,19 +37,17 @@ trait AuthenticatedRoute {
     protected implicit val executionContext: ExecutionContext
 
     /** Creates HTTP BasicAuth handler */
-    protected def basicauth(implicit transid: TransactionId) = {
-        new BasicHttpAuthenticator[Identity](realm = "whisk rest service", validateCredentials _) {
-            override def apply(ctx: RequestContext) = {
-                super.apply(ctx) recover {
-                    case t: IllegalStateException => Left(CustomRejection(InternalServerError))
-                    case t                        => Left(CustomRejection(ServiceUnavailable))
-                }
+    def basicAuth[A](verify: Option[BasicHttpCredentials] => Future[Option[A]]) = {
+        authenticateOrRejectWithChallenge[BasicHttpCredentials, A] { creds =>
+            verify(creds).map {
+                case Some(t) => AuthenticationResult.success(t)
+                case None    => AuthenticationResult.failWithChallenge(HttpChallenges.basic("OpenWhisk secure realm"))
             }
         }
     }
 
     /** Validates credentials against database of subjects */
-    protected def validateCredentials(userpass: Option[UserPass])(implicit transid: TransactionId): Future[Option[Identity]]
+    protected def validateCredentials(credentials: Option[BasicHttpCredentials])(implicit transid: TransactionId): Future[Option[Identity]]
 }
 
 /** A trait for authenticated routes. */
diff --git a/core/controller/src/main/scala/whisk/core/controller/AuthorizedRouteDispatcher.scala b/core/controller/src/main/scala/whisk/core/controller/AuthorizedRouteDispatcher.scala
index 33a49fa..d82f9e3 100644
--- a/core/controller/src/main/scala/whisk/core/controller/AuthorizedRouteDispatcher.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/AuthorizedRouteDispatcher.scala
@@ -22,15 +22,19 @@ import scala.language.postfixOps
 import scala.util.Failure
 import scala.util.Success
 import scala.util.Try
+import scala.concurrent.Future
 
-import spray.http.HttpMethod
-import spray.http.StatusCodes.InternalServerError
-import spray.routing.Directive1
-import spray.routing.Directives
-import spray.routing.RequestContext
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.model.HttpMethod
+import akka.http.scaladsl.server.RequestContext
+import akka.http.scaladsl.server.RouteResult
+import akka.http.scaladsl.model.StatusCodes.InternalServerError
+import akka.http.scaladsl.server.Directive1
+
+import whisk.core.entitlement.Privilege.Privilege
+import whisk.core.entitlement.Collection
 import whisk.common.TransactionId
 import whisk.core.entitlement._
-import whisk.core.entitlement.Privilege.Privilege
 import whisk.core.entitlement.Resource
 import whisk.core.entity._
 import whisk.core.entity.size._
@@ -62,7 +66,7 @@ trait BasicAuthorizedRouteProvider extends Directives {
         method: HttpMethod,
         user: Identity,
         resource: Resource)(
-            implicit transid: TransactionId): RequestContext => Unit = {
+            implicit transid: TransactionId): RequestContext => Future[RouteResult] = {
         val right = collection.determineRight(method, resource.entity)
 
         onComplete(entitlementProvider.check(user, right, resource)) {
@@ -72,7 +76,7 @@ trait BasicAuthorizedRouteProvider extends Directives {
     }
 
     protected def handleEntitlementFailure(failure: Throwable)(
-        implicit transid: TransactionId): RequestContext => Unit = {
+        implicit transid: TransactionId): RequestContext => Future[RouteResult] = {
         failure match {
             case (r: RejectRequest) => terminate(r.code, r.message)
             case t                  => terminate(InternalServerError)
@@ -84,7 +88,7 @@ trait BasicAuthorizedRouteProvider extends Directives {
         user: Identity,
         op: Privilege,
         resource: Resource)(
-            implicit transid: TransactionId): RequestContext => Unit
+            implicit transid: TransactionId): RequestContext => Future[RouteResult]
 
     /** Extracts namespace for user from the matched path segment. */
     protected def namespace(user: Identity, ns: String) = {
diff --git a/core/controller/src/main/scala/whisk/core/controller/Controller.scala b/core/controller/src/main/scala/whisk/core/controller/Controller.scala
index 12a1086..27a63bf 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Controller.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Controller.scala
@@ -19,39 +19,37 @@ package whisk.core.controller
 
 import scala.concurrent.Await
 import scala.concurrent.duration.DurationInt
-import akka.actor.Actor
-import akka.actor.ActorContext
+import scala.util.{Failure, Success}
+
+import akka.actor._
 import akka.actor.ActorSystem
 import akka.japi.Creator
-import spray.http.StatusCodes._
-import spray.http.Uri
-import spray.httpx.SprayJsonSupport._
-import spray.json.DefaultJsonProtocol._
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.model.Uri
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+
 import spray.json._
-import spray.routing.Directive.pimpApply
-import spray.routing.Route
+import spray.json.DefaultJsonProtocol._
+
 import whisk.common.AkkaLogging
 import whisk.common.Logging
 import whisk.common.LoggingMarkers
 import whisk.common.TransactionId
 import whisk.core.WhiskConfig
 import whisk.core.entitlement._
-import whisk.core.entitlement.EntitlementProvider
 import whisk.core.entity._
 import whisk.core.entity.ActivationId.ActivationIdGenerator
 import whisk.core.entity.ExecManifest.Runtimes
 import whisk.core.loadBalancer.LoadBalancerService
 import whisk.http.BasicHttpService
 import whisk.http.BasicRasService
-import scala.util.{Failure, Success}
-
 
 /**
  * The Controller is the service that provides the REST API for OpenWhisk.
  *
  * It extends the BasicRasService so it includes a ping endpoint for monitoring.
  *
- * Spray sends messages to akka Actors -- the Controller is an Actor, ready to receive messages.
+ * Akka sends messages to akka Actors -- the Controller is an Actor, ready to receive messages.
  *
  * It is possible to deploy a hot-standby controller. Each controller needs its own instance. This instance is a
  * consecutive numbering, starting with 0.
@@ -62,8 +60,7 @@ import scala.util.{Failure, Success}
  * back to the base controller, there could be an inconsistency in the cache (e.g. if a user has updated an action). This
  * inconsistency will be resolved by its own after removing the cached item, 5 minutes after it has been generated.
  *
- * @Idioglossia uses the spray-routing DSL
- * http://spray.io/documentation/1.1.3/spray-routing/advanced-topics/understanding-dsl-structure/
+ * Uses the Akka routing DSL: http://doc.akka.io/docs/akka-http/current/scala/http/routing-dsl/overview.html
  *
  * @param config A set of properties needed to run an instance of the controller service
  * @param instance if running in scale-out, a unique identifier for this instance in the group
@@ -72,46 +69,31 @@ import scala.util.{Failure, Success}
  */
 class Controller(
     override val instance: InstanceId,
+    override val port: Int,
     runtimes: Runtimes,
     implicit val whiskConfig: WhiskConfig,
     implicit val logging: Logging)
-    extends BasicRasService
-    with Actor {
-
-    // each akka Actor has an implicit context
-    override def actorRefFactory: ActorContext = context
+    extends BasicRasService {
 
     override val numberOfInstances = whiskConfig.controllerInstances.toInt
 
+    TransactionId.controller.mark(this, LoggingMarkers.CONTROLLER_STARTUP(instance.toInt), s"starting controller instance ${instance.toInt}")
+
     /**
-     * A Route in spray is technically a function taking a RequestContext as a parameter.
-     *
-     * @Idioglossia The ~ spray DSL operator composes two independent Routes, building a routing
-     * tree structure.
-     * @see http://spray.io/documentation/1.2.3/spray-routing/key-concepts/routes/#composing-routes
-     */
+      * A Route in Akka is technically a function taking a RequestContext as a parameter.
+      *
+      * The "~" Akka DSL operator composes two independent Routes, building a routing tree structure.
+      * @see http://doc.akka.io/docs/akka-http/current/scala/http/routing-dsl/routes.html#composing-routes
+      */
     override def routes(implicit transid: TransactionId): Route = {
-        // handleRejections wraps the inner Route with a logical error-handler for unmatched paths
-        handleRejections(customRejectionHandler) {
-            super.routes ~ {
-                (pathEndOrSingleSlash & get) {
-                    complete(OK, info)
-                }
-            } ~ {
-                apiv1.routes
-            } ~ {
-                swagger.swaggerRoutes
-            } ~ {
-                internalInvokerHealth
+        super.routes ~ {
+            (pathEndOrSingleSlash & get) {
+                complete(info)
             }
-        }
+        } ~ apiV1.routes ~ swagger.swaggerRoutes ~ internalInvokerHealth
     }
 
-    TransactionId.controller.mark(this, LoggingMarkers.CONTROLLER_STARTUP(instance.toInt), s"starting controller instance ${instance.toInt}")
-
     // initialize datastores
-    private implicit val actorSystem = context.system
-    private implicit val executionContext = actorSystem.dispatcher
     private implicit val authStore = WhiskAuthStore.datastore(whiskConfig)
     private implicit val entityStore = WhiskEntityStore.datastore(whiskConfig)
     private implicit val activationStore = WhiskActivationStore.datastore(whiskConfig)
@@ -126,7 +108,7 @@ class Controller(
 
     /** The REST APIs. */
     implicit val controllerInstance = instance
-    private val apiv1 = new RestAPIVersion("api", "v1")
+    private val apiV1 = new RestAPIVersion(whiskConfig, "api", "v1")
     private val swagger = new SwaggerDocs(Uri.Path.Empty, "infoswagger.json")
 
     /**
@@ -145,7 +127,7 @@ class Controller(
     }
 
     // controller top level info
-    private val info = Controller.info(whiskConfig, runtimes, List(apiv1.basepath()))
+    private val info = Controller.info(whiskConfig, runtimes, List(apiV1.basepath()))
 }
 
 /**
@@ -156,8 +138,7 @@ object Controller {
     // requiredProperties is a Map whose keys define properties that must be bound to
     // a value, and whose values are default values.   A null value in the Map means there is
     // no default value specified, so it must appear in the properties file
-    def requiredProperties = Map(WhiskConfig.servicePort -> 8080.toString) ++
-        Map(WhiskConfig.controllerInstances -> null) ++
+    def requiredProperties = Map(WhiskConfig.controllerInstances -> null) ++
         ExecManifest.requiredProperties ++
         RestApiCommons.requiredProperties ++
         LoadBalancerService.requiredProperties ++
@@ -178,9 +159,9 @@ object Controller {
         "runtimes" -> runtimes.toJson)
 
     // akka-style factory to create a Controller object
-    private class ServiceBuilder(config: WhiskConfig, instance: InstanceId, logging: Logging) extends Creator[Controller] {
+    private class ServiceBuilder(config: WhiskConfig, instance: InstanceId, logging: Logging, port: Int) extends Creator[Controller] {
         // this method is not reached unless ExecManifest was initialized successfully
-        def create = new Controller(instance, ExecManifest.runtimesManifest, config, logging)
+        def create = new Controller(instance, port, ExecManifest.runtimesManifest, config, logging)
     }
 
     def main(args: Array[String]): Unit = {
@@ -189,6 +170,7 @@ object Controller {
 
         // extract configuration data from the environment
         val config = new WhiskConfig(requiredProperties, optionalProperties)
+        val port = config.servicePort.toInt
 
         // if deploying multiple instances (scale out), must pass the instance number as the
         require(args.length >= 1, "controller instance required")
@@ -207,8 +189,7 @@ object Controller {
 
         ExecManifest.initialize(config) match {
             case Success(_) =>
-                val port = config.servicePort.toInt
-                BasicHttpService.startService(actorSystem, "controller", "0.0.0.0", port, new ServiceBuilder(config, InstanceId(instance), logger))
+                BasicHttpService.startService(actorSystem, "controller", "0.0.0.0", new ServiceBuilder(config, InstanceId(instance), logger, port))
 
             case Failure(t) =>
                 logger.error(this, s"Invalid runtimes manifest: $t")
diff --git a/core/controller/src/main/scala/whisk/core/controller/Entities.scala b/core/controller/src/main/scala/whisk/core/controller/Entities.scala
index df8f45f..ebafac5 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Entities.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Entities.scala
@@ -19,16 +19,20 @@ package whisk.core.controller
 
 import scala.language.postfixOps
 import scala.util.Try
+import scala.concurrent.Future
+
+import akka.http.scaladsl.model.StatusCodes.RequestEntityTooLarge
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.Directive0
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.server.RequestContext
+import akka.http.scaladsl.server.RouteResult
+import akka.http.scaladsl.server.Route
 
-import shapeless.HNil
-import spray.http.StatusCodes.RequestEntityTooLarge
-import spray.httpx.SprayJsonSupport._
-import spray.routing.Directive0
-import spray.routing.Directives
-import spray.routing.RequestContext
-import spray.routing.Route
 import whisk.common.TransactionId
 import whisk.core.entitlement.Privilege._
+import whisk.core.entitlement.Privilege.Privilege
+import whisk.core.entitlement.Privilege.READ
 import whisk.core.entitlement.Resource
 import whisk.core.entity._
 import whisk.core.entity.ActivationEntityLimit
@@ -39,10 +43,10 @@ import whisk.http.Messages
 protected[controller] trait ValidateRequestSize extends Directives {
     protected def validateSize(check: => Option[SizeError])(
         implicit tid: TransactionId) = new Directive0 {
-        def happly(f: HNil => Route) = {
+        def tapply(f: Unit => Route) = {
             check map {
                 case e: SizeError => terminate(RequestEntityTooLarge, Messages.entityTooBig(e))
-            } getOrElse f(HNil)
+            } getOrElse f(None)
         }
     }
 
@@ -72,19 +76,19 @@ trait WhiskCollectionAPI
     services: WhiskServices =>
 
     /** Creates an entity, or updates an existing one, in namespace. Terminates HTTP request. */
-    protected def create(user: Identity, entityName: FullyQualifiedEntityName)(implicit transid: TransactionId): RequestContext => Unit
+    protected def create(user: Identity, entityName: FullyQualifiedEntityName)(implicit transid: TransactionId): RequestContext => Future[RouteResult]
 
     /** Activates entity. Examples include invoking an action, firing a trigger, enabling/disabling a rule. */
-    protected def activate(user: Identity, entityName: FullyQualifiedEntityName, env: Option[Parameters])(implicit transid: TransactionId): RequestContext => Unit
+    protected def activate(user: Identity, entityName: FullyQualifiedEntityName, env: Option[Parameters])(implicit transid: TransactionId): RequestContext => Future[RouteResult]
 
     /** Removes entity from namespace. Terminates HTTP request. */
-    protected def remove(user: Identity, entityName: FullyQualifiedEntityName)(implicit transid: TransactionId): RequestContext => Unit
+    protected def remove(user: Identity, entityName: FullyQualifiedEntityName)(implicit transid: TransactionId): RequestContext => Future[RouteResult]
 
     /** Gets entity from namespace. Terminates HTTP request. */
-    protected def fetch(user: Identity, entityName: FullyQualifiedEntityName, env: Option[Parameters])(implicit transid: TransactionId): RequestContext => Unit
+    protected def fetch(user: Identity, entityName: FullyQualifiedEntityName, env: Option[Parameters])(implicit transid: TransactionId): RequestContext => Future[RouteResult]
 
     /** Gets all entities from namespace. If necessary filter only entities that are shared. Terminates HTTP request. */
-    protected def list(user: Identity, path: EntityPath, excludePrivate: Boolean)(implicit transid: TransactionId): RequestContext => Unit
+    protected def list(user: Identity, path: EntityPath, excludePrivate: Boolean)(implicit transid: TransactionId): RequestContext => Future[RouteResult]
 
     /** Indicates if listing entities in collection requires filtering out private entities. */
     protected val listRequiresPrivateEntityFilter = false // currently supported on PACKAGES only
@@ -101,8 +105,8 @@ trait WhiskCollectionAPI
                         }
                     }
                 case ACTIVATE =>
-                    extract(_.request.entity.data.length) { length =>
-                        validateSize(isWhithinRange(length))(transid) {
+                   extract(_.request.entity.contentLengthOption) { length =>
+                        validateSize(isWhithinRange(length.getOrElse(0)))(transid) {
                             activate(user, FullyQualifiedEntityName(resource.namespace, name), resource.env)
                         }
                     }
diff --git a/core/controller/src/main/scala/whisk/core/controller/Namespaces.scala b/core/controller/src/main/scala/whisk/core/controller/Namespaces.scala
index 0b3c144..86dbb8b 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Namespaces.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Namespaces.scala
@@ -17,14 +17,18 @@
 
 package whisk.core.controller
 
+import scala.concurrent.Future
 import scala.util.Failure
 import scala.util.Success
 
-import spray.http.StatusCodes.InternalServerError
-import spray.http.StatusCodes.OK
-import spray.httpx.SprayJsonSupport._
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.server.RequestContext
+import akka.http.scaladsl.server.RouteResult
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+
 import spray.json.DefaultJsonProtocol._
-import spray.routing.Directives
+
 import whisk.common.TransactionId
 import whisk.core.entitlement.Collection
 import whisk.core.entitlement.Privilege.Privilege
@@ -95,7 +99,7 @@ trait WhiskNamespacesApi
      * - 200 Map [ String (collection name), List[EntitySummary] ] as JSON
      * - 500 Internal Server Error
      */
-    private def getAllInNamespace(namespace: EntityPath)(implicit transid: TransactionId) = {
+    private def getAllInNamespace(namespace: EntityPath)(implicit transid: TransactionId): RequestContext => Future[RouteResult] = {
         onComplete(listEntitiesInNamespace(entityStore, namespace, false)) {
             case Success(entities) => {
                 complete(OK, Namespaces.emptyNamespace ++ entities - WhiskActivation.collectionName)
diff --git a/core/controller/src/main/scala/whisk/core/controller/Packages.scala b/core/controller/src/main/scala/whisk/core/controller/Packages.scala
index 9ed59da..ce6f4cf 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Packages.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Packages.scala
@@ -22,11 +22,13 @@ import scala.util.Failure
 import scala.util.Success
 import scala.util.Try
 
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.RequestContext
+import akka.http.scaladsl.server.RouteResult
+
 import spray.json._
-import spray.routing.Directive.pimpApply
-import spray.routing.RequestContext
+
 import whisk.common.TransactionId
 import whisk.core.database.DocumentTypeMismatchException
 import whisk.core.database.NoDocumentException
@@ -252,7 +254,7 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with ReferencedEntities {
     }
 
     private def rewriteEntitlementFailure(failure: Throwable)(
-        implicit transid: TransactionId): RequestContext => Unit = {
+        implicit transid: TransactionId): RequestContext => Future[RouteResult] = {
         logging.info(this, s"rewriting failure $failure")
         failure match {
             case RejectRequest(NotFound, _) => terminate(BadRequest, Messages.bindingDoesNotExist)
@@ -279,7 +281,7 @@ trait WhiskPackagesApi extends WhiskCollectionAPI with ReferencedEntities {
      * If this is a binding, fetch package for binding, merge parameters then emit.
      * Otherwise this is a package, emit it.
      */
-    private def mergePackageWithBinding(ref: Option[WhiskPackage] = None)(wp: WhiskPackage)(implicit transid: TransactionId): RequestContext => Unit = {
+    private def mergePackageWithBinding(ref: Option[WhiskPackage] = None)(wp: WhiskPackage)(implicit transid: TransactionId): RequestContext => Future[RouteResult] = {
         wp.binding map {
             case b: Binding =>
                 val docid = b.fullyQualifiedName.toDocId
diff --git a/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala b/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
index 2088831..1d060d8 100644
--- a/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/RestAPIs.scala
@@ -17,39 +17,37 @@
 
 package whisk.core.controller
 
-import scala.concurrent.ExecutionContext
-
-import RestApiCommons._
 import akka.actor.ActorSystem
-import spray.http.AllOrigins
-import spray.http.HttpHeaders._
-import spray.http.StatusCodes._
-import spray.http.Uri
-import spray.httpx.SprayJsonSupport._
+import akka.stream.ActorMaterializer
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.model.Uri
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.model.headers._
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
-import spray.routing.Directive.pimpApply
-import spray.routing.Directives
-import spray.routing.Route
-import whisk.common.Logging
+
+import scala.concurrent.ExecutionContext
+
 import whisk.common.TransactionId
 import whisk.core.WhiskConfig
 import whisk.core.WhiskConfig.whiskVersionBuildno
 import whisk.core.WhiskConfig.whiskVersionDate
-import whisk.core.entitlement._
+import whisk.core.entity.WhiskAuthStore
+import whisk.common.Logging
+import whisk.common.TransactionId
 import whisk.core.entity._
-import whisk.core.entity.ActivationId.ActivationIdGenerator
 import whisk.core.entity.types._
+import whisk.core.entitlement._
+import whisk.core.entity.ActivationId.ActivationIdGenerator
 import whisk.core.loadBalancer.LoadBalancerService
 
 /**
  * Abstract class which provides basic Directives which are used to construct route structures
  * which are common to all versions of the Rest API.
  */
-protected[controller] class SwaggerDocs(
-    apipath: Uri.Path,
-    doc: String)(
-        implicit actorSystem: ActorSystem)
+protected[controller] class SwaggerDocs(apipath: Uri.Path, doc: String)(implicit actorSystem: ActorSystem)
     extends Directives {
 
     /** Swagger end points. */
@@ -79,40 +77,41 @@ protected[controller] class SwaggerDocs(
     private def apiDocsUrl = basepath(apipath / swaggerdocpath)
 }
 
-/**
- * A singleton object which defines properties needed to instantiate a service for v1 or v2
- * of the REST API.
- */
 protected[controller] object RestApiCommons {
-    def requiredProperties =
+    def requiredProperties = Map(WhiskConfig.servicePort -> 8080.toString) ++
         WhiskConfig.whiskVersion ++
-            WhiskAuthStore.requiredProperties ++
-            WhiskEntityStore.requiredProperties ++
-            WhiskActivationStore.requiredProperties ++
-            EntitlementProvider.requiredProperties ++
-            WhiskActionsApi.requiredProperties ++
-            Authenticate.requiredProperties ++
-            Collection.requiredProperties
+        WhiskAuthStore.requiredProperties ++
+        WhiskEntityStore.requiredProperties ++
+        WhiskActivationStore.requiredProperties ++
+        EntitlementProvider.requiredProperties ++
+        WhiskActionsApi.requiredProperties ++
+        Authenticate.requiredProperties ++
+        Collection.requiredProperties
+
+    import akka.http.scaladsl.unmarshalling.FromEntityUnmarshaller
+    import akka.http.scaladsl.unmarshalling.Unmarshaller
+    import akka.http.scaladsl.model.MediaTypes.`application/json`
+    import akka.http.scaladsl.model.HttpCharsets
 
     /**
-     * The web actions API is available in both v1 and v2.
-     * It handles web actions.
+     * Extract an empty entity into a JSON object. This is useful for the
+     * main APIs which accept JSON content type by default but may accept
+     * no entity in the request.
      */
-    protected[controller] class WebActionsApi(
-        override val webInvokePathSegments: Seq[String],
-        override val webApiDirectives: WebApiDirectives)(
-            implicit override val authStore: AuthStore,
-            implicit val entityStore: EntityStore,
-            override val activeAckTopicIndex: InstanceId,
-            override val activationStore: ActivationStore,
-            override val entitlementProvider: EntitlementProvider,
-            override val activationIdFactory: ActivationIdGenerator,
-            override val loadBalancer: LoadBalancerService,
-            override val actorSystem: ActorSystem,
-            override val executionContext: ExecutionContext,
-            override val logging: Logging,
-            override val whiskConfig: WhiskConfig)
-        extends WhiskWebActionsApi with WhiskServices
+    implicit val emptyEntityToJsObject: FromEntityUnmarshaller[JsObject] = {
+        Unmarshaller.byteStringUnmarshaller.forContentTypes(`application/json`).mapWithCharset { (data, charset) =>
+            if (data.size == 0) {
+                JsObject()
+            } else {
+                val input = {
+                    if (charset == HttpCharsets.`UTF-8`) ParserInput(data.toArray)
+                    else ParserInput(data.decodeString(charset.nioCharset))
+                }
+
+                JsonParser(input).asJsObject
+            }
+        }
+    }
 }
 
 /**
@@ -120,60 +119,65 @@ protected[controller] object RestApiCommons {
  * Useful for CORS.
  */
 protected[controller] trait RespondWithHeaders extends Directives {
-    val allowOrigin = `Access-Control-Allow-Origin`(AllOrigins)
+    val allowOrigin = `Access-Control-Allow-Origin`.*
     val allowHeaders = `Access-Control-Allow-Headers`("Authorization", "Content-Type")
-
     val sendCorsHeaders = respondWithHeaders(allowOrigin, allowHeaders)
 }
 
-/**
- * An object which creates the Routes that define v1 of the whisk REST API.
- */
-protected[controller] class RestAPIVersion(apipath: String, apiversion: String)(
+class RestAPIVersion(config: WhiskConfig, apiPath: String, apiVersion: String)(
     implicit val activeAckTopicIndex: InstanceId,
-    implicit val authStore: AuthStore,
+    implicit val actorSystem: ActorSystem,
+    implicit val logging: Logging,
     implicit val entityStore: EntityStore,
-    implicit val activationStore: ActivationStore,
     implicit val entitlementProvider: EntitlementProvider,
     implicit val activationIdFactory: ActivationIdGenerator,
     implicit val loadBalancer: LoadBalancerService,
-    implicit val actorSystem: ActorSystem,
-    implicit val executionContext: ExecutionContext,
-    implicit val logging: Logging,
+    implicit val activationStore: ActivationStore,
     implicit val whiskConfig: WhiskConfig)
-    extends SwaggerDocs(Uri.Path(apipath) / apiversion, "apiv1swagger.json")
+    extends SwaggerDocs(Uri.Path(apiPath) / apiVersion, "apiv1swagger.json")
     with Authenticate
     with AuthenticatedRoute
     with RespondWithHeaders {
+    implicit val materializer = ActorMaterializer()
+    implicit val executionContext = actorSystem.dispatcher
+    implicit val authStore = WhiskAuthStore.datastore(config)
+
+    def prefix = pathPrefix(apiPath / apiVersion)
 
     /**
-     * Here is the key method: it defines the Route (route tree) which implement v1 of the REST API.
-     *
-     * @Idioglossia This relies on the spray routing DSL.
-     * @see http://spray.io/documentation/1.2.2/spray-routing/
+     * Describes details of a particular API path.
      */
+    val info = (pathEndOrSingleSlash & get) {
+        complete(OK, JsObject(
+            "description" -> "OpenWhisk API".toJson,
+            "api_version" -> SemVer(1, 0, 0).toJson,
+            "api_version_path" -> apiVersion.toJson,
+            "build" -> whiskConfig(whiskVersionDate).toJson,
+            "buildno" -> whiskConfig(whiskVersionBuildno).toJson,
+            "swagger_paths" -> JsObject(
+                "ui" -> s"/$swaggeruipath".toJson,
+                "api-docs" -> s"/$swaggerdocpath".toJson)).toString)
+    }
+
     def routes(implicit transid: TransactionId): Route = {
-        pathPrefix(apipath / apiversion) {
+        prefix {
             sendCorsHeaders {
-                (pathEndOrSingleSlash & get) {
-                    complete(OK, info)
-                } ~ authenticate(basicauth) {
-                    user =>
-                        namespaces.routes(user) ~
-                            pathPrefix(Collection.NAMESPACES) {
-                                actions.routes(user) ~
-                                    triggers.routes(user) ~
-                                    rules.routes(user) ~
-                                    activations.routes(user) ~
-                                    packages.routes(user)
-                            }
+                info ~ basicAuth(validateCredentials) { user =>
+                    namespaces.routes(user) ~
+                    pathPrefix(Collection.NAMESPACES) {
+                        actions.routes(user) ~
+                        triggers.routes(user) ~
+                        rules.routes(user) ~
+                        activations.routes(user) ~
+                        packages.routes(user)
+                    }
                 } ~ {
                     swaggerRoutes
                 }
             } ~ {
                 // web actions are distinct to separate the cors header
                 // and allow the actions themselves to respond to options
-                authenticate(basicauth) { user =>
+                basicAuth(validateCredentials) { user =>
                     web.routes(user) ~ webexp.routes(user)
                 } ~ {
                     web.routes() ~ webexp.routes()
@@ -184,102 +188,107 @@ protected[controller] class RestAPIVersion(apipath: String, apiversion: String)(
                 }
             }
         }
+
     }
 
-    private val namespaces = new NamespacesApi(apipath, apiversion)
-    private val actions = new ActionsApi(apipath, apiversion)
-    private val triggers = new TriggersApi(apipath, apiversion)
-    private val rules = new RulesApi(apipath, apiversion)
-    private val activations = new ActivationsApi(apipath, apiversion)
-    private val packages = new PackagesApi(apipath, apiversion)
+    private val namespaces = new NamespacesApi(apiPath, apiVersion)
+    private val actions = new ActionsApi(apiPath, apiVersion)
+    private val packages = new PackagesApi(apiPath, apiVersion)
+    private val triggers = new TriggersApi(apiPath, apiVersion)
+    private val activations = new ActivationsApi(apiPath, apiVersion)
+    private val rules = new RulesApi(apiPath, apiVersion)
     private val webexp = new WebActionsApi(Seq("experimental", "web"), WebApiDirectives.exp)
     private val web = new WebActionsApi(Seq("web"), WebApiDirectives.web)
 
-    /**
-     * Describes details of a particular API path.
-     */
-    private val info = JsObject(
-        "description" -> "OpenWhisk API".toJson,
-        "api_version" -> SemVer(1, 0, 0).toJson,
-        "api_version_path" -> apiversion.toJson,
-        "build" -> whiskConfig(whiskVersionDate).toJson,
-        "buildno" -> whiskConfig(whiskVersionBuildno).toJson,
-        "swagger_paths" -> JsObject(
-            "ui" -> s"/$swaggeruipath".toJson,
-            "api-docs" -> s"/$swaggerdocpath".toJson))
-
     class NamespacesApi(
-        val apipath: String,
-        val apiversion: String)(
-            implicit override val entityStore: EntityStore,
-            override val entitlementProvider: EntitlementProvider,
-            override val executionContext: ExecutionContext,
-            override val logging: Logging)
-        extends WhiskNamespacesApi
+       val apiPath: String,
+       val apiVersion: String)(
+       implicit override val entityStore: EntityStore,
+       override val entitlementProvider: EntitlementProvider,
+       override val executionContext: ExecutionContext,
+       override val logging: Logging)
+    extends WhiskNamespacesApi
 
     class ActionsApi(
-        val apipath: String,
-        val apiversion: String)(
-            implicit override val actorSystem: ActorSystem,
-            override val activeAckTopicIndex: InstanceId,
-            override val entityStore: EntityStore,
-            override val activationStore: ActivationStore,
-            override val entitlementProvider: EntitlementProvider,
-            override val activationIdFactory: ActivationIdGenerator,
-            override val loadBalancer: LoadBalancerService,
-            override val executionContext: ExecutionContext,
-            override val logging: Logging,
-            override val whiskConfig: WhiskConfig)
-        extends WhiskActionsApi with WhiskServices {
+        val apiPath: String,
+        val apiVersion: String)(
+        implicit override val actorSystem: ActorSystem,
+        override val activeAckTopicIndex: InstanceId,
+        override val entityStore: EntityStore,
+        override val activationStore: ActivationStore,
+        override val entitlementProvider: EntitlementProvider,
+        override val activationIdFactory: ActivationIdGenerator,
+        override val loadBalancer: LoadBalancerService,
+        override val executionContext: ExecutionContext,
+        override val logging: Logging,
+        override val whiskConfig: WhiskConfig)
+    extends WhiskActionsApi with WhiskServices {
         logging.info(this, s"actionSequenceLimit '${whiskConfig.actionSequenceLimit}'")
         assert(whiskConfig.actionSequenceLimit.toInt > 0)
     }
 
-    class TriggersApi(
-        val apipath: String,
-        val apiversion: String)(
-            implicit override val actorSystem: ActorSystem,
-            implicit override val entityStore: EntityStore,
-            override val entitlementProvider: EntitlementProvider,
-            override val activationStore: ActivationStore,
-            override val activationIdFactory: ActivationIdGenerator,
-            override val loadBalancer: LoadBalancerService,
-            override val executionContext: ExecutionContext,
-            override val logging: Logging,
-            override val whiskConfig: WhiskConfig)
-        extends WhiskTriggersApi with WhiskServices
+    class ActivationsApi(
+        val apiPath: String,
+        val apiVersion: String)(
+        implicit override val activationStore: ActivationStore,
+        override val entitlementProvider: EntitlementProvider,
+        override val executionContext: ExecutionContext,
+        override val logging: Logging)
+    extends WhiskActivationsApi
+
+    class PackagesApi(
+        val apiPath: String,
+        val apiVersion: String)(
+        implicit override val entityStore: EntityStore,
+        override val entitlementProvider: EntitlementProvider,
+        override val activationIdFactory: ActivationIdGenerator,
+        override val loadBalancer: LoadBalancerService,
+        override val executionContext: ExecutionContext,
+        override val logging: Logging,
+        override val whiskConfig: WhiskConfig)
+    extends WhiskPackagesApi with WhiskServices
 
     class RulesApi(
-        val apipath: String,
-        val apiversion: String)(
-            implicit override val actorSystem: ActorSystem,
-            override val entityStore: EntityStore,
-            override val entitlementProvider: EntitlementProvider,
-            override val activationIdFactory: ActivationIdGenerator,
-            override val loadBalancer: LoadBalancerService,
-            override val executionContext: ExecutionContext,
-            override val logging: Logging,
-            override val whiskConfig: WhiskConfig)
-        extends WhiskRulesApi with WhiskServices
+        val apiPath: String,
+        val apiVersion: String)(
+        implicit override val actorSystem: ActorSystem,
+        override val entityStore: EntityStore,
+        override val entitlementProvider: EntitlementProvider,
+        override val activationIdFactory: ActivationIdGenerator,
+        override val loadBalancer: LoadBalancerService,
+        override val executionContext: ExecutionContext,
+        override val logging: Logging,
+        override val whiskConfig: WhiskConfig)
+    extends WhiskRulesApi with WhiskServices
 
-    class ActivationsApi(
-        val apipath: String,
-        val apiversion: String)(
-            implicit override val activationStore: ActivationStore,
-            override val entitlementProvider: EntitlementProvider,
-            override val executionContext: ExecutionContext,
-            override val logging: Logging)
-        extends WhiskActivationsApi
+    class TriggersApi(
+        val apiPath: String,
+        val apiVersion: String)(
+        implicit override val actorSystem: ActorSystem,
+        implicit override val entityStore: EntityStore,
+        override val entitlementProvider: EntitlementProvider,
+        override val activationStore: ActivationStore,
+        override val activationIdFactory: ActivationIdGenerator,
+        override val loadBalancer: LoadBalancerService,
+        override val executionContext: ExecutionContext,
+        override val logging: Logging,
+        override val whiskConfig: WhiskConfig,
+        override val materializer: ActorMaterializer)
+    extends WhiskTriggersApi with WhiskServices
 
-    class PackagesApi(
-        val apipath: String,
-        val apiversion: String)(
-            implicit override val entityStore: EntityStore,
-            override val entitlementProvider: EntitlementProvider,
-            override val activationIdFactory: ActivationIdGenerator,
-            override val loadBalancer: LoadBalancerService,
-            override val executionContext: ExecutionContext,
-            override val logging: Logging,
-            override val whiskConfig: WhiskConfig)
-        extends WhiskPackagesApi with WhiskServices
+    protected[controller] class WebActionsApi(
+        override val webInvokePathSegments: Seq[String],
+        override val webApiDirectives: WebApiDirectives)(
+        implicit override val authStore: AuthStore,
+        implicit val entityStore: EntityStore,
+        override val activeAckTopicIndex: InstanceId,
+        override val activationStore: ActivationStore,
+        override val entitlementProvider: EntitlementProvider,
+        override val activationIdFactory: ActivationIdGenerator,
+        override val loadBalancer: LoadBalancerService,
+        override val actorSystem: ActorSystem,
+        override val executionContext: ExecutionContext,
+        override val logging: Logging,
+        override val whiskConfig: WhiskConfig)
+    extends WhiskWebActionsApi with WhiskServices
 }
diff --git a/core/controller/src/main/scala/whisk/core/controller/Rules.scala b/core/controller/src/main/scala/whisk/core/controller/Rules.scala
index c3882fa..be0e6eb 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Rules.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Rules.scala
@@ -22,19 +22,22 @@ import scala.util.Failure
 import scala.util.Success
 
 import akka.actor.ActorSystem
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.StandardRoute
+
 import spray.json.DeserializationException
-import spray.routing.Directive.pimpApply
-import spray.routing.RequestContext
+
 import whisk.common.TransactionId
 import whisk.core.database.DocumentConflictException
 import whisk.core.database.NoDocumentException
-import whisk.core.entitlement._
 import whisk.core.entity._
 import whisk.core.entity.types.EntityStore
 import whisk.http.ErrorResponse.terminate
 import whisk.http.Messages._
+import whisk.core.entitlement.Collection
+import whisk.core.entitlement.Privilege
+import whisk.core.entitlement.ReferencedEntities
 
 /** A trait implementing the rules API */
 trait WhiskRulesApi extends WhiskCollectionAPI with ReferencedEntities {
@@ -338,7 +341,7 @@ trait WhiskRulesApi extends WhiskCollectionAPI with ReferencedEntities {
      * @param rule the rule to send
      * @param status the status to include in the response
      */
-    private def completeAsRuleResponse(rule: WhiskRule, status: Status = Status.INACTIVE): RequestContext => Unit = {
+    private def completeAsRuleResponse(rule: WhiskRule, status: Status = Status.INACTIVE): StandardRoute = {
         complete(OK, rule.withStatus(status))
     }
 
diff --git a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
index c2dba80..6e0028c 100644
--- a/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/Triggers.scala
@@ -21,29 +21,32 @@ import java.time.Clock
 import java.time.Instant
 
 import scala.concurrent.Future
-import scala.util.Failure
-import scala.util.Success
-import spray.client.pipelining.Post
+import scala.util.{ Failure, Success }
 
 import akka.actor.ActorSystem
-import spray.client.pipelining._
-import spray.http.BasicHttpCredentials
-import spray.http.HttpRequest
-import spray.http.StatusCodes.BadRequest
-import spray.http.StatusCodes.InternalServerError
-import spray.http.StatusCodes.OK
-import spray.http.Uri
-import spray.http.Uri.Path
-import spray.httpx.SprayJsonSupport.sprayJsonMarshaller
-import spray.httpx.SprayJsonSupport.sprayJsonUnmarshaller
-import spray.httpx.marshalling.ToResponseMarshallable.isMarshallable
-import spray.json.JsObject
-import spray.json.JsString
+import akka.http.scaladsl.model.headers.BasicHttpCredentials
+import akka.http.scaladsl.model.HttpRequest
+import akka.http.scaladsl.model.StatusCodes
+import akka.http.scaladsl.model.StatusCodes.BadRequest
+import akka.http.scaladsl.model.StatusCodes.InternalServerError
+import akka.http.scaladsl.model.StatusCodes.OK
+import akka.http.scaladsl.model.Uri
+import akka.http.scaladsl.model.Uri.Path
+import akka.http.scaladsl.server.RouteResult
+import akka.http.scaladsl.model.HttpMethods.POST
+import akka.http.scaladsl.model.headers.Authorization
+import akka.http.scaladsl.model.HttpMethods._
+import akka.http.scaladsl.model.MediaTypes
+import akka.http.scaladsl.model.HttpEntity
+import akka.http.scaladsl.server.RequestContext
+import akka.http.scaladsl.Http
+import akka.http.scaladsl.model.HttpResponse
+import akka.stream.ActorMaterializer
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+
+import spray.json._
 import spray.json.DefaultJsonProtocol.RootJsObjectFormat
-import spray.routing.Directive.pimpApply
-import spray.routing.directives.OnCompleteFutureMagnet.apply
-import spray.routing.directives.ParamDefMagnet.apply
-import spray.routing.RequestContext
+
 import whisk.common.TransactionId
 import whisk.core.entitlement.Collection
 import whisk.core.entity.ActivationResponse
@@ -58,8 +61,6 @@ import whisk.core.entity.WhiskTriggerPut
 import whisk.core.entity.types.ActivationStore
 import whisk.core.entity.types.EntityStore
 import whisk.http.ErrorResponse.terminate
-import spray.httpx.UnsuccessfulResponseException
-import spray.http.StatusCodes
 import whisk.core.entity.Identity
 import whisk.core.entity.FullyQualifiedEntityName
 
@@ -81,6 +82,10 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
     /** Path to Triggers REST API. */
     protected val triggersPath = "triggers"
 
+    protected implicit val materializer: ActorMaterializer
+
+    import RestApiCommons.emptyEntityToJsObject
+
     /**
      * Creates or updates trigger if it already exists. The PUT content is deserialized into a WhiskTriggerPut
      * which is a subset of WhiskTrigger (it eschews the namespace and entity name since the former is derived
@@ -139,12 +144,7 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
                         val saveTriggerActivation = WhiskActivation.put(activationStore, triggerActivation) map {
                             _ => triggerActivationId
                         }
-
                         val url = Uri(s"http://localhost:${whiskConfig.servicePort}")
-                        val pipeline: HttpRequest => Future[JsObject] = (
-                            addCredentials(BasicHttpCredentials(user.authkey.uuid.toString, user.authkey.key.toString))
-                            ~> sendReceive
-                            ~> unmarshal[JsObject])
 
                         trigger.rules.map {
                             _.filter {
@@ -173,16 +173,20 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
                                             Path.SingleSlash + rule.action.name.asString
                                         }
                                     }.toString
-
                                     val actionUrl = Path("/api/v1") / "namespaces" / actionNamespace / "actions"
+                                    val request = HttpRequest(
+                                        method = POST,
+                                        uri = url.withPath(actionUrl + actionPath),
+                                        headers = List(Authorization(BasicHttpCredentials(user.authkey.uuid.asString, user.authkey.key.asString))),
+                                        entity = HttpEntity(MediaTypes.`application/json`, args.getOrElse(JsObject()).compactPrint))
 
-                                    pipeline(Post(url.withPath(actionUrl + actionPath), args)) onComplete {
-                                        case Success(o) =>
-                                            logging.info(this, s"successfully invoked ${rule.action} -> ${o.fields("activationId")}")
-                                        case Failure(usr: UnsuccessfulResponseException) if usr.response.status == StatusCodes.NotFound =>
+                                    Http().singleRequest(request).map {
+                                        case HttpResponse(StatusCodes.OK, headers, entity, _) =>
+                                            logging.info(this, s"successfully invoked ${rule.action} -> ")
+                                        case HttpResponse(StatusCodes.NotFound, _, _, _) =>
                                             logging.info(this, s"action ${rule.action} could not be found")
-                                        case Failure(t) =>
-                                            logging.warn(this, s"action ${rule.action} could not be invoked due to ${t.getMessage}")
+                                        case HttpResponse(code, _, entity, _) =>
+                                            logging.warn(this, s"action ${rule.action} could not be invoked due to ${entity.getDataBytes.toString}")
                                     }
                             }
                         }
@@ -321,7 +325,7 @@ trait WhiskTriggersApi extends WhiskCollectionAPI {
      * @param rule the rule to send
      * @param status the status to include in the response
      */
-    private def completeAsTriggerResponse(trigger: WhiskTrigger): RequestContext => Unit = {
+    private def completeAsTriggerResponse(trigger: WhiskTrigger): RequestContext => Future[RouteResult] = {
         complete(OK, trigger.withoutRules)
     }
 }
diff --git a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
index 138bea9..1c9ec8e 100644
--- a/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/WebActions.scala
@@ -17,31 +17,39 @@
 
 package whisk.core.controller
 
-import java.util.Base64
-
 import java.nio.charset.StandardCharsets
+import java.util.Base64
 
 import scala.concurrent.Future
-import scala.util.Failure
-import scala.util.Success
-import scala.util.Try
+import scala.util.{ Failure, Success, Try }
+
+import akka.http.scaladsl.model.HttpEntity.Empty
+import akka.http.scaladsl.server.Directives
+import akka.http.scaladsl.model.HttpMethod
+import akka.http.scaladsl.model.HttpHeader
+import akka.http.scaladsl.model.MediaType
+import akka.http.scaladsl.model.MediaTypes
+import akka.http.scaladsl.model.MediaTypes._
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.model.StatusCode
+import akka.http.scaladsl.model.headers.RawHeader
+import akka.http.scaladsl.model.headers._
+import akka.http.scaladsl.model.Uri.Query
+import akka.http.scaladsl.model.HttpEntity
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.model.headers.`Content-Type`
+import akka.http.scaladsl.model.ContentType
+import akka.http.scaladsl.model.ContentTypes
+import akka.http.scaladsl.model.FormData
+import akka.http.scaladsl.model.HttpMethods.{ OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH}
+import akka.http.scaladsl.model.HttpCharsets
 
-import WhiskWebActionsApi.MediaExtension
-import spray.http._
-import spray.http.HttpEntity.Empty
-import spray.http.HttpEntity.NonEmpty
-import spray.http.HttpHeaders._
-import spray.http.MediaTypes._
-import spray.http.StatusCodes._
-import spray.http.Uri.Query
-import spray.http.parser.HttpParser
-import spray.httpx.SprayJsonSupport._
 import spray.json._
 import spray.json.DefaultJsonProtocol._
-import spray.routing.Directives
-import spray.routing.RequestContext
-import spray.routing.Route
-import spray.http.HttpMethods.{ OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH }
+
+import WhiskWebActionsApi.MediaExtension
+
 import whisk.common.TransactionId
 import whisk.core.controller.actions.PostActionActivation
 import whisk.core.database._
@@ -87,7 +95,7 @@ protected[controller] object WebApiDirectives {
 private case class Context(
     propertyMap: WebApiDirectives,
     method: HttpMethod,
-    headers: List[HttpHeader],
+    headers: Seq[HttpHeader],
     path: String,
     query: Query,
     body: Option[JsValue] = None) {
@@ -106,7 +114,7 @@ private case class Context(
         (queryParams ++ bodyParams) intersect reservedParams
     }
 
-    // attach the body to the context
+    // attach the body to the Context
     def withBody(b: Option[JsValue]) = Context(propertyMap, method, headers, path, query, b)
 
     def metadata(user: Option[Identity]): Map[String, JsValue] = {
@@ -118,7 +126,7 @@ private case class Context(
 
     def toActionArgument(user: Option[Identity], boxQueryAndBody: Boolean): Map[String, JsValue] = {
         val queryParams = if (boxQueryAndBody) {
-            Map(propertyMap.query -> JsString(query.render(new StringRendering, StandardCharsets.UTF_8).get))
+            Map(propertyMap.query -> JsString(query.toString))
         } else {
             queryAsMap.map(kv => kv._1 -> JsString(kv._2))
         }
@@ -178,21 +186,21 @@ protected[core] object WhiskWebActionsApi extends Directives {
         extension: String,
         defaultProjection: Option[List[String]],
         projectionAllowed: Boolean,
-        transcoder: (JsValue, TransactionId, WebApiDirectives) => RequestContext => Unit) {
+        transcoder: (JsValue, TransactionId, WebApiDirectives) => Route) {
         val extensionLength = extension.length
     }
 
-    private def resultAsHtml(result: JsValue, transid: TransactionId, rp: WebApiDirectives): RequestContext => Unit = result match {
-        case JsString(html) => respondWithMediaType(`text/html`) { complete(OK, html) }
+    private def resultAsHtml(result: JsValue, transid: TransactionId, rp: WebApiDirectives) = result match {
+        case JsString(html) => complete(HttpEntity(ContentTypes.`text/html(UTF-8)`, html))
         case _              => terminate(BadRequest, Messages.invalidMedia(`text/html`))(transid)
     }
 
-    private def resultAsSvg(result: JsValue, transid: TransactionId, rp: WebApiDirectives): RequestContext => Unit = result match {
-        case JsString(svg) => respondWithMediaType(`image/svg+xml`) { complete(OK, svg) }
+    private def resultAsSvg(result: JsValue, transid: TransactionId, rp: WebApiDirectives) = result match {
+        case JsString(svg) => complete(HttpEntity(`image/svg+xml`, svg.getBytes))
         case _             => terminate(BadRequest, Messages.invalidMedia(`image/svg+xml`))(transid)
     }
 
-    private def resultAsText(result: JsValue, transid: TransactionId, rp: WebApiDirectives): RequestContext => Unit = {
+    private def resultAsText(result: JsValue, transid: TransactionId, rp: WebApiDirectives) = {
         result match {
             case r: JsObject  => complete(OK, r.prettyPrint)
             case r: JsArray   => complete(OK, r.prettyPrint)
@@ -203,7 +211,7 @@ protected[core] object WhiskWebActionsApi extends Directives {
         }
     }
 
-    private def resultAsJson(result: JsValue, transid: TransactionId, rp: WebApiDirectives): RequestContext => Unit = {
+    private def resultAsJson(result: JsValue, transid: TransactionId, rp: WebApiDirectives) = {
         result match {
             case r: JsObject => complete(OK, r)
             case r: JsArray  => complete(OK, r)
@@ -211,7 +219,7 @@ protected[core] object WhiskWebActionsApi extends Directives {
         }
     }
 
-    private def resultAsHttp(result: JsValue, transid: TransactionId, rp: WebApiDirectives): RequestContext => Unit = {
+    private def resultAsHttp(result: JsValue, transid: TransactionId, rp: WebApiDirectives) = {
         Try {
             val JsObject(fields) = result
             val headers = fields.get("headers").map {
@@ -256,36 +264,31 @@ protected[core] object WhiskWebActionsApi extends Directives {
         case _            => throw new Throwable("Invalid header")
     }
 
-    private def interpretHttpResponse(code: StatusCode, headers: List[RawHeader], str: String, transid: TransactionId): RequestContext => Unit = {
+    private def interpretHttpResponse(code: StatusCode, headers: List[RawHeader], str: String, transid: TransactionId) = {
         val parsedHeader: Try[MediaType] = headers.find(_.lowercaseName == `Content-Type`.lowercaseName) match {
             case Some(header) =>
-                HttpParser.parseHeader(header) match {
-                    case Right(header: `Content-Type`) =>
-                        val mediaType = header.contentType.mediaType
+                MediaType.parse(header.value) match {
+                    case Right(mediaType: MediaType) =>
                         // lookup the media type specified in the content header to see if it is a recognized type
                         MediaTypes.getForKey(mediaType.mainType -> mediaType.subType).map(Success(_)).getOrElse {
                             // this is a content-type that is not recognized, reject it
                             Failure(RejectRequest(BadRequest, Messages.httpUnknownContentType)(transid))
                         }
-
-                    case _ =>
-                        Failure(RejectRequest(BadRequest, Messages.httpUnknownContentType)(transid))
+                    case _ => Failure(RejectRequest(BadRequest, Messages.httpUnknownContentType)(transid))
                 }
             case None => Success(`text/html`)
         }
 
         parsedHeader.flatMap { mediaType =>
-            if (mediaType.binary) {
-                Try(HttpData(Base64.getDecoder().decode(str))).map((mediaType, _))
+            if (mediaType.binary || mediaType == `application/json`) {
+                Try(new String(Base64.getDecoder().decode(str), StandardCharsets.UTF_8)).map((mediaType, _))
             } else {
-                Success(mediaType, HttpData(str))
+                Success(mediaType, str)
             }
         } match {
-            case Success((mediaType, data)) =>
+            case Success((mediaType, data: String)) =>
                 respondWithHeaders(headers) {
-                    respondWithMediaType(mediaType) {
-                        complete(code, data)
-                    }
+                    complete(code, HttpEntity(ContentType(MediaType.customWithFixedCharset(mediaType.mainType, mediaType.subType, HttpCharsets.`UTF-8`)), data))
                 }
 
             case Failure(RejectRequest(code, message)) =>
@@ -314,7 +317,7 @@ trait WhiskWebActionsApi
 
     /** The prefix for web invokes e.g., /web. */
     private lazy val webRoutePrefix = {
-        pathPrefix(webInvokePathSegments.map(segmentStringToPathMatcher(_)).reduceLeft(_ / _))
+        pathPrefix(webInvokePathSegments.map(_segmentStringToPathMatcher(_)).reduceLeft(_ / _))
     }
 
     /** Allowed verbs. */
@@ -324,7 +327,7 @@ trait WhiskWebActionsApi
     private lazy val packagePrefix = pathPrefix("default".r | EntityName.REGEX.r)
 
     private val defaultCorsResponse = List(
-        `Access-Control-Allow-Origin`(AllOrigins),
+        `Access-Control-Allow-Origin`.*,
         `Access-Control-Allow-Methods`(OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH),
         `Access-Control-Allow-Headers`(`Authorization`.name, `Content-Type`.name))
 
@@ -332,7 +335,7 @@ trait WhiskWebActionsApi
     private val requestMethodParamsAndPath = {
         extract { ctx =>
             val method = ctx.request.method
-            val query = ctx.request.message.uri.query
+            val query = ctx.request.uri.query()
             val path = ctx.unmatchedPath.toString
             val headers = ctx.request.headers
             Context(webApiDirectives, method, headers, path, query)
@@ -415,7 +418,7 @@ trait WhiskWebActionsApi
                 // as the context body which may be the incoming request when the content type is JSON or formdata, or
                 // the raw body as __ow_body (and query parameters as __ow_query) otherwise
                 extract(_.request.entity) { e =>
-                    validateSize(isWhithinRange(e.data.length))(transid) {
+                    validateSize(isWhithinRange(e.contentLengthOption.getOrElse(0)))(transid) {
                         requestMethodParamsAndPath { context =>
                             provide(fullyQualifiedActionName(actionName)) { fullActionName =>
                                 onComplete(verifyWebAction(fullActionName, onBehalfOf.isDefined)) {
@@ -502,25 +505,26 @@ trait WhiskWebActionsApi
                 case Empty =>
                     process(None, isRawHttpAction)
 
-                case NonEmpty(ContentType(`application/json`, _), json) if !isRawHttpAction =>
+                case HttpEntity.Strict(ContentTypes.`application/json`, _) if !isRawHttpAction =>
                     entity(as[JsObject]) { body =>
                         process(Some(body), isRawHttpAction)
                     }
 
-                case NonEmpty(ContentType(`application/x-www-form-urlencoded`, _), form) if !isRawHttpAction =>
+                case HttpEntity.Strict(ContentType(MediaTypes.`application/x-www-form-urlencoded`, Some(HttpCharsets.`UTF-8`)), _) if !isRawHttpAction =>
                     entity(as[FormData]) { form =>
                         val body = form.fields.toMap.toJson.asJsObject
                         process(Some(body), isRawHttpAction)
                     }
 
-                case NonEmpty(contentType, data) =>
-                    if (contentType.mediaType.binary) {
-                        Try(JsString(Base64.getEncoder.encodeToString(data.toByteArray))) match {
+                case HttpEntity.Strict(contentType, data) =>
+                    // application/json is not a binary type in Akka, but is binary in Spray
+                    if (contentType.mediaType.binary || contentType.mediaType == `application/json`) {
+                        Try(JsString(Base64.getEncoder.encodeToString(data.toArray))) match {
                             case Success(bytes) => process(Some(bytes), isRawHttpAction)
                             case Failure(t)     => terminate(BadRequest, Messages.unsupportedContentType(contentType.mediaType))
                         }
                     } else {
-                        val str = JsString(data.asString(HttpCharsets.`UTF-8`))
+                        val str = JsString(data.utf8String)
                         process(Some(str), isRawHttpAction)
                     }
 
@@ -596,8 +600,7 @@ trait WhiskWebActionsApi
                 logging.info(this, "activation waiting period expired")
                 terminate(Accepted, Messages.responseNotReady)
 
-            case Failure(t: RejectRequest) =>
-                terminate(t.code, t.message)
+            case Failure(t: RejectRequest) => terminate(t.code, t.message)
 
             case Failure(t) =>
                 logging.error(this, s"exception in completeRequest: $t")
diff --git a/core/controller/src/main/scala/whisk/core/controller/actions/PostActionActivation.scala b/core/controller/src/main/scala/whisk/core/controller/actions/PostActionActivation.scala
index abd294a..03fc8be 100644
--- a/core/controller/src/main/scala/whisk/core/controller/actions/PostActionActivation.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/actions/PostActionActivation.scala
@@ -20,8 +20,10 @@ package whisk.core.controller.actions
 import scala.concurrent.Future
 import scala.concurrent.duration.FiniteDuration
 
-import spray.http.StatusCodes.BadRequest
+import akka.http.scaladsl.model.StatusCodes.BadRequest
+
 import spray.json._
+
 import whisk.common.TransactionId
 import whisk.core.controller.RejectRequest
 import whisk.core.controller.WhiskServices
diff --git a/core/controller/src/main/scala/whisk/core/controller/actions/SequenceActions.scala b/core/controller/src/main/scala/whisk/core/controller/actions/SequenceActions.scala
index 9efaf17..55bfac4 100644
--- a/core/controller/src/main/scala/whisk/core/controller/actions/SequenceActions.scala
+++ b/core/controller/src/main/scala/whisk/core/controller/actions/SequenceActions.scala
@@ -32,6 +32,7 @@ import scala.util.Success
 import akka.actor.ActorSystem
 
 import spray.json._
+
 import whisk.common.Logging
 import whisk.common.TransactionId
 import whisk.core.controller.WhiskServices
diff --git a/core/controller/src/main/scala/whisk/core/entitlement/ActionCollection.scala b/core/controller/src/main/scala/whisk/core/entitlement/ActionCollection.scala
index 3656085..1691f4a 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/ActionCollection.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/ActionCollection.scala
@@ -17,10 +17,11 @@
 
 package whisk.core.entitlement
 
+import whisk.core.entitlement.Privilege._
+
 import scala.concurrent.ExecutionContext
 import scala.concurrent.Future
 
-import Privilege.Privilege
 import whisk.common.TransactionId
 import whisk.core.entity.Identity
 import whisk.core.entity.types.EntityStore
diff --git a/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala b/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
index 2cb1c7c..be406a6 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/Collection.scala
@@ -17,15 +17,17 @@
 
 package whisk.core.entitlement
 
+import whisk.core.entitlement.Privilege._
+
 import scala.concurrent.ExecutionContext
 import scala.concurrent.Future
 
-import Privilege.Privilege
-import spray.http.HttpMethod
-import spray.http.HttpMethods.DELETE
-import spray.http.HttpMethods.GET
-import spray.http.HttpMethods.POST
-import spray.http.HttpMethods.PUT
+import akka.http.scaladsl.model.HttpMethod
+import akka.http.scaladsl.model.HttpMethods.DELETE
+import akka.http.scaladsl.model.HttpMethods.GET
+import akka.http.scaladsl.model.HttpMethods.POST
+import akka.http.scaladsl.model.HttpMethods.PUT
+
 import whisk.common.Logging
 import whisk.common.TransactionId
 import whisk.core.entity.Identity
diff --git a/core/controller/src/main/scala/whisk/core/entitlement/Entitlement.scala b/core/controller/src/main/scala/whisk/core/entitlement/Entitlement.scala
index e6b6d19..aa5d634 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/Entitlement.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/Entitlement.scala
@@ -22,12 +22,13 @@ import scala.concurrent.Future
 import scala.util.Failure
 import scala.util.Success
 
-import Privilege.ACTIVATE
-import Privilege.Privilege
-import Privilege.REJECT
 import akka.actor.ActorSystem
-import spray.http.StatusCodes.Forbidden
-import spray.http.StatusCodes.TooManyRequests
+import akka.http.scaladsl.model.StatusCodes.Forbidden
+import akka.http.scaladsl.model.StatusCodes.TooManyRequests
+
+import whisk.core.entitlement.Privilege.ACTIVATE
+import whisk.core.entitlement.Privilege._
+import whisk.core.entitlement.Privilege.REJECT
 import whisk.common.Logging
 import whisk.common.TransactionId
 import whisk.core.WhiskConfig
diff --git a/core/controller/src/main/scala/whisk/core/entitlement/LocalEntitlement.scala b/core/controller/src/main/scala/whisk/core/entitlement/LocalEntitlement.scala
index 0f9f854..740d60a 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/LocalEntitlement.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/LocalEntitlement.scala
@@ -20,8 +20,9 @@ package whisk.core.entitlement
 import scala.collection.concurrent.TrieMap
 import scala.concurrent.Future
 
-import Privilege.Privilege
 import akka.actor.ActorSystem
+
+import whisk.core.entitlement.Privilege._
 import whisk.common.Logging
 import whisk.common.TransactionId
 import whisk.core.WhiskConfig
diff --git a/core/controller/src/main/scala/whisk/core/entitlement/PackageCollection.scala b/core/controller/src/main/scala/whisk/core/entitlement/PackageCollection.scala
index 170aaaa..ce4f6ea 100644
--- a/core/controller/src/main/scala/whisk/core/entitlement/PackageCollection.scala
+++ b/core/controller/src/main/scala/whisk/core/entitlement/PackageCollection.scala
@@ -20,8 +20,9 @@ package whisk.core.entitlement
 import scala.concurrent.ExecutionContext
 import scala.concurrent.Future
 
-import Privilege.Privilege
-import spray.http.StatusCodes._
+import akka.http.scaladsl.model.StatusCodes._
+
+import whisk.core.entitlement.Privilege._
 import whisk.common.Logging
 import whisk.common.TransactionId
 import whisk.core.controller.RejectRequest
diff --git a/core/invoker/src/main/scala/whisk/core/invoker/Invoker.scala b/core/invoker/src/main/scala/whisk/core/invoker/Invoker.scala
index e5dc5dc..57e7110 100644
--- a/core/invoker/src/main/scala/whisk/core/invoker/Invoker.scala
+++ b/core/invoker/src/main/scala/whisk/core/invoker/Invoker.scala
@@ -454,7 +454,7 @@ object Invoker {
         implicit val logger = new AkkaLogging(akka.event.Logging.getLogger(actorSystem, this))
 
         // load values for the required properties from the environment
-        val config = new WhiskConfig(requiredProperties)
+        implicit val config = new WhiskConfig(requiredProperties)
 
         def abort() = {
             logger.error(this, "Bad configuration, cannot start.")
@@ -497,8 +497,9 @@ object Invoker {
         })
 
         val port = config.servicePort.toInt
-        BasicHttpService.startService(actorSystem, "invoker", "0.0.0.0", port, new Creator[InvokerServer] {
-            def create = new InvokerServer(invokerInstance, invokerInstance.toInt)
+
+        BasicHttpService.startService(actorSystem, "invoker", "0.0.0.0", new Creator[InvokerServer] {
+            def create = new InvokerServer(invokerInstance, invokerInstance.toInt, port)
         })
     }
 }
diff --git a/core/invoker/src/main/scala/whisk/core/invoker/InvokerServer.scala b/core/invoker/src/main/scala/whisk/core/invoker/InvokerServer.scala
index cd083cb..56e28ad 100644
--- a/core/invoker/src/main/scala/whisk/core/invoker/InvokerServer.scala
+++ b/core/invoker/src/main/scala/whisk/core/invoker/InvokerServer.scala
@@ -17,10 +17,10 @@
 
 package whisk.core.invoker
 
-import akka.actor.Actor
+import whisk.http.BasicRasService
+import whisk.core.WhiskConfig
 import whisk.common.Logging
 import whisk.core.entity.InstanceId
-import whisk.http.BasicRasService
 
 /**
  * Implements web server to handle certain REST API calls.
@@ -28,10 +28,9 @@ import whisk.http.BasicRasService
  */
 class InvokerServer(
     override val instance: InstanceId,
-    override val numberOfInstances: Int)(
-        override implicit val logging: Logging)
-    extends BasicRasService
-    with Actor {
-
-    override def actorRefFactory = context
+    override val numberOfInstances: Int,
+    override val port: Int)(
+        override implicit val logging: Logging,
+        implicit val whiskConfig: WhiskConfig)
+    extends BasicRasService {
 }
diff --git a/tests/build.gradle b/tests/build.gradle
index b33e2c6..979a2f8 100644
--- a/tests/build.gradle
+++ b/tests/build.gradle
@@ -48,11 +48,10 @@ dependencies {
     compile 'junit:junit:4.11'
     compile 'com.jayway.restassured:rest-assured:2.6.0'
     compile 'org.scalatest:scalatest_2.11:3.0.1'
-    compile 'io.spray:spray-testkit_2.11:1.3.3'
-    compile 'com.typesafe.akka:akka-testkit_2.11:2.4.16'
     compile 'com.google.code.gson:gson:2.3.1'
     compile 'org.scalamock:scalamock-scalatest-support_2.11:3.4.2'
-
+    compile 'com.typesafe.akka:akka-testkit_2.11:2.4.16'
+    compile 'com.typesafe.akka:akka-http-testkit_2.11:10.0.9'
     compile project(':common:scala')
     compile project(':core:controller')
     compile project(':core:invoker')
diff --git a/tests/src/test/scala/services/HeadersTests.scala b/tests/src/test/scala/services/HeadersTests.scala
index 82a85d6..4316956 100644
--- a/tests/src/test/scala/services/HeadersTests.scala
+++ b/tests/src/test/scala/services/HeadersTests.scala
@@ -20,6 +20,7 @@ package services
 import scala.concurrent.Future
 import scala.concurrent.duration.DurationInt
 import scala.language.postfixOps
+import scala.collection.immutable.Seq
 
 import org.junit.runner.RunWith
 import org.scalatest.FlatSpec
@@ -33,29 +34,24 @@ import common.WhiskProperties
 import common.Wsk
 import common.WskProps
 import common.WskTestHelpers
-import spray.client.pipelining.Get
-import spray.client.pipelining.Options
-import spray.client.pipelining.Post
-import spray.client.pipelining.WithTransformation
-import spray.client.pipelining.WithTransformerConcatenation
-import spray.client.pipelining.addCredentials
-import spray.client.pipelining.sendReceive
-import spray.client.pipelining.unmarshal
-import spray.http.AllOrigins
-import spray.http.BasicHttpCredentials
-import spray.http.HttpHeader
-import spray.http.HttpHeaders.`Access-Control-Allow-Headers`
-import spray.http.HttpHeaders.`Access-Control-Allow-Origin`
-import spray.http.HttpMethods.DELETE
-import spray.http.HttpMethods.GET
-import spray.http.HttpMethods.POST
-import spray.http.HttpMethods.PUT
-import spray.http.HttpRequest
-import spray.http.HttpResponse
-import spray.http.StatusCodes.Accepted
-import spray.http.StatusCodes.OK
-import spray.http.Uri
-import spray.http.Uri.Path
+
+import akka.http.scaladsl.model.Uri
+import akka.http.scaladsl.model.Uri.Path
+import akka.http.scaladsl.model.headers.BasicHttpCredentials
+import akka.http.scaladsl.model.HttpRequest
+import akka.http.scaladsl.model.StatusCodes.Accepted
+import akka.http.scaladsl.model.StatusCodes.OK
+import akka.http.scaladsl.model.HttpMethods.DELETE
+import akka.http.scaladsl.model.HttpMethods.GET
+import akka.http.scaladsl.model.HttpMethods.POST
+import akka.http.scaladsl.model.HttpMethods.PUT
+import akka.http.scaladsl.model.HttpMethods._
+import akka.http.scaladsl.Http
+import akka.http.scaladsl.model.HttpResponse
+import akka.http.scaladsl.model.headers._
+import akka.http.scaladsl.model.HttpMethod
+import akka.http.scaladsl.model.HttpHeader
+import akka.stream.ActorMaterializer
 
 import common.WskActorSystem
 
@@ -68,17 +64,23 @@ class HeadersTests extends FlatSpec
 
     behavior of "Headers at general API"
 
+    implicit val materializer = ActorMaterializer()
+
     val whiskAuth = WhiskProperties.getBasicAuth
     val creds = BasicHttpCredentials(whiskAuth.fst, whiskAuth.snd)
-
     val allMethods = Some(Set(DELETE.name, GET.name, POST.name, PUT.name))
-    val allowOrigin = `Access-Control-Allow-Origin`(AllOrigins)
+    val allowOrigin = `Access-Control-Allow-Origin`.*
     val allowHeaders = `Access-Control-Allow-Headers`("Authorization", "Content-Type")
-
     val url = Uri(s"http://${WhiskProperties.getBaseControllerAddress()}")
-    val pipeline: HttpRequest => Future[HttpResponse] = (
-        sendReceive
-        ~> unmarshal[HttpResponse])
+
+    def request(method: HttpMethod, uri: Uri, headers: Option[Seq[HttpHeader]] = None): Future[HttpResponse] = {
+        val httpRequest = headers match {
+            case Some(headers)  => HttpRequest(method, uri, headers)
+            case None           => HttpRequest(method, uri)
+        }
+
+        Http().singleRequest(httpRequest)
+    }
 
     implicit val config = PatienceConfig(10 seconds, 0 milliseconds)
 
@@ -90,7 +92,7 @@ class HeadersTests extends FlatSpec
      * Checks, if the required headers are in the list of all headers.
      * For the allowed method, it checks, if only the allowed methods are in the response headers.
      */
-    def containsHeaders(headers: List[HttpHeader], allowedMethods: Option[Set[String]] = None) = {
+    def containsHeaders(headers: Seq[HttpHeader], allowedMethods: Option[Set[String]] = None) = {
         headers should contain allOf (allowOrigin, allowHeaders)
 
         // TODO: commented out for now as allowed methods are not supported currently
@@ -104,19 +106,19 @@ class HeadersTests extends FlatSpec
     }
 
     it should "respond to OPTIONS with all headers" in {
-        pipeline(Options(url.withPath(basePath))).futureValue.headers should contain allOf (allowOrigin, allowHeaders)
+        request(OPTIONS, url.withPath(basePath)).futureValue.headers should contain allOf (allowOrigin, allowHeaders)
     }
 
     ignore should "not respond to OPTIONS for non existing path" in {
         val path = basePath / "foo" / "bar"
 
-        pipeline(Options(url.withPath(path))).futureValue.status should not be OK
+        request(OPTIONS, url.withPath(path)).futureValue.status should not be OK
     }
 
     // Actions
     it should "respond to OPTIONS for listing actions" in {
         val path = basePath / "namespaces" / "barfoo" / "actions"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -124,7 +126,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to OPTIONS for actions path" in {
         val path = basePath / "namespaces" / "barfoo" / "actions" / "foobar"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, allMethods)
@@ -143,7 +145,7 @@ class HeadersTests extends FlatSpec
                 (action, _) => action.create(fullActionName, Some(TestUtils.getTestActionFilename("hello.js")))
             }
             val path = basePath / "namespaces" / "_" / "actions" / packageName / actionName
-            val response = pipeline(Post(url.withPath(path)) ~> addCredentials(creds)) futureValue
+            val response = request(POST, url.withPath(path), Some(List(Authorization(creds)))).futureValue
 
             response.status shouldBe Accepted
             containsHeaders(response.headers)
@@ -152,7 +154,7 @@ class HeadersTests extends FlatSpec
     // Activations
     it should "respond to OPTIONS for listing activations" in {
         val path = basePath / "namespaces" / "barfoo" / "activations"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response =  request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -160,7 +162,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to OPTIONS for activations get" in {
         val path = basePath / "namespaces" / "barfoo" / "activations" / "foobar"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -168,7 +170,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to OPTIONS for activations logs" in {
         val path = basePath / "namespaces" / "barfoo" / "activations" / "foobar" / "logs"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -176,7 +178,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to OPTIONS for activations results" in {
         val path = basePath / "namespaces" / "barfoo" / "activations" / "foobar" / "result"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -184,7 +186,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to GET for listing activations with Headers" in {
         val path = basePath / "namespaces" / "_" / "activations"
-        val response = pipeline(Get(url.withPath(path)) ~> addCredentials(creds)) futureValue
+        val response = request(GET, url.withPath(path), Some(List(Authorization(creds)))).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers)
@@ -193,7 +195,7 @@ class HeadersTests extends FlatSpec
     // Namespaces
     it should "respond to OPTIONS for listing namespaces" in {
         val path = basePath / "namespaces"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -201,7 +203,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to OPTIONS for namespaces getEntities" in {
         val path = basePath / "namespaces" / "barfoo"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -209,7 +211,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to GET for namespaces getEntities with Headers" in {
         val path = basePath / "namespaces" / "_"
-        val response = pipeline(Get(url.withPath(path)) ~> addCredentials(creds)) futureValue
+        val response = request(GET, url.withPath(path), Some(List(Authorization(creds)))).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers)
@@ -218,7 +220,7 @@ class HeadersTests extends FlatSpec
     // Packages
     it should "respond to OPTIONS for listing packages" in {
         val path = basePath / "namespaces" / "barfoo" / "packages"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -226,7 +228,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to OPTIONS for packages path" in {
         val path = basePath / "namespaces" / "barfoo" / "packages" / "foobar"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("DELETE", "GET", "PUT")))
@@ -234,7 +236,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to GET for listing packages with headers" in {
         val path = basePath / "namespaces" / "_" / "packages"
-        val response = pipeline(Get(url.withPath(path)) ~> addCredentials(creds)) futureValue
+        val response = request(GET, url.withPath(path), Some(List(Authorization(creds)))).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers)
@@ -243,7 +245,7 @@ class HeadersTests extends FlatSpec
     // Rules
     it should "respond to OPTIONS for listing rules" in {
         val path = basePath / "namespaces" / "barfoo" / "rules"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -251,7 +253,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to OPTIONS for rules path" in {
         val path = basePath / "namespaces" / "barfoo" / "rules" / "foobar"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, allMethods)
@@ -259,7 +261,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to GET for listing rules with headers" in {
         val path = basePath / "namespaces" / "_" / "rules"
-        val response = pipeline(Get(url.withPath(path)) ~> addCredentials(creds)) futureValue
+        val response = request(GET, url.withPath(path), Some(List(Authorization(creds)))).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers)
@@ -268,7 +270,7 @@ class HeadersTests extends FlatSpec
     // Triggers
     it should "respond to OPTIONS for listing triggers" in {
         val path = basePath / "namespaces" / "barfoo" / "triggers"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, Some(Set("GET")))
@@ -276,7 +278,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to OPTIONS for triggers path" in {
         val path = basePath / "namespaces" / "barfoo" / "triggers" / "foobar"
-        val response = pipeline(Options(url.withPath(path))) futureValue
+        val response = request(OPTIONS, url.withPath(path)).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers, allMethods)
@@ -284,7 +286,7 @@ class HeadersTests extends FlatSpec
 
     it should "respond to GET for listing triggers with headers" in {
         val path = basePath / "namespaces" / "_" / "triggers"
-        val response = pipeline(Get(url.withPath(path)) ~> addCredentials(creds)) futureValue
+        val response = request(GET, url.withPath(path), Some(List(Authorization(creds)))).futureValue
 
         response.status shouldBe OK
         containsHeaders(response.headers)
diff --git a/tests/src/test/scala/system/basic/WskSequenceTests.scala b/tests/src/test/scala/system/basic/WskSequenceTests.scala
index 726c897..906bfbb 100644
--- a/tests/src/test/scala/system/basic/WskSequenceTests.scala
+++ b/tests/src/test/scala/system/basic/WskSequenceTests.scala
@@ -34,9 +34,12 @@ import common.TestUtils._
 import common.Wsk
 import common.WskProps
 import common.WskTestHelpers
+
+import akka.http.scaladsl.testkit.ScalatestRouteTest
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
-import spray.testkit.ScalatestRouteTest
+
 import whisk.core.WhiskConfig
 import whisk.http.Messages.sequenceIsTooLong
 
diff --git a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
index 517371c..d6712d5 100644
--- a/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
+++ b/tests/src/test/scala/whisk/core/cli/test/WskWebActionsTests.scala
@@ -295,7 +295,7 @@ trait WskWebActionsTests
             val url = host + s"$testRoutePath/$namespace/default/webaction.http"
             val response = RestAssured.given().header("accept", "application/json").config(sslconfig).get(url)
             response.statusCode shouldBe 406
-            response.body.asString should include("Resource representation is only available with these Content-Types:\\ntext/html")
+            response.body.asString should include("Resource representation is only available with these types:\\ntext/html")
     }
 
     it should "support multiple response header values" in withAssetCleaner(wskprops) {
diff --git a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
index bdd9a31..150a04e 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActionsApiTests.scala
@@ -25,11 +25,14 @@ import scala.language.postfixOps
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
 
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport.sprayJsonMarshaller
-import spray.httpx.SprayJsonSupport.sprayJsonUnmarshaller
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonMarshaller
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonUnmarshaller
+import akka.http.scaladsl.server.Route
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
+
 import whisk.core.controller.WhiskActionsApi
 import whisk.core.entity._
 import whisk.core.entity.size._
@@ -69,7 +72,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         }.toList
         actions foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskAction, namespace, 2)
-        Get(s"$collectionPath") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             actions.length should be(response.length)
@@ -85,7 +88,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         }.toList
         actions foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskAction, namespace, 2)
-        Get(s"$collectionPath?docs=true") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?docs=true") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[WhiskAction]]
             actions.length should be(response.length)
@@ -100,7 +103,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         }.toList
         actions foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskAction, namespace, 2)
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             actions.length should be(response.length)
@@ -109,14 +112,14 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
 
         // it should "reject list action with explicit namespace not owned by subject" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
 
     it should "list should reject request with post" in {
         implicit val tid = transid()
-        Post(s"$collectionPath") ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath") ~> Route.seal(routes(creds)) ~> check {
             status should be(MethodNotAllowed)
         }
     }
@@ -126,7 +129,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val action = WhiskAction(namespace, aname, jsDefault("??"), Parameters("x", "b"))
         put(entityStore, action)
-        Get(s"$collectionPath/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(action)
@@ -137,7 +140,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val action = WhiskAction(namespace, aname, jsDefault("??"), Parameters("x", "b"))
         put(entityStore, action)
-        Get(s"/$namespace/${collection.path}/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(action)
@@ -145,14 +148,14 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
 
         // it should "reject get action by name in explicit namespace not owned by subject" in
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
 
     it should "report NotFound for get non existent action" in {
         implicit val tid = transid()
-        Get(s"$collectionPath/xyz") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/xyz") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -161,7 +164,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname)
         put(entityStore, trigger)
-        Get(s"/$namespace/${collection.path}/${trigger.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}/${trigger.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Conflict)
         }
     }
@@ -169,7 +172,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
     it should "reject long entity names" in {
         implicit val tid = transid()
         val longName = "a" * (EntityName.ENTITY_NAME_MAX_LENGTH + 1)
-        Get(s"/$longName/${collection.path}/$longName") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$longName/${collection.path}/$longName") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] shouldBe {
                 Messages.entityNameTooLong(
@@ -182,7 +185,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
             s"/$namespace/${collection.path}/$longName/a",
             s"/$namespace/${collection.path}/$longName/$longName").
             foreach { p =>
-                Get(p) ~> sealRoute(routes(creds)) ~> check {
+                Get(p) ~> Route.seal(routes(creds)) ~> check {
                     status should be(BadRequest)
                     responseAs[String] shouldBe {
                         Messages.entityNameTooLong(
@@ -200,11 +203,11 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
 
         // it should "reject delete action by name not owned by subject" in
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Delete(s"$collectionPath/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(action)
@@ -213,7 +216,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
 
     it should "report NotFound for delete non existent action" in {
         implicit val tid = transid()
-        Delete(s"$collectionPath/xyz") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/xyz") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -221,16 +224,16 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
     //// PUT /actions/name
     it should "put should reject request missing json content" in {
         implicit val tid = transid()
-        Put(s"$collectionPath/xxx", "") ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/xxx", "") ~> Route.seal(routes(creds)) ~> check {
             val response = responseAs[String]
-            status should be(BadRequest)
+            status should be(UnsupportedMediaType)
         }
     }
 
     it should "put should reject request missing property exec" in {
         implicit val tid = transid()
         val content = """|{"name":"name","publish":true}""".stripMargin.parseJson.asJsObject
-        Put(s"$collectionPath/xxx", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/xxx", content) ~> Route.seal(routes(creds)) ~> check {
             val response = responseAs[String]
             status should be(BadRequest)
         }
@@ -241,7 +244,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val content = """|{"name":"name",
                          |"publish":true,
                          |"exec":""}""".stripMargin.parseJson.asJsObject
-        Put(s"$collectionPath/xxx", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/xxx", content) ~> Route.seal(routes(creds)) ~> check {
             val response = responseAs[String]
             status should be(BadRequest)
         }
@@ -252,7 +255,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val code = "a" * (actionLimit.toBytes.toInt + 1)
         val exec: Exec = jsDefault(code)
         val content = JsObject("exec" -> exec.toJson)
-        Put(s"$collectionPath/${aname}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskAction.execFieldName, exec.size, Exec.sizeLimit))
@@ -268,7 +271,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val exec: Exec = jsDefault(code)
         val content = JsObject("exec" -> exec.toJson)
         put(entityStore, action)
-        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskAction.execFieldName, exec.size, Exec.sizeLimit))
@@ -283,7 +286,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
             Parameters(key.toString, "a" * 10)
         } reduce (_ ++ _)
         val content = s"""{"exec":{"kind":"nodejs","code":"??"},"parameters":$parameters}""".stripMargin
-        Put(s"$collectionPath/${aname}", content.parseJson.asJsObject) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname}", content.parseJson.asJsObject) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskEntity.paramsFieldName, parameters.size, Parameters.sizeLimit))
@@ -298,7 +301,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
             Parameters(key.toString, "a" * 10)
         } reduce (_ ++ _)
         val content = s"""{"exec":{"kind":"nodejs","code":"??"},"annotations":$annotations}""".stripMargin
-        Put(s"$collectionPath/${aname}", content.parseJson.asJsObject) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname}", content.parseJson.asJsObject) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskEntity.annotationsFieldName, annotations.size, Parameters.sizeLimit))
@@ -310,10 +313,10 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val code = "a" * (allowedActivationEntitySize.toInt + 1)
         val content = s"""{"a":"$code"}""".stripMargin
-        Post(s"$collectionPath/${aname}", content.parseJson.asJsObject) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${aname}", content.parseJson.asJsObject) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
-                Messages.entityTooBig(SizeError(fieldDescriptionForSizeError, (content.length + 5).B, allowedActivationEntitySize.B))
+                Messages.entityTooBig(SizeError(fieldDescriptionForSizeError, (content.length).B, allowedActivationEntitySize.B))
             }
         }
     }
@@ -322,7 +325,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val action = WhiskAction(namespace, aname, jsDefault("??"))
         val content = WhiskActionPut(Some(action.exec))
-        Put(s"$collectionPath/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -336,7 +339,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val action = WhiskAction(namespace, aname, bb("bb"))
         val content = Map("exec" -> Map("kind" -> "blackbox", "code" -> "", "image" -> "bb")).toJson.asJsObject
-        Put(s"$collectionPath/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -352,7 +355,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val action = WhiskAction(namespace, aname, bb("bb", "cc"))
         val content = Map("exec" -> Map("kind" -> "blackbox", "code" -> "cc", "image" -> "bb")).toJson.asJsObject
-        Put(s"$collectionPath/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -379,7 +382,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, action, false)
 
         // create an action sequence
-        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -398,7 +401,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, action, false)
 
         // create an action sequence
-        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -414,11 +417,11 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
 
         // it should "reject put action in namespace not owned by subject" in
         val auser = WhiskAuthHelpers.newIdentity()
-        Put(s"/$namespace/${collection.path}/${action.name}", content) ~> sealRoute(routes(auser)) ~> check {
+        Put(s"/$namespace/${collection.path}/${action.name}", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Put(s"$collectionPath/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -434,7 +437,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val content = WhiskActionPut(Some(action.exec), Some(action.parameters))
         val params = """{ "parameters": { "a": "b" } }""".parseJson.asJsObject
         val json = JsObject(WhiskActionPut.serdes.write(content).asJsObject.fields ++ params.fields)
-        Put(s"$collectionPath/${action.name}", json) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}", json) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -443,7 +446,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val action = WhiskAction(namespace, aname, jsDefault("??"), Parameters("x", "b"))
         val content = WhiskActionPut(Some(action.exec), Some(action.parameters), Some(ActionLimitsOption(Some(action.limits.timeout), Some(action.limits.memory), Some(action.limits.logs))))
-        Put(s"$collectionPath/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -459,7 +462,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val name = action.name
 
         // first request invalidates any previous entries and caches new result
-        Put(s"$collectionPath/$name", content) ~> sealRoute(routes(creds)(transid())) ~> check {
+        Put(s"$collectionPath/$name", content) ~> Route.seal(routes(creds)(transid())) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(WhiskAction(action.namespace, action.name, action.exec,
@@ -470,7 +473,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         stream.reset()
 
         // second request should fetch from cache
-        Get(s"$collectionPath/$name") ~> sealRoute(routes(creds)(transid())) ~> check {
+        Get(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(WhiskAction(action.namespace, action.name, action.exec,
@@ -482,7 +485,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         stream.reset()
 
         // delete should invalidate cache
-        Delete(s"$collectionPath/$name") ~> sealRoute(routes(creds)(transid())) ~> check {
+        Delete(s"$collectionPath/$name") ~> Route.seal(routes(creds)(transid())) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(WhiskAction(action.namespace, action.name, action.exec,
@@ -498,7 +501,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(namespace, aname, jsDefault("??"), Parameters("x", "b"))
         val content = WhiskActionPut(Some(action.exec))
         put(entityStore, action)
-        Put(s"$collectionPath/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Conflict)
         }
     }
@@ -508,7 +511,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(namespace, aname, jsDefault("??"), Parameters("x", "b"))
         val content = WhiskActionPut(Some(jsDefault("_")), Some(Parameters("x", "X")))
         put(entityStore, action)
-        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -524,7 +527,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(namespace, aname, jsDefault("??"), Parameters("x", "b"))
         val content = WhiskActionPut(parameters = Some(Parameters("x", "X")))
         put(entityStore, action)
-        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -544,18 +547,18 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
 
         // it should "reject post to action in namespace not owned by subject"
         val auser = WhiskAuthHelpers.newIdentity()
-        Post(s"/$namespace/${collection.path}/${action.name}", args) ~> sealRoute(routes(auser)) ~> check {
+        Post(s"/$namespace/${collection.path}/${action.name}", args) ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Post(s"$collectionPath/${action.name}", args) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${action.name}", args) ~> Route.seal(routes(creds)) ~> check {
             status should be(Accepted)
             val response = responseAs[JsObject]
             response.fields("activationId") should not be None
         }
 
         // it should "ignore &result when invoking nonblocking action"
-        Post(s"$collectionPath/${action.name}?result=true", args) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${action.name}?result=true", args) ~> Route.seal(routes(creds)) ~> check {
             status should be(Accepted)
             val response = responseAs[JsObject]
             response.fields("activationId") should not be None
@@ -566,7 +569,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val action = WhiskAction(namespace, aname, jsDefault("??"))
         put(entityStore, action)
-        Post(s"$collectionPath/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Accepted)
             val response = responseAs[JsObject]
             response.fields("activationId") should not be None
@@ -577,7 +580,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val action = WhiskAction(namespace, aname, jsDefault("??"), limits = ActionLimits(TimeLimit(1 second), MemoryLimit(), LogLimit()))
         put(entityStore, action)
-        Post(s"$collectionPath/${action.name}?blocking=true") ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${action.name}?blocking=true") ~> Route.seal(routes(creds)) ~> check {
             // status should be accepted because there is no active ack response and
             // db polling will fail since there is no record of the activation
             status should be(Accepted)
@@ -599,14 +602,14 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         // the one generated by the api handler
         put(activationStore, activation)
         try {
-            Post(s"$collectionPath/${action.name}?blocking=true") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response should be(activation.withoutLogs.toExtendedJson)
             }
 
             // repeat invoke, get only result back
-            Post(s"$collectionPath/${action.name}?blocking=true&result=true") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true&result=true") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response should be(activation.resultAsJson)
@@ -629,14 +632,14 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
             // do not store the activation in the db, instead register it as the response to generate on active ack
             loadBalancer.whiskActivationStub = Some((1.milliseconds, activation))
 
-            Post(s"$collectionPath/${action.name}?blocking=true") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response should be(activation.withoutLogs.toExtendedJson)
             }
 
             // repeat invoke, get only result back
-            Post(s"$collectionPath/${action.name}?blocking=true&result=true") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true&result=true") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response should be(activation.resultAsJson)
@@ -659,23 +662,23 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
             // do not store the activation in the db, instead register it as the response to generate on active ack
             loadBalancer.whiskActivationStub = Some((300.milliseconds, activation))
 
-            Post(s"$collectionPath/${action.name}?blocking=true&timeout=0") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true&timeout=0") ~> Route.seal(routes(creds)) ~> check {
                 status shouldBe BadRequest
                 responseAs[String] should include(Messages.invalidTimeout(WhiskActionsApi.maxWaitForBlockingActivation))
             }
 
-            Post(s"$collectionPath/${action.name}?blocking=true&timeout=65000") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true&timeout=65000") ~> Route.seal(routes(creds)) ~> check {
                 status shouldBe BadRequest
                 responseAs[String] should include(Messages.invalidTimeout(WhiskActionsApi.maxWaitForBlockingActivation))
             }
 
             // will not wait long enough should get accepted status
-            Post(s"$collectionPath/${action.name}?blocking=true&timeout=100") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true&timeout=100") ~> Route.seal(routes(creds)) ~> check {
                 status shouldBe Accepted
             }
 
             // repeat this time wait longer than active ack delay
-            Post(s"$collectionPath/${action.name}?blocking=true&timeout=500") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true&timeout=500") ~> Route.seal(routes(creds)) ~> check {
                 status shouldBe OK
                 val response = responseAs[JsObject]
                 response shouldBe activation.withoutLogs.toExtendedJson
@@ -698,7 +701,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         // the one generated by the api handler
         put(activationStore, activation)
         try {
-            Post(s"$collectionPath/${action.name}?blocking=true") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$collectionPath/${action.name}?blocking=true") ~> Route.seal(routes(creds)) ~> check {
                 status should be(InternalServerError)
                 val response = responseAs[JsObject]
                 response should be(activation.withoutLogs.toExtendedJson)
@@ -713,7 +716,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val entity = BadEntity(namespace, aname)
         put(entityStore, entity)
 
-        Delete(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -724,7 +727,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val entity = BadEntity(namespace, aname)
         put(entityStore, entity)
 
-        Get(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -738,7 +741,7 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val components = Vector(stringToFullyQualifiedName(s"$namespace/${entity.name}"))
         val content = WhiskActionPut(Some(sequence(components)))
 
-        Put(s"$collectionPath/$aname", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$aname", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -751,44 +754,44 @@ class ActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val okUpdate = WhiskActionPut(Some(swift3("_")))
         val badUpdate = WhiskActionPut(Some(swift("_")))
 
-        Put(s"$collectionPath/${action.name}", WhiskActionPut(Some(action.exec))) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}", WhiskActionPut(Some(action.exec))) ~> Route.seal(routes(creds)) ~> check {
             status shouldBe BadRequest
             responseAs[ErrorResponse].error shouldBe Messages.runtimeDeprecated(action.exec)
         }
 
-        Put(s"$collectionPath/${action.name}?overwrite=true", WhiskActionPut(Some(action.exec))) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", WhiskActionPut(Some(action.exec))) ~> Route.seal(routes(creds)) ~> check {
             status shouldBe BadRequest
             responseAs[ErrorResponse].error shouldBe Messages.runtimeDeprecated(action.exec)
         }
 
         put(entityStore, action)
 
-        Put(s"$collectionPath/${action.name}?overwrite=true", JsObject()) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", JsObject()) ~> Route.seal(routes(creds)) ~> check {
             status shouldBe BadRequest
             responseAs[ErrorResponse].error shouldBe Messages.runtimeDeprecated(action.exec)
         }
 
-        Put(s"$collectionPath/${action.name}?overwrite=true", badUpdate) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", badUpdate) ~> Route.seal(routes(creds)) ~> check {
             status shouldBe BadRequest
             responseAs[ErrorResponse].error shouldBe Messages.runtimeDeprecated(action.exec)
         }
 
-        Post(s"$collectionPath/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status shouldBe BadRequest
             responseAs[ErrorResponse].error shouldBe Messages.runtimeDeprecated(action.exec)
         }
 
-        Get(s"$collectionPath/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status shouldBe OK
         }
 
-        Delete(s"$collectionPath/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status shouldBe OK
         }
 
         put(entityStore, action)
 
-        Put(s"$collectionPath/${action.name}?overwrite=true", okUpdate) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", okUpdate) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status shouldBe OK
         }
diff --git a/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
index 66209d2..c205856 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ActivationsApiTests.scala
@@ -21,10 +21,14 @@ import java.time.Clock
 import java.time.Instant
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
+
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.Route
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
+
 import whisk.core.controller.WhiskActivationsApi
 import whisk.core.database.ArtifactStoreProvider
 import whisk.core.entity._
@@ -72,7 +76,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         activations foreach { put(activationStore, _) }
         waitOnView(activationStore, namespace, 2)
         whisk.utils.retry {
-            Get(s"$collectionPath") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val rawResponse = responseAs[List[JsObject]]
                 val response = responseAs[List[JsObject]]
@@ -84,7 +88,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
 
         // it should "list activations with explicit namespace owned by subject" in {
         whisk.utils.retry {
-            Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val rawResponse = responseAs[List[JsObject]]
                 val response = responseAs[List[JsObject]]
@@ -96,7 +100,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
 
         // it should "reject list activations with explicit namespace not owned by subject" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -118,7 +122,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         waitOnView(activationStore, namespace, 2)
 
         whisk.utils.retry {
-            Get(s"$collectionPath?docs=true") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath?docs=true") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 activations.length should be(response.length)
@@ -151,7 +155,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
 
         // get between two time stamps
         whisk.utils.retry {
-            Get(s"$collectionPath?docs=true&since=${since.toEpochMilli}&upto=${upto.toEpochMilli}") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath?docs=true&since=${since.toEpochMilli}&upto=${upto.toEpochMilli}") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 val expected = activations filter {
@@ -164,7 +168,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
 
         // get 'upto' with no defined since value should return all activation 'upto'
         whisk.utils.retry {
-            Get(s"$collectionPath?docs=true&upto=${upto.toEpochMilli}") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath?docs=true&upto=${upto.toEpochMilli}") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 val expected = activations filter {
@@ -177,7 +181,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
 
         // get 'since' with no defined upto value should return all activation 'since'
         whisk.utils.retry {
-            Get(s"$collectionPath?docs=true&since=${since.toEpochMilli}") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath?docs=true&since=${since.toEpochMilli}") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 val expected = activations filter {
@@ -206,7 +210,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         waitOnView(activationStore, namespace, 2)
 
         whisk.utils.retry {
-            Get(s"$collectionPath?name=xyz") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath?name=xyz") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 activations.length should be(response.length)
@@ -218,7 +222,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
     it should "reject activation list when limit is greater than maximum allowed value" in {
         implicit val tid = transid()
         val exceededMaxLimit = WhiskActivationsApi.maxActivationLimit + 1
-        val response = Get(s"$collectionPath?limit=$exceededMaxLimit") ~> sealRoute(routes(creds)) ~> check {
+        val response = Get(s"$collectionPath?limit=$exceededMaxLimit") ~> Route.seal(routes(creds)) ~> check {
             val response = responseAs[String]
             response should include(Messages.maxActivationLimitExceeded(exceededMaxLimit, WhiskActivationsApi.maxActivationLimit))
             status should be(BadRequest)
@@ -227,17 +231,17 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
 
     it should "reject get activation by namespace and action name when action name is not a valid name" in {
         implicit val tid = transid()
-        Get(s"$collectionPath?name=0%20") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?name=0%20") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
 
     it should "reject get activation with invalid since/upto value" in {
         implicit val tid = transid()
-        Get(s"$collectionPath?since=xxx") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?since=xxx") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
-        Get(s"$collectionPath?upto=yyy") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?upto=yyy") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -248,14 +252,14 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         val activation = WhiskActivation(namespace, aname, creds.subject, ActivationId(), start = Instant.now, end = Instant.now)
         put(activationStore, activation)
 
-        Get(s"$collectionPath/${activation.activationId.asString}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${activation.activationId.asString}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[JsObject]
             response should be(activation.toExtendedJson)
         }
 
         // it should "get activation by name in explicit namespace owned by subject" in
-        Get(s"/$namespace/${collection.path}/${activation.activationId.asString}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}/${activation.activationId.asString}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[JsObject]
             response should be(activation.toExtendedJson)
@@ -263,7 +267,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
 
         // it should "reject get activation by name in explicit namespace not owned by subject" in
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${activation.activationId.asString}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${activation.activationId.asString}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -274,7 +278,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         val activation = WhiskActivation(namespace, aname, creds.subject, ActivationId(), start = Instant.now, end = Instant.now)
         put(activationStore, activation)
 
-        Get(s"$collectionPath/${activation.activationId.asString}/result") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${activation.activationId.asString}/result") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[JsObject]
             response should be(activation.response.toExtendedJson)
@@ -287,7 +291,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         val activation = WhiskActivation(namespace, aname, creds.subject, ActivationId(), start = Instant.now, end = Instant.now)
         put(activationStore, activation)
 
-        Get(s"$collectionPath/${activation.activationId.asString}/logs") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${activation.activationId.asString}/logs") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[JsObject]
             response should be(activation.logs.toJsonObject)
@@ -300,7 +304,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         val activation = WhiskActivation(namespace, aname, creds.subject, ActivationId(), start = Instant.now, end = Instant.now)
         put(entityStore, activation)
 
-        Get(s"$collectionPath/${activation.activationId.asString}/bogus") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${activation.activationId.asString}/bogus") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -312,38 +316,38 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         val toolong = activationId + "xxx"
         val malformed = tooshort + "z"
 
-        Get(s"$collectionPath/$tooshort") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/$tooshort") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] shouldBe Messages.activationIdLengthError(SizeError("Activation id", tooshort.length.B, 32.B))
         }
 
-        Get(s"$collectionPath/$toolong") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/$toolong") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] shouldBe Messages.activationIdLengthError(SizeError("Activation id", toolong.length.B, 32.B))
         }
 
-        Get(s"$collectionPath/$malformed") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/$malformed") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
 
     it should "reject request with put" in {
         implicit val tid = transid()
-        Put(s"$collectionPath/${ActivationId()}") ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${ActivationId()}") ~> Route.seal(routes(creds)) ~> check {
             status should be(MethodNotAllowed)
         }
     }
 
     it should "reject request with post" in {
         implicit val tid = transid()
-        Post(s"$collectionPath/${ActivationId()}") ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${ActivationId()}") ~> Route.seal(routes(creds)) ~> check {
             status should be(MethodNotAllowed)
         }
     }
 
     it should "reject request with delete" in {
         implicit val tid = transid()
-        Delete(s"$collectionPath/${ActivationId()}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${ActivationId()}") ~> Route.seal(routes(creds)) ~> check {
             status should be(MethodNotAllowed)
         }
     }
@@ -355,7 +359,7 @@ class ActivationsApiTests extends ControllerTestCommon with WhiskActivationsApi
         val entity = BadEntity(namespace, EntityName(ActivationId().toString))
         put(activationStore, entity)
 
-        Get(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
diff --git a/tests/src/test/scala/whisk/core/controller/test/AuthenticateTests.scala b/tests/src/test/scala/whisk/core/controller/test/AuthenticateTests.scala
index 8e3cca3..e964856 100644
--- a/tests/src/test/scala/whisk/core/controller/test/AuthenticateTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/AuthenticateTests.scala
@@ -22,15 +22,10 @@ import scala.concurrent.Await
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
 
-import spray.http.BasicHttpCredentials
-import spray.http.StatusCodes._
-import spray.routing.authentication.UserPass
-import spray.routing.directives.AuthMagnet.fromContextAuthenticator
-import whisk.common.TransactionCounter
+import akka.http.scaladsl.model.headers.BasicHttpCredentials
+
 import whisk.core.controller.Authenticate
-import whisk.core.controller.AuthenticatedRoute
 import whisk.core.entity._
-import whisk.http.BasicHttpService
 import whisk.core.entitlement.Privilege
 
 /**
@@ -62,7 +57,7 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate {
         // Try to login with each specific namespace
         namespaces.foreach { ns =>
             println(s"Trying to login to $ns")
-            val pass = UserPass(ns.authkey.uuid.asString, ns.authkey.key.asString)
+            val pass = BasicHttpCredentials(ns.authkey.uuid.asString, ns.authkey.key.asString)
             val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
             user.get shouldBe Identity(subject, ns.name, ns.authkey, Privilege.ALL)
 
@@ -82,7 +77,7 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate {
         val ns = namespaces.head
         val key = ns.authkey.key.asString
         Seq(key.drop(1), key.dropRight(1), key + "x", AuthKey().key.asString).foreach { k =>
-            val pass = UserPass(ns.authkey.uuid.asString, k)
+            val pass = BasicHttpCredentials(ns.authkey.uuid.asString, k)
             val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
             user shouldBe empty
         }
@@ -91,7 +86,7 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate {
     it should "not log key during validation" in {
         implicit val tid = transid()
         val creds = WhiskAuthHelpers.newIdentity()
-        val pass = UserPass(creds.authkey.uuid.asString, creds.authkey.key.asString)
+        val pass = BasicHttpCredentials(creds.authkey.uuid.asString, creds.authkey.key.asString)
         val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
         user should be(None)
         stream.toString should not include creds.authkey.key.asString
@@ -100,7 +95,7 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate {
     it should "not authorize an unknown user" in {
         implicit val tid = transid()
         val creds = WhiskAuthHelpers.newIdentity()
-        val pass = UserPass(creds.authkey.uuid.asString, creds.authkey.key.asString)
+        val pass = BasicHttpCredentials(creds.authkey.uuid.asString, creds.authkey.key.asString)
         val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
         user should be(None)
     }
@@ -113,61 +108,22 @@ class AuthenticateTests extends ControllerTestCommon with Authenticate {
 
     it should "not authorize when malformed user is provided" in {
         implicit val tid = transid()
-        val pass = UserPass("x", Secret().asString)
+        val pass = BasicHttpCredentials("x", Secret().asString)
         val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
         user should be(None)
     }
 
     it should "not authorize when malformed secret is provided" in {
         implicit val tid = transid()
-        val pass = UserPass(UUID().asString, "x")
+        val pass = BasicHttpCredentials(UUID().asString, "x")
         val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
         user should be(None)
     }
 
     it should "not authorize when malformed creds are provided" in {
         implicit val tid = transid()
-        val pass = UserPass("x", "y")
+        val pass = BasicHttpCredentials("x", "y")
         val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
         user should be(None)
     }
 }
-
-class AuthenticatedRouteTests
-    extends ControllerTestCommon
-    with Authenticate
-    with AuthenticatedRoute
-    with TransactionCounter {
-
-    behavior of "Authenticated Route"
-
-    val route = sealRoute {
-        implicit val tid = transid()
-        handleRejections(BasicHttpService.customRejectionHandler) {
-            path("secured") {
-                authenticate(basicauth) {
-                    user => complete("ok")
-                }
-            }
-
-        }
-    }
-
-    it should "authorize a known user" in {
-        implicit val tid = transid()
-        val entry = WhiskAuthHelpers.newAuth()
-        put(authStore, entry) // this test entry is reclaimed when the test completes
-
-        val validCredentials = BasicHttpCredentials(entry.namespaces.head.authkey.uuid.asString, entry.namespaces.head.authkey.key.asString)
-        Get("/secured") ~> addCredentials(validCredentials) ~> route ~> check {
-            status should be(OK)
-        }
-    }
-
-    it should "not authorize an unknown user" in {
-        val invalidCredentials = BasicHttpCredentials(UUID().asString, Secret().asString)
-        Get("/secured") ~> addCredentials(invalidCredentials) ~> route ~> check {
-            status should be(Unauthorized)
-        }
-    }
-}
diff --git a/tests/src/test/scala/whisk/core/controller/test/AuthenticateV2Tests.scala b/tests/src/test/scala/whisk/core/controller/test/AuthenticateV2Tests.scala
index 0485b05..9bca5b7 100644
--- a/tests/src/test/scala/whisk/core/controller/test/AuthenticateV2Tests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/AuthenticateV2Tests.scala
@@ -22,7 +22,8 @@ import scala.concurrent.Await
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
 
-import spray.routing.authentication.UserPass
+import akka.http.scaladsl.model.headers.BasicHttpCredentials
+
 import whisk.core.controller.Authenticate
 import whisk.core.entity.AuthKey
 import whisk.core.entity.Subject
@@ -66,7 +67,7 @@ class AuthenticateV2Tests extends ControllerTestCommon with Authenticate {
         // Try to login with each specific namespace
         namespaces.foreach { ns =>
             println(s"Trying to login to $ns")
-            val pass = UserPass(ns.authkey.uuid.asString, ns.authkey.key.asString)
+            val pass = BasicHttpCredentials(ns.authkey.uuid.asString, ns.authkey.key.asString)
             val user = Await.result(validateCredentials(Some(pass)), dbOpTimeout)
             user.get shouldBe Identity(subject, ns.name, ns.authkey, Privilege.ALL)
         }
diff --git a/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala b/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala
index 49864b9..d652272 100644
--- a/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/ControllerTestCommon.scala
@@ -28,10 +28,13 @@ import org.scalatest.FlatSpec
 import org.scalatest.Matchers
 
 import common.StreamLogging
+
+import akka.http.scaladsl.testkit.ScalatestRouteTest
+import akka.http.scaladsl.testkit.RouteTestTimeout
+
 import spray.json.DefaultJsonProtocol
 import spray.json.JsString
-import spray.routing.HttpService
-import spray.testkit.ScalatestRouteTest
+
 import whisk.common.TransactionCounter
 import whisk.common.TransactionId
 import whisk.core.WhiskConfig
@@ -55,14 +58,12 @@ protected trait ControllerTestCommon
     with DbUtils
     with ExecHelpers
     with WhiskServices
-    with HttpService
     with StreamLogging {
 
     override val instance = InstanceId(0)
     override val numberOfInstances = 1
     val activeAckTopicIndex = InstanceId(0)
 
-    override val actorRefFactory = null
     implicit val routeTestTimeout = RouteTestTimeout(90 seconds)
 
     override implicit val actorSystem = system // defined in ScalatestRouteTest
diff --git a/tests/src/test/scala/whisk/core/controller/test/EntitlementProviderTests.scala b/tests/src/test/scala/whisk/core/controller/test/EntitlementProviderTests.scala
index ce1205b..e372abb 100644
--- a/tests/src/test/scala/whisk/core/controller/test/EntitlementProviderTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/EntitlementProviderTests.scala
@@ -24,7 +24,8 @@ import org.junit.runner.RunWith
 import org.scalatest.concurrent.ScalaFutures
 import org.scalatest.junit.JUnitRunner
 
-import spray.http.StatusCodes._
+import akka.http.scaladsl.model.StatusCodes._
+
 import whisk.core.controller.RejectRequest
 import whisk.core.entitlement._
 import whisk.core.entitlement.Privilege._
diff --git a/tests/src/test/scala/whisk/core/controller/test/NamespacesApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/NamespacesApiTests.scala
index a601cd5..545918d 100644
--- a/tests/src/test/scala/whisk/core/controller/test/NamespacesApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/NamespacesApiTests.scala
@@ -20,16 +20,22 @@ package whisk.core.controller.test
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
 
-import spray.http.StatusCodes.Forbidden
-import spray.http.StatusCodes.MethodNotAllowed
-import spray.http.StatusCodes.OK
-import spray.httpx.SprayJsonSupport.sprayJsonUnmarshaller
+import akka.http.scaladsl.model.StatusCodes.OK
+import akka.http.scaladsl.model.StatusCodes.Forbidden
+import akka.http.scaladsl.model.StatusCodes.MethodNotAllowed
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport.sprayJsonUnmarshaller
+import akka.http.scaladsl.server.Route
+
 import spray.json.DefaultJsonProtocol._
 import spray.json._
 
 import whisk.core.controller.Namespaces
 import whisk.core.controller.WhiskNamespacesApi
 import whisk.core.entity.EntityPath
+import spray.json.JsObject
+import spray.json.JsObject
+import whisk.core.controller.Namespaces
+import spray.json.JsArray
 
 /**
  * Tests Namespaces API.
@@ -55,7 +61,7 @@ class NamespacesApiTests extends ControllerTestCommon with WhiskNamespacesApi {
 
     it should "list namespaces for subject" in {
         implicit val tid = transid()
-        Get(collectionPath) ~> sealRoute(routes(creds)) ~> check {
+        Get(collectionPath) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val ns = responseAs[List[EntityPath]]
             ns should be(List(EntityPath(creds.subject.asString)))
@@ -64,7 +70,7 @@ class NamespacesApiTests extends ControllerTestCommon with WhiskNamespacesApi {
 
     it should "list namespaces for subject with trailing /" in {
         implicit val tid = transid()
-        Get(s"$collectionPath/") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val ns = responseAs[List[EntityPath]]
             ns should be(List(EntityPath(creds.subject.asString)))
@@ -73,7 +79,7 @@ class NamespacesApiTests extends ControllerTestCommon with WhiskNamespacesApi {
 
     it should "get namespace entities for subject" in {
         implicit val tid = transid()
-        Get(s"$collectionPath/${creds.subject}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${creds.subject}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val ns = responseAs[JsObject]
             ns should be(JsObject(Namespaces.emptyNamespace map { kv => (kv._1, JsArray()) }))
@@ -83,28 +89,28 @@ class NamespacesApiTests extends ControllerTestCommon with WhiskNamespacesApi {
     it should "reject get namespace entities for unauthorized subject" in {
         implicit val tid = transid()
         val anothercred = WhiskAuthHelpers.newIdentity()
-        Get(s"$collectionPath/${anothercred.subject}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${anothercred.subject}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
 
     it should "reject request with put" in {
         implicit val tid = transid()
-        Put(s"$collectionPath/${creds.subject}") ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${creds.subject}") ~> Route.seal(routes(creds)) ~> check {
             status should be(MethodNotAllowed)
         }
     }
 
     it should "reject request with post" in {
         implicit val tid = transid()
-        Post(s"$collectionPath/${creds.subject}") ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${creds.subject}") ~> Route.seal(routes(creds)) ~> check {
             status should be(MethodNotAllowed)
         }
     }
 
     it should "reject request with delete" in {
         implicit val tid = transid()
-        Delete(s"$collectionPath/${creds.subject}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${creds.subject}") ~> Route.seal(routes(creds)) ~> check {
             status should be(MethodNotAllowed)
         }
     }
diff --git a/tests/src/test/scala/whisk/core/controller/test/PackageActionsApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/PackageActionsApiTests.scala
index cec0489..4b8241a 100644
--- a/tests/src/test/scala/whisk/core/controller/test/PackageActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/PackageActionsApiTests.scala
@@ -20,10 +20,14 @@ package whisk.core.controller.test
 import scala.concurrent.duration.DurationInt
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
+
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.Route
+
 import spray.json.DefaultJsonProtocol._
 import spray.json._
+
 import whisk.core.controller.WhiskActionsApi
 import whisk.core.entity._
 import whisk.core.entitlement.Resource
@@ -66,7 +70,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider)
         actions foreach { put(entityStore, _) }
         whisk.utils.retry {
-            Get(s"$collectionPath/${provider.name}/") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath/${provider.name}/") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 actions.length should be(response.length)
@@ -86,7 +90,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, reference)
         actions foreach { put(entityStore, _) }
         whisk.utils.retry {
-            Get(s"$collectionPath/${reference.name}/") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath/${reference.name}/") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 actions.length should be(response.length)
@@ -104,7 +108,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, action1)
         put(entityStore, action2)
         whisk.utils.retry {
-            Get(s"$collectionPath") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 response.length should be(2)
@@ -119,7 +123,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val provider = WhiskPackage(namespace, aname, None)
         put(entityStore, provider)
         whisk.utils.retry {
-            Get(s"$collectionPath/${provider.name}") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
                 status should be(Conflict)
             }
         }
@@ -129,7 +133,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         implicit val tid = transid()
         val provider = WhiskPackage(namespace, aname, None)
         put(entityStore, provider)
-        Delete(s"$collectionPath/${provider.name}/") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${provider.name}/") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -141,7 +145,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         val content = WhiskActionPut(Some(action.exec))
         put(entityStore, provider)
-        Put(s"$collectionPath/${provider.name}/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${provider.name}/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -156,7 +160,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val provider = WhiskPackage(namespace, aname)
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         val content = WhiskActionPut(Some(action.exec))
-        Put(s"$collectionPath/${provider.name}/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${provider.name}/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -167,7 +171,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val binding = WhiskPackage(namespace, aname, provider.bind)
         val content = WhiskActionPut(Some(jsDefault("??")))
         put(entityStore, binding)
-        Put(s"$collectionPath/${binding.name}/$aname", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${binding.name}/$aname", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -179,7 +183,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val content = WhiskActionPut(Some(jsDefault("??")))
         put(entityStore, provider)
         put(entityStore, binding)
-        Put(s"$collectionPath/${binding.name}/$aname", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${binding.name}/$aname", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -189,7 +193,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val provider = WhiskPackage(EntityPath(Subject().asString), aname, publish = true)
         val content = WhiskActionPut(Some(jsDefault("??")))
         put(entityStore, provider)
-        Put(s"/${provider.namespace}/${collection.path}/${provider.name}/$aname", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"/${provider.namespace}/${collection.path}/${provider.name}/$aname", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -204,11 +208,11 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
 
         // it should "reject delete action in package owned by different subject" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Delete(s"/${provider.namespace}/${collection.path}/${provider.name}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+        Delete(s"/${provider.namespace}/${collection.path}/${provider.name}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Delete(s"$collectionPath/${provider.name}/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${provider.name}/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(action)
@@ -220,7 +224,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val provider = WhiskPackage(namespace, aname)
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         put(entityStore, action)
-        Delete(s"$collectionPath/${provider.name}/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${provider.name}/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -230,7 +234,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val provider = WhiskPackage(namespace, aname)
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         put(entityStore, provider)
-        Delete(s"$collectionPath/${provider.name}/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${provider.name}/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -241,7 +245,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val binding = WhiskPackage(namespace, aname, provider.bind)
         val content = WhiskActionPut(Some(jsDefault("??")))
         put(entityStore, binding)
-        Delete(s"$collectionPath/${binding.name}/$aname") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${binding.name}/$aname") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -253,7 +257,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val content = WhiskActionPut(Some(jsDefault("??")))
         put(entityStore, provider)
         put(entityStore, binding)
-        Delete(s"$collectionPath/${binding.name}/$aname") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${binding.name}/$aname") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -264,7 +268,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         put(entityStore, provider)
         put(entityStore, action)
-        Delete(s"/${provider.namespace}/${collection.path}/${provider.name}/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"/${provider.namespace}/${collection.path}/${provider.name}/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -277,7 +281,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider)
         put(entityStore, action)
         whisk.utils.retry {
-            Get(s"$collectionPath/${provider.name}/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath/${provider.name}/${action.name}") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[WhiskAction]
                 response should be(action inherit provider.parameters)
@@ -295,7 +299,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, binding)
         put(entityStore, action)
         whisk.utils.retry {
-            Get(s"$collectionPath/${binding.name}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+            Get(s"$collectionPath/${binding.name}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
                 status should be(OK)
                 val response = responseAs[WhiskAction]
                 response should be(action inherit (provider.parameters ++ binding.parameters))
@@ -313,7 +317,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, binding)
         put(entityStore, action)
         whisk.utils.retry {
-            Get(s"$collectionPath/${binding.name}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+            Get(s"$collectionPath/${binding.name}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
                 status should be(OK)
                 val response = responseAs[WhiskAction]
                 response should be(action inherit (provider.parameters ++ binding.parameters))
@@ -334,7 +338,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, action)
         val pkgaccess = Resource(provider.namespace, PACKAGES, Some(provider.name.asString))
         Await.result(entitlementProvider.grant(auser.subject, READ, pkgaccess), 1 second)
-        Get(s"$collectionPath/${binding.name}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"$collectionPath/${binding.name}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(action inherit (provider.parameters ++ binding.parameters))
@@ -346,7 +350,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val provider = WhiskPackage(namespace, aname)
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         put(entityStore, action)
-        Get(s"$collectionPath/${provider.name}/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${provider.name}/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -356,7 +360,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val provider = WhiskPackage(namespace, aname)
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         put(entityStore, provider)
-        Get(s"$collectionPath/${provider.name}/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${provider.name}/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -370,7 +374,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"), Parameters("a", "A"))
         put(entityStore, provider)
         put(entityStore, action)
-        Get(s"$collectionPath/${binding.name}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"$collectionPath/${binding.name}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(NotFound)
         }
     }
@@ -384,7 +388,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"), Parameters("a", "A"))
         put(entityStore, binding)
         put(entityStore, action)
-        Get(s"$collectionPath/${binding.name}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"$collectionPath/${binding.name}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden) // do not leak that package does not exist
         }
     }
@@ -398,7 +402,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"), Parameters("a", "A"))
         put(entityStore, provider)
         put(entityStore, binding)
-        Get(s"$collectionPath/${binding.name}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"$collectionPath/${binding.name}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(NotFound)
         }
     }
@@ -412,7 +416,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider)
         put(entityStore, binding)
         put(entityStore, action)
-        Get(s"$collectionPath/${binding.name}/${action.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"$collectionPath/${binding.name}/${action.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -425,7 +429,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val content = JsObject("xxx" -> "yyy".toJson)
         put(entityStore, provider)
         put(entityStore, action)
-        Post(s"$collectionPath/${provider.name}/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${provider.name}/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Accepted)
             val response = responseAs[JsObject]
             response.fields("activationId") should not be None
@@ -440,7 +444,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val content = JsObject("xxx" -> "yyy".toJson)
         put(entityStore, provider)
         put(entityStore, action)
-        Post(s"/$namespace/${collection.path}/${provider.name}/${action.name}", content) ~> sealRoute(routes(auser)) ~> check {
+        Post(s"/$namespace/${collection.path}/${provider.name}/${action.name}", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Accepted)
             val response = responseAs[JsObject]
             response.fields("activationId") should not be None
@@ -457,7 +461,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider)
         put(entityStore, reference)
         put(entityStore, action)
-        Post(s"$collectionPath/${reference.name}/${action.name}", content) ~> sealRoute(routes(auser)) ~> check {
+        Post(s"$collectionPath/${reference.name}/${action.name}", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Accepted)
             val response = responseAs[JsObject]
             response.fields("activationId") should not be None
@@ -478,7 +482,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, action)
         val pkgaccess = Resource(provider.namespace, PACKAGES, Some(provider.name.asString))
         Await.result(entitlementProvider.grant(auser.subject, ACTIVATE, pkgaccess), 1 second)
-        Post(s"$collectionPath/${reference.name}/${action.name}", content) ~> sealRoute(routes(auser)) ~> check {
+        Post(s"$collectionPath/${reference.name}/${action.name}", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Accepted)
             val response = responseAs[JsObject]
             response.fields("activationId") should not be None
@@ -493,7 +497,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val content = JsObject("xxx" -> "yyy".toJson)
         put(entityStore, provider)
         put(entityStore, action)
-        Post(s"/$namespace/${collection.path}/${provider.name}/${action.name}", content) ~> sealRoute(routes(auser)) ~> check {
+        Post(s"/$namespace/${collection.path}/${provider.name}/${action.name}", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -504,7 +508,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         val content = JsObject("xxx" -> "yyy".toJson)
         put(entityStore, action)
-        Post(s"$collectionPath/${provider.name}/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${provider.name}/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -515,7 +519,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         val action = WhiskAction(provider.fullPath, aname, jsDefault("??"))
         val content = JsObject("xxx" -> "yyy".toJson)
         put(entityStore, action)
-        Post(s"$collectionPath/${provider.name}/${action.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${provider.name}/${action.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -530,7 +534,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider)
         put(entityStore, reference)
         put(entityStore, action)
-        Post(s"$collectionPath/${reference.name}/${action.name}", content) ~> sealRoute(routes(auser)) ~> check {
+        Post(s"$collectionPath/${reference.name}/${action.name}", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -542,7 +546,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider)
         put(entityStore, entity)
 
-        Delete(s"$collectionPath/${provider.name}/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${provider.name}/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
     }
@@ -554,7 +558,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider, false)
         put(entityStore, entity, false)
 
-        Delete(s"$collectionPath/${provider.name}/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${provider.name}/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -567,13 +571,13 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider)
         put(entityStore, entity)
 
-        Get(s"$collectionPath/${provider.name}/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${provider.name}/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
 
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/${provider.namespace}/${collection.path}/${provider.name}/${entity.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/${provider.namespace}/${collection.path}/${provider.name}/${entity.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
             responseAs[ErrorResponse].error shouldBe Messages.notAuthorizedtoOperateOnResource
         }
@@ -586,7 +590,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, provider)
         put(entityStore, entity)
 
-        Get(s"$collectionPath/${provider.name}/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${provider.name}/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -600,7 +604,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, entity)
 
         val content = WhiskActionPut()
-        Put(s"$collectionPath/${provider.name}/${entity.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${provider.name}/${entity.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -614,7 +618,7 @@ class PackageActionsApiTests extends ControllerTestCommon with WhiskActionsApi {
         put(entityStore, entity)
 
         val content = WhiskActionPut()
-        Put(s"$collectionPath/${provider.name}/${entity.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${provider.name}/${entity.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
diff --git a/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
index 9df7087..688e444 100644
--- a/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/PackagesApiTests.scala
@@ -21,10 +21,14 @@ import scala.language.postfixOps
 
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
+
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.Route
+
 import spray.json.DefaultJsonProtocol._
 import spray.json._
+
 import whisk.core.entity._
 import whisk.core.controller.WhiskPackagesApi
 import whisk.http.ErrorResponse
@@ -73,7 +77,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         providers foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskPackage, namespace, providers.length)
         whisk.utils.retry {
-            Get(s"$collectionPath") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 providers.length should be(response.length)
@@ -82,7 +86,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         }
 
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(auser)) ~> check {
             val response = responseAs[List[JsObject]]
             response should be(List()) // cannot list packages that are private in another namespace
         }
@@ -105,7 +109,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         waitOnView(entityStore, WhiskPackage, namespaces(1), 1)
         waitOnView(entityStore, WhiskPackage, namespaces(2), 1)
         waitOnView(entityStore, WhiskPackage, namespaces(0), 1 + 4)
-        Get(s"$collectionPath") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             val expected = providers.filter { _.namespace == namespace } ++ references
@@ -114,7 +118,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         }
 
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(auser)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             val expected = providers.filter {
@@ -144,7 +148,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         waitOnView(entityStore, WhiskPackage, namespaces(1), 1)
         waitOnView(entityStore, WhiskPackage, namespaces(2), 1)
         waitOnView(entityStore, WhiskPackage, namespaces(0), 1 + 4)
-        Get(s"$collectionPath?public=true") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?public=true") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             val expected = providers filter { _.publish }
@@ -167,7 +171,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         waitOnView(entityStore, WhiskPackage, namespaces(0), 1)
         waitOnView(entityStore, WhiskPackage, namespaces(1), 1)
         waitOnView(entityStore, WhiskPackage, namespaces(2), 1)
-        Get(s"$collectionPath?public=true") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?public=true") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             val expected = providers filter { _.publish }
@@ -179,7 +183,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
     // confirm ?public disabled
     it should "ignore ?public on list all packages" in {
         implicit val tid = transid()
-        Get(s"$collectionPath?public=true") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?public=true") ~> Route.seal(routes(creds)) ~> check {
             implicit val tid = transid()
             // create packages and package bindings, set some public and confirm API lists only public packages
             val namespaces = Seq(namespace, EntityPath(aname().toString), EntityPath(aname().toString))
@@ -192,7 +196,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
             waitOnView(entityStore, WhiskPackage, namespaces(0), 1)
             waitOnView(entityStore, WhiskPackage, namespaces(1), 1)
             waitOnView(entityStore, WhiskPackage, namespaces(2), 1)
-            Get(s"$collectionPath?public=true") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath?public=true") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[List[JsObject]]
                 val expected = providers filter { _.namespace == creds.namespace.toPath }
@@ -205,7 +209,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
     // ?public disabled
     ignore should "reject list all public packages with invalid parameters" in {
         implicit val tid = transid()
-        Get(s"$collectionPath?public=true&docs=true") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?public=true&docs=true") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -215,13 +219,13 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         implicit val tid = transid()
         val provider = WhiskPackage(namespace, aname(), None)
         put(entityStore, provider)
-        Get(s"$collectionPath/${provider.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskPackageWithActions]
             response should be(provider withActions ())
         }
 
-        Get(s"$collectionPath/${provider.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskPackageWithActions]
             response should be(provider withActions ())
@@ -234,7 +238,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val reference = WhiskPackage(namespace, aname(), provider.bind, Parameters("b", "b") ++ Parameters("c", "C"))
         put(entityStore, provider)
         put(entityStore, reference)
-        Get(s"$collectionPath/${reference.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${reference.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskPackageWithActions]
             response should be(reference inherit provider.parameters withActions ())
@@ -252,7 +256,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val reference = WhiskPackage(namespace, aname(), provider.bind)
         put(entityStore, provider)
         put(entityStore, reference)
-        Get(s"$collectionPath/${reference.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${reference.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -268,11 +272,11 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         // it should "reject get private package from other subject" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${provider.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${provider.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Get(s"$collectionPath/${provider.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskPackageWithActions]
             response should be(provider withActions (List(action, feed)))
@@ -292,11 +296,11 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         // it should "reject get package reference from other subject" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${reference.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${reference.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Get(s"$collectionPath/${reference.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${reference.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskPackageWithActions]
             response should be(reference withActions (List(action, feed)))
@@ -318,11 +322,11 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         // it should "reject get package reference from other subject" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${reference.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${reference.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Get(s"$collectionPath/${reference.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${reference.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -334,7 +338,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         // binding annotation should be removed
         val someBindingAnnotation = Parameters(WhiskPackage.bindingFieldName, "???")
         val content = WhiskPackagePut(annotations = Some(someBindingAnnotation ++ Parameters("a", "b")))
-        Put(s"$collectionPath/${provider.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${provider.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deletePackage(provider.docid)
             status should be(OK)
             val response = responseAs[WhiskPackage]
@@ -353,11 +357,11 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         // it should "reject create package reference in some other namespace" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Put(s"/$namespace/${collection.path}/${reference.name}", content) ~> sealRoute(routes(auser)) ~> check {
+        Put(s"/$namespace/${collection.path}/${reference.name}", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Put(s"/$namespace/${collection.path}/${reference.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"/$namespace/${collection.path}/${reference.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deletePackage(reference.docid)
             status should be(OK)
             val response = responseAs[WhiskPackage]
@@ -376,7 +380,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val content = WhiskPackagePut(reference.binding)
         put(entityStore, provider)
 
-        Put(s"/$namespace/${collection.path}/${reference.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"/$namespace/${collection.path}/${reference.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -387,7 +391,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val reference = WhiskPackage(namespace, aname(), Some(Binding(EntityPath.DEFAULT.root, provider.name)))
         val content = WhiskPackagePut(reference.binding)
         put(entityStore, provider)
-        Put(s"$collectionPath/${reference.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${reference.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deletePackage(reference.docid)
             status should be(OK)
             val response = responseAs[WhiskPackage]
@@ -402,7 +406,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         implicit val tid = transid()
         val binding = Some(Binding(namespace.root, aname()))
         val content = WhiskPackagePut(binding)
-        Put(s"$collectionPath/${aname()}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error should include(Messages.bindingDoesNotExist)
         }
@@ -415,7 +419,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         val binding = Some(Binding(privateNamespace.root, aname()))
         val content = WhiskPackagePut(binding)
-        Put(s"$collectionPath/${aname()}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -427,7 +431,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val content = WhiskPackagePut(Some(Binding(reference.namespace.root, reference.name)))
         put(entityStore, provider)
         put(entityStore, reference)
-        Put(s"$collectionPath/${aname()}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error should include(Messages.bindingCannotReferenceBinding)
         }
@@ -440,7 +444,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
             Parameters(key.toString, "a" * 10)
         } reduce (_ ++ _)
         val content = s"""{"annotations":$annotations}""".parseJson.asJsObject
-        Put(s"$collectionPath/${aname()}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskEntity.annotationsFieldName, annotations.size, Parameters.sizeLimit))
@@ -455,7 +459,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
             Parameters(key.toString, "a" * 10)
         } reduce (_ ++ _)
         val content = s"""{"parameters":$parameters}""".parseJson.asJsObject
-        Put(s"$collectionPath/${aname()}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskEntity.paramsFieldName, parameters.size, Parameters.sizeLimit))
@@ -472,7 +476,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val provider = WhiskPackage(namespace, aname())
         val content = s"""{"parameters":$parameters}""".parseJson.asJsObject
         put(entityStore, provider)
-        Put(s"$collectionPath/${aname()}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskEntity.paramsFieldName, parameters.size, Parameters.sizeLimit))
@@ -488,11 +492,11 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         // it should "reject update package owned by different user" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Put(s"/$namespace/${collection.path}/${provider.name}?overwrite=true", content) ~> sealRoute(routes(auser)) ~> check {
+        Put(s"/$namespace/${collection.path}/${provider.name}?overwrite=true", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Put(s"$collectionPath/${provider.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${provider.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deletePackage(provider.docid)
             val response = responseAs[WhiskPackage]
             response should be(WhiskPackage(namespace, provider.name, None, version = provider.version.upPatch, publish = true))
@@ -511,11 +515,11 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         // it should "reject update package reference owned by different user"
         val auser = WhiskAuthHelpers.newIdentity()
-        Put(s"/$namespace/${collection.path}/${reference.name}?overwrite=true", content) ~> sealRoute(routes(auser)) ~> check {
+        Put(s"/$namespace/${collection.path}/${reference.name}?overwrite=true", content) ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Put(s"$collectionPath/${reference.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${reference.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deletePackage(reference.docid)
             status should be(OK)
             val response = responseAs[WhiskPackage]
@@ -533,7 +537,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val provider = WhiskPackage(namespace, aname())
         val content = WhiskPackagePut(provider.bind)
         put(entityStore, provider)
-        Put(s"$collectionPath/${provider.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${provider.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Conflict)
             responseAs[ErrorResponse].error should include(Messages.packageCannotBecomeBinding)
         }
@@ -545,7 +549,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val reference = WhiskPackage(namespace, aname(), provider.bind)
         val content = WhiskPackagePut(reference.binding)
         put(entityStore, reference)
-        Put(s"$collectionPath/${reference.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${reference.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error should include(Messages.bindingDoesNotExist)
         }
@@ -560,7 +564,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val reference = WhiskPackage(namespace, aname(), provider.bind)
         val content = WhiskPackagePut(reference.binding)
         put(entityStore, reference)
-        Put(s"$collectionPath/${reference.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${reference.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -575,7 +579,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val content = WhiskPackagePut(reference.binding)
         put(entityStore, provider)
         put(entityStore, reference)
-        Put(s"$collectionPath/${reference.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${reference.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -588,11 +592,11 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         // it should "reject deleting package owned by different user" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${provider.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${provider.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Delete(s"$collectionPath/${provider.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskPackage]
             response should be(provider)
@@ -607,11 +611,11 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         // it should "reject deleting package reference owned by different user" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${reference.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${reference.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
 
-        Delete(s"$collectionPath/${reference.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${reference.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskPackage]
             response should be(reference)
@@ -625,14 +629,14 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         put(entityStore, provider)
         put(entityStore, action)
         whisk.utils.retry {
-            Get(s"$collectionPath/${provider.name}") ~> sealRoute(routes(creds)) ~> check {
+            Get(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response.fields("actions").asInstanceOf[JsArray].elements.length should be(1)
             }
         }
 
-        Delete(s"$collectionPath/${provider.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${provider.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Conflict)
             val response = responseAs[ErrorResponse]
             response.error should include("Package not empty (contains 1 entity)")
@@ -645,7 +649,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         implicit val tid = transid()
         val provider = WhiskPackage(namespace, aname())
         put(entityStore, provider)
-        Get(s"$collectionPath/${provider.name}/bar") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${provider.name}/bar") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -658,7 +662,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
 
         put(entityStore, action)
 
-        Put(s"$collectionPath/${reference.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${reference.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(Conflict)
             responseAs[ErrorResponse].error should include(Messages.requestedBindingIsNotValid)
         }
@@ -669,7 +673,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val entity = BadEntity(namespace, aname())
         put(entityStore, entity)
 
-        Delete(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -680,7 +684,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         val entity = BadEntity(namespace, aname())
         put(entityStore, entity)
 
-        Get(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
         }
     }
@@ -691,7 +695,7 @@ class PackagesApiTests extends ControllerTestCommon with WhiskPackagesApi {
         put(entityStore, entity)
 
         val content = WhiskPackagePut()
-        Put(s"$collectionPath/${entity.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${entity.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
diff --git a/tests/src/test/scala/whisk/core/controller/test/RespondWithHeadersTests.scala b/tests/src/test/scala/whisk/core/controller/test/RespondWithHeadersTests.scala
index 48cbd6b..fc8ddb9 100644
--- a/tests/src/test/scala/whisk/core/controller/test/RespondWithHeadersTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/RespondWithHeadersTests.scala
@@ -20,10 +20,10 @@ package whisk.core.controller.test
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
 
-import spray.http.StatusCodes.NotFound
-import spray.http.StatusCodes.OK
-import spray.httpx.marshalling.ToResponseMarshallable.isMarshallable
-import spray.routing.Directive.pimpApply
+import akka.http.scaladsl.model.StatusCodes.NotFound
+import akka.http.scaladsl.model.StatusCodes.OK
+import akka.http.scaladsl.server.Route
+
 import whisk.core.controller.RespondWithHeaders
 
 /**
@@ -62,28 +62,28 @@ class RespondWithHeadersTests extends ControllerTestCommon with RespondWithHeade
     }
 
     it should "respond to options" in {
-        Options("/api/v1") ~> sealRoute(routes) ~> check {
+        Options("/api/v1") ~> Route.seal(routes) ~> check {
             headers should contain allOf (allowOrigin, allowHeaders)
         }
     }
 
     it should "respond to options on every route under /api/v1" in {
-        Options("/api/v1/one") ~> sealRoute(routes) ~> check {
+        Options("/api/v1/one") ~> Route.seal(routes) ~> check {
             headers should contain allOf (allowOrigin, allowHeaders)
         }
-        Options("/api/v1/two") ~> sealRoute(routes) ~> check {
+        Options("/api/v1/two") ~> Route.seal(routes) ~> check {
             headers should contain allOf (allowOrigin, allowHeaders)
         }
     }
 
     it should "respond to options even on bogus routes under /api/v1" in {
-        Options("/api/v1/bogus") ~> sealRoute(routes) ~> check {
+        Options("/api/v1/bogus") ~> Route.seal(routes) ~> check {
             headers should contain allOf (allowOrigin, allowHeaders)
         }
     }
 
     it should "not respond to options on routes before /api/v1" in {
-        Options("/api") ~> sealRoute(routes) ~> check {
+        Options("/api") ~> Route.seal(routes) ~> check {
             status shouldBe NotFound
         }
     }
diff --git a/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
index 6a2d93d..d224080 100644
--- a/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/RulesApiTests.scala
@@ -21,10 +21,14 @@ import scala.language.postfixOps
 
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
+
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.Route
+
 import spray.json.DefaultJsonProtocol._
 import spray.json._
+
 import whisk.core.controller.WhiskRulesApi
 import whisk.core.entity._
 import whisk.core.entity.test.OldWhiskTrigger
@@ -69,7 +73,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         }.toList
         rules foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskRule, namespace, 2)
-        Get(s"$collectionPath") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             rules.length should be(response.length)
@@ -77,7 +81,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         }
 
         // it should "list trirulesggers with explicit namespace owned by subject" in {
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             rules.length should be(response.length)
@@ -86,7 +90,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         // it should "reject list rules with explicit namespace not owned by subject" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -100,7 +104,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         }.toList
         rules foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskRule, namespace, 2)
-        Get(s"$collectionPath?docs=true") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?docs=true") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[WhiskRule]]
             rules.length should be(response.length)
@@ -114,14 +118,14 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         val rule = WhiskRule(namespace, aname(), afullname(namespace, "bogus trigger"), afullname(namespace, "bogus action"))
         put(entityStore, rule)
-        Get(s"$collectionPath/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskRuleResponse]
             response should be(rule.withStatus(Status.INACTIVE))
         }
 
         // it should "get trigger by name in explicit namespace owned by subject" in
-        Get(s"/$namespace/${collection.path}/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskRuleResponse]
             response should be(rule.withStatus(Status.INACTIVE))
@@ -129,7 +133,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         // it should "reject get trigger by name in explicit namespace not owned by subject" in
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${rule.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${rule.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -137,7 +141,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
     it should "reject get of non existent rule" in {
         implicit val tid = transid()
 
-        Get(s"$collectionPath/xxx") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/xxx") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -153,7 +157,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger)
         put(entityStore, rule)
 
-        Get(s"$collectionPath/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskRuleResponse]
             response should be(rule.withStatus(Status.ACTIVE))
@@ -169,7 +173,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger)
         put(entityStore, rule)
 
-        Get(s"$collectionPath/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskRuleResponse]
             response should be(rule.withStatus(Status.INACTIVE))
@@ -183,7 +187,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, trigger)
 
-        Get(s"/$namespace/${collection.path}/${trigger.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}/${trigger.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Conflict)
         }
     }
@@ -200,7 +204,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger)
         put(entityStore, rule)
 
-        Delete(s"$collectionPath/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             deleteTrigger(trigger.docid)
 
             status should be(OK)
@@ -219,7 +223,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, rule)
 
-        Delete(s"$collectionPath/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
 
@@ -237,7 +241,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, rule)
 
-        Delete(s"$collectionPath/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskRuleResponse]
             response should be(rule.withStatus(Status.INACTIVE))
@@ -253,7 +257,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, rule)
 
-        Delete(s"$collectionPath/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             deleteTrigger(trigger.docid)
 
             status should be(OK)
@@ -274,7 +278,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, action)
 
-        Put(s"$collectionPath/${rule.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -297,7 +301,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, action)
 
-        Put(s"$collectionPath/${rule.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -318,12 +322,12 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         val contentT = JsObject("trigger" -> trigger.name.toJson, "action" -> action.fullyQualifiedName(false).toDocId.toJson)
         val contentA = JsObject("action" -> action.name.toJson, "trigger" -> trigger.fullyQualifiedName(false).toDocId.toJson)
 
-        Put(s"$collectionPath/${rule.name}", contentT) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}", contentT) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] shouldBe s"The request content was malformed:\nrequirement failed: ${Messages.malformedFullyQualifiedEntityName}"
         }
 
-        Put(s"$collectionPath/${rule.name}", contentA) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}", contentA) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] shouldBe s"The request content was malformed:\nrequirement failed: ${Messages.malformedFullyQualifiedEntityName}"
         }
@@ -342,7 +346,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, action)
 
-        Put(s"$collectionPath/${rule.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -370,7 +374,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, action)
 
-        Put(s"$collectionPath/${rule.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -397,7 +401,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, action)
 
-        Put(s"$collectionPath/${aname()}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
 
@@ -425,7 +429,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, action)
         put(entityStore, rule)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
 
@@ -444,7 +448,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, trigger)
 
-        Put(s"$collectionPath/xxx", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/xxx", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] === s"${content.action.get.qualifiedNameWithLeadingSlash} does not exist"
         }
@@ -458,7 +462,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, action)
 
-        Put(s"$collectionPath/xxx", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/xxx", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] === s"${content.trigger.get.qualifiedNameWithLeadingSlash} does not exist"
         }
@@ -469,7 +473,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         val content = WhiskRulePut(Some(afullname(namespace, "bogus trigger")), Some(afullname(namespace, "bogus action")))
 
-        Put(s"$collectionPath/xxx", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/xxx", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String].contains("does not exist") should be(true)
         }
@@ -487,7 +491,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, action)
         put(entityStore, rule, false)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -512,7 +516,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, action)
         put(entityStore, rule, false)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -536,7 +540,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, action)
         put(entityStore, rule, false)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -560,7 +564,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, action)
         put(entityStore, rule, false)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -583,7 +587,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, action)
         put(entityStore, rule, false)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteTrigger(trigger.docid)
             deleteRule(rule.docid)
 
@@ -603,7 +607,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, action)
         put(entityStore, rule)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] === s"${rule.trigger.qualifiedNameWithLeadingSlash} does not exist"
         }
@@ -619,7 +623,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger)
         put(entityStore, rule)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] === s"${rule.action.qualifiedNameWithLeadingSlash} does not exist"
         }
@@ -632,7 +636,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, rule)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[String] should {
                 include(s"${rule.action.qualifiedNameWithLeadingSlash} does not exist") or
@@ -655,7 +659,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, action)
         put(entityStore, rule, false)
 
-        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -675,7 +679,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, rule)
 
-        Post(s"$collectionPath/${rule.name}", inactiveStatus) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${rule.name}", inactiveStatus) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
         }
     }
@@ -691,7 +695,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger)
         put(entityStore, rule)
 
-        Post(s"$collectionPath/${rule.name}", activeStatus) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${rule.name}", activeStatus) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
         }
     }
@@ -699,7 +703,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
     it should "reject post with status undefined" in {
         implicit val tid = transid()
 
-        Post(s"$collectionPath/xyz") ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/xyz") ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -709,7 +713,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         val badStatus = s"""{"status":"xxx"}""".parseJson.asJsObject
 
-        Post(s"$collectionPath/xyz", badStatus) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/xyz", badStatus) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -725,7 +729,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, rule)
 
-        Post(s"$collectionPath/${rule.name}", activeStatus) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${rule.name}", activeStatus) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
 
@@ -744,7 +748,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, rule)
 
-        Post(s"$collectionPath/${rule.name}", activeStatus) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${rule.name}", activeStatus) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
 
@@ -760,7 +764,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, rule)
 
-        Post(s"$collectionPath/${rule.name}", activeStatus) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${rule.name}", activeStatus) ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -776,7 +780,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, rule)
 
-        Post(s"$collectionPath/${rule.name}", inactiveStatus) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${rule.name}", inactiveStatus) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
 
@@ -793,7 +797,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, rule)
 
-        Get(s"$collectionPath/${rule.name}/bar") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${rule.name}/bar") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -806,7 +810,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
 
         put(entityStore, rule)
 
-        Get(s"$collectionPath/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskRuleResponse]
             response should be(rule.toWhiskRule.withStatus(Status.INACTIVE))
@@ -825,7 +829,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, action)
 
-        Put(s"$collectionPath/${rule.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${rule.name}", content) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
             deleteRule(rule.docid)
@@ -849,7 +853,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger, false)
         put(entityStore, rule)
 
-        Post(s"$collectionPath/${rule.name}", activeStatus) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${rule.name}", activeStatus) ~> Route.seal(routes(creds)) ~> check {
             val t = get(entityStore, trigger.docid, WhiskTrigger)
             deleteTrigger(t.docid)
 
@@ -864,7 +868,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         val entity = BadEntity(namespace, aname())
         put(entityStore, entity)
 
-        Delete(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -875,7 +879,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         val entity = BadEntity(namespace, aname())
         put(entityStore, entity)
 
-        Get(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -887,7 +891,7 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, entity)
 
         val content = WhiskRulePut()
-        Put(s"$collectionPath/${entity.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${entity.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -910,17 +914,17 @@ class RulesApiTests extends ControllerTestCommon with WhiskRulesApi {
         put(entityStore, trigger)
         put(entityStore, action)
 
-        Put(s"$collectionPath/${aname()}", contenta) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", contenta) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
 
-        Put(s"$collectionPath/${aname()}", contentb) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", contentb) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
 
-        Put(s"$collectionPath/${aname()}", contentc) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", contentc) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
diff --git a/tests/src/test/scala/whisk/core/controller/test/SequenceApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/SequenceApiTests.scala
index fccc86f..689dd9a 100644
--- a/tests/src/test/scala/whisk/core/controller/test/SequenceApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/SequenceApiTests.scala
@@ -23,10 +23,13 @@ import scala.language.postfixOps
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
 
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.Route
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
+
 import whisk.common.TransactionId
 import whisk.core.controller.WhiskActionsApi
 import whisk.core.entity._
@@ -62,7 +65,7 @@ class SequenceApiTests
         val content = WhiskActionPut(Some(sequence(components.toVector)))
 
         // create an action sequence
-        Put(s"$collectionPath/${seqName.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${seqName.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceIsTooLong
         }
@@ -75,7 +78,7 @@ class SequenceApiTests
         val content = WhiskActionPut(Some(sequence(Vector())))
 
         // create an action sequence
-        Put(s"$collectionPath/$seqName", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$seqName", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceNoComponent
         }
@@ -91,7 +94,7 @@ class SequenceApiTests
         val updateContent = WhiskActionPut(Some(sequence(Vector())))
 
         // create an action sequence
-        Put(s"$collectionPath/$seqName?overwrite=true", updateContent) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$seqName?overwrite=true", updateContent) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceNoComponent
         }
@@ -106,7 +109,7 @@ class SequenceApiTests
         val content = WhiskActionPut(Some(sequence(components)))
 
         // create an action sequence
-        Put(s"$collectionPath/${seqName.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${seqName.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceComponentNotFound
         }
@@ -119,7 +122,7 @@ class SequenceApiTests
 
         // create an action sequence
         val content = WhiskActionPut(Some(sSeq.exec))
-        Put(s"$collectionPath/$seqName", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$seqName", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceIsCyclic
         }
@@ -137,7 +140,7 @@ class SequenceApiTests
 
         // create an action sequence
         val content = WhiskActionPut(Some(sSeq.exec))
-        Put(s"$collectionPath/$seqName", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$seqName", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceIsCyclic
         }
@@ -154,7 +157,7 @@ class SequenceApiTests
         val content = WhiskActionPut(Some(sequence(components.toVector)))
 
         // create a valid action sequence first
-        Put(s"$collectionPath/${seqName.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${seqName.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
         }
 
@@ -164,7 +167,7 @@ class SequenceApiTests
         val updatedContent = WhiskActionPut(Some(sequence(updatedSeq.toVector)))
 
         // update the sequence
-        Put(s"$collectionPath/${seqName.name}?overwrite=true", updatedContent) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${seqName.name}?overwrite=true", updatedContent) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceIsCyclic
         }
@@ -182,7 +185,7 @@ class SequenceApiTests
         val content = WhiskActionPut(Some(sequence(components.toVector)))
 
         // create an action sequence
-        Put(s"$collectionPath/${seqName.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${seqName.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
         }
     }
@@ -212,7 +215,7 @@ class SequenceApiTests
         val content = WhiskActionPut(Some(sequence(components)))
 
         // create an action sequence
-        Put(s"$collectionPath/${seqName.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${seqName.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[String]
         }
@@ -235,7 +238,7 @@ class SequenceApiTests
         // create an action sequence
         val namespaceWithPkg = EntityPath(s"/$namespace/$pkg")
         val content = WhiskActionPut(Some(sequence(components.toVector)))
-        Put(s"$collectionPath/$pkg/${seqName.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$pkg/${seqName.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
         }
 
@@ -250,7 +253,7 @@ class SequenceApiTests
         val updatedContent = WhiskActionPut(Some(sequence(updatedSeq.toVector)))
 
         // update the sequence
-        Put(s"$collectionPath/$pkg/${seqName.name}?overwrite=true", updatedContent) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$pkg/${seqName.name}?overwrite=true", updatedContent) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceIsCyclic
         }
@@ -260,7 +263,7 @@ class SequenceApiTests
         implicit val tid = transid()
         val content = JsObject("exec" -> JsObject("kind" -> Exec.SEQUENCE.toJson, "components" -> Vector("a", "b").toJson))
 
-        Put(s"$collectionPath/${aname()}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             // the content will fail to deserialize on the route directive,
             // and without a custom rejection, the response will be a string
@@ -273,7 +276,7 @@ class SequenceApiTests
         val content = JsObject("exec" -> JsObject("kind" -> Exec.SEQUENCE.toJson, "components" -> Vector("a", "b").toJson))
 
         // update an action sequence
-        Put(s"$collectionPath/${aname()}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname()}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             // the content will fail to deserialize on the route directive,
             // and without a custom rejection, the response will be a string
@@ -296,7 +299,7 @@ class SequenceApiTests
         val content = WhiskActionPut(Some(sSeq.exec))
 
         Console.withOut(stream) {
-            Put(s"$collectionPath/$sSeqName", content) ~> sealRoute(routes(creds)) ~> check {
+            Put(s"$collectionPath/$sSeqName", content) ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 logContains(s"atomic action count ${2 * actionCnt}")(stream)
             }
@@ -337,7 +340,7 @@ class SequenceApiTests
 
         stream.reset()
         Console.withOut(stream) {
-            Put(s"$collectionPath/$sAct", content) ~> sealRoute(routes(creds)) ~> check {
+            Put(s"$collectionPath/$sAct", content) ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
             }
             logContains("atomic action count 4")(stream)
@@ -346,7 +349,7 @@ class SequenceApiTests
         // update action z to point to s --- should be rejected
         val zUpdate = makeSimpleSequence(zAct, namespace, Vector(s"$sAct"), false) // s in the db already
         val zUpdateContent = WhiskActionPut(Some(zUpdate.exec))
-        Put(s"$collectionPath/$zAct?overwrite=true", zUpdateContent) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$zAct?overwrite=true", zUpdateContent) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceIsCyclic
         }
@@ -354,7 +357,7 @@ class SequenceApiTests
         // update action s to point to a, s, b --- should be rejected
         val sUpdate = makeSimpleSequence(sAct, namespace, Vector(s"$aAct", s"$sAct", s"$bAct"), false) // s in the db already
         val sUpdateContent = WhiskActionPut(Some(sUpdate.exec))
-        Put(s"$collectionPath/$sAct?overwrite=true", sUpdateContent) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/$sAct?overwrite=true", sUpdateContent) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
             responseAs[ErrorResponse].error shouldBe Messages.sequenceIsCyclic
         }
@@ -364,7 +367,7 @@ class SequenceApiTests
         val updateContent = WhiskActionPut(Some(zSeq.exec))
         stream.reset()
         Console.withOut(stream) {
-            Put(s"$collectionPath/$zAct?overwrite=true", updateContent) ~> sealRoute(routes(creds)) ~> check {
+            Put(s"$collectionPath/$zAct?overwrite=true", updateContent) ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
             }
             logContains("atomic action count 1")(stream)
@@ -374,7 +377,7 @@ class SequenceApiTests
         val newSContent = WhiskActionPut(Some(newS.exec))
         stream.reset()
         Console.withOut(stream) {
-            Put(s"${collectionPath}/$sAct?overwrite=true", newSContent) ~> sealRoute(routes(creds)) ~> check {
+            Put(s"${collectionPath}/$sAct?overwrite=true", newSContent) ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
             }
             logContains("atomic action count 6")(stream)
diff --git a/tests/src/test/scala/whisk/core/controller/test/SwaggerRoutesTests.scala b/tests/src/test/scala/whisk/core/controller/test/SwaggerRoutesTests.scala
index 1eb341b..b63a818 100644
--- a/tests/src/test/scala/whisk/core/controller/test/SwaggerRoutesTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/SwaggerRoutesTests.scala
@@ -21,11 +21,15 @@ import org.junit.runner.RunWith
 import org.scalatest.BeforeAndAfterEach
 import org.scalatest.junit.JUnitRunner
 
-import spray.http.StatusCodes._
-import spray.http.Uri
-import spray.httpx.SprayJsonSupport.sprayJsonUnmarshaller
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+//import akka.http.scaladsl.model.Uri.Query
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.model.Uri
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
+
 import whisk.core.controller.SwaggerDocs
 
 /**
@@ -44,12 +48,12 @@ class SwaggerRoutesTests extends ControllerTestCommon with BeforeAndAfterEach {
     it should "server docs" in {
         implicit val tid = transid()
         val swagger = new SwaggerDocs(Uri.Path.Empty, "infoswagger.json")
-        Get("/docs") ~> sealRoute(swagger.swaggerRoutes) ~> check {
+        Get("/docs") ~> Route.seal(swagger.swaggerRoutes) ~> check {
             status shouldBe PermanentRedirect
             header("location").get.value shouldBe "docs/index.html?url=/api-docs"
         }
 
-        Get("/api-docs") ~> sealRoute(swagger.swaggerRoutes) ~> check {
+        Get("/api-docs") ~> Route.seal(swagger.swaggerRoutes) ~> check {
             status shouldBe OK
             responseAs[JsObject].fields("swagger") shouldBe JsString("2.0")
         }
diff --git a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
index 5501f0d..26195ba 100644
--- a/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/TriggersApiTests.scala
@@ -24,10 +24,14 @@ import scala.language.postfixOps
 import org.junit.runner.RunWith
 import org.scalatest.junit.JUnitRunner
 
-import spray.http.StatusCodes._
-import spray.httpx.SprayJsonSupport._
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.model.StatusCodes._
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
+
 import whisk.core.controller.WhiskTriggersApi
 import whisk.core.entity._
 import whisk.core.entity.WhiskRule
@@ -68,7 +72,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         }.toList
         triggers foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskTrigger, namespace, 2)
-        Get(s"$collectionPath") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             triggers.length should be(response.length)
@@ -76,7 +80,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         }
 
         // it should "list triggers with explicit namespace owned by subject" in {
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
             triggers.length should be(response.length)
@@ -85,7 +89,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
 
         // it should "reject list triggers with explicit namespace not owned by subject" in {
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -98,7 +102,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         }.toList
         triggers foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskTrigger, namespace, 2)
-        Get(s"$collectionPath?docs=true") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath?docs=true") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[WhiskTrigger]]
             triggers.length should be(response.length)
@@ -111,14 +115,14 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname, Parameters("x", "b"))
         put(entityStore, trigger)
-        Get(s"$collectionPath/${trigger.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${trigger.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskTrigger]
             response should be(trigger.withoutRules)
         }
 
         // it should "get trigger by name in explicit namespace owned by subject" in
-        Get(s"/$namespace/${collection.path}/${trigger.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}/${trigger.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskTrigger]
             response should be(trigger.withoutRules)
@@ -126,7 +130,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
 
         // it should "reject get trigger by name in explicit namespace not owned by subject" in
         val auser = WhiskAuthHelpers.newIdentity()
-        Get(s"/$namespace/${collection.path}/${trigger.name}") ~> sealRoute(routes(auser)) ~> check {
+        Get(s"/$namespace/${collection.path}/${trigger.name}") ~> Route.seal(routes(auser)) ~> check {
             status should be(Forbidden)
         }
     }
@@ -135,7 +139,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val rule = WhiskRule(namespace, aname, FullyQualifiedEntityName(namespace, aname), FullyQualifiedEntityName(namespace, aname))
         put(entityStore, rule)
-        Get(s"/$namespace/${collection.path}/${rule.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}/${rule.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(Conflict)
         }
     }
@@ -145,7 +149,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname, Parameters("x", "b"))
         put(entityStore, trigger)
-        Delete(s"$collectionPath/${trigger.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${trigger.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskTrigger]
             response should be(trigger.withoutRules)
@@ -157,7 +161,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname)
         val content = WhiskTriggerPut()
-        Put(s"$collectionPath/${trigger.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${trigger.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deleteTrigger(trigger.docid)
             status should be(OK)
             val response = responseAs[WhiskTrigger]
@@ -169,7 +173,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname, annotations = Parameters(Parameters.Feed, "xyz"))
         val content = WhiskTriggerPut(annotations = Some(trigger.annotations))
-        Put(s"$collectionPath/${trigger.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${trigger.name}", content) ~> Route.seal(routes(creds)) ~> check {
             deleteTrigger(trigger.docid)
             status should be(OK)
             val response = responseAs[WhiskTrigger]
@@ -181,7 +185,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname, annotations = Parameters(Parameters.Feed, ""))
         val content = WhiskTriggerPut(annotations = Some(trigger.annotations))
-        Put(s"$collectionPath/${trigger.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${trigger.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -190,7 +194,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname, annotations = Parameters(Parameters.Feed, "a,b"))
         val content = WhiskTriggerPut(annotations = Some(trigger.annotations))
-        Put(s"$collectionPath/${trigger.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${trigger.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -199,10 +203,10 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val code = "a" * (allowedActivationEntitySize.toInt + 1)
         val content = s"""{"a":"$code"}""".stripMargin
-        Post(s"$collectionPath/${aname}", content.parseJson.asJsObject) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${aname}", content.parseJson.asJsObject) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
-                Messages.entityTooBig(SizeError(fieldDescriptionForSizeError, (content.length + 5).B, allowedActivationEntitySize.B))
+                Messages.entityTooBig(SizeError(fieldDescriptionForSizeError, (content.length).B, allowedActivationEntitySize.B))
             }
         }
     }
@@ -214,7 +218,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
             Parameters(key.toString, "a" * 10)
         } reduce (_ ++ _)
         val content = s"""{"parameters":$parameters}""".parseJson.asJsObject
-        Put(s"$collectionPath/${aname}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskEntity.paramsFieldName, parameters.size, Parameters.sizeLimit))
@@ -229,7 +233,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
             Parameters(key.toString, "a" * 10)
         } reduce (_ ++ _)
         val content = s"""{"annotations":$annotations}""".parseJson.asJsObject
-        Put(s"$collectionPath/${aname}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${aname}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskEntity.annotationsFieldName, annotations.size, Parameters.sizeLimit))
@@ -246,7 +250,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         } reduce (_ ++ _)
         val content = s"""{"parameters":$parameters}""".parseJson.asJsObject
         put(entityStore, trigger)
-        Put(s"$collectionPath/${trigger.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${trigger.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(RequestEntityTooLarge)
             responseAs[String] should include {
                 Messages.entityTooBig(SizeError(WhiskEntity.paramsFieldName, parameters.size, Parameters.sizeLimit))
@@ -259,7 +263,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         val trigger = WhiskTrigger(namespace, aname, Parameters("x", "b"))
         val content = WhiskTriggerPut()
         put(entityStore, trigger)
-        Put(s"$collectionPath/${trigger.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${trigger.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteTrigger(trigger.docid)
             status should be(OK)
             val response = responseAs[WhiskTrigger]
@@ -272,7 +276,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         val trigger = WhiskTrigger(namespace, aname, annotations = Parameters(Parameters.Feed, "xyz"))
         val content = WhiskTriggerPut(annotations = Some(trigger.annotations))
         put(entityStore, trigger)
-        Put(s"$collectionPath/${trigger.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${trigger.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -282,7 +286,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         val trigger = WhiskTrigger(namespace, aname)
         val content = WhiskTriggerPut(annotations = Some(Parameters(Parameters.Feed, "xyz")))
         put(entityStore, trigger)
-        Put(s"$collectionPath/${trigger.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${trigger.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(BadRequest)
         }
     }
@@ -293,7 +297,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         val trigger = WhiskTrigger(namespace, aname, Parameters("x", "b"))
         val content = JsObject("xxx" -> "yyy".toJson)
         put(entityStore, trigger)
-        Post(s"$collectionPath/${trigger.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${trigger.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[JsObject]
             val JsString(id) = response.fields("activationId")
@@ -312,7 +316,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname, Parameters("x", "b"))
         put(entityStore, trigger)
-        Post(s"$collectionPath/${trigger.name}") ~> sealRoute(routes(creds)) ~> check {
+        Post(s"$collectionPath/${trigger.name}") ~> Route.seal(routes(creds)) ~> check {
             val response = responseAs[JsObject]
             val JsString(id) = response.fields("activationId")
             val activationId = ActivationId(id)
@@ -326,7 +330,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = WhiskTrigger(namespace, aname)
         put(entityStore, trigger)
-        Get(s"$collectionPath/${trigger.name}/bar") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${trigger.name}/bar") ~> Route.seal(routes(creds)) ~> check {
             status should be(NotFound)
         }
     }
@@ -336,7 +340,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         implicit val tid = transid()
         val trigger = OldWhiskTrigger(namespace, aname)
         put(entityStore, trigger)
-        Get(s"$collectionPath/${trigger.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${trigger.name}") ~> Route.seal(routes(creds)) ~> check {
             val response = responseAs[WhiskTrigger]
             status should be(OK)
 
@@ -349,7 +353,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         val entity = BadEntity(namespace, aname)
         put(entityStore, entity)
 
-        Delete(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Delete(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -360,7 +364,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         val entity = BadEntity(namespace, aname)
         put(entityStore, entity)
 
-        Get(s"$collectionPath/${entity.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${entity.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
@@ -372,7 +376,7 @@ class TriggersApiTests extends ControllerTestCommon with WhiskTriggersApi {
         put(entityStore, entity)
 
         val content = WhiskTriggerPut()
-        Put(s"$collectionPath/${entity.name}", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${entity.name}", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(InternalServerError)
             responseAs[ErrorResponse].error shouldBe Messages.corruptedEntity
         }
diff --git a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
index 3a7722a..d0b4f0a 100644
--- a/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/WebActionsApiTests.scala
@@ -29,19 +29,22 @@ import org.scalatest.junit.JUnitRunner
 import org.scalatest.Matchers
 import org.scalatest.FlatSpec
 
-import spray.http.FormData
-import spray.http.HttpEntity
-import spray.http.HttpMethods
-import spray.http.MediaTypes
-import spray.http.StatusCodes._
-import spray.http.HttpCharsets
-import spray.http.HttpHeader
-import spray.http.HttpHeaders
-import spray.http.HttpResponse
-import spray.http.Uri.Query
-import spray.httpx.SprayJsonSupport._
-import spray.httpx.SprayJsonSupport.sprayJsonMarshaller
-import spray.httpx.SprayJsonSupport.sprayJsonUnmarshaller
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+
+import akka.http.scaladsl.model.FormData
+import akka.http.scaladsl.model.HttpEntity
+import akka.http.scaladsl.model.MediaTypes
+import akka.http.scaladsl.model.StatusCodes._
+import akka.http.scaladsl.model.HttpCharsets
+import akka.http.scaladsl.model.HttpHeader
+import akka.http.scaladsl.model.HttpResponse
+import akka.http.scaladsl.model.Uri.Query
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.model.HttpMethods
+import akka.http.scaladsl.model.headers.`Content-Type`
+import akka.http.scaladsl.model.ContentTypes
+import akka.http.scaladsl.model.headers.RawHeader
+
 import spray.json._
 import spray.json.DefaultJsonProtocol._
 
@@ -178,12 +181,16 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
         assert(invocationsAllowed == invocationCount, "allowed invoke count did not match actual")
     }
 
-    val allowedMethods = {
-        val nonModifierMethods = Seq(Get, Options, Head)
+    val allowedMethodsWithEntity = {
+        val nonModifierMethods = Seq(Get, Options)
         val modifierMethods = Seq(Post, Put, Delete, Patch)
         modifierMethods ++ nonModifierMethods
     }
 
+    val allowedMethods = {
+        allowedMethodsWithEntity ++ Seq(Head)
+    }
+
     // there is only one package that is predefined 'proxy'
     val packages = Seq(
         WhiskPackage(
@@ -349,7 +356,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             Seq("a", "a/b", "/a", s"$systemId/c", s"$systemId/export_c").
                 foreach { path =>
                     allowedMethods.foreach { m =>
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(NotFound)
                         }
                     }
@@ -366,7 +373,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             Seq((???, MethodNotAllowed)).
                 foreach {
                     case (m, code) =>
-                        m(s"$testRoutePath/$systemId/proxy/export_c.json") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$systemId/proxy/export_c.json") ~> Route.seal(routes(creds)) ~> check {
                             status should be(code)
                         }
                 }
@@ -386,11 +393,11 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     allowedMethods.foreach { m =>
                         failActionLookup = path.endsWith("fail")
 
-                        m(s"$testRoutePath/${path}.json") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/${path}.json") ~> Route.seal(routes(creds)) ~> check {
                             status should be(NotFound)
                         }
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             if (webApiDirectives.enforceExtension) {
                                 status should be(NotAcceptable)
                                 confirmErrorWithTid(responseAs[JsObject], Some(Messages.contentTypeExtensionNotSupported(WhiskWebActionsApi.allowedExtensions)))
@@ -412,7 +419,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             invocationsAllowed += 1
                         requireAuthentication = true
 
-                        m(s"$testRoutePath/${path}.json") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/${path}.json") ~> Route.seal(routes(creds)) ~> check {
                             creds match {
                                 case None => status should be(Unauthorized)
                                 case Some(user) =>
@@ -436,7 +443,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             allowedMethods.foreach { m =>
                 invocationsAllowed += 1
 
-                m(s"$testRoutePath/$systemId/proxy/export_c.json") ~> sealRoute(routes(creds)) ~> check {
+                m(s"$testRoutePath/$systemId/proxy/export_c.json") ~> Route.seal(routes(creds)) ~> check {
                     status should be(Accepted)
                     val response = responseAs[JsObject]
                     confirmErrorWithTid(response, Some("Response not yet ready."))
@@ -451,7 +458,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             allowedMethods.foreach { m =>
                 invocationsAllowed += 1
 
-                m(s"$testRoutePath/$systemId/proxy/export_c.json") ~> sealRoute(routes(creds)) ~> check {
+                m(s"$testRoutePath/$systemId/proxy/export_c.json") ~> Route.seal(routes(creds)) ~> check {
                     status should be(InternalServerError)
                     val response = responseAs[JsObject]
                     confirmErrorWithTid(response)
@@ -467,7 +474,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     allowedMethods.foreach { m =>
                         invocationsAllowed += 1
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             val response = responseAs[JsObject]
                             response shouldBe JsObject(
@@ -489,11 +496,11 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             // both of these should produce full result objects (trailing slash is ok)
             Seq(s"$systemId/proxy/export_c.json", s"$systemId/proxy/export_c.json/").
                 foreach { path =>
-                    allowedMethods.foreach { m =>
+                    allowedMethodsWithEntity.foreach { m =>
                         val content = JsObject("extra" -> "read all about it".toJson, "yummy" -> true.toJson)
                         val p = if (path.endsWith("/")) "/" else ""
                         invocationsAllowed += 1
-                        m(s"$testRoutePath/$path", content) ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path", content) ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             val response = responseAs[JsObject]
                             response shouldBe JsObject(
@@ -516,11 +523,11 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
 
             Seq(s"$systemId/proxy/export_c.json?a=b&c=d").
                 foreach { path =>
-                    allowedMethods.foreach { m =>
+                    allowedMethodsWithEntity.foreach { m =>
                         val content = JsObject("extra" -> "read all about it".toJson, "yummy" -> true.toJson)
                         invocationsAllowed += 1
 
-                        m(s"$testRoutePath/$path", content) ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path", content) ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             val response = responseAs[JsObject]
                             response shouldBe JsObject(
@@ -545,7 +552,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     allowedMethods.foreach { m =>
                         invocationsAllowed += 1
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             val response = responseAs[JsObject]
                             response shouldBe JsObject(
@@ -565,7 +572,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     allowedMethods.foreach { m =>
                         invocationsAllowed += 1
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             val response = responseAs[JsObject]
                             response shouldBe metaPayload(m.method.name.toLowerCase, JsObject(), creds, path = "/content", pkgName = "proxy")
@@ -578,7 +585,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     allowedMethods.foreach { m =>
                         invocationsAllowed += 1
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             val response = responseAs[String]
                             response shouldBe "Z"
@@ -597,7 +604,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject("a" -> JsString("b")))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(BadRequest)
                             confirmErrorWithTid(responseAs[JsObject], Some(Messages.invalidMedia(MediaTypes.`application/json`)))
                         }
@@ -613,7 +620,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     allowedMethods.foreach { m =>
                         invocationsAllowed += 1
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(NotFound)
                             confirmErrorWithTid(responseAs[JsObject], Some(Messages.propertyNotFound))
                         }
@@ -631,7 +638,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         actionResult = Some(JsObject("headers" -> JsObject("location" -> "http://openwhisk.org".toJson), webApiDirectives.statusCode -> Found.intValue.toJson))
                         invocationsAllowed += 1
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(Found)
                             header("location").get.toString shouldBe "location: http://openwhisk.org"
                         }
@@ -648,7 +655,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject("headers" -> JsObject("location" -> "http://openwhisk.org".toJson), webApiDirectives.statusCode -> Found.intValue.toJson))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(Found)
                             header("location").get.toString shouldBe "location: http://openwhisk.org"
                         }
@@ -662,7 +669,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject("text" -> JsString(text)))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             contentType shouldBe MediaTypes.`text/plain`.withCharset(HttpCharsets.`UTF-8`)
                             val response = responseAs[String]
@@ -677,7 +684,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject("foobar" -> JsString("foobar")))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             val response = responseAs[JsObject]
                             response shouldBe actionResult.get
@@ -692,7 +699,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject("html" -> JsString(html)))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             contentType shouldBe MediaTypes.`text/html`.withCharset(HttpCharsets.`UTF-8`)
                             val response = responseAs[String]
@@ -708,9 +715,9 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject("svg" -> JsString(svg)))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
-                            contentType shouldBe MediaTypes.`image/svg+xml`.withCharset(HttpCharsets.`UTF-8`)
+                            //contentType shouldBe MediaTypes.`image/svg+xml`.withCharset(HttpCharsets.`UTF-8`)
                             val response = responseAs[String]
                             response shouldBe svg
                         }
@@ -727,7 +734,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject())
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             response.entity shouldBe HttpEntity.Empty
                             withClue(headers) {
@@ -748,7 +755,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject("res" -> jsval))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             responseAs[String] shouldBe {
                                 jsval match {
                                     case _: JsObject  => jsval.prettyPrint
@@ -779,7 +786,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                                 JsObject("field" -> "value".toJson).compactPrint.getBytes
                             }.toJson))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             header("content-type").get.toString shouldBe "content-type: application/json"
                             responseAs[JsObject] shouldBe JsObject("field" -> "value".toJson)
@@ -799,7 +806,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             webApiDirectives.statusCode -> OK.intValue.toJson,
                             "body" -> "hello world".toJson))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             responseAs[String] shouldBe "hello world"
                         }
@@ -820,7 +827,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             webApiDirectives.statusCode -> OK.intValue.toJson,
                             "body" -> "hello world".toJson))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(BadRequest)
                             confirmErrorWithTid(responseAs[JsObject], Some(Messages.httpContentTypeError))
                         }
@@ -841,7 +848,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             webApiDirectives.statusCode -> OK.intValue.toJson,
                             "body" -> "hello world".toJson))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(BadRequest)
                             confirmErrorWithTid(responseAs[JsObject], Some(Messages.httpUnknownContentType))
                         }
@@ -861,7 +868,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                                 webApiDirectives.statusCode -> OK.intValue.toJson,
                                 "body" -> "no hello for you".toJson)))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(OK)
                             responseAs[String] shouldBe "no hello for you"
                         }
@@ -878,7 +885,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         invocationsAllowed += 1
                         actionResult = Some(JsObject("application_error" -> "bad response type".toJson))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(BadRequest)
                             confirmErrorWithTid(responseAs[JsObject], Some(Messages.invalidMedia(MediaTypes.`application/json`)))
                         }
@@ -896,7 +903,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             invocationsAllowed += 1
                             actionResult = Some(JsObject(e -> "bad response type".toJson))
 
-                            m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                            m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                                 status should be(BadRequest)
                                 if (e == "application_error") {
                                     confirmErrorWithTid(responseAs[JsObject], Some(Messages.invalidMedia(MediaTypes.`application/json`)))
@@ -914,10 +921,11 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
 
             Seq(s"$systemId/proxy/export_c.text/content/field1", s"$systemId/proxy/export_c.text/content/field2").
                 foreach { path =>
-                    val form = FormData(Seq("field1" -> "value1", "field2" -> "value2"))
+
+                    val form = FormData(Map("field1" -> "value1", "field2" -> "value2"))
                     invocationsAllowed += 1
 
-                    Post(s"$testRoutePath/$path", form) ~> sealRoute(routes(creds)) ~> check {
+                    Post(s"$testRoutePath/$path", form) ~> Route.seal(routes(creds)) ~> check {
                         status should be(OK)
                         responseAs[String] should (be("value1") or be("value2"))
                     }
@@ -932,17 +940,17 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     val largeEntity = "a" * (allowedActivationEntitySize.toInt + 1)
 
                     val content = s"""{"a":"$largeEntity"}"""
-                    Post(s"$testRoutePath/$path", content.parseJson.asJsObject) ~> sealRoute(routes(creds)) ~> check {
+                    Post(s"$testRoutePath/$path", content.parseJson.asJsObject) ~> Route.seal(routes(creds)) ~> check {
                         status should be(RequestEntityTooLarge)
                         val expectedErrorMsg = Messages.entityTooBig(SizeError(
                             fieldDescriptionForSizeError,
-                            (largeEntity.length + 13).B,
+                            (largeEntity.length + 8).B,
                             allowedActivationEntitySize.B))
                         confirmErrorWithTid(responseAs[JsObject], Some(expectedErrorMsg))
                     }
 
-                    val form = FormData(Seq("a" -> largeEntity))
-                    Post(s"$testRoutePath/$path", form) ~> sealRoute(routes(creds)) ~> check {
+                    val form = FormData(Map("a" -> largeEntity))
+                    Post(s"$testRoutePath/$path", form) ~> Route.seal(routes(creds)) ~> check {
                         status should be(RequestEntityTooLarge)
                         val expectedErrorMsg = Messages.entityTooBig(SizeError(
                             fieldDescriptionForSizeError,
@@ -962,7 +970,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     allowedMethods.foreach { m =>
                         actionResult = Some(JsObject("statusCode" -> 201.toJson))
 
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             if (webApiDirectives.enforceExtension) {
                                 status should be(NotAcceptable)
                                 confirmErrorWithTid(responseAs[JsObject], Some(Messages.contentTypeExtensionNotSupported(WhiskWebActionsApi.allowedExtensions)))
@@ -978,14 +986,14 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
         it should s"reject request that tries to override reserved properties (auth? ${creds.isDefined})" in {
             implicit val tid = transid()
 
-            allowedMethods.foreach { m =>
+            allowedMethodsWithEntity.foreach { m =>
                 webApiDirectives.reservedProperties.foreach { p =>
-                    m(s"$testRoutePath/$systemId/proxy/export_c.json?$p=YYY") ~> sealRoute(routes(creds)) ~> check {
+                    m(s"$testRoutePath/$systemId/proxy/export_c.json?$p=YYY") ~> Route.seal(routes(creds)) ~> check {
                         status should be(BadRequest)
                         responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
                     }
 
-                    m(s"$testRoutePath/$systemId/proxy/export_c.json", JsObject(p -> "YYY".toJson)) ~> sealRoute(routes(creds)) ~> check {
+                    m(s"$testRoutePath/$systemId/proxy/export_c.json", JsObject(p -> "YYY".toJson)) ~> Route.seal(routes(creds)) ~> check {
                         status should be(BadRequest)
                         responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
                     }
@@ -998,30 +1006,30 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             val contentX = JsObject("x" -> "overriden".toJson)
             val contentZ = JsObject("z" -> "overriden".toJson)
 
-            allowedMethods.foreach { m =>
+            allowedMethodsWithEntity.foreach { m =>
                 invocationsAllowed += 1
 
-                m(s"$testRoutePath/$systemId/proxy/export_c.json?x=overriden") ~> sealRoute(routes(creds)) ~> check {
+                m(s"$testRoutePath/$systemId/proxy/export_c.json?x=overriden") ~> Route.seal(routes(creds)) ~> check {
                     status should be(BadRequest)
                     responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
                 }
 
-                m(s"$testRoutePath/$systemId/proxy/export_c.json?y=overriden") ~> sealRoute(routes(creds)) ~> check {
+                m(s"$testRoutePath/$systemId/proxy/export_c.json?y=overriden") ~> Route.seal(routes(creds)) ~> check {
                     status should be(BadRequest)
                     responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
                 }
 
-                m(s"$testRoutePath/$systemId/proxy/export_c.json", contentX) ~> sealRoute(routes(creds)) ~> check {
+                m(s"$testRoutePath/$systemId/proxy/export_c.json", contentX) ~> Route.seal(routes(creds)) ~> check {
                     status should be(BadRequest)
                     responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
                 }
 
-                m(s"$testRoutePath/$systemId/proxy/export_c.json?y=overriden", contentZ) ~> sealRoute(routes(creds)) ~> check {
+                m(s"$testRoutePath/$systemId/proxy/export_c.json?y=overriden", contentZ) ~> Route.seal(routes(creds)) ~> check {
                     status should be(BadRequest)
                     responseAs[ErrorResponse].error shouldBe Messages.parametersNotAllowed
                 }
 
-                m(s"$testRoutePath/$systemId/proxy/export_c.json?empty=overriden") ~> sealRoute(routes(creds)) ~> check {
+                m(s"$testRoutePath/$systemId/proxy/export_c.json?empty=overriden") ~> Route.seal(routes(creds)) ~> check {
                     status should be(OK)
                     val response = responseAs[JsObject]
                     response shouldBe JsObject(
@@ -1044,15 +1052,15 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             /*
              * Now supporting all content types with inlined "body".
              *
-             Post(s"$testRoutePath/$systemId/proxy/export_c.json?a=b&c=d", "1,2,3") ~> sealRoute(routes(creds)) ~> check {
+             Post(s"$testRoutePath/$systemId/proxy/export_c.json?a=b&c=d", "1,2,3") ~> Route.seal(routes(creds)) ~> check {
                  status should be(BadRequest)
                  confirmErrorWithTid(responseAs[JsObject], Some(Messages.contentTypeNotSupported))
              }
              *
              */
 
-            Post(s"$testRoutePath/$systemId/proxy/export_c.json", str) ~> addHeader("Content-type", MediaTypes.`text/html`.value) ~> sealRoute(routes(creds)) ~> check {
-                //status should be(OK)
+            Post(s"$testRoutePath/$systemId/proxy/export_c.json", str) ~> addHeader("Content-type", ContentTypes.`text/html(UTF-8)`.value) ~> Route.seal(routes(creds)) ~> check {
+                status should be(OK)
                 val response = responseAs[JsObject]
                 response shouldBe JsObject(
                     "pkg" -> s"$systemId/proxy".toJson,
@@ -1062,10 +1070,10 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         JsObject(webApiDirectives.body -> str.toJson),
                         creds,
                         pkgName = "proxy",
-                        headers = List(HttpHeaders.`Content-Type`(MediaTypes.`text/html`))))
+                        headers = List(`Content-Type`(ContentTypes.`text/html(UTF-8)`))))
             }
 
-            Post(s"$testRoutePath/$systemId/proxy/export_c.json?a=b&c=d") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$testRoutePath/$systemId/proxy/export_c.json?a=b&c=d") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response shouldBe JsObject(
@@ -1078,7 +1086,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         pkgName = "proxy"))
             }
 
-            Post(s"$testRoutePath/$systemId/proxy/export_c.json?a=b&c=d", JsObject()) ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$testRoutePath/$systemId/proxy/export_c.json?a=b&c=d", JsObject()) ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response shouldBe JsObject(
@@ -1100,7 +1108,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                 foreach { path =>
                     allowedMethods.foreach { m =>
                         failThrottleForSubject = Some(systemId)
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             status should be(TooManyRequests)
                             confirmErrorWithTid(responseAs[JsObject], Some(Messages.tooManyRequests))
                         }
@@ -1120,7 +1128,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             "headers" -> JsObject(
                                 "Access-Control-Allow-Methods" -> "OPTIONS, GET, PATCH".toJson)))
 
-                    Options(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                    Options(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                         header("Access-Control-Allow-Origin") shouldBe None
                         header("Access-Control-Allow-Methods").get.toString shouldBe "Access-Control-Allow-Methods: OPTIONS, GET, PATCH"
                     }
@@ -1138,10 +1146,10 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             "headers" -> JsObject(
                                 "Set-Cookie" -> JsArray(JsString("a=b"), JsString("c=d; Path = /")))))
 
-                    Options(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                    Options(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                         headers should contain allOf (
-                            HttpHeaders.RawHeader("Set-Cookie", "a=b"),
-                            HttpHeaders.RawHeader("Set-Cookie", "c=d; Path = /")
+                            RawHeader("Set-Cookie", "a=b"),
+                            RawHeader("Set-Cookie", "c=d; Path = /")
                         )
                     }
                 }
@@ -1155,7 +1163,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                 foreach { path =>
                     allowedMethods.foreach { m =>
                         invocationsAllowed += 1
-                        m(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                        m(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                             header("Access-Control-Allow-Origin").get.toString shouldBe "Access-Control-Allow-Origin: *"
                             header("Access-Control-Allow-Methods").get.toString shouldBe "Access-Control-Allow-Methods: OPTIONS, GET, DELETE, POST, PUT, HEAD, PATCH"
                             header("Access-Control-Allow-Headers").get.toString shouldBe "Access-Control-Allow-Headers: Authorization, Content-Type"
@@ -1177,7 +1185,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             "headers" -> JsObject(
                                 "location" -> "http://openwhisk.org".toJson)))
 
-                    Head(s"$testRoutePath/$path") ~> sealRoute(routes(creds)) ~> check {
+                    Head(s"$testRoutePath/$path") ~> Route.seal(routes(creds)) ~> check {
                         header("location").get.toString shouldBe "location: http://openwhisk.org"
                     }
                 }
@@ -1198,7 +1206,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             case (res, expectedMediaType) =>
                                 actionResult = Some(JsObject("html" -> res.toJson))
 
-                                Get(s"$testRoutePath/$path") ~> addHeader("Accept", expectedMediaType.value) ~> sealRoute(routes(creds)) ~> check {
+                                Get(s"$testRoutePath/$path") ~> addHeader("Accept", expectedMediaType.value) ~> Route.seal(routes(creds)) ~> check {
                                     status should be(OK)
                                     responseAs[String] shouldBe res
                                     mediaType shouldBe expectedMediaType
@@ -1212,7 +1220,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             invocationsAllowed = 2
 
             val queryString = "x=overriden&key2=value2"
-            Post(s"$testRoutePath/$systemId/proxy/raw_export_c.json?$queryString") ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$testRoutePath/$systemId/proxy/raw_export_c.json?$queryString") ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response shouldBe JsObject(
@@ -1226,7 +1234,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         pkgName = "proxy"))
             }
 
-            Post(s"$testRoutePath/$systemId/proxy/raw_export_c.json", JsObject("x" -> "overriden".toJson, "key2" -> "value2".toJson)) ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$testRoutePath/$systemId/proxy/raw_export_c.json", JsObject("x" -> "overriden".toJson, "key2" -> "value2".toJson)) ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response shouldBe JsObject(
@@ -1236,12 +1244,11 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                         Post.method.name.toLowerCase,
                         Map(webApiDirectives.query -> "".toJson,
                             webApiDirectives.body -> Base64.getEncoder.encodeToString {
-                                JsObject("x" -> JsString("overriden"), "key2" -> JsString("value2")).prettyPrint.getBytes
+                                JsObject("x" -> JsString("overriden"), "key2" -> JsString("value2")).compactPrint.getBytes
                             }.toJson).toJson.asJsObject,
                         creds,
                         pkgName = "proxy"))
             }
-
         }
 
         it should s"invoke raw action ensuring body and query arguments are set properly (auth? ${creds.isDefined})" in {
@@ -1250,7 +1257,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
             invocationsAllowed = 1
 
             val queryString = "key1=value1&key2=value2"
-            Post(s"$testRoutePath/$systemId/proxy/raw_export_c.json?$queryString", str) ~> addHeader("Content-type", MediaTypes.`application/json`.value) ~> sealRoute(routes(creds)) ~> check {
+            Post(s"$testRoutePath/$systemId/proxy/raw_export_c.json?$queryString", str) ~> addHeader("Content-type", MediaTypes.`application/json`.value) ~> Route.seal(routes(creds)) ~> check {
                 status should be(OK)
                 val response = responseAs[JsObject]
                 response shouldBe JsObject(
@@ -1262,7 +1269,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                             webApiDirectives.query -> queryString.toJson).toJson.asJsObject,
                         creds,
                         pkgName = "proxy",
-                        headers = List(HttpHeaders.`Content-Type`(MediaTypes.`application/json`))))
+                        headers = List(`Content-Type`(ContentTypes.`application/json`))))
             }
         }
 
@@ -1274,9 +1281,9 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     actionResult = Some(JsObject("body" -> "Plain text".toJson))
                     invocationsAllowed += 1
 
-                    Get(s"$testRoutePath/$path") ~> addHeader("Accept", "application/json") ~> sealRoute(routes(creds)) ~> check {
+                    Get(s"$testRoutePath/$path") ~> addHeader("Accept", "application/json") ~> Route.seal(routes(creds)) ~> check {
                         status should be(NotAcceptable)
-                        response shouldBe HttpResponse(NotAcceptable, "Resource representation is only available with these Content-Types:\ntext/html")
+                        response shouldBe HttpResponse(NotAcceptable, entity = "Resource representation is only available with these types:\ntext/html")
                     }
                 }
         }
@@ -1291,7 +1298,7 @@ trait WebActionsApiTests extends ControllerTestCommon with BeforeAndAfterEach wi
                     invocationsAllowed += 1
                     actionResult = Some(JsObject("html" -> xml.toJson))
 
-                    Get(s"$testRoutePath/$path") ~> addHeader("Accept", MediaTypes.`text/xml`.value) ~> sealRoute(routes(creds)) ~> check {
+                    Get(s"$testRoutePath/$path") ~> addHeader("Accept", MediaTypes.`text/xml`.value) ~> Route.seal(routes(creds)) ~> check {
                         status should be(NotAcceptable)
                     }
                 }
diff --git a/tests/src/test/scala/whisk/core/controller/test/migration/SequenceActionApiMigrationTests.scala b/tests/src/test/scala/whisk/core/controller/test/migration/SequenceActionApiMigrationTests.scala
index 6624fba..1689af1 100644
--- a/tests/src/test/scala/whisk/core/controller/test/migration/SequenceActionApiMigrationTests.scala
+++ b/tests/src/test/scala/whisk/core/controller/test/migration/SequenceActionApiMigrationTests.scala
@@ -24,10 +24,14 @@ import org.scalatest.junit.JUnitRunner
 
 import common.TestHelpers
 import common.WskTestHelpers
-import spray.http.StatusCodes.OK
-import spray.httpx.SprayJsonSupport._
+
+import akka.http.scaladsl.server.Route
+import akka.http.scaladsl.model.StatusCodes.OK
+import akka.http.scaladsl.marshallers.sprayjson.SprayJsonSupport._
+
 import spray.json.DefaultJsonProtocol._
 import spray.json._
+
 import whisk.core.controller.WhiskActionsApi
 import whisk.core.controller.test.ControllerTestCommon
 import whisk.core.controller.test.WhiskAuthHelpers
@@ -59,7 +63,7 @@ class SequenceActionApiMigrationTests extends ControllerTestCommon
         }.toList
         actions foreach { put(entityStore, _) }
         waitOnView(entityStore, WhiskAction, namespace, 2)
-        Get(s"/$namespace/${collection.path}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"/$namespace/${collection.path}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[List[JsObject]]
 
@@ -73,7 +77,7 @@ class SequenceActionApiMigrationTests extends ControllerTestCommon
         val components = Vector("/_/a", "/_/x/b", "/n/a", "/n/x/c").map(stringToFullyQualifiedName(_))
         val action = WhiskAction(namespace, aname, sequence(components))
         put(entityStore, action)
-        Get(s"$collectionPath/${action.name}") ~> sealRoute(routes(creds)) ~> check {
+        Get(s"$collectionPath/${action.name}") ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response should be(action)
@@ -90,7 +94,7 @@ class SequenceActionApiMigrationTests extends ControllerTestCommon
         put(entityStore, action, false)
 
         // create an action sequence
-        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -109,7 +113,7 @@ class SequenceActionApiMigrationTests extends ControllerTestCommon
         put(entityStore, action, false)
 
         // create an action sequence
-        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[WhiskAction]
@@ -127,7 +131,7 @@ class SequenceActionApiMigrationTests extends ControllerTestCommon
         put(entityStore, action, false)
 
         // create an action sequence
-        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${action.name}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             deleteAction(action.docid)
             status should be(OK)
             val response = responseAs[String]
@@ -159,7 +163,7 @@ class SequenceActionApiMigrationTests extends ControllerTestCommon
         val content = WhiskActionPut(Some(seqAction.exec), Some(Parameters()))
 
         // update an action sequence
-        Put(s"$collectionPath/${seqName}?overwrite=true", content) ~> sealRoute(routes(creds)) ~> check {
+        Put(s"$collectionPath/${seqName}?overwrite=true", content) ~> Route.seal(routes(creds)) ~> check {
             status should be(OK)
             val response = responseAs[WhiskAction]
             response.exec.kind should be(Exec.SEQUENCE)

-- 
To stop receiving notification emails like this one, please contact
['"commits@openwhisk.apache.org" <co...@openwhisk.apache.org>'].