diff --git a/Spock.cabal b/Spock.cabal
--- a/Spock.cabal
+++ b/Spock.cabal
@@ -1,5 +1,5 @@
 name:                Spock
-version:             0.5.1.0
+version:             0.6.0.0
 synopsis:            Another Haskell web framework for rapid development
 description:         This toolbox provides everything you need to get a quick start into web hacking with haskell: routing, middleware, json, blaze, sessions, cookies, database helper, csrf-protection, global state
 Homepage:            https://github.com/agrafix/Spock
@@ -22,7 +22,8 @@
                        Web.Spock.Digestive,
                        Web.Spock.Core,
                        Web.Spock.Wire,
-                       Web.Spock.Routing
+                       Web.Spock.Routing,
+                       Web.Spock.Util
   build-depends:       aeson >= 0.6.2.1 && < 0.8,
                        base >= 4 && < 5,
                        base64-bytestring ==1.*,
@@ -52,11 +53,10 @@
                        unordered-containers ==0.2.*,
                        vault ==0.3.*,
                        vector ==0.10.*,
-                       wai-extra >=2.0.0.1,
-                       wai-util ==0.7,
-                       warp >= 2.0.0.1  && < 2.1,
-                       xsd ==0.4.*,
-                       wai >=2.0
+                       wai >=3.0 && <4.0,
+                       wai-extra >=3.0 && <4.0,
+                       warp >=3.0 && <4.0,
+                       xsd ==0.4.*
   ghc-options: -Wall -fno-warn-orphans
 
 test-suite spocktests
@@ -94,10 +94,9 @@
                        unordered-containers ==0.2.*,
                        vault ==0.3.*,
                        vector ==0.10.*,
-                       wai >=2.0,
-                       wai-extra >=2.0.0.1,
-                       wai-util ==0.7,
-                       warp >= 2.0.0.1  && < 2.1,
+                       wai >=3.0 && <4.0,
+                       wai-extra >=3.0 && <4.0,
+                       warp >=3.0 && <4.0,
                        xsd ==0.4.*
   ghc-options: -Wall -fno-warn-orphans
 
diff --git a/src/Web/Spock/Core.hs b/src/Web/Spock/Core.hs
--- a/src/Web/Spock/Core.hs
+++ b/src/Web/Spock/Core.hs
@@ -15,7 +15,6 @@
 import Control.Monad
 import Control.Monad.Reader
 import Control.Monad.State hiding (get, put)
-import Data.Conduit.Lazy (lazyConsume)
 import Data.Time
 import Network.HTTP.Types.Method
 import Network.HTTP.Types.Status
@@ -93,17 +92,16 @@
       parseCookie = first T.init . T.breakOnEnd "="
 
 -- | Get the raw request body
-body :: MonadIO m => ActionT m BSL.ByteString
+body :: MonadIO m => ActionT m BS.ByteString
 body =
     do req <- request
-       chunks <- liftIO $ lazyConsume (Wai.requestBody req)
-       return $ BSL.fromChunks chunks
+       liftIO $ Wai.requestBody req
 
 -- | Parse the request body as json
 jsonBody :: (MonadIO m, A.FromJSON a) => ActionT m (Maybe a)
 jsonBody =
     do b <- body
-       return $ A.decode b
+       return $ A.decodeStrict b
 
 -- | Get uploaded files
 files :: MonadIO m => ActionT m (HM.HashMap T.Text UploadedFile)
diff --git a/src/Web/Spock/SafeActions.hs b/src/Web/Spock/SafeActions.hs
--- a/src/Web/Spock/SafeActions.hs
+++ b/src/Web/Spock/SafeActions.hs
@@ -59,5 +59,6 @@
                Nothing ->
                    do setStatus status404
                       text "File not found"
-               Just (PackedSafeAction action) ->
-                   runSafeAction action
+               Just p@(PackedSafeAction action) ->
+                   do runSafeAction action
+                      (sm_removeSafeAction mgr) p
diff --git a/src/Web/Spock/SessionManager.hs b/src/Web/Spock/SessionManager.hs
--- a/src/Web/Spock/SessionManager.hs
+++ b/src/Web/Spock/SessionManager.hs
@@ -7,6 +7,7 @@
 
 import Web.Spock.Types
 import Web.Spock.Core
+import Web.Spock.Util
 
 import Control.Arrow (first)
 import Control.Concurrent
@@ -26,7 +27,6 @@
 import qualified Data.Text.Lazy.Encoding as TL
 import qualified Data.Vault.Lazy as V
 import qualified Network.Wai as Wai
-import qualified Network.Wai.Util as Wai
 
 createSessionManager :: SessionCfg sess -> IO (SessionManager conn sess st)
 createSessionManager cfg =
@@ -141,7 +141,7 @@
                   -> V.Key SessionId
                   -> UserSessions conn sess st
                   -> Wai.Middleware
-sessionMiddleware cfg vK sessionRef app req =
+sessionMiddleware cfg vK sessionRef app req respond =
     case getCookieFromReq (sc_cookieName cfg) of
       Just sid ->
           do mSess <- loadSessionImpl sessionRef sid
@@ -178,8 +178,11 @@
               cookieC = ("Set-Cookie", BSL.toStrict $ TL.encodeUtf8 cookieContent)
           in (cookieC : responseHeaders)
       withSess shouldSetCookie sess =
-          do resp <- app (req { Wai.vault = V.insert vK (sess_id sess) v })
-             return $ if shouldSetCookie then Wai.mapHeaders (addCookie sess) resp else resp
+          app (req { Wai.vault = V.insert vK (sess_id sess) v }) $ \unwrappedResp ->
+              respond $
+              if shouldSetCookie
+              then mapReqHeaders (addCookie sess) unwrappedResp
+              else unwrappedResp
       mkNew =
           do newSess <- newSessionImpl cfg sessionRef defVal
              withSess True newSess
diff --git a/src/Web/Spock/Util.hs b/src/Web/Spock/Util.hs
new file mode 100644
--- /dev/null
+++ b/src/Web/Spock/Util.hs
@@ -0,0 +1,12 @@
+module Web.Spock.Util where
+
+import Network.HTTP.Types
+import Network.Wai.Internal
+
+mapReqHeaders :: (ResponseHeaders -> ResponseHeaders) -> Response -> Response
+mapReqHeaders f resp =
+    case resp of
+      (ResponseFile s h b1 b2) -> ResponseFile s (f h) b1 b2
+      (ResponseBuilder s h b) -> ResponseBuilder s (f h) b
+      (ResponseStream s h b) -> ResponseStream s (f h) b
+      (ResponseRaw x r) -> ResponseRaw x (mapReqHeaders f r)
diff --git a/src/Web/Spock/Wire.hs b/src/Web/Spock/Wire.hs
--- a/src/Web/Spock/Wire.hs
+++ b/src/Web/Spock/Wire.hs
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
 {-# LANGUAGE OverloadedStrings #-}
 {-# LANGUAGE GeneralizedNewtypeDeriving #-}
 {-# LANGUAGE KindSignatures #-}
@@ -18,7 +19,11 @@
 import Data.Maybe
 import Network.HTTP.Types.Method
 import Network.HTTP.Types.Status
+#if MIN_VERSION_base(4,6,0)
+import Prelude
+#else
 import Prelude hiding (catch)
+#endif
 import System.Directory
 import qualified Data.ByteString as BS
 import qualified Data.ByteString.Lazy as BSL
@@ -131,10 +136,11 @@
          -> IO Wai.Application
 buildApp spockLift spockActions =
     do spockState <- spockLift $ execStateT (runSpockT spockActions) initState
-       let app req =
+       let app :: Wai.Application
+           app req respond =
             case parseMethod $ Wai.requestMethod req of
               Left _ ->
-                  return invalidReq
+                  respond invalidReq
               Right stdMethod ->
                   case HM.lookup stdMethod $ ss_treeMap spockState of
                     Just routeTree ->
@@ -166,11 +172,11 @@
                                  forM_ (HM.elems uploadedFiles) $ \uploadedFile ->
                                      do stillThere <- doesFileExist (uf_tempLocation uploadedFile)
                                         when stillThere $ removeFile (uf_tempLocation uploadedFile)
-                                 return $ respStateToResponse respState
+                                 respond $ respStateToResponse respState
                           Nothing ->
-                              return notFound
+                              respond notFound
                     Nothing ->
-                        return notFound
+                        respond notFound
        return $ ss_middleware spockState $ app
 
 -- | Hook up a 'Wai.Middleware'
