diff --git a/LICENSE b/LICENSE
--- a/LICENSE
+++ b/LICENSE
@@ -1,4 +1,5 @@
 Copyright (c) 2009, Snap Framework authors (see CONTRIBUTORS)
+Copyright (c) 2010, Google, Inc.
 All rights reserved.
 
 Redistribution and use in source and binary forms, with or without
diff --git a/README.SNAP.md b/README.SNAP.md
--- a/README.SNAP.md
+++ b/README.SNAP.md
@@ -1,10 +1,9 @@
 Snap Framework
 --------------
 
-This is the first developer prerelease of the Snap framework.  Snap is a simple
-and fast web development framework and server written in Haskell. For more
-information or to download the latest version, you can visit the Snap project
-website at http://snapframework.com/.
+Snap is a simple and fast web development framework and server written in
+Haskell. For more information or to download the latest version, you can visit
+the Snap project website at http://snapframework.com/.
 
 
 Snap Status and Features
@@ -23,8 +22,8 @@
     bind Haskell functionality to XML tags without getting PHP-style tag soup
     all over your pants
 
-Snap currently only runs on Unix platforms; it has been tested on Linux and Mac
-OSX Snow Leopard.
+Snap is currently only officially supported on Unix platforms; it has been
+tested on Linux and Mac OSX Snow Leopard, and is reported to work on Windows.
 
 
 Snap Philosophy
@@ -41,15 +40,3 @@
   * Excellent documentation
 
   * Robustness and high test coverage
-
-
-Snap Roadmap
-------------
-
-Where are we going?
-
-1. First prerelease: HTTP server, monad, template system
-
-2. Second prerelease: component system with a collection of useful stock
-modules (called "Snaplets") for things like user and session management,
-caching, an administrative interface, etc.
diff --git a/README.md b/README.md
--- a/README.md
+++ b/README.md
@@ -1,13 +1,12 @@
 Snap Framework Core
 ===================
 
-This is the first developer prerelease of the Snap Framework Core library.  For
-more information about Snap, read the `README.SNAP.md` or visit the Snap
-project website at http://www.snapframework.com/.
+This is the Snap Framework Core library.  For more information about Snap, read
+the `README.SNAP.md` or visit the Snap project website at
+http://www.snapframework.com/.
 
-Snap is a nascent web framework for Haskell, based on iteratee I/O (as
-[popularized by Oleg
-Kiselyov](http://okmij.org/ftp/Streams.html#iteratee)).
+Snap is a web framework for Haskell, based on iteratee I/O (as [popularized by
+Oleg Kiselyov](http://okmij.org/ftp/Streams.html#iteratee)).
 
 
 ## Library contents
@@ -57,11 +56,8 @@
 
 ## Building the testsuite
 
-Snap is still in its very early stages, so most of the "action" (and a big
-chunk of the code) right now is centred on the test suite. Snap aims for 100%
-test coverage, and we're trying hard to stick to that.
-
-To build the test suite, `cd` into the `test/` directory and run
+Snap-core has a fairly comprehensive testsuite. To build it, `cd` into the
+`test/` directory and run
 
     $ cabal configure
     $ cabal build
@@ -69,6 +65,5 @@
 From here you can invoke the testsuite by running:
 
     $ ./runTestsAndCoverage.sh 
-
 
 The testsuite generates an `hpc` test coverage report in `test/dist/hpc`.
diff --git a/cbits/timefuncs.c b/cbits/timefuncs.c
--- a/cbits/timefuncs.c
+++ b/cbits/timefuncs.c
@@ -1,4 +1,5 @@
 #define _XOPEN_SOURCE
+#define _BSD_SOURCE
 #include <time.h>
 #include <locale.h>
 
diff --git a/project_template/barebones/foo.cabal b/project_template/barebones/foo.cabal
deleted file mode 100644
--- a/project_template/barebones/foo.cabal
+++ /dev/null
@@ -1,35 +0,0 @@
-Name:                projname
-Version:             0.1
-Synopsis:            Project Synopsis Here
-Description:         Project Description Here
-License:             AllRightsReserved
-Author:              Author
-Maintainer:          maintainer@example.com
-Stability:           Experimental
-Category:            Web
-Build-type:          Simple
-Cabal-version:       >=1.2
-
-Executable projname
-  hs-source-dirs: src
-  main-is: Main.hs
-
-  Build-depends:
-    base >= 4,
-    haskell98,
-    monads-fd >= 0.1 && <0.2,
-    bytestring >= 0.9.1 && <0.10,
-    snap-core >= 0.2 && <0.3,
-    snap-server >= 0.2 && <0.3,
-    xhtml-combinators,
-    unix,
-    text,
-    containers,
-    MonadCatchIO-transformers,
-    filepath >= 1.1 && <1.2
-
-  if impl(ghc >= 6.12.0)
-    ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2
-                 -fno-warn-unused-do-bind
-  else
-    ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2
diff --git a/project_template/barebones/src/Main.hs b/project_template/barebones/src/Main.hs
deleted file mode 100644
--- a/project_template/barebones/src/Main.hs
+++ /dev/null
@@ -1,22 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module Main where
-
-import           Control.Applicative
-import           Snap.Types
-import           Snap.Util.FileServe
-
-import           Server
-
-main :: IO ()
-main = quickServer $
-    ifTop (writeBS "hello world") <|>
-    route [ ("foo", writeBS "bar")
-          , ("echo/:echoparam", echoHandler)
-          ] <|>
-    dir "static" (fileServe ".")
-
-echoHandler :: Snap ()
-echoHandler = do
-    param <- getParam "echoparam"
-    maybe (writeBS "must specify echo/param in URL")
-          writeBS param
diff --git a/project_template/barebones/src/Server.hs b/project_template/barebones/src/Server.hs
deleted file mode 100644
--- a/project_template/barebones/src/Server.hs
+++ /dev/null
@@ -1,111 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module Server
-    ( ServerConfig(..)
-    , emptyServerConfig
-    , commandLineConfig
-    , server
-    , quickServer
-    ) where
-import qualified Data.ByteString.Char8 as B
-import           Data.ByteString.Char8 (ByteString)
-import           Data.Char
-import           Control.Concurrent
-import           Control.Exception (SomeException)
-import           Control.Monad.CatchIO
-import qualified Data.Text as T
-import           Prelude hiding (catch)
-import           Snap.Http.Server
-import           Snap.Types
-import           Snap.Util.GZip
-import           System hiding (getEnv)
-import           System.Posix.Env
-import qualified Text.XHtmlCombinators.Escape as XH
-
-
-data ServerConfig = ServerConfig
-    { locale          :: String
-    , interface       :: ByteString
-    , port            :: Int
-    , hostname        :: ByteString
-    , accessLog       :: Maybe FilePath
-    , errorLog        :: Maybe FilePath
-    , compression     :: Bool
-    , error500Handler :: SomeException -> Snap ()
-    }
-
-
-emptyServerConfig :: ServerConfig
-emptyServerConfig = ServerConfig
-    { locale          = "en_US"
-    , interface       = "0.0.0.0"
-    , port            = 8000
-    , hostname        = "myserver"
-    , accessLog       = Just "access.log"
-    , errorLog        = Just "error.log"
-    , compression     = True
-    , error500Handler = \e -> do
-        let t = T.pack $ show e
-            r = setContentType "text/html; charset=utf-8" $
-                setResponseStatus 500 "Internal Server Error" emptyResponse
-        putResponse r
-        writeBS "<html><head><title>Internal Server Error</title></head>"
-        writeBS "<body><h1>Internal Server Error</h1>"
-        writeBS "<p>A web handler threw an exception. Details:</p>"
-        writeBS "<pre>\n"
-        writeText $ XH.escape t
-        writeBS "\n</pre></body></html>"
-    }
-
-
-commandLineConfig :: IO ServerConfig
-commandLineConfig = do
-    args <- getArgs
-    let conf = case args of
-         []        -> emptyServerConfig
-         (port':_) -> emptyServerConfig { port = read port' }
-    locale' <- getEnv "LANG"
-    return $ case locale' of
-        Nothing -> conf
-        Just l  -> conf {locale = takeWhile (\c -> isAlpha c || c == '_') l}
-
-server :: ServerConfig -> Snap () -> IO ()
-server config handler = do
-    putStrLn $ "Listening on " ++ (B.unpack $ interface config)
-             ++ ":" ++ show (port config)
-    setUTF8Locale (locale config)
-    try $ httpServe
-             (interface config)
-             (port      config)
-             (hostname  config)
-             (accessLog config)
-             (errorLog  config)
-             (catch500 $ compress $ handler)
-             :: IO (Either SomeException ())
-    threadDelay 1000000
-    putStrLn "Shutting down"
-  where
-    catch500 = (`catch` (error500Handler config))
-    compress = if compression config then withCompression else id
-
-
-quickServer :: Snap () -> IO ()
-quickServer = (commandLineConfig >>=) . flip server
-
-
-setUTF8Locale :: String -> IO ()
-setUTF8Locale locale' = do
-    mapM_ (\k -> setEnv k (locale' ++ ".UTF-8") True)
-          [ "LANG"
-          , "LC_CTYPE"
-          , "LC_NUMERIC"
-          , "LC_TIME"
-          , "LC_COLLATE"
-          , "LC_MONETARY"
-          , "LC_MESSAGES"
-          , "LC_PAPER"
-          , "LC_NAME"
-          , "LC_ADDRESS"
-          , "LC_TELEPHONE"
-          , "LC_MEASUREMENT"
-          , "LC_IDENTIFICATION"
-          , "LC_ALL" ]
diff --git a/project_template/default/foo.cabal b/project_template/default/foo.cabal
deleted file mode 100644
--- a/project_template/default/foo.cabal
+++ /dev/null
@@ -1,37 +0,0 @@
-Name:                projname
-Version:             0.1
-Synopsis:            Project Synopsis Here
-Description:         Project Description Here
-License:             AllRightsReserved
-Author:              Author
-Maintainer:          maintainer@example.com
-Stability:           Experimental
-Category:            Web
-Build-type:          Simple
-Cabal-version:       >=1.2
-
-Executable projname
-  hs-source-dirs: src
-  main-is: Main.hs
-
-  Build-depends:
-    base >= 4,
-    haskell98,
-    monads-fd >= 0.1 && <0.2,
-    bytestring >= 0.9.1 && <0.10,
-    snap-core >= 0.2 && <0.3,
-    snap-server >= 0.2 && <0.3,
-    heist >= 0.2.4 && <0.3,
-    hexpat >= 0.19 && <0.20,
-    xhtml-combinators,
-    unix,
-    text,
-    containers,
-    MonadCatchIO-transformers,
-    filepath >= 1.1 && <1.2
-
-  if impl(ghc >= 6.12.0)
-    ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2
-                 -fno-warn-unused-do-bind
-  else
-    ghc-options: -threaded -Wall -fwarn-tabs -funbox-strict-fields -O2
diff --git a/project_template/default/src/Glue.hs b/project_template/default/src/Glue.hs
deleted file mode 100644
--- a/project_template/default/src/Glue.hs
+++ /dev/null
@@ -1,46 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module Glue
-    ( templateHandler
-    , defaultReloadHandler
-    , templateServe
-    , render
-    ) where
-
-import           Control.Applicative
-import           Control.Monad
-import           Data.ByteString.Char8 (ByteString)
-import qualified Data.ByteString.Char8 as B
-import           Prelude hiding (catch)
-import           Snap.Types hiding (dir)
-import           Snap.Util.FileServe
-import           Text.Templating.Heist
-import           Text.Templating.Heist.TemplateDirectory
-
-
-templateHandler :: TemplateDirectory Snap
-                -> (TemplateDirectory Snap -> Snap ())
-                -> (TemplateState Snap -> Snap ())
-                -> Snap ()
-templateHandler td reload f = reload td <|> (f =<< getDirectoryTS td)
-
-
-defaultReloadHandler :: TemplateDirectory Snap -> Snap ()
-defaultReloadHandler td = path "admin/reload" $ do
-    e <- reloadTemplateDirectory td
-    modifyResponse $ setContentType "text/plain; charset=utf-8"
-    writeBS . B.pack $ either id (const "Templates loaded successfully.") e
-
-
-render :: TemplateState Snap -> ByteString -> Snap ()
-render ts template = do
-    bytes <- renderTemplate ts template
-    flip (maybe pass) bytes $ \x -> do
-        modifyResponse $ setContentType "text/html; charset=utf-8"
-        writeBS x
-
-
-templateServe :: TemplateState Snap -> Snap ()
-templateServe ts = ifTop (render ts "index") <|> do
-    path' <- getSafePath
-    when (head path' == '_') pass
-    render ts $ B.pack path'
diff --git a/project_template/default/src/Main.hs b/project_template/default/src/Main.hs
deleted file mode 100644
--- a/project_template/default/src/Main.hs
+++ /dev/null
@@ -1,30 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module Main where
-
-import           Control.Applicative
-import           Snap.Types
-import           Snap.Util.FileServe
-import           Text.Templating.Heist
-import           Text.Templating.Heist.TemplateDirectory
-
-import           Glue
-import           Server
-
-
-main :: IO ()
-main = do
-    td <- newTemplateDirectory' "templates" emptyTemplateState
-    quickServer $ templateHandler td defaultReloadHandler $ \ts ->
-        ifTop (writeBS "hello world") <|>
-        route [ ("foo", writeBS "bar")
-              , ("echo/:echoparam", echoHandler)
-              ] <|>
-        templateServe ts <|>
-        dir "static" (fileServe ".")
-
-
-echoHandler :: Snap ()
-echoHandler = do
-    param <- getParam "echoparam"
-    maybe (writeBS "must specify echo/param in URL")
-          writeBS param
diff --git a/project_template/default/src/Server.hs b/project_template/default/src/Server.hs
deleted file mode 100644
--- a/project_template/default/src/Server.hs
+++ /dev/null
@@ -1,111 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-module Server
-    ( ServerConfig(..)
-    , emptyServerConfig
-    , commandLineConfig
-    , server
-    , quickServer
-    ) where
-import qualified Data.ByteString.Char8 as B
-import           Data.ByteString.Char8 (ByteString)
-import           Data.Char
-import           Control.Concurrent
-import           Control.Exception (SomeException)
-import           Control.Monad.CatchIO
-import qualified Data.Text as T
-import           Prelude hiding (catch)
-import           Snap.Http.Server
-import           Snap.Types
-import           Snap.Util.GZip
-import           System hiding (getEnv)
-import           System.Posix.Env
-import qualified Text.XHtmlCombinators.Escape as XH
-
-
-data ServerConfig = ServerConfig
-    { locale          :: String
-    , interface       :: ByteString
-    , port            :: Int
-    , hostname        :: ByteString
-    , accessLog       :: Maybe FilePath
-    , errorLog        :: Maybe FilePath
-    , compression     :: Bool
-    , error500Handler :: SomeException -> Snap ()
-    }
-
-
-emptyServerConfig :: ServerConfig
-emptyServerConfig = ServerConfig
-    { locale          = "en_US"
-    , interface       = "0.0.0.0"
-    , port            = 8000
-    , hostname        = "myserver"
-    , accessLog       = Just "access.log"
-    , errorLog        = Just "error.log"
-    , compression     = True
-    , error500Handler = \e -> do
-        let t = T.pack $ show e
-            r = setContentType "text/html; charset=utf-8" $
-                setResponseStatus 500 "Internal Server Error" emptyResponse
-        putResponse r
-        writeBS "<html><head><title>Internal Server Error</title></head>"
-        writeBS "<body><h1>Internal Server Error</h1>"
-        writeBS "<p>A web handler threw an exception. Details:</p>"
-        writeBS "<pre>\n"
-        writeText $ XH.escape t
-        writeBS "\n</pre></body></html>"
-    }
-
-
-commandLineConfig :: IO ServerConfig
-commandLineConfig = do
-    args <- getArgs
-    let conf = case args of
-         []        -> emptyServerConfig
-         (port':_) -> emptyServerConfig { port = read port' }
-    locale' <- getEnv "LANG"
-    return $ case locale' of
-        Nothing -> conf
-        Just l  -> conf {locale = takeWhile (\c -> isAlpha c || c == '_') l}
-
-server :: ServerConfig -> Snap () -> IO ()
-server config handler = do
-    putStrLn $ "Listening on " ++ (B.unpack $ interface config)
-             ++ ":" ++ show (port config)
-    setUTF8Locale (locale config)
-    try $ httpServe
-             (interface config)
-             (port      config)
-             (hostname  config)
-             (accessLog config)
-             (errorLog  config)
-             (catch500 $ compress $ handler)
-             :: IO (Either SomeException ())
-    threadDelay 1000000
-    putStrLn "Shutting down"
-  where
-    catch500 = (`catch` (error500Handler config))
-    compress = if compression config then withCompression else id
-
-
-quickServer :: Snap () -> IO ()
-quickServer = (commandLineConfig >>=) . flip server
-
-
-setUTF8Locale :: String -> IO ()
-setUTF8Locale locale' = do
-    mapM_ (\k -> setEnv k (locale' ++ ".UTF-8") True)
-          [ "LANG"
-          , "LC_CTYPE"
-          , "LC_NUMERIC"
-          , "LC_TIME"
-          , "LC_COLLATE"
-          , "LC_MONETARY"
-          , "LC_MESSAGES"
-          , "LC_PAPER"
-          , "LC_NAME"
-          , "LC_ADDRESS"
-          , "LC_TELEPHONE"
-          , "LC_MEASUREMENT"
-          , "LC_IDENTIFICATION"
-          , "LC_ALL" ]
diff --git a/snap-core.cabal b/snap-core.cabal
--- a/snap-core.cabal
+++ b/snap-core.cabal
@@ -1,5 +1,5 @@
 name:           snap-core
-version:        0.2.16
+version:        0.3.0
 synopsis:       Snap: A Haskell Web Framework (Core)
 
 description:
@@ -73,13 +73,6 @@
   extra/logo.gif,
   haddock.sh,
   LICENSE,
-  project_template/barebones/foo.cabal,
-  project_template/barebones/src/Main.hs,
-  project_template/barebones/src/Server.hs,
-  project_template/default/foo.cabal,
-  project_template/default/src/Glue.hs,
-  project_template/default/src/Main.hs,
-  project_template/default/src/Server.hs,
   README.md,
   README.SNAP.md,
   Setup.hs,
@@ -140,6 +133,7 @@
     Snap.Util.GZip
 
   other-modules:
+    Snap.Internal.Instances,
     Snap.Internal.Parsing,
     Snap.Internal.Routing,
     Snap.Internal.Types
@@ -155,60 +149,16 @@
     deepseq >= 1.1 && <1.2,
     directory,
     dlist >= 0.5 && < 0.6,
+    enumerator > 0.4.2 && < 0.5,
     filepath,
-    iteratee >= 0.3.1 && < 0.4,
-    ListLike >= 1 && < 2,
     MonadCatchIO-transformers >= 0.2.1 && < 0.3,
-    monads-fd < 0.1.0.3,
-    old-locale,
-    old-time,
-    text >= 0.10 && <0.11,
-    time,
-    transformers,
-    unix-compat,
-    zlib
-
-  ghc-prof-options: -prof -auto-all
-
-  if impl(ghc >= 6.12.0)
-    ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
-                 -fno-warn-unused-do-bind
-  else
-    ghc-options: -Wall -fwarn-tabs -funbox-strict-fields -O2
-
-
-Executable snap
-  hs-source-dirs: src
-  main-is: Snap/Starter.hs
-
-  other-modules: Snap.StarterTH
-
-  if flag(no-debug)
-    cpp-options: -DNODEBUG
-
-  build-depends:
-    attoparsec >= 0.8.1 && < 0.9,
-    base >= 4 && < 5,
-    bytestring,
-    bytestring-nums,
-    bytestring-show >= 0.3.2 && < 0.4,
-    cereal >= 0.3 && < 0.4,
-    containers,
-    deepseq >= 1.1 && <1.2,
-    directory,
-    directory-tree,
-    dlist >= 0.5 && < 0.6,
-    filepath,
-    haskell98,
-    iteratee >= 0.3.1 && <0.4,
-    monads-fd,
+    mtl == 2.0.*,
     old-locale,
     old-time,
-    template-haskell,
-    text >= 0.10 && <0.11,
+    text >= 0.11 && <0.12,
     time,
-    transformers,
-    unix-compat,
+    transformers == 0.2.*,
+    unix-compat == 0.2.*,
     zlib
 
   ghc-prof-options: -prof -auto-all
diff --git a/src/Data/CIByteString.hs b/src/Data/CIByteString.hs
--- a/src/Data/CIByteString.hs
+++ b/src/Data/CIByteString.hs
@@ -1,6 +1,7 @@
 {-# LANGUAGE BangPatterns #-}
 {-# LANGUAGE OverloadedStrings #-}
 
+------------------------------------------------------------------------------
 -- | "Data.CIByteString" is a module containing 'CIByteString', a wrapper for
 -- 'ByteString' which provides case-insensitive (ASCII-wise) 'Ord' and 'Eq'
 -- instances.
@@ -11,7 +12,8 @@
 --
 -- @
 -- \> let a = \"Foo\" in
---   putStrLn $ (show $ unCI a) ++ \"==\\\"FoO\\\" is \" ++ show (a == \"FoO\")
+--   putStrLn $ (show $ unCI a) ++ \"==\\\"FoO\\\" is \" ++
+--              show (a == \"FoO\")
 -- \"Foo\"==\"FoO\" is True
 -- @
 
@@ -22,6 +24,8 @@
  , ciToLower
  ) where
 
+
+------------------------------------------------------------------------------
 -- for IsString instance
 import           Data.ByteString.Char8 ()
 import           Data.ByteString (ByteString)
@@ -31,30 +35,45 @@
 import           Data.String
 
 
+------------------------------------------------------------------------------
 -- | A case-insensitive newtype wrapper for 'ByteString'
 data CIByteString = CIByteString { unCI        :: !ByteString
                                  , _lowercased :: !ByteString }
 
+
+------------------------------------------------------------------------------
 toCI :: ByteString -> CIByteString
 toCI s = CIByteString s t
   where
     t = lowercase s
 
+
+------------------------------------------------------------------------------
 ciToLower :: CIByteString -> ByteString
 ciToLower = _lowercased
 
+
+------------------------------------------------------------------------------
 instance Show CIByteString where
     show (CIByteString s _) = show s
 
+
+------------------------------------------------------------------------------
 lowercase :: ByteString -> ByteString
 lowercase = S.map (c2w . toLower . w2c)
 
+
+------------------------------------------------------------------------------
 instance Eq CIByteString where
     (CIByteString _ a) == (CIByteString _ b) = a == b
     (CIByteString _ a) /= (CIByteString _ b) = a /= b
 
+
+------------------------------------------------------------------------------
 instance Ord CIByteString where
     (CIByteString _ a) <= (CIByteString _ b) = a <= b
 
+
+------------------------------------------------------------------------------
 instance IsString CIByteString where
     fromString = toCI . fromString
diff --git a/src/Snap/Internal/Debug.hs b/src/Snap/Internal/Debug.hs
--- a/src/Snap/Internal/Debug.hs
+++ b/src/Snap/Internal/Debug.hs
@@ -8,6 +8,7 @@
 
 {-# LANGUAGE BangPatterns        #-}
 {-# LANGUAGE CPP                 #-}
+{-# LANGUAGE PackageImports      #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 {-# OPTIONS_GHC -fno-cse         #-}
 
@@ -15,18 +16,18 @@
 module Snap.Internal.Debug where
 
 ------------------------------------------------------------------------------
-import           Control.Concurrent
-import           Control.DeepSeq
-import           Control.Exception
-import           Control.Monad.Trans
-import           Data.Char
-import           Data.List
-import           Data.Maybe
-import           Foreign.C.Error
-import           System.Environment
-import           System.IO
-import           System.IO.Unsafe
-import           Text.Printf
+import             Control.Concurrent
+import             Control.DeepSeq
+import             Control.Exception
+import             Control.Monad.Trans
+import             Data.Char
+import             Data.List
+import             Data.Maybe
+import             Foreign.C.Error
+import             System.Environment
+import             System.IO
+import             System.IO.Unsafe
+import             Text.Printf
 ------------------------------------------------------------------------------
 
 debug, debugErrno :: forall m . (MonadIO m => String -> m ())
@@ -37,7 +38,7 @@
 {-# NOINLINE debug #-}
 debug = let !x = unsafePerformIO $! do
             !e <- try $ getEnv "DEBUG"
-            
+
             !f <- either (\(_::SomeException) -> return debugIgnore)
                          (\y -> if y == "1" || y == "on"
                                   then return debugOn
@@ -52,7 +53,7 @@
 {-# NOINLINE debugErrno #-}
 debugErrno = let !x = unsafePerformIO $ do
                  e <- try $ getEnv "DEBUG"
-                 
+
                  !f <- either (\(_::SomeException) -> return debugErrnoIgnore)
                               (\y -> if y == "1" || y == "on"
                                        then return debugErrnoOn
diff --git a/src/Snap/Internal/Http/Types.hs b/src/Snap/Internal/Http/Types.hs
--- a/src/Snap/Internal/Http/Types.hs
+++ b/src/Snap/Internal/Http/Types.hs
@@ -57,6 +57,7 @@
 
 ------------------------------------------------------------------------------
 import           Data.CIByteString
+import           Snap.Iteratee (Enumerator)
 import qualified Snap.Iteratee as I
 
 
@@ -79,8 +80,6 @@
 
 #endif
 
-------------------------------------------------------------------------------
-type Enumerator a = I.Enumerator IO a
 
 ------------------------------------------------------------------------------
 -- | A type alias for a case-insensitive key-value mapping.
@@ -99,8 +98,9 @@
 
 
 ------------------------------------------------------------------------------
--- | Adds a header key-value-pair to the 'HasHeaders' datatype. If a header with
--- the same name already exists, the new value is appended to the headers list.
+-- | Adds a header key-value-pair to the 'HasHeaders' datatype. If a header
+-- with the same name already exists, the new value is appended to the headers
+-- list.
 addHeader :: (HasHeaders a) => CIByteString -> ByteString -> a -> a
 addHeader k v = updateHeaders $ Map.insertWith' (++) k [v]
 
@@ -175,7 +175,7 @@
 ------------------------------------------------------------------------------
 
 -- | An existential wrapper for the 'Enumerator' type
-data SomeEnumerator = SomeEnumerator (forall a . Enumerator a)
+data SomeEnumerator = SomeEnumerator (forall a . Enumerator ByteString IO a)
 
 
 ------------------------------------------------------------------------------
@@ -223,11 +223,11 @@
     , rqCookies        :: [Cookie]
 
 
-      -- | We'll be doing web components (or \"snaplets\") for version 0.2. The
-      -- \"snaplet path\" refers to the place on the URL where your containing
-      -- snaplet is hung. The value of 'rqSnapletPath' is either @\"\"@ (at the
-      -- top-level context) or is a path beginning with a slash, but not ending
-      -- with one.
+      -- | We'll be doing web components (or \"snaplets\") for version 0.2.
+      -- The \"snaplet path\" refers to the place on the URL where your
+      -- containing snaplet is hung. The value of 'rqSnapletPath' is either
+      -- @\"\"@ (at the top-level context) or is a path beginning with a
+      -- slash, but not ending with one.
       --
       -- An identity is that:
       --
@@ -235,18 +235,18 @@
       -- >                       , rqContextPath r
       -- >                       , rqPathInfo r ]
       --
-      -- note that until we introduce snaplets in v0.2, 'rqSnapletPath' will be
-      -- \"\"
+      -- note that until we introduce snaplets in v0.2, 'rqSnapletPath' will
+      -- be \"\"
     , rqSnapletPath    :: !ByteString
 
       -- | Handlers can (/will be; --ed/) be hung on a @URI@ \"entry point\";
       -- this is called the \"context path\". If a handler is hung on the
-      -- context path @\"\/foo\/\"@, and you request @\"\/foo\/bar\"@, the value
-      -- of 'rqPathInfo' will be @\"bar\"@.
+      -- context path @\"\/foo\/\"@, and you request @\"\/foo\/bar\"@, the
+      -- value of 'rqPathInfo' will be @\"bar\"@.
     , rqPathInfo       :: !ByteString
 
-      -- | The \"context path\" of the request; catenating 'rqContextPath', and
-      -- 'rqPathInfo' should get you back to the original 'rqURI'. The
+      -- | The \"context path\" of the request; catenating 'rqContextPath',
+      -- and 'rqPathInfo' should get you back to the original 'rqURI'. The
       -- 'rqContextPath' always begins and ends with a slash (@\"\/\"@)
       -- character, and represents the path (relative to your
       -- component\/snaplet) you took to get to your handler.
@@ -351,7 +351,7 @@
 -- response type
 ------------------------------------------------------------------------------
 
-data ResponseBody = Enum (forall a . Enumerator a)
+data ResponseBody = Enum (forall a . Enumerator ByteString IO a)
                       -- ^ output body is enumerator
 
                   | SendFile FilePath (Maybe (Int64,Int64))
@@ -360,14 +360,15 @@
 
 
 ------------------------------------------------------------------------------
-rspBodyMap :: (forall a . Enumerator a -> Enumerator a)
+rspBodyMap :: (forall a .
+               Enumerator ByteString IO a -> Enumerator ByteString IO a)
            -> ResponseBody
            -> ResponseBody
 rspBodyMap f b      = Enum $ f $ rspBodyToEnum b
 
 
 ------------------------------------------------------------------------------
-rspBodyToEnum :: ResponseBody -> Enumerator a
+rspBodyToEnum :: ResponseBody -> Enumerator ByteString IO a
 rspBodyToEnum (Enum e) = e
 rspBodyToEnum (SendFile fp Nothing)  = I.enumFile fp
 rspBodyToEnum (SendFile fp (Just s)) = I.enumFilePartial fp s
@@ -429,8 +430,8 @@
 ------------------------------------------------------------------------------
 -- | Looks up the value(s) for the given named parameter. Parameters initially
 -- come from the request's query string and any decoded POST body (if the
--- request's @Content-Type@ is @application\/x-www-form-urlencoded@). Parameter
--- values can be modified within handlers using "rqModifyParams".
+-- request's @Content-Type@ is @application\/x-www-form-urlencoded@).
+-- Parameter values can be modified within handlers using "rqModifyParams".
 rqParam :: ByteString           -- ^ parameter name to look up
         -> Request              -- ^ HTTP request
         -> Maybe [ByteString]
@@ -439,8 +440,8 @@
 
 
 ------------------------------------------------------------------------------
--- | Modifies the parameters mapping (which is a @Map ByteString ByteString@) in
--- a 'Request' using the given function.
+-- | Modifies the parameters mapping (which is a @Map ByteString ByteString@)
+-- in a 'Request' using the given function.
 rqModifyParams :: (Params -> Params) -> Request -> Request
 rqModifyParams f r = r { rqParams = p }
   where
@@ -449,7 +450,8 @@
 
 
 ------------------------------------------------------------------------------
--- | Writes a key-value pair to the parameters mapping within the given request.
+-- | Writes a key-value pair to the parameters mapping within the given
+-- request.
 rqSetParam :: ByteString        -- ^ parameter name
            -> [ByteString]      -- ^ parameter values
            -> Request           -- ^ request
@@ -462,16 +464,16 @@
 ------------------------------------------------------------------------------
 
 -- | An empty 'Response'.
-emptyResponse       :: Response
-emptyResponse       = Response Map.empty (1,1) Nothing (Enum return) 200
-                               "OK" False
+emptyResponse :: Response
+emptyResponse = Response Map.empty (1,1) Nothing (Enum (I.enumBS "")) 200
+                         "OK" False
 
 
 ------------------------------------------------------------------------------
 -- | Sets an HTTP response body to the given 'Enumerator' value.
-setResponseBody     :: (forall a . Enumerator a)  -- ^ new response body
-                                                  -- enumerator
-                    -> Response                   -- ^ response to modify
+setResponseBody     :: (forall a . Enumerator ByteString IO a)
+                                   -- ^ new response body enumerator
+                    -> Response    -- ^ response to modify
                     -> Response
 setResponseBody e r = r { rspBody = Enum e }
 {-# INLINE setResponseBody #-}
@@ -502,7 +504,8 @@
 
 ------------------------------------------------------------------------------
 -- | Modifies a response body.
-modifyResponseBody  :: (forall a . Enumerator a -> Enumerator a)
+modifyResponseBody  :: (forall a . Enumerator ByteString IO a
+                                -> Enumerator ByteString IO a)
                     -> Response
                     -> Response
 modifyResponseBody f r = r { rspBody = rspBodyMap f (rspBody r) }
@@ -528,21 +531,22 @@
     path    = maybe "" (S.append "; path=") mbPath
     domain  = maybe "" (S.append "; domain=") mbDomain
     exptime = maybe "" (S.append "; expires=" . fmt) mbExpTime
-    fmt     = fromStr . formatTime defaultTimeLocale "%a, %d-%b-%Y %H:%M:%S GMT"
+    fmt     = fromStr .
+              formatTime defaultTimeLocale "%a, %d-%b-%Y %H:%M:%S GMT"
 
 
 ------------------------------------------------------------------------------
 -- | A note here: if you want to set the @Content-Length@ for the response,
--- Snap forces you to do it with this function rather than by setting it in the
--- headers; the @Content-Length@ in the headers will be ignored.
+-- Snap forces you to do it with this function rather than by setting it in
+-- the headers; the @Content-Length@ in the headers will be ignored.
 --
 -- The reason for this is that Snap needs to look up the value of
 -- @Content-Length@ for each request, and looking the string value up in the
 -- headers and parsing the number out of the text will be too expensive.
 --
 -- If you don't set a content length in your response, HTTP keep-alive will be
--- disabled for HTTP\/1.0 clients, forcing a @Connection: close@. For HTTP\/1.1
--- clients, Snap will switch to the chunked transfer encoding if
+-- disabled for HTTP\/1.0 clients, forcing a @Connection: close@. For
+-- HTTP\/1.1 clients, Snap will switch to the chunked transfer encoding if
 -- @Content-Length@ is not specified.
 setContentLength    :: Int64 -> Response -> Response
 setContentLength l r = r { rspContentLength = Just l }
@@ -590,10 +594,10 @@
     toUTCTime = posixSecondsToUTCTime . realToFrac
 
 
-parseHttpTime = return . toCTime . parse . toStr
+parseHttpTime = return . toCTime . prs . toStr
   where
-    parse :: String -> Maybe UTCTime
-    parse = parseTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT"
+    prs :: String -> Maybe UTCTime
+    prs = parseTime defaultTimeLocale "%a, %d %b %Y %H:%M:%S GMT"
 
     toCTime :: Maybe UTCTime -> CTime
     toCTime (Just t) = fromInteger $ truncate $ utcTimeToPOSIXSeconds t
@@ -651,7 +655,7 @@
         when (S.length hx /= 2 ||
                (not $ S.all (isHexDigit . w2c) hx)) $
              fail "bad hex in url"
-          
+
         let code = (Cvt.hex hx) :: Word8
         nextChunk $ DL.snoc l (S.singleton code)
 
diff --git a/src/Snap/Internal/Instances.hs b/src/Snap/Internal/Instances.hs
new file mode 100644
--- /dev/null
+++ b/src/Snap/Internal/Instances.hs
@@ -0,0 +1,82 @@
+{-# OPTIONS_GHC -fno-warn-orphans #-}
+{-# LANGUAGE PackageImports #-}
+
+module Snap.Internal.Instances where
+
+import                       Control.Applicative
+import                       Control.Monad.Cont
+import                       Control.Monad.Error
+import                       Control.Monad.List
+import                       Control.Monad.RWS.Strict hiding (pass)
+import qualified             Control.Monad.RWS.Lazy as LRWS
+import                       Control.Monad.Reader
+import                       Control.Monad.State.Strict
+import qualified             Control.Monad.State.Lazy as LState
+import                       Control.Monad.Writer.Strict hiding (pass)
+import qualified             Control.Monad.Writer.Lazy as LWriter
+import                       Prelude hiding (catch)
+
+------------------------------------------------------------------------------
+import                       Snap.Internal.Types
+
+
+------------------------------------------------------------------------------
+instance MonadPlus m => MonadPlus (ContT c m) where
+    mzero = lift mzero
+    m `mplus` n = ContT $ \ f -> runContT m f `mplus` runContT n f
+
+
+------------------------------------------------------------------------------
+instance MonadPlus m => Alternative (ContT c m) where
+    empty = mzero
+    (<|>) = mplus
+
+
+------------------------------------------------------------------------------
+instance MonadSnap m => MonadSnap (ContT c m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance (MonadSnap m, Error e) => MonadSnap (ErrorT e m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance MonadSnap m => MonadSnap (ListT m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance (MonadSnap m, Monoid w) => MonadSnap (RWST r w s m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance (MonadSnap m, Monoid w) => MonadSnap (LRWS.RWST r w s m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance MonadSnap m => MonadSnap (ReaderT r m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance MonadSnap m => MonadSnap (StateT s m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance MonadSnap m => MonadSnap (LState.StateT s m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance (MonadSnap m, Monoid w) => MonadSnap (WriterT w m) where
+    liftSnap = lift . liftSnap
+
+
+------------------------------------------------------------------------------
+instance (MonadSnap m, Monoid w) => MonadSnap (LWriter.WriterT w m) where
+    liftSnap = lift . liftSnap
diff --git a/src/Snap/Internal/Iteratee/Debug.hs b/src/Snap/Internal/Iteratee/Debug.hs
--- a/src/Snap/Internal/Iteratee/Debug.hs
+++ b/src/Snap/Internal/Iteratee/Debug.hs
@@ -3,9 +3,9 @@
 -- /N.B./ this is an internal interface, please don't write user code that
 -- depends on it.
 
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-{-# LANGUAGE CPP #-}
+{-# LANGUAGE CPP               #-}
 {-# LANGUAGE FlexibleInstances #-}
+{-# LANGUAGE PackageImports    #-}
 
 
 module Snap.Internal.Iteratee.Debug
@@ -14,9 +14,9 @@
   ) where
 
 ------------------------------------------------------------------------------
-import           Data.Iteratee.WrappedByteString
-import           Data.Word (Word8)
-import           System.IO
+import             Control.Monad.Trans
+import             Data.ByteString (ByteString)
+import             System.IO
 ------------------------------------------------------------------------------
 #ifndef NODEBUG
 import           Snap.Internal.Debug
@@ -26,47 +26,52 @@
 
 
 ------------------------------------------------------------------------------
-instance Show (WrappedByteString Word8) where
-    show (WrapBS s) = show s
-
-
-------------------------------------------------------------------------------
-debugIteratee :: Iteratee IO ()
-debugIteratee = IterateeG f
+debugIteratee :: Iteratee ByteString IO ()
+debugIteratee = continue f
   where
-    f c@(EOF _) = do
-        putStrLn $ "got EOF: " ++ show c
-        hFlush stdout
-        return (Done () c)
+    f EOF = do
+        liftIO $ putStrLn $ "got EOF"
+        liftIO $ hFlush stdout
+        yield () EOF
 
-    f c@(Chunk _) = do
-        putStrLn $ "got chunk: " ++ show c
-        hFlush stdout
-        return $ Cont debugIteratee Nothing
+    f (Chunks xs) = do
+        liftIO $ putStrLn $ "got chunk: " ++ show (xs)
+        liftIO $ hFlush stdout
+        continue f
 
 
 #ifndef NODEBUG
 
-iterateeDebugWrapper :: String -> Iteratee IO a -> Iteratee IO a
-iterateeDebugWrapper name iter = IterateeG f
+iterateeDebugWrapper :: (Show a, MonadIO m) =>
+                        String
+                     -> Iteratee a m b
+                     -> Iteratee a m b
+iterateeDebugWrapper name iter = do
+    debug $ name ++ ": BEGIN"
+    step <- lift $ runIteratee iter
+    whatWasReturn step
+    check step
+
   where
-    f c@(EOF Nothing) = do
-        debug $ name ++ ": got EOF: " ++ show c
-        runIter iter c
+    whatWasReturn (Continue _) = debug $ name ++ ": continue"
+    whatWasReturn (Yield _ z)  = debug $ name ++ ": yield, with remainder "
+                                              ++ show z
+    whatWasReturn (Error e)    = debug $ name ++ ": error, with " ++ show e
 
-    f c@(EOF (Just _)) = do
-        debug $ name ++ ": got EOF **error**: " ++ show c
-        runIter iter c
+    check (Continue k) = continue $ f k
+    check st           = returnI st
 
-    f c@(Chunk _) = do
-        debug $ name ++ ": got chunk: " ++ show c
-        wrapResult $ runIter iter c
 
-    wrapResult m = do
-        iv <- m
-        let i = liftI iv
+    f k EOF = do
+        debug $ name ++ ": got EOF"
+        k EOF
 
-        return $ Cont (iterateeDebugWrapper name i) Nothing
+    f k ch@(Chunks xs) = do
+        debug $ name ++ ": got chunk: " ++ show xs
+        step <- lift $ runIteratee $ k ch
+        whatWasReturn step
+        check step
+
 
 #else
 
diff --git a/src/Snap/Internal/Routing.hs b/src/Snap/Internal/Routing.hs
--- a/src/Snap/Internal/Routing.hs
+++ b/src/Snap/Internal/Routing.hs
@@ -1,3 +1,5 @@
+{-# LANGUAGE RankNTypes #-}
+{-# LANGUAGE FlexibleContexts #-}
 module Snap.Internal.Routing where
 
 
@@ -34,14 +36,16 @@
 fallback and try that, which is the baz action.
 
 -}
-data Route a = Action (Snap a)                        -- wraps a 'Snap' action
-             | Capture ByteString (Route a) (Route a) -- captures the dir in a param
-             | Dir (Map.Map ByteString (Route a)) (Route a)  -- match on a dir
-             | NoRoute
+data Route a m = Action ((MonadSnap m) => m a)   -- wraps a 'Snap' action
+               -- captures the dir in a param
+               | Capture ByteString (Route a m) (Route a m)
+               -- match on a dir
+               | Dir (Map.Map ByteString (Route a m)) (Route a m)
+               | NoRoute
 
 
 ------------------------------------------------------------------------------
-instance Monoid (Route a) where
+instance Monoid (Route a m) where
     mempty = NoRoute
 
     mappend NoRoute r = r
@@ -81,14 +85,14 @@
 
 
 ------------------------------------------------------------------------------
-routeHeight :: Route a -> Int
+routeHeight :: Route a m -> Int
 routeHeight r = case r of
   NoRoute          -> 1
   (Action _)       -> 1
   (Capture _ r' _) -> 1+routeHeight r'
   (Dir rm _)       -> 1+foldl max 1 (map routeHeight $ Map.elems rm)
 
-routeEarliestNC :: Route a -> Int -> Int
+routeEarliestNC :: Route a m -> Int -> Int
 routeEarliestNC r n = case r of
   NoRoute           -> n
   (Action _)        -> n
@@ -135,8 +139,8 @@
 --
 -- > [ ("a", h1), ("a/b", h2), ("a/:x", h3) ]
 --
--- a request for \"@\/a\/b@\" will go to @h2@, \"@\/a\/s@\" for any /s/ will go
--- to @h3@, and \"@\/a@\" will go to @h1@.
+-- a request for \"@\/a\/b@\" will go to @h2@, \"@\/a\/s@\" for any /s/ will
+-- go to @h3@, and \"@\/a@\" will go to @h1@.
 --
 -- The following example matches \"@\/article@\" to an article index,
 -- \"@\/login@\" to a login, and \"@\/article\/...@\" to an article renderer.
@@ -145,38 +149,40 @@
 -- >       , ("article/:id", renderArticle)
 -- >       , ("login",       method POST doLogin) ]
 --
-route :: [(ByteString, Snap a)] -> Snap a
+route :: MonadSnap m => [(ByteString, m a)] -> m a
 route rts = do
-  p <- getRequest >>= return . rqPathInfo
+  p <- getRequest >>= maybe pass return . urlDecode . rqPathInfo
   route' (return ()) ([], splitPath p) Map.empty rts'
   where
     rts' = mconcat (map pRoute rts)
 
 
 ------------------------------------------------------------------------------
--- | The 'routeLocal' function is the same as 'route'', except it doesn't change
--- the request's context path. This is useful if you want to route to a
+-- | The 'routeLocal' function is the same as 'route'', except it doesn't
+-- change the request's context path. This is useful if you want to route to a
 -- particular handler but you want that handler to receive the 'rqPathInfo' as
 -- it is.
-routeLocal :: [(ByteString, Snap a)] -> Snap a
+routeLocal :: MonadSnap m => [(ByteString, m a)] -> m a
 routeLocal rts = do
     req    <- getRequest
     let ctx = rqContextPath req
     let p   = rqPathInfo req
+    p' <- maybe pass return $ urlDecode p
     let md  = modifyRequest $ \r -> r {rqContextPath=ctx, rqPathInfo=p}
 
-    (route' md ([], splitPath p) Map.empty rts') <|> (md >> pass)
+    (route' md ([], splitPath p') Map.empty rts') <|> (md >> pass)
 
   where
     rts' = mconcat (map pRoute rts)
 
+
 ------------------------------------------------------------------------------
 splitPath :: ByteString -> [ByteString]
 splitPath = B.splitWith (== (c2w '/'))
 
 
 ------------------------------------------------------------------------------
-pRoute :: (ByteString, Snap a) -> Route a
+pRoute :: MonadSnap m => (ByteString, m a) -> Route a m
 pRoute (r, a) = foldr f (Action a) hier
   where
     hier   = filter (not . B.null) $ B.splitWith (== (c2w '/')) r
@@ -186,11 +192,12 @@
 
 
 ------------------------------------------------------------------------------
-route' :: Snap ()
+route' :: MonadSnap m
+       => m ()
        -> ([ByteString], [ByteString])
        -> Params
-       -> Route a
-       -> Snap a
+       -> Route a m
+       -> m a
 route' pre (ctx, _) params (Action action) =
     localRequest (updateContextPath (B.length ctx') . updateParams)
                  (pre >> action)
diff --git a/src/Snap/Internal/Types.hs b/src/Snap/Internal/Types.hs
--- a/src/Snap/Internal/Types.hs
+++ b/src/Snap/Internal/Types.hs
@@ -1,34 +1,38 @@
 {-# LANGUAGE DeriveDataTypeable #-}
 {-# LANGUAGE EmptyDataDecls #-}
+{-# LANGUAGE FlexibleInstances #-}
 {-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE PackageImports #-}
 {-# LANGUAGE RankNTypes #-}
 
 module Snap.Internal.Types where
 
 ------------------------------------------------------------------------------
-import           Control.Applicative
-import           Control.Exception (throwIO, ErrorCall(..))
-import           Control.Monad.CatchIO
-import           Control.Monad.State.Strict
-import           Data.ByteString.Char8 (ByteString)
-import qualified Data.ByteString.Char8 as S
-import qualified Data.ByteString.Lazy.Char8 as L
-import qualified Data.CIByteString as CIB
-import           Data.Int
-import           Data.IORef
-import qualified Data.Iteratee as Iter
-import           Data.Maybe
-import qualified Data.Text as T
-import qualified Data.Text.Encoding as T
-import qualified Data.Text.Lazy as LT
-import qualified Data.Text.Lazy.Encoding as LT
+import "MonadCatchIO-transformers" Control.Monad.CatchIO
 
-import           Data.Typeable
+import                       Control.Applicative
+import                       Control.Exception (throwIO, ErrorCall(..))
+import                       Control.Monad
+import                       Control.Monad.State
+import                       Data.ByteString.Char8 (ByteString)
+import qualified             Data.ByteString.Char8 as S
+import qualified             Data.ByteString.Lazy.Char8 as L
+import qualified             Data.CIByteString as CIB
+import                       Data.Int
+import                       Data.IORef
+import                       Data.Maybe
+import qualified             Data.Text as T
+import qualified             Data.Text.Encoding as T
+import qualified             Data.Text.Lazy as LT
+import qualified             Data.Text.Lazy.Encoding as LT
+import                       Data.Typeable
+import                       Prelude hiding (catch, take)
 
+
 ------------------------------------------------------------------------------
-import           Snap.Iteratee hiding (Enumerator)
-import           Snap.Internal.Http.Types
-import           Snap.Internal.Iteratee.Debug
+import                       Snap.Internal.Http.Types
+import                       Snap.Iteratee
+import                       Snap.Internal.Iteratee.Debug
 
 
 ------------------------------------------------------------------------------
@@ -74,19 +78,36 @@
    >   r <- getResponse
    >   finishWith r
 
-   then any subsequent processing will be skipped and supplied 'Response' value
-   will be returned from 'runSnap' as-is.
+   then any subsequent processing will be skipped and supplied 'Response'
+   value will be returned from 'runSnap' as-is.
 
 6. access to the 'IO' monad through a 'MonadIO' instance:
 
    > a :: Snap ()
    > a = liftIO fireTheMissiles
+
+You may notice that most of the type signatures in this module contain a
+@(MonadSnap m) => ...@ typeclass constraint. 'MonadSnap' is a typeclass which,
+in essence, says \"you can get back to the 'Snap' monad from here\". Using
+'MonadSnap' you can extend the 'Snap' monad with additional functionality and
+still have access to most of the 'Snap' functions without writing 'lift'
+everywhere. Instances are already provided for most of the common monad
+transformers ('ReaderT', 'WriterT', 'StateT', etc.).
+
 -}
 
+------------------------------------------------------------------------------
+-- | 'MonadSnap' is a type class, analogous to 'MonadIO' for 'IO', that makes
+-- it easy to wrap 'Snap' inside monad transformers.
+class (Monad m, MonadIO m, MonadCatchIO m, MonadPlus m, Functor m,
+       Applicative m, Alternative m) => MonadSnap m where
+    liftSnap :: Snap a -> m a
 
+
 ------------------------------------------------------------------------------
 newtype Snap a = Snap {
-      unSnap :: StateT SnapState (Iteratee IO) (Maybe (Either Response a))
+      unSnap :: StateT SnapState (Iteratee ByteString IO)
+                (Maybe (Either Response a))
 }
 
 
@@ -154,7 +175,14 @@
     empty = mzero
     (<|>) = mplus
 
+
 ------------------------------------------------------------------------------
+instance MonadSnap Snap where
+    liftSnap = id
+
+
+
+------------------------------------------------------------------------------
 -- | The Typeable instance is here so Snap can be dynamically executed with
 -- Hint.
 snapTyCon :: TyCon
@@ -166,51 +194,52 @@
 
 
 ------------------------------------------------------------------------------
-liftIter :: Iteratee IO a -> Snap a
-liftIter i = Snap (lift i >>= return . Just . Right)
+liftIter :: MonadSnap m => Iteratee ByteString IO a -> m a
+liftIter i = liftSnap $ Snap (lift i >>= return . Just . Right)
 
 
 ------------------------------------------------------------------------------
 -- | Sends the request body through an iteratee (data consumer) and
 -- returns the result.
-runRequestBody :: Iteratee IO a -> Snap a
+runRequestBody :: MonadSnap m => Iteratee ByteString IO a -> m a
 runRequestBody iter = do
     req  <- getRequest
     senum <- liftIO $ readIORef $ rqBody req
     let (SomeEnumerator enum) = senum
 
     -- make sure the iteratee consumes all of the output
-    let iter' = iter >>= (\a -> Iter.skipToEof >> return a)
+    let iter' = iter >>= \a -> skipToEof >> return a
 
     -- run the iteratee
-    result <- liftIter $ Iter.joinIM $ enum iter'
+    step   <- liftIO $ runIteratee iter'
+    result <- liftIter $ enum step
 
     -- stuff a new dummy enumerator into the request, so you can only try to
     -- read the request body from the socket once
     liftIO $ writeIORef (rqBody req)
-                        (SomeEnumerator $ return . Iter.joinI . Iter.take 0 )
+                        (SomeEnumerator $ joinI . take 0 )
 
     return result
 
 
 ------------------------------------------------------------------------------
 -- | Returns the request body as a bytestring.
-getRequestBody :: Snap L.ByteString
-getRequestBody = liftM fromWrap $ runRequestBody stream2stream
+getRequestBody :: MonadSnap m => m L.ByteString
+getRequestBody = liftM L.fromChunks $ runRequestBody consume
 {-# INLINE getRequestBody #-}
 
 
 ------------------------------------------------------------------------------
--- | Normally Snap is careful to ensure that the request body is fully consumed
--- after your web handler runs, but before the 'Response' enumerator is
--- streamed out the socket. If you want to transform the request body into some
--- output in O(1) space, you should use this function.
+-- | Normally Snap is careful to ensure that the request body is fully
+-- consumed after your web handler runs, but before the 'Response' enumerator
+-- is streamed out the socket. If you want to transform the request body into
+-- some output in O(1) space, you should use this function.
 --
 -- Note that upon calling this function, response processing finishes early as
 -- if you called 'finishWith'. Make sure you set any content types, headers,
 -- cookies, etc. before you call this function.
 --
-transformRequestBody :: (forall a . Enumerator a)
+transformRequestBody :: (forall a . Enumerator ByteString IO a)
                          -- ^ the output 'Iteratee' is passed to this
                          -- 'Enumerator', and then the resulting 'Iteratee' is
                          -- fed the request body stream. Your 'Enumerator' is
@@ -221,14 +250,16 @@
     let ioref = rqBody req
     senum <- liftIO $ readIORef ioref
     let (SomeEnumerator enum) = senum
-    liftIO $ writeIORef ioref
-               (SomeEnumerator $ return . Iter.joinI . Iter.take 0)
+    liftIO $ writeIORef ioref (SomeEnumerator enumEOF)
 
     origRsp <- getResponse
     let rsp = setResponseBody
                 (\writeEnd -> do
-                     i <- trans writeEnd
-                     enum $ iterateeDebugWrapper "transformRequestBody" i)
+                     let i = iterateeDebugWrapper "transformRequestBody"
+                                                  $ trans writeEnd
+                     st <- liftIO $ runIteratee i
+
+                     enum st)
                 $ origRsp { rspTransformingRqBody = True }
     finishWith rsp
 
@@ -236,8 +267,8 @@
 ------------------------------------------------------------------------------
 -- | Short-circuits a 'Snap' monad action early, storing the given
 -- 'Response' value in its state.
-finishWith :: Response -> Snap a
-finishWith = Snap . return . Just . Left
+finishWith :: MonadSnap m => Response -> m a
+finishWith = liftSnap . Snap . return . Just . Left
 {-# INLINE finishWith #-}
 
 
@@ -245,14 +276,14 @@
 -- | Fails out of a 'Snap' monad action.  This is used to indicate
 -- that you choose not to handle the given request within the given
 -- handler.
-pass :: Snap a
+pass :: MonadSnap m => m a
 pass = empty
 
 
 ------------------------------------------------------------------------------
 -- | Runs a 'Snap' monad action only if the request's HTTP method matches
 -- the given method.
-method :: Method -> Snap a -> Snap a
+method :: MonadSnap m => Method -> m a -> m a
 method m action = do
     req <- getRequest
     unless (rqMethod req == m) pass
@@ -276,10 +307,11 @@
 ------------------------------------------------------------------------------
 -- Runs a 'Snap' monad action only if the 'rqPathInfo' matches the given
 -- predicate.
-pathWith :: (ByteString -> ByteString -> Bool)
+pathWith :: MonadSnap m
+         => (ByteString -> ByteString -> Bool)
          -> ByteString
-         -> Snap a
-         -> Snap a
+         -> m a
+         -> m a
 pathWith c p action = do
     req <- getRequest
     unless (c p (rqPathInfo req)) pass
@@ -294,9 +326,10 @@
 --
 -- Will fail if 'rqPathInfo' is not \"@\/foo@\" or \"@\/foo\/...@\", and will
 -- add @\"foo\/\"@ to the handler's local 'rqContextPath'.
-dir :: ByteString  -- ^ path component to match
-    -> Snap a      -- ^ handler to run
-    -> Snap a
+dir :: MonadSnap m
+    => ByteString  -- ^ path component to match
+    -> m a         -- ^ handler to run
+    -> m a
 dir = pathWith f
   where
     f dr pinfo = dr == x
@@ -306,20 +339,21 @@
 
 
 ------------------------------------------------------------------------------
--- | Runs a 'Snap' monad action only for requests where 'rqPathInfo' is exactly
--- equal to the given string. If the path matches, locally sets 'rqContextPath'
--- to the old value of 'rqPathInfo', sets 'rqPathInfo'=\"\", and runs the given
--- handler.
-path :: ByteString  -- ^ path to match against
-     -> Snap a      -- ^ handler to run
-     -> Snap a
+-- | Runs a 'Snap' monad action only for requests where 'rqPathInfo' is
+-- exactly equal to the given string. If the path matches, locally sets
+-- 'rqContextPath' to the old value of 'rqPathInfo', sets 'rqPathInfo'=\"\",
+-- and runs the given handler.
+path :: MonadSnap m
+     => ByteString  -- ^ path to match against
+     -> m a         -- ^ handler to run
+     -> m a
 path = pathWith (==)
 {-# INLINE path #-}
 
 
 ------------------------------------------------------------------------------
 -- | Runs a 'Snap' monad action only when 'rqPathInfo' is empty.
-ifTop :: Snap a -> Snap a
+ifTop :: MonadSnap m => m a -> m a
 ifTop = path ""
 {-# INLINE ifTop #-}
 
@@ -340,53 +374,55 @@
 
 ------------------------------------------------------------------------------
 -- | Grabs the 'Request' object out of the 'Snap' monad.
-getRequest :: Snap Request
-getRequest = liftM _snapRequest sget
+getRequest :: MonadSnap m => m Request
+getRequest = liftSnap $ liftM _snapRequest sget
 {-# INLINE getRequest #-}
 
 
 ------------------------------------------------------------------------------
 -- | Grabs the 'Response' object out of the 'Snap' monad.
-getResponse :: Snap Response
-getResponse = liftM _snapResponse sget
+getResponse :: MonadSnap m => m Response
+getResponse = liftSnap $ liftM _snapResponse sget
 {-# INLINE getResponse #-}
 
 
 ------------------------------------------------------------------------------
 -- | Puts a new 'Response' object into the 'Snap' monad.
-putResponse :: Response -> Snap ()
-putResponse r = smodify $ \ss -> ss { _snapResponse = r }
+putResponse :: MonadSnap m => Response -> m ()
+putResponse r = liftSnap $ smodify $ \ss -> ss { _snapResponse = r }
 {-# INLINE putResponse #-}
 
 
 ------------------------------------------------------------------------------
 -- | Puts a new 'Request' object into the 'Snap' monad.
-putRequest :: Request -> Snap ()
-putRequest r = smodify $ \ss -> ss { _snapRequest = r }
+putRequest :: MonadSnap m => Request -> m ()
+putRequest r = liftSnap $ smodify $ \ss -> ss { _snapRequest = r }
 {-# INLINE putRequest #-}
 
 
 ------------------------------------------------------------------------------
 -- | Modifies the 'Request' object stored in a 'Snap' monad.
-modifyRequest :: (Request -> Request) -> Snap ()
-modifyRequest f = smodify $ \ss -> ss { _snapRequest = f $ _snapRequest ss }
+modifyRequest :: MonadSnap m => (Request -> Request) -> m ()
+modifyRequest f = liftSnap $
+    smodify $ \ss -> ss { _snapRequest = f $ _snapRequest ss }
 {-# INLINE modifyRequest #-}
 
 
 ------------------------------------------------------------------------------
 -- | Modifes the 'Response' object stored in a 'Snap' monad.
-modifyResponse :: (Response -> Response) -> Snap ()
-modifyResponse f = smodify $ \ss -> ss { _snapResponse = f $ _snapResponse ss }
+modifyResponse :: MonadSnap m => (Response -> Response) -> m ()
+modifyResponse f = liftSnap $
+     smodify $ \ss -> ss { _snapResponse = f $ _snapResponse ss }
 {-# INLINE modifyResponse #-}
 
 
 ------------------------------------------------------------------------------
 -- | Performs a redirect by setting the @Location@ header to the given target
 -- URL/path and the status code to 302 in the 'Response' object stored in a
--- 'Snap' monad. Note that the target URL is not validated in any way. Consider
--- using 'redirect\'' instead, which allows you to choose the correct status
--- code.
-redirect :: ByteString -> Snap ()
+-- 'Snap' monad. Note that the target URL is not validated in any way.
+-- Consider using 'redirect\'' instead, which allows you to choose the correct
+-- status code.
+redirect :: MonadSnap m => ByteString -> m ()
 redirect target = redirect' target 302
 {-# INLINE redirect #-}
 
@@ -396,7 +432,7 @@
 -- URL/path and the status code (should be one of 301, 302, 303 or 307) in the
 -- 'Response' object stored in a 'Snap' monad. Note that the target URL is not
 -- validated in any way.
-redirect' :: ByteString -> Int -> Snap ()
+redirect' :: MonadSnap m => ByteString -> Int -> m ()
 redirect' target status = do
     r <- getResponse
 
@@ -411,8 +447,8 @@
 
 ------------------------------------------------------------------------------
 -- | Log an error message in the 'Snap' monad
-logError :: ByteString -> Snap ()
-logError s = Snap $ gets _snapLogError >>= (\l -> liftIO $ l s)
+logError :: MonadSnap m => ByteString -> m ()
+logError s = liftSnap $ Snap $ gets _snapLogError >>= (\l -> liftIO $ l s)
                                        >>  return (Just $ Right ())
 {-# INLINE logError #-}
 
@@ -420,41 +456,42 @@
 ------------------------------------------------------------------------------
 -- | Adds the output from the given enumerator to the 'Response'
 -- stored in the 'Snap' monad state.
-addToOutput :: (forall a . Enumerator a)   -- ^ output to add
-            -> Snap ()
-addToOutput enum = modifyResponse $ modifyResponseBody (>. enum)
+addToOutput :: MonadSnap m
+            => (forall a . Enumerator ByteString IO a)   -- ^ output to add
+            -> m ()
+addToOutput enum = modifyResponse $ modifyResponseBody (>==> enum)
 
 
 ------------------------------------------------------------------------------
--- | Adds the given strict 'ByteString' to the body of the 'Response' stored in
--- the 'Snap' monad state.
+-- | Adds the given strict 'ByteString' to the body of the 'Response' stored
+-- in the 'Snap' monad state.
 --
 -- Warning: This function is intentionally non-strict. If any pure
 -- exceptions are raised by the expression creating the 'ByteString',
 -- the exception won't actually be raised within the Snap handler.
-writeBS :: ByteString -> Snap ()
+writeBS :: MonadSnap m => ByteString -> m ()
 writeBS s = addToOutput $ enumBS s
 
 
 ------------------------------------------------------------------------------
--- | Adds the given lazy 'L.ByteString' to the body of the 'Response' stored in
--- the 'Snap' monad state.
+-- | Adds the given lazy 'L.ByteString' to the body of the 'Response' stored
+-- in the 'Snap' monad state.
 --
 -- Warning: This function is intentionally non-strict. If any pure
 -- exceptions are raised by the expression creating the 'ByteString',
 -- the exception won't actually be raised within the Snap handler.
-writeLBS :: L.ByteString -> Snap ()
+writeLBS :: MonadSnap m => L.ByteString -> m ()
 writeLBS s = addToOutput $ enumLBS s
 
 
 ------------------------------------------------------------------------------
--- | Adds the given strict 'T.Text' to the body of the 'Response' stored in the
--- 'Snap' monad state.
+-- | Adds the given strict 'T.Text' to the body of the 'Response' stored in
+-- the 'Snap' monad state.
 --
 -- Warning: This function is intentionally non-strict. If any pure
 -- exceptions are raised by the expression creating the 'ByteString',
 -- the exception won't actually be raised within the Snap handler.
-writeText :: T.Text -> Snap ()
+writeText :: MonadSnap m => T.Text -> m ()
 writeText s = writeBS $ T.encodeUtf8 s
 
 
@@ -465,7 +502,7 @@
 -- Warning: This function is intentionally non-strict. If any pure
 -- exceptions are raised by the expression creating the 'ByteString',
 -- the exception won't actually be raised within the Snap handler.
-writeLazyText :: LT.Text -> Snap ()
+writeLazyText :: MonadSnap m => LT.Text -> m ()
 writeLazyText s = writeLBS $ LT.encodeUtf8 s
 
 
@@ -477,24 +514,24 @@
 -- 'sendFile', Snap will use the efficient @sendfile()@ system call on
 -- platforms that support it.
 --
--- If the response body is modified (using 'modifyResponseBody'), the file will
--- be read using @mmap()@.
-sendFile :: FilePath -> Snap ()
+-- If the response body is modified (using 'modifyResponseBody'), the file
+-- will be read using @mmap()@.
+sendFile :: (MonadSnap m) => FilePath -> m ()
 sendFile f = modifyResponse $ \r -> r { rspBody = SendFile f Nothing }
 
 
 ------------------------------------------------------------------------------
--- | Sets the output to be the contents of the specified file, within the given
--- (start,end) range.
+-- | Sets the output to be the contents of the specified file, within the
+-- given (start,end) range.
 --
--- Calling 'sendFilePartial' will overwrite any output queued to be sent in the
--- 'Response'. If the response body is not modified after the call to
+-- Calling 'sendFilePartial' will overwrite any output queued to be sent in
+-- the 'Response'. If the response body is not modified after the call to
 -- 'sendFilePartial', Snap will use the efficient @sendfile()@ system call on
 -- platforms that support it.
 --
--- If the response body is modified (using 'modifyResponseBody'), the file will
--- be read using @mmap()@.
-sendFilePartial :: FilePath -> (Int64,Int64) -> Snap ()
+-- If the response body is modified (using 'modifyResponseBody'), the file
+-- will be read using @mmap()@.
+sendFilePartial :: (MonadSnap m) => FilePath -> (Int64,Int64) -> m ()
 sendFilePartial f rng = modifyResponse $ \r ->
                         r { rspBody = SendFile f (Just rng) }
 
@@ -503,7 +540,7 @@
 -- | Runs a 'Snap' action with a locally-modified 'Request' state
 -- object. The 'Request' object in the Snap monad state after the call
 -- to localRequest will be unchanged.
-localRequest :: (Request -> Request) -> Snap a -> Snap a
+localRequest :: MonadSnap m => (Request -> Request) -> m a -> m a
 localRequest f m = do
     req <- getRequest
 
@@ -520,14 +557,14 @@
 
 ------------------------------------------------------------------------------
 -- | Fetches the 'Request' from state and hands it to the given action.
-withRequest :: (Request -> Snap a) -> Snap a
+withRequest :: MonadSnap m => (Request -> m a) -> m a
 withRequest = (getRequest >>=)
 {-# INLINE withRequest #-}
 
 
 ------------------------------------------------------------------------------
 -- | Fetches the 'Response' from state and hands it to the given action.
-withResponse :: (Response -> Snap a) -> Snap a
+withResponse :: MonadSnap m => (Response -> m a) -> m a
 withResponse = (getResponse >>=)
 {-# INLINE withResponse #-}
 
@@ -545,7 +582,7 @@
 -- address can get it in a uniform manner. It has specifically limited
 -- functionality to ensure that its transformation can be trusted,
 -- when used correctly.
-ipHeaderFilter :: Snap ()
+ipHeaderFilter :: MonadSnap m => m ()
 ipHeaderFilter = ipHeaderFilter' "x-forwarded-for"
 
 
@@ -562,7 +599,7 @@
 -- address can get it in a uniform manner. It has specifically limited
 -- functionality to ensure that its transformation can be trusted,
 -- when used correctly.
-ipHeaderFilter' :: CIB.CIByteString -> Snap ()
+ipHeaderFilter' :: MonadSnap m => CIB.CIByteString -> m ()
 ipHeaderFilter' header = do
     headerContents <- getHeader header <$> getRequest
 
@@ -576,6 +613,35 @@
 
 
 ------------------------------------------------------------------------------
+-- | This function brackets a Snap action in resource acquisition and
+-- release. This is provided because MonadCatchIO's 'bracket' function
+-- doesn't work properly in the case of a short-circuit return from
+-- the action being bracketed.
+--
+-- In order to prevent confusion regarding the effects of the
+-- aquisition and release actions on the Snap state, this function
+-- doesn't accept Snap actions for the acquire or release actions.
+--
+-- This function will run the release action in all cases where the
+-- acquire action succeeded.  This includes the following behaviors
+-- from the bracketed Snap action.
+--
+-- 1. Normal completion
+--
+-- 2. Short-circuit completion, either from calling 'fail' or 'finishWith'
+--
+-- 3. An exception being thrown.
+bracketSnap :: IO a -> (a -> IO b) -> (a -> Snap c) -> Snap c
+bracketSnap before after thing = block . Snap $ do
+    a <- liftIO before
+    let after' = liftIO $ after a
+        (Snap thing') = thing a
+    r <- unblock thing' `onException` after'
+    _ <- after'
+    return r
+
+
+------------------------------------------------------------------------------
 -- | This exception is thrown if the handler you supply to 'runSnap' fails.
 data NoHandlerException = NoHandlerException
    deriving (Eq, Typeable)
@@ -595,7 +661,7 @@
 runSnap :: Snap a
         -> (ByteString -> IO ())
         -> Request
-        -> Iteratee IO (Request,Response)
+        -> Iteratee ByteString IO (Request,Response)
 runSnap (Snap m) logerr req = do
     (r, ss') <- runStateT m ss
 
@@ -613,7 +679,7 @@
   where
     fourohfour = setContentLength 3 $
                  setResponseStatus 404 "Not Found" $
-                 modifyResponseBody (>. enumBS "404") $
+                 modifyResponseBody (>==> enumBS "404") $
                  emptyResponse
 
     dresp = emptyResponse { rspHttpVersion = rqVersion req }
@@ -626,7 +692,7 @@
 evalSnap :: Snap a
          -> (ByteString -> IO ())
          -> Request
-         -> Iteratee IO a
+         -> Iteratee ByteString IO a
 evalSnap (Snap m) logerr req = do
     (r, _) <- runStateT m ss
 
@@ -652,10 +718,20 @@
 --
 -- @    'S.intercalate' \" \"@
 --
-getParam :: ByteString          -- ^ parameter name to look up
-         -> Snap (Maybe ByteString)
+getParam :: MonadSnap m
+         => ByteString          -- ^ parameter name to look up
+         -> m (Maybe ByteString)
 getParam k = do
     rq <- getRequest
     return $ liftM (S.intercalate " ") $ rqParam k rq
+
+
+------------------------------------------------------------------------------
+-- | Gets the HTTP 'Cookie' with the specified name.
+getCookie :: MonadSnap m
+          => ByteString
+          -> m (Maybe Cookie)
+getCookie name = withRequest $
+    return . listToMaybe . filter (\c -> cookieName c == name) . rqCookies
 
 
diff --git a/src/Snap/Iteratee.hs b/src/Snap/Iteratee.hs
--- a/src/Snap/Iteratee.hs
+++ b/src/Snap/Iteratee.hs
@@ -7,264 +7,334 @@
 {-# LANGUAGE TypeSynonymInstances #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
+------------------------------------------------------------------------------
 -- | Snap Framework type aliases and utilities for iteratees. Note that as a
--- convenience, this module also exports everything from @Data.Iteratee@ in the
--- @iteratee@ library.
---
--- /WARNING/: Note that all of these types are scheduled to change in the
--- @darcs@ head version of the @iteratee@ library; John Lato et al. are working
--- on a much improved iteratee formulation.
+-- convenience, this module also exports everything from @Data.Enumerator@ in
+-- the @enumerator@ library.
 
 module Snap.Iteratee
-  ( -- * Convenience aliases around types from @Data.Iteratee@
-    Stream
-  , IterV
-  , Iteratee
-  , Enumerator
-
-    -- * Re-export types and functions from @Data.Iteratee@
-  , module Data.Iteratee
-
-    -- * Helper functions
-
-    -- ** Enumerators
-  , enumBS
+  (
+    -- * Enumerators
+    enumBS
   , enumLBS
   , enumFile
   , enumFilePartial
   , InvalidRangeException
 
-    -- ** Conversion to/from 'WrappedByteString'
-  , fromWrap
-  , toWrap
 
-    -- ** Iteratee utilities
-  , drop'
-  , takeExactly
-  , takeNoMoreThan
+    -- * Iteratee utilities
   , countBytes
-  , bufferIteratee
+  , drop'
   , mkIterateeBuffer
   , unsafeBufferIterateeWithBuffer
   , unsafeBufferIteratee
+  , take
+  , drop
+  , takeExactly
+  , takeNoMoreThan
+  , skipToEof
+
+  , TooManyBytesReadException
+  , ShortWriteException
+
+    -- * Re-export types and functions from @Data.Enumerator@
+  , Stream (..)
+  , Step (..)
+  , Iteratee (..)
+  , Enumerator
+  , Enumeratee
+
+    -- ** Primitives
+    -- *** Combinators
+    -- | These are common patterns which occur whenever iteratees are
+    -- being defined.
+  , returnI
+  , yield
+  , continue
+  , throwError
+  , catchError
+  , liftI
+  , (>>==)
+  , (==<<)
+  , ($$)
+  , (>==>)
+  , (<==<)
+
+    -- *** Iteratees
+  , run
+  , run_
+  , consume
+  , Data.Enumerator.isEOF
+  , liftTrans
+  , liftFoldL
+  , liftFoldL'
+  , liftFoldM
+  , printChunks
+  , head
+  , peek
+
+    -- *** Enumerators
+  , enumEOF
+  , enumList
+  , concatEnums
+    -- *** Enumeratees
+  , checkDone
+  , Data.Enumerator.map
+  , Data.Enumerator.sequence
+  , joinI
+
+
+{-
+    -- ** Iteratee utilities
+  , drop'
+
+-}
   ) where
 
 ------------------------------------------------------------------------------
-import             Control.Monad
-import             Control.Monad.CatchIO
+
+{-
+
 import             Control.Exception (SomeException)
-import             Data.ByteString (ByteString)
-import qualified   Data.ByteString as S
-import qualified   Data.ByteString.Unsafe as S
-import qualified   Data.ByteString.Lazy as L
+import "MonadCatchIO-transformers" Control.Monad.CatchIO
 import qualified   Data.DList as D
-import             Data.Int
 import             Data.IORef
-import             Data.Iteratee
-import             Data.Iteratee.IO (enumHandle)
-import qualified   Data.Iteratee.Base.StreamChunk as SC
-import             Data.Iteratee.WrappedByteString
-import qualified   Data.ListLike as LL
+import             Prelude hiding (catch,drop)
+
+-}
+
+import             Control.DeepSeq
+import             Control.Exception (SomeException, assert)
+import             Control.Monad
+import "MonadCatchIO-transformers" Control.Monad.CatchIO
+import             Control.Monad.Trans (MonadIO, lift, liftIO)
+import             Data.ByteString (ByteString)
+import qualified   Data.ByteString.Char8 as S
+import qualified   Data.ByteString.Unsafe as S
+import qualified   Data.ByteString.Lazy.Char8 as L
+import             Data.Enumerator hiding (drop)
+import             Data.Enumerator.IO (enumHandle)
+import             Data.List (foldl')
 import             Data.Monoid (mappend)
 import             Data.Typeable
-import             Foreign
+import             Foreign hiding (peek)
 import             Foreign.C.Types
 import             GHC.ForeignPtr
-import             Prelude hiding (catch,drop)
+import             Prelude hiding (drop, head, take)
 import             System.IO
-import "monads-fd" Control.Monad.Trans (liftIO)
 
 #ifndef PORTABLE
 import           System.IO.Posix.MMap
 import           System.PosixCompat.Files
+import           System.PosixCompat.Types
 #endif
 
-------------------------------------------------------------------------------
 
-type Stream         = StreamG WrappedByteString Word8
-type IterV      m   = IterGV WrappedByteString Word8 m
-type Iteratee   m   = IterateeG WrappedByteString Word8 m
-type Enumerator m a = Iteratee m a -> m (Iteratee m a)
-
-
 ------------------------------------------------------------------------------
 instance (Functor m, MonadCatchIO m) =>
-         MonadCatchIO (IterateeG s el m) where
+         MonadCatchIO (Iteratee s m) where
     --catch  :: Exception  e => m a -> (e -> m a) -> m a
-    catch m handler = IterateeG $ \str -> do
-        ee <- try $ runIter m str
+    catch m handler = Iteratee $ do
+        ee <- try $ runIteratee m
         case ee of
-          (Left e)  -> runIter (handler e) str
+          (Left e)  -> runIteratee (handler e)
           (Right v) -> return v
 
     --block :: m a -> m a
-    block m = IterateeG $ \str -> block $ runIter m str
-    unblock m = IterateeG $ \str -> unblock $ runIter m str
+    block m = Iteratee $ block $ runIteratee m
+    unblock m = Iteratee $ unblock $ runIteratee m
 
 
+
 ------------------------------------------------------------------------------
--- | Wraps an 'Iteratee', counting the number of bytes consumed by it.
-countBytes :: (Monad m) => Iteratee m a -> Iteratee m (a, Int64)
-countBytes = go 0
-  where
-    go !n iter = IterateeG $ f n iter
+-- | Get the length of a bytestring Stream
+streamLength :: Stream ByteString -> Int
+streamLength (Chunks xs) = foldl' (\c s -> c + S.length s) 0 xs
+streamLength EOF         = 0
 
-    f !n !iter ch@(Chunk ws) = do
-        iterv <- runIter iter ch
-        case iterv of
-          Done x rest -> let !n' = n + m - len rest
-                         in return $! Done (x, n') rest
-          Cont i err  -> return $ Cont ((go $! n + m) i) err
-      where
-        m = fromIntegral $ S.length (unWrap ws)
 
-        len (EOF _)   = 0
-        len (Chunk s) = fromIntegral $ S.length (unWrap s)
-
-    f !n !iter stream = do
-        iterv <- runIter iter stream
-        case iterv of
-          Done x rest -> return $ Done (x, n) rest
-          Cont i err  -> return $ Cont (go n i) err
+------------------------------------------------------------------------------
+-- | Enumerates a strict bytestring.
+enumBS :: (Monad m) => ByteString -> Enumerator ByteString m a
+enumBS bs (Continue k) = k (Chunks [bs])
+enumBS bs (Yield x s)  = Iteratee $ return $ Yield x (s `mappend` Chunks [bs])
+enumBS _  (Error e)    = Iteratee $ return $ Error e
+{-# INLINE enumBS #-}
 
 
 ------------------------------------------------------------------------------
--- | Buffers an iteratee.
---
--- Our enumerators produce a lot of little strings; rather than spending all
--- our time doing kernel context switches for 4-byte write() calls, we buffer
--- the iteratee to send 8KB at a time.
---
--- The IORef returned can be set to True to "cancel" buffering. We added this
--- so that transfer-encoding: chunked (which needs its own buffer and therefore
--- doesn't need /its/ output buffered) can switch the outer buffer off.
---
-bufferIteratee :: Iteratee IO a -> IO (Iteratee IO a, IORef Bool)
-bufferIteratee iteratee = do
-    esc <- newIORef False
-    return $ (start esc iteratee, esc)
+-- | Enumerates a lazy bytestring.
 
-  where
-    blocksize = 8192
+enumLBS :: (Monad m) => L.ByteString -> Enumerator ByteString m a
+enumLBS bs = enumList 1 (L.toChunks bs)
+{-# INLINE enumLBS #-}
 
-    start esc iter = IterateeG $! checkRef esc iter
 
-    checkRef esc iter ch = do
-        quit <- readIORef esc
-        if quit
-          then runIter iter ch
-          else f (D.empty,0) iter ch
+------------------------------------------------------------------------------
+skipToEof :: (Monad m) => Iteratee a m ()
+skipToEof = continue k
+  where
+    k EOF = return ()
+    k _   = skipToEof
 
-    --go :: (DList ByteString, Int) -> Iteratee m a -> Iteratee m a
-    go (!dl,!n) iter = IterateeG $! f (dl,n) iter
 
-    --f :: (DList ByteString, Int) -> Iteratee m a -> Stream -> m (IterV m a)
-    f _       !iter ch@(EOF (Just _)) = runIter iter ch
-    f (!dl,_) !iter ch@(EOF Nothing)  = do
-        iter' <- if S.null str
-                   then return iter
-                   else liftM liftI $ runIter iter $ Chunk big
-        runIter iter' ch
-      where
-        str = S.concat $ D.toList dl
-        big = WrapBS str
+------------------------------------------------------------------------------
+-- | Wraps an 'Iteratee', counting the number of bytes consumed by it.
+countBytes :: (Monad m) => forall a .
+              Iteratee ByteString m a
+           -> Iteratee ByteString m (a, Int64)
+countBytes i = Iteratee $ do
+    step <- runIteratee i
+    case step of
+      (Continue k) -> return (Continue $ go 0 k)
+      (Yield x s)  -> return $ Yield (x,0) s
+      (Error e)    -> return $ Error e
 
-    f (!dl,!n) iter (Chunk (WrapBS s)) =
-        if n' >= blocksize
-           then do
-               iterv <- runIter iter (Chunk big)
-               case iterv of
-                  Done x rest     -> return $ Done x rest
-                  Cont i (Just e) -> return $ Cont i (Just e)
-                  Cont i Nothing  -> return $ Cont (go (D.empty,0) i) Nothing
-           else return $ Cont (go (dl',n') iter) Nothing
-      where
-        m   = S.length s
-        n'  = n+m
-        dl' = D.snoc dl s
-        big = WrapBS $ S.concat $ D.toList dl'
+  where
+    go !n k str = Iteratee $ do
+        let len = toEnum $ streamLength str
+        step <- runIteratee (k str)
+        case step of
+          (Continue k') -> return (Continue $ go (n + len) k')
+          (Yield x s)   -> let len' = n + len - (toEnum $ streamLength s)
+                           in return (Yield (x, len') s)
+          (Error e)     -> return (Error e)
 
 
+------------------------------------------------------------------------------
 bUFSIZ :: Int
 bUFSIZ = 8192
 
 
+------------------------------------------------------------------------------
 -- | Creates a buffer to be passed into 'unsafeBufferIterateeWithBuffer'.
 mkIterateeBuffer :: IO (ForeignPtr CChar)
 mkIterateeBuffer = mallocPlainForeignPtrBytes bUFSIZ
 
+
 ------------------------------------------------------------------------------
--- | Buffers an iteratee, \"unsafely\". Here we use a fixed binary buffer which
--- we'll re-use, meaning that if you hold on to any of the bytestring data
--- passed into your iteratee (instead of, let's say, shoving it right out a
--- socket) it'll get changed out from underneath you, breaking referential
+-- | Buffers an iteratee, \"unsafely\". Here we use a fixed binary buffer
+-- which we'll re-use, meaning that if you hold on to any of the bytestring
+-- data passed into your iteratee (instead of, let's say, shoving it right out
+-- a socket) it'll get changed out from underneath you, breaking referential
 -- transparency. Use with caution!
---
-unsafeBufferIteratee :: Iteratee IO a -> IO (Iteratee IO a)
-unsafeBufferIteratee iter = do
+unsafeBufferIteratee :: Iteratee ByteString IO a
+                     -> IO (Iteratee ByteString IO a)
+unsafeBufferIteratee step = do
     buf <- mkIterateeBuffer
-    unsafeBufferIterateeWithBuffer buf iter
+    return $ unsafeBufferIterateeWithBuffer buf step
 
 
 ------------------------------------------------------------------------------
--- | Buffers an iteratee, \"unsafely\". Here we use a fixed binary buffer which
--- we'll re-use, meaning that if you hold on to any of the bytestring data
--- passed into your iteratee (instead of, let's say, shoving it right out a
--- socket) it'll get changed out from underneath you, breaking referential
+-- | Buffers an iteratee, \"unsafely\". Here we use a fixed binary buffer
+-- which we'll re-use, meaning that if you hold on to any of the bytestring
+-- data passed into your iteratee (instead of, let's say, shoving it right out
+-- a socket) it'll get changed out from underneath you, breaking referential
 -- transparency. Use with caution!
 --
 -- This version accepts a buffer created by 'mkIterateeBuffer'.
 --
 unsafeBufferIterateeWithBuffer :: ForeignPtr CChar
-                               -> Iteratee IO a
-                               -> IO (Iteratee IO a)
-unsafeBufferIterateeWithBuffer buf iteratee = do
-    return $! start iteratee
+                               -> Iteratee ByteString IO a
+                               -> Iteratee ByteString IO a
+unsafeBufferIterateeWithBuffer buf iter = Iteratee $ do
+    step <- runIteratee iter
+    start step
 
   where
-    start iter = IterateeG $! f 0 iter
-    go bytesSoFar iter =
-        {-# SCC "unsafeBufferIteratee/go" #-}
-        IterateeG $! f bytesSoFar iter
+    --------------------------------------------------------------------------
+    start :: Step ByteString IO a -> IO (Step ByteString IO a)
+    start (Continue k) = return $ Continue $ go 0 k
+    start s@_          = return s
 
-    sendBuf n iter =
-        {-# SCC "unsafeBufferIteratee/sendBuf" #-}
+
+    --------------------------------------------------------------------------
+    sendBuf :: Int
+            -> (Stream ByteString -> Iteratee ByteString IO a)
+            -> IO (Step ByteString IO a)
+    sendBuf n k =
+      {-# SCC "unsafeBufferIteratee/sendBuf" #-} do
+        assert (n > 0)       (return ())
+        assert (n <= bUFSIZ) (return ())
+
         withForeignPtr buf $ \ptr -> do
-            s <- S.unsafePackCStringLen (ptr, n)
-            runIter iter $ Chunk $ WrapBS s
+            !s <- S.unsafePackCStringLen (ptr, n)
+            runIteratee $ k $ Chunks [s]
 
-    copy c@(EOF _) = c
-    copy (Chunk (WrapBS s)) = Chunk $ WrapBS $ S.copy s
 
-    f _ iter ch@(EOF (Just _)) = runIter iter ch
+    --------------------------------------------------------------------------
+    copy EOF         = EOF
+    copy (Chunks xs) = zs `deepseq` Chunks ys
+      where
+        !ys  = Prelude.map S.copy xs
+        !zs  = Prelude.map (`seq` ()) ys
 
-    f !n iter ch@(EOF Nothing) =
+    --------------------------------------------------------------------------
+    go :: Int
+       -> (Stream ByteString -> Iteratee ByteString IO a)
+       -> (Stream ByteString -> Iteratee ByteString IO a)
+    go !n !k EOF = Iteratee $ do
         if n == 0
-          then runIter iter ch
+          then runIteratee $ k EOF
           else do
-              iter' <- liftM liftI $ sendBuf n iter
-              runIter iter' ch
+              assert (n > 0)       (return ())
+              assert (n <= bUFSIZ) (return ())
 
-    f !n iter (Chunk (WrapBS s)) = do
+              step  <- sendBuf n k
+              step2 <- runIteratee $ enumEOF step
+              return $ copyStep step2
+
+
+    go !n !k (Chunks xs) = Iteratee $ do
+        assert (n >= 0)      (return ())
+        assert (n <= bUFSIZ) (return ())
+
+        let s = S.concat xs
         let m = S.length s
-        if m+n > bUFSIZ
-          then overflow n iter s m
-          else copyAndCont n iter s m
+        if m+n >= bUFSIZ
+          then overflow    n k s m
+          else copyAndCont n k s m
 
-    copyAndCont n iter s m =
+
+    --------------------------------------------------------------------------
+    copyStep (Yield x r) = let !z = copy r in Yield x z
+    copyStep x           = x
+
+
+    --------------------------------------------------------------------------
+    copyAndCont :: Int
+                -> (Stream ByteString -> Iteratee ByteString IO a)
+                -> ByteString
+                -> Int
+                -> IO (Step ByteString IO a)
+    copyAndCont !n k !s !m =
       {-# SCC "unsafeBufferIteratee/copyAndCont" #-} do
-        S.unsafeUseAsCStringLen s $ \(p,sz) ->
-            withForeignPtr buf $ \bufp -> do
-                let b' = plusPtr bufp n
-                copyBytes b' p sz
+        assert (n >= 0) (return ())
+        assert (n+m < bUFSIZ) (return ())
+        S.unsafeUseAsCStringLen s $ \(p,sz) -> do
+          assert (m == sz) (return ())
+          withForeignPtr buf $ \bufp -> do
+            let b' = plusPtr bufp n
+            copyBytes b' p sz
 
-        return $ Cont (go (n+m) iter) Nothing
+        return $ Continue $ go (n+m) k
 
 
-    overflow n iter s m =
+
+    --------------------------------------------------------------------------
+    overflow :: Int
+             -> (Stream ByteString -> Iteratee ByteString IO a)
+             -> ByteString
+             -> Int
+             -> IO (Step ByteString IO a)
+    overflow !n k !s !m =
       {-# SCC "unsafeBufferIteratee/overflow" #-} do
-        let rest = bUFSIZ - n
-        let m2   = m - rest
+        assert (n+m >= bUFSIZ) (return ())
+        assert (n < bUFSIZ)    (return ())
+
+        let rest    = bUFSIZ - n
+        let m2      = m - rest
+
         let (s1,s2) = S.splitAt rest s
 
         S.unsafeUseAsCStringLen s1 $ \(p,_) ->
@@ -272,165 +342,206 @@
             let b' = plusPtr bufp n
             copyBytes b' p rest
 
-            iv <- sendBuf bUFSIZ iter
+            iv <- sendBuf bUFSIZ k
             case iv of
-              Done x r        -> return $
-                                 Done x (copy r `mappend` (Chunk $ WrapBS s2))
-              Cont i (Just e) -> return $ Cont i (Just e)
-              Cont i Nothing  -> do
+              (Yield x r)   -> let !z = copy r
+                               in return $ Yield x $ (z `mappend` Chunks [s2])
+              (Error e)     -> return $ Error e
+              (Continue k') -> do
                   -- check the size of the remainder; if it's bigger than the
                   -- buffer size then just send it
                   if m2 >= bUFSIZ
                     then do
-                        iv' <- runIter i (Chunk $ WrapBS s2)
-                        case iv' of
-                          Done x r         -> return $ Done x (copy r)
-                          Cont i' (Just e) -> return $ Cont i' (Just e)
-                          Cont i' Nothing  -> return $ Cont (go 0 i') Nothing
-                    else copyAndCont 0 i s2 m2
+                        step <- runIteratee $ k' $ Chunks [s2]
+                        case step of
+                          (Yield x r)    -> let !z = copy r
+                                            in return $! Yield x z
+                          (Error e)      -> return $ Error e
+                          (Continue k'') -> return $ Continue $ go 0 k''
 
+                    else copyAndCont 0 k' s2 m2
 
-------------------------------------------------------------------------------
--- | Enumerates a strict bytestring.
-enumBS :: (Monad m) => ByteString -> Enumerator m a
-enumBS bs = enumPure1Chunk $ WrapBS bs
-{-# INLINE enumBS #-}
 
+------------------------------------------------------------------------------
+-- | Skip n elements of the stream, if there are that many
+drop :: (Monad m) => Int -> Iteratee ByteString m ()
+drop k = drop' (toEnum k)
 
 ------------------------------------------------------------------------------
--- | Enumerates a lazy bytestring.
-enumLBS :: (Monad m) => L.ByteString -> Enumerator m a
-enumLBS lbs = el chunks
+-- | Skip n elements of the stream, if there are that many
+drop' :: (Monad m) => Int64 -> Iteratee ByteString m ()
+drop' 0  = return ()
+drop' !n = continue k
+
   where
-    el [] i     = return i
-    el (x:xs) i = do
-        i' <- liftM liftI $ runIter i (Chunk $ WrapBS x)
-        el xs i'
+    k EOF         = return ()
+    k (Chunks xs) = chunks n xs
 
-    chunks = L.toChunks lbs
+    chunks !m []     = drop' m
+    chunks !m (x:xs) = do
+        let strlen = toEnum $ S.length x
+        if strlen <= m
+          then chunks (m-strlen) xs
+          else yield () $ Chunks ((S.drop (fromEnum m) x):xs)
 
 
 ------------------------------------------------------------------------------
--- | Converts a lazy bytestring to a wrapped bytestring.
-toWrap :: L.ByteString -> WrappedByteString Word8
-toWrap = WrapBS . S.concat . L.toChunks
-{-# INLINE toWrap #-}
+data ShortWriteException = ShortWriteException
+   deriving (Typeable)
 
 
 ------------------------------------------------------------------------------
--- | Converts a wrapped bytestring to a lazy bytestring.
-fromWrap :: WrappedByteString Word8 -> L.ByteString
-fromWrap = L.fromChunks . (:[]) . unWrap
-{-# INLINE fromWrap #-}
+instance Show ShortWriteException where
+    show ShortWriteException = "Short write"
 
 
 ------------------------------------------------------------------------------
--- | Skip n elements of the stream, if there are that many
--- This is the Int64 version of the drop function in the iteratee library
-drop' :: (SC.StreamChunk s el, Monad m)
-       => Int64
-       -> IterateeG s el m ()
-drop' 0 = return ()
-drop' n = IterateeG step
-  where
-  step (Chunk str)
-    | strlen <= n  = return $ Cont (drop' (n - strlen)) Nothing
-      where
-        strlen = fromIntegral $ SC.length str
-  step (Chunk str) = return $ Done () (Chunk (LL.drop (fromIntegral n) str))
-  step stream      = return $ Done () stream
+instance Exception ShortWriteException
 
 
 ------------------------------------------------------------------------------
--- | Reads n elements from a stream and applies the given iteratee to
--- the stream of the read elements. Reads exactly n elements, and if
--- the stream is short propagates an error.
-takeExactly :: (SC.StreamChunk s el, Monad m)
-            => Int64
-            -> EnumeratorN s el s el m a
-takeExactly 0 iter = return iter
-takeExactly n' iter =
-    if n' < 0
-      then takeExactly 0 iter
-      else IterateeG (step n')
+data TooManyBytesReadException = TooManyBytesReadException
+   deriving (Typeable)
+
+
+------------------------------------------------------------------------------
+instance Show TooManyBytesReadException where
+    show TooManyBytesReadException = "Too many bytes read"
+
+
+------------------------------------------------------------------------------
+instance Exception TooManyBytesReadException
+
+
+
+------------------------------------------------------------------------------
+take :: (Monad m) => Int -> Enumeratee ByteString ByteString m a
+take k = take' (toEnum k)
+
+
+------------------------------------------------------------------------------
+take' :: (Monad m) => Int64 -> Enumeratee ByteString ByteString m a
+take' _ y@(Yield _ _   ) = return y
+take' _   (Error e     ) = throwError e
+take' !n st@(Continue k) = do
+    if n == 0
+      then lift $ runIteratee $ k EOF
+      else do
+        mbX <- head
+        maybe (lift $ runIteratee $ k EOF)
+              check
+              mbX
+
   where
-  step n chk@(Chunk str)
-    | SC.null str = return $ Cont (takeExactly n iter) Nothing
-    | strlen < n  = liftM (flip Cont Nothing) inner
-    | otherwise   = done (Chunk s1) (Chunk s2)
+    check x | S.null x    = take' n st
+            | strlen <= n = do
+                  newStep <- lift $ runIteratee $ k $ Chunks [x]
+                  take' (n-strlen) newStep
+            | otherwise = do
+                  step1 <- lift $ runIteratee $ k $ Chunks [s1]
+                  step2 <- lift $ runIteratee $ enumEOF step1
+
+                  case step2 of
+                    (Yield v _)    -> yield (Yield v EOF) (Chunks [s2])
+                    (Error e)      -> throwError e
+                    (Continue _)   -> error "divergent iteratee"
       where
-        strlen = fromIntegral $ SC.length str
-        inner  = liftM (check (n - strlen)) (runIter iter chk)
-        (s1, s2) = SC.splitAt (fromIntegral n) str
-  step _n (EOF (Just e))    = return $ Cont undefined (Just e)
-  step _n (EOF Nothing)     = return $ Cont undefined (Just (Err "short write"))
-  check n (Done x _)        = drop' n >> return (return x)
-  check n (Cont x Nothing)  = takeExactly n x
-  check n (Cont _ (Just e)) = drop' n >> throwErr e
-  done s1 s2 = liftM (flip Done s2) (runIter iter s1 >>= checkIfDone return)
+        strlen  = toEnum $ S.length x
+        (s1,s2) = S.splitAt (fromEnum n) x
 
 
 ------------------------------------------------------------------------------
--- | Reads up to n elements from a stream and applies the given iteratee to the
--- stream of the read elements. If more than n elements are read, propagates an
--- error.
-takeNoMoreThan :: (SC.StreamChunk s el, Monad m)
-               => Int64
-               -> EnumeratorN s el s el m a
-takeNoMoreThan n' iter =
-    if n' < 0
-      then takeNoMoreThan 0 iter
-      else IterateeG (step n')
+-- | Reads n bytes from a stream and applies the given iteratee to the stream
+-- of the read elements. Reads exactly n bytes, and if the stream is short
+-- propagates an error.
+takeExactly :: (Monad m)
+            => Int64
+            -> Enumeratee ByteString ByteString m a
+takeExactly 0   s = do
+    s' <- lift $ runIteratee $ enumEOF s
+    case s' of
+      (Continue _) -> error "divergent iteratee"
+      (Error e)    -> throwError e
+      (Yield v _)  -> yield (Yield v EOF) EOF
+
+takeExactly !n  y@(Yield _ _ ) = drop' n >> return y
+takeExactly _     (Error e   ) = throwError e
+takeExactly !n st@(Continue k) = do
+    if n == 0
+      then lift $ runIteratee $ k EOF
+      else do
+        mbX <- head
+        maybe (throwError ShortWriteException)
+              check
+              mbX
+
   where
-    step n chk@(Chunk str)
-      | SC.null str = return $ Cont (takeNoMoreThan n iter) Nothing
-      | strlen < n  = liftM (flip Cont Nothing) inner
-      | otherwise   = done (Chunk s1) (Chunk s2)
-          where
-            strlen   = fromIntegral $ SC.length str
-            inner    = liftM (check (n - strlen)) (runIter iter chk)
-            (s1, s2) = SC.splitAt (fromIntegral n) str
+    check x | S.null x   = takeExactly n st
+            | strlen < n = do
+                  newStep <- lift $ runIteratee $ k $ Chunks [x]
+                  takeExactly (n-strlen) newStep
+            | otherwise = do
+                  step1 <- lift $ runIteratee $ k $ Chunks [s1]
+                  step2 <- lift $ runIteratee $ enumEOF step1
 
-    step _n (EOF (Just e))    = return $ Cont undefined (Just e)
-    step _n chk@(EOF Nothing) = do
-        v  <- runIter iter chk
+                  case step2 of
+                    (Continue _) -> error "divergent iteratee"
+                    (Error e)    -> throwError e
+                    (Yield v _)  -> yield (Yield v EOF) (Chunks [s2])
 
-        case v of
-          (Done x s)        -> return $ Done (return x) s
-          (Cont _ (Just e)) -> return $ Cont undefined (Just e)
-          (Cont _ Nothing)  -> return $ Cont (throwErr $ Err "premature EOF") Nothing
+      where
+        strlen  = toEnum $ S.length x
+        (s1,s2) = S.splitAt (fromEnum n) x
 
-    check _ v@(Done _ _)      = return $ liftI v
-    check n (Cont x Nothing)  = takeNoMoreThan n x
-    check _ (Cont _ (Just e)) = throwErr e
 
-    done _ (EOF _) = error "impossible"
-    done s1 s2@(Chunk s2') = do
-        v <- runIter iter s1
-        case v of
-          (Done x s')       -> return $ Done (return x) (s' `mappend` s2)
-          (Cont _ (Just e)) -> return $ Cont undefined (Just e)
-          (Cont i Nothing)  ->
-              if SC.null s2'
-                then return $ Cont (takeNoMoreThan 0 i) Nothing
-                else return $ Cont undefined (Just $ Err "too many bytes")
+------------------------------------------------------------------------------
+takeNoMoreThan :: (Monad m) =>
+                  Int64 -> Enumeratee ByteString ByteString m a
+takeNoMoreThan _   y@(Yield _ _)  = return y
+takeNoMoreThan _     (Error e  )  = throwError e
+takeNoMoreThan !n st@(Continue k) = do
+    mbX <- head
+    maybe (lift $ runIteratee $ k EOF)
+          check
+          mbX
 
+  where
+    check x | S.null x    = takeNoMoreThan n st
+            | strlen <= n = do
+                  newStep <- lift $ runIteratee $ k $ Chunks [x]
+                  takeNoMoreThan (n-strlen) newStep
+            | otherwise = do
+                  step1 <- lift $ runIteratee $ k $ Chunks [s1]
+                  case step1 of
+                    (Yield v rest) -> yield (Yield v EOF)
+                                            (rest `mappend` Chunks [s2])
+                    (Error e)      -> throwError e
+                    (Continue _)   -> throwError TooManyBytesReadException
+      where
+        strlen  = toEnum $ S.length x
+        (s1,s2) = S.splitAt (fromEnum n) x
 
+
 ------------------------------------------------------------------------------
 {-# INLINE _enumFile #-}
-_enumFile :: FilePath -> Iteratee IO a -> IO (Iteratee IO a)
+_enumFile :: FilePath
+          -> Enumerator ByteString IO a
 _enumFile fp iter = do
     h  <- liftIO $ openBinaryFile fp ReadMode
-    enumHandle h iter `finally` hClose h
+    enumHandle 32678 h iter `finally` (liftIO $ hClose h)
 
 
 ------------------------------------------------------------------------------
 data InvalidRangeException = InvalidRangeException
    deriving (Typeable)
 
+
+------------------------------------------------------------------------------
 instance Show InvalidRangeException where
     show InvalidRangeException = "Invalid range"
 
+
+------------------------------------------------------------------------------
 instance Exception InvalidRangeException
 
 
@@ -438,22 +549,26 @@
 {-# INLINE _enumFilePartial #-}
 _enumFilePartial :: FilePath
                  -> (Int64,Int64)
-                 -> Iteratee IO a
-                 -> IO (Iteratee IO a)
+                 -> Enumerator ByteString IO a
 _enumFilePartial fp (start,end) iter = do
     let len = end - start
-    withBinaryFile fp ReadMode
-      (\h -> do
-        unless (start == 0) $ hSeek h AbsoluteSeek $ toInteger start
-        enumHandle h $ joinI $ takeExactly len iter)
 
+    bracket (liftIO $ openBinaryFile fp ReadMode)
+            (liftIO . hClose)
+            (\h -> do
+                 unless (start == 0) $ liftIO $
+                        hSeek h AbsoluteSeek $ toInteger start
+                 step <- lift $ runIteratee $ joinI $ takeExactly len iter
+                 enumHandle 32678 h step)
 
-enumFile :: FilePath -> Iteratee IO a -> IO (Iteratee IO a)
+
+------------------------------------------------------------------------------
+enumFile :: FilePath -> Enumerator ByteString IO a
 enumFilePartial :: FilePath
                 -> (Int64,Int64)
-                -> Iteratee IO a
-                -> IO (Iteratee IO a)
+                -> Enumerator ByteString IO a
 
+
 #ifdef PORTABLE
 
 enumFile = _enumFile
@@ -467,53 +582,49 @@
 maxMMapFileSize :: FileOffset
 maxMMapFileSize = 41943040
 
+
+------------------------------------------------------------------------------
 tooBigForMMap :: FilePath -> IO Bool
 tooBigForMMap fp = do
     stat <- getFileStatus fp
     return $ fileSize stat > maxMMapFileSize
 
 
-enumFile fp iter = do
-    -- for small files we'll use mmap to save ourselves a copy, otherwise we'll
-    -- stream it
-    tooBig <- tooBigForMMap fp
+------------------------------------------------------------------------------
+enumFile _  (Error e)    = throwError e
+enumFile _  (Yield x _)  = yield x EOF
+enumFile fp st@(Continue k) = do
+    -- for small files we'll use mmap to save ourselves a copy, otherwise
+    -- we'll stream it
+    tooBig <- lift $ tooBigForMMap fp
 
     if tooBig
-      then _enumFile fp iter
+      then _enumFile fp st
       else do
-        es <- try $
-              liftM WrapBS $
-              unsafeMMapFile fp
-
+        es <- try $ lift $ unsafeMMapFile fp
         case es of
-          (Left (e :: SomeException)) -> return $ throwErr
-                                                $ Err
-                                                $ "IO error" ++ show e
-
-          (Right s) -> liftM liftI $ runIter iter $ Chunk s
+          (Left (e :: SomeException)) -> throwError e
+          (Right s) -> k $ Chunks [s]
 
 
-enumFilePartial fp rng@(start,end) iter = do
-    when (end < start) $ throw InvalidRangeException
+------------------------------------------------------------------------------
+enumFilePartial _ _ (Error e)   = throwError e
+enumFilePartial _ _ (Yield x _) = yield x EOF
+enumFilePartial fp rng@(start,end) st@(Continue k) = do
+    when (end < start) $ throwError InvalidRangeException
 
     let len = end - start
 
-    tooBig <- tooBigForMMap fp
+    tooBig <- lift $ tooBigForMMap fp
 
     if tooBig
-      then _enumFilePartial fp rng iter
+      then _enumFilePartial fp rng st
       else do
-        es <- try $ unsafeMMapFile fp
+        es <- try $ lift $ unsafeMMapFile fp
 
         case es of
-          (Left (e::SomeException)) -> return $ throwErr
-                                              $ Err
-                                              $ "IO error" ++ show e
-
-          (Right s) -> liftM liftI $ runIter iter
-                                   $ Chunk
-                                   $ WrapBS
-                                   $ S.take (fromEnum len)
-                                   $ S.drop (fromEnum start) s
+          (Left (e::SomeException)) -> throwError e
+          (Right s) -> k $ Chunks [ S.take (fromEnum len) $
+                                    S.drop (fromEnum start) s ]
 
 #endif
diff --git a/src/Snap/Starter.hs b/src/Snap/Starter.hs
deleted file mode 100644
--- a/src/Snap/Starter.hs
+++ /dev/null
@@ -1,99 +0,0 @@
-{-# LANGUAGE TemplateHaskell #-}
-module Main where
-
-------------------------------------------------------------------------------
-import           Char
-import           Data.List
-import qualified Data.Text as T
-import           System
-import           System.Directory
-import           System.Console.GetOpt
-import           System.FilePath
-------------------------------------------------------------------------------
-
-import Snap.StarterTH
-
-
-------------------------------------------------------------------------------
--- Creates a value tDir :: ([String], [(String, String)])
-$(buildData "tDirDefault"   "default")
-$(buildData "tDirBareBones" "barebones")
-
-
-------------------------------------------------------------------------------
-usage :: String
-usage = unlines
-    ["Usage:"
-    ,""
-    ,"  snap <action>"
-    ,""
-    ,"    <action> can be one of:"
-    ,"      init - create a new project directory structure in the current directory"
-    ]
-
-
-------------------------------------------------------------------------------
-data InitFlag = InitBareBones
-              | InitHelp
-  deriving (Show, Eq)
-
-
-setup :: String -> ([FilePath], [(String, String)]) -> IO ()
-setup projName tDir = do
-    mapM createDirectory (fst tDir)
-    mapM_ write (snd tDir)
-  where
-    write (f,c) =
-        if isSuffixOf "foo.cabal" f
-          then writeFile (projName++".cabal") (insertProjName $ T.pack c)
-          else writeFile f c
-    isNameChar c = isAlphaNum c || c == '-'
-    insertProjName c = T.unpack $ T.replace
-                           (T.pack "projname")
-                           (T.pack $ filter isNameChar projName) c
-
-------------------------------------------------------------------------------
-initProject :: [String] -> IO ()
-initProject args = do
-    case getOpt Permute options args of
-      (flags, _, [])
-        | InitHelp `elem` flags -> do putStrLn initUsage
-                                      exitFailure
-        | otherwise             -> init' (InitBareBones `elem` flags)
-
-      (_, _, errs) -> do putStrLn $ concat errs
-                         putStrLn initUsage
-                         exitFailure
-  where
-    initUsage = unlines
-        ["Usage:"
-        ,""
-        ,"  snap init"
-        ,""
-        ,"    -b  --barebones   Depend only on -core and -server"
-        ,"    -h  --help        Print this message"
-        ]
-
-    options =
-        [ Option ['b'] ["barebones"] (NoArg InitBareBones)
-                 "Depend only on -core and -server"
-        , Option ['h'] ["help"]      (NoArg InitHelp)
-                 "Print this message"
-        ]
-
-    init' isBareBones = do
-        cur <- getCurrentDirectory
-        let dirs = splitDirectories cur
-            projName = last dirs
-        setup projName (if isBareBones then tDirBareBones else tDirDefault)
-
-
-------------------------------------------------------------------------------
-main :: IO ()
-main = do
-    args <- getArgs
-    case args of
-        ("init":args') -> initProject args'
-        _              -> do putStrLn usage
-                             exitFailure
-
diff --git a/src/Snap/StarterTH.hs b/src/Snap/StarterTH.hs
deleted file mode 100644
--- a/src/Snap/StarterTH.hs
+++ /dev/null
@@ -1,55 +0,0 @@
-{-# LANGUAGE TemplateHaskell #-}
-module Snap.StarterTH where
-
-------------------------------------------------------------------------------
-import qualified Data.Foldable as F
-import           Data.List
-import           Language.Haskell.TH
-import           System.Directory.Tree
-------------------------------------------------------------------------------
-
-
-------------------------------------------------------------------------------
--- Convenience types
-type FileData = (String, String)
-type DirData = FilePath
-
-
-------------------------------------------------------------------------------
--- Gets all the directorys in a DirTree
-getDirs :: [FilePath] -> DirTree a -> [FilePath]
-getDirs prefix (Dir n c) = (intercalate "/" (reverse (n:prefix))) : concatMap (getDirs (n:prefix)) c
-getDirs _ (File _ _) = []
-getDirs _ (Failed _ _) = []
-
-
-------------------------------------------------------------------------------
--- Reads a directory and returns a tuple of the list of all directories
--- encountered and a list of filenames and content strings.
-readTree :: FilePath -> IO ([DirData], [FileData])
-readTree dir = do
-    d <- readDirectory $ dir++"/."
-    let ps = zipPaths $ "" :/ (free d)
-        fd = F.foldr (:) [] ps
-        dirs = tail $ getDirs [] $ free d
-    return $ (dirs, fd)
-
-
-------------------------------------------------------------------------------
--- Calls readTree and returns it's value in a quasiquote.
-dirQ :: FilePath -> Q Exp
-dirQ tplDir = do
-    d <- runIO $ readTree $ "project_template/"++tplDir
-    runQ [| d |]
-
-
-------------------------------------------------------------------------------
--- Creates a declaration assigning the specified name the value returned by
--- dirQ.
-buildData :: String -> FilePath -> Q [Dec]
-buildData dirName tplDir = do
-    v <- valD (varP (mkName dirName))
-                    (normalB $ dirQ tplDir)
-                    []
-    return [v]
-
diff --git a/src/Snap/Types.hs b/src/Snap/Types.hs
--- a/src/Snap/Types.hs
+++ b/src/Snap/Types.hs
@@ -9,9 +9,11 @@
     -- * The Snap Monad
     Snap
   , runSnap
+  , MonadSnap(..)
   , NoHandlerException(..)
 
     -- ** Functions for control flow and early termination
+  , bracketSnap
   , finishWith
   , pass
 
@@ -92,6 +94,7 @@
   , rspStatusReason
   , setContentType
   , addCookie
+  , getCookie
   , setContentLength
   , clearContentLength
   , redirect
@@ -121,8 +124,10 @@
 
 ------------------------------------------------------------------------------
 import           Snap.Internal.Http.Types
+import           Snap.Internal.Instances ()
 import           Snap.Internal.Routing
 import           Snap.Internal.Types
+import           Snap.Iteratee (Enumerator)
 ------------------------------------------------------------------------------
 
 -- $httpDoc
diff --git a/src/Snap/Util/FileServe.hs b/src/Snap/Util/FileServe.hs
--- a/src/Snap/Util/FileServe.hs
+++ b/src/Snap/Util/FileServe.hs
@@ -167,11 +167,13 @@
 -- | Gets a path from the 'Request' using 'rqPathInfo' and makes sure it is
 -- safe to use for opening files.  A path is safe if it is a relative path
 -- and has no ".." elements to escape the intended directory structure.
-getSafePath :: Snap FilePath
+getSafePath :: MonadSnap m => m FilePath
 getSafePath = do
     req <- getRequest
-    let p = S.unpack $ rqPathInfo req
+    let mp = urlDecode $ rqPathInfo req
 
+    p <- maybe pass (return . S.unpack) mp
+
     -- relative paths only!
     when (not $ isRelative p) pass
 
@@ -184,23 +186,25 @@
 
 ------------------------------------------------------------------------------
 -- | Serves files out of the given directory. The relative path given in
--- 'rqPathInfo' is searched for the given file, and the file is served with the
--- appropriate mime type if it is found. Absolute paths and \"@..@\" are prohibited
--- to prevent files from being served from outside the sandbox.
+-- 'rqPathInfo' is searched for the given file, and the file is served with
+-- the appropriate mime type if it is found. Absolute paths and \"@..@\" are
+-- prohibited to prevent files from being served from outside the sandbox.
 --
 -- Uses 'defaultMimeTypes' to determine the @Content-Type@ based on the file's
 -- extension.
-fileServe :: FilePath  -- ^ root directory
-          -> Snap ()
+fileServe :: MonadSnap m
+          => FilePath  -- ^ root directory
+          -> m ()
 fileServe = fileServe' defaultMimeTypes
 {-# INLINE fileServe #-}
 
 
 ------------------------------------------------------------------------------
 -- | Same as 'fileServe', with control over the MIME mapping used.
-fileServe' :: MimeMap           -- ^ MIME type mapping
+fileServe' :: MonadSnap m
+           => MimeMap           -- ^ MIME type mapping
            -> FilePath          -- ^ root directory
-           -> Snap ()
+           -> m ()
 fileServe' mm root = do
     sp <- getSafePath
     let fp   = root </> sp
@@ -218,8 +222,9 @@
 -- | Serves a single file specified by a full or relative path.  The
 -- path restrictions on fileServe don't apply to this function since
 -- the path is not being supplied by the user.
-fileServeSingle :: FilePath          -- ^ path to file
-                -> Snap ()
+fileServeSingle :: MonadSnap m
+                => FilePath          -- ^ path to file
+                -> m ()
 fileServeSingle fp =
     fileServeSingle' (fileType defaultMimeTypes (takeFileName fp)) fp
 {-# INLINE fileServeSingle #-}
@@ -227,9 +232,10 @@
 
 ------------------------------------------------------------------------------
 -- | Same as 'fileServeSingle', with control over the MIME mapping used.
-fileServeSingle' :: ByteString        -- ^ MIME type mapping
+fileServeSingle' :: MonadSnap m
+                 => ByteString        -- ^ MIME type mapping
                  -> FilePath          -- ^ path to file
-                 -> Snap ()
+                 -> m ()
 fileServeSingle' mime fp = do
     reqOrig <- getRequest
 
@@ -273,7 +279,8 @@
 
 
     -- now check: is this a range request? If there is an 'If-Range' header
-    -- with an old modification time we skip this check and send a 200 response
+    -- with an old modification time we skip this check and send a 200
+    -- response
     let skipRangeCheck = maybe (False)
                                (\lt -> mt > lt)
                                mbIfRange
@@ -282,7 +289,7 @@
     -- partial response if it matches.
     wasRange <- if skipRangeCheck
                   then return False
-                  else checkRangeReq req fp sz
+                  else liftSnap $ checkRangeReq req fp sz
 
     dbg $ "was this a range request? " ++ Prelude.show wasRange
 
@@ -290,7 +297,7 @@
     unless wasRange $ do
       modifyResponse $ setResponseCode 200
                      . setContentLength sz
-      sendFile fp
+      liftSnap $ sendFile fp
 
   where
     --------------------------------------------------------------------------
@@ -341,7 +348,7 @@
 
 
 ------------------------------------------------------------------------------
-checkRangeReq :: Request -> FilePath -> Int64 -> Snap Bool
+checkRangeReq :: (MonadSnap m) => Request -> FilePath -> Int64 -> m Bool
 checkRangeReq req fp sz = do
     -- TODO/FIXME: multiple ranges
     dbg $ "checkRangeReq, fp=" ++ fp ++ ", sz=" ++ Prelude.show sz
@@ -409,7 +416,7 @@
                let crng = S.concat $
                           L.toChunks $
                           L.concat ["bytes */", show sz]
-               
+
                modifyResponse $ setResponseCode 416
                               . setHeader "Content-Range" crng
                               . setContentLength 0
@@ -417,10 +424,10 @@
                               . deleteHeader "Content-Encoding"
                               . deleteHeader "Transfer-Encoding"
                               . setResponseBody (enumBS "")
-               
-               return True
 
+               return True
 
 
+------------------------------------------------------------------------------
 dbg :: (MonadIO m) => String -> m ()
 dbg s = debug $ "FileServe:" ++ s
diff --git a/src/Snap/Util/GZip.hs b/src/Snap/Util/GZip.hs
--- a/src/Snap/Util/GZip.hs
+++ b/src/Snap/Util/GZip.hs
@@ -1,34 +1,34 @@
 {-# LANGUAGE DeriveDataTypeable        #-}
 {-# LANGUAGE ExistentialQuantification #-}
 {-# LANGUAGE OverloadedStrings         #-}
+{-# LANGUAGE PackageImports            #-}
 {-# LANGUAGE ScopedTypeVariables       #-}
 
 module Snap.Util.GZip
 ( withCompression
 , withCompression' ) where
 
-import qualified Codec.Compression.GZip as GZip
-import qualified Codec.Compression.Zlib as Zlib
-import           Control.Concurrent
-import           Control.Applicative hiding (many)
-import           Control.Exception
-import           Control.Monad
-import           Control.Monad.Trans
-import           Data.Attoparsec.Char8 hiding (Done)
-import qualified Data.ByteString.Lazy.Char8 as L
-import           Data.ByteString.Char8 (ByteString)
-import           Data.Iteratee.WrappedByteString
-import           Data.Maybe
-import qualified Data.Set as Set
-import           Data.Set (Set)
-import           Data.Typeable
-import           Prelude hiding (catch, takeWhile)
+import   qualified Codec.Compression.GZip as GZip
+import   qualified Codec.Compression.Zlib as Zlib
+import             Control.Concurrent
+import             Control.Applicative hiding (many)
+import             Control.Exception
+import             Control.Monad
+import             Control.Monad.Trans
+import             Data.Attoparsec.Char8 hiding (Done)
+import   qualified Data.ByteString.Lazy.Char8 as L
+import             Data.ByteString.Char8 (ByteString)
+import             Data.Maybe
+import   qualified Data.Set as Set
+import             Data.Set (Set)
+import             Data.Typeable
+import             Prelude hiding (catch, takeWhile)
 
 ------------------------------------------------------------------------------
-import           Snap.Internal.Debug
-import           Snap.Internal.Parsing
-import           Snap.Iteratee hiding (Enumerator)
-import           Snap.Types
+import             Snap.Internal.Debug
+import             Snap.Internal.Parsing
+import             Snap.Iteratee
+import             Snap.Types
 
 
 ------------------------------------------------------------------------------
@@ -54,26 +54,28 @@
 --
 -- Then the given handler's output stream will be compressed,
 -- @Content-Encoding@ will be set in the output headers, and the
--- @Content-Length@ will be cleared if it was set. (We can't process the stream
--- in O(1) space if the length is known beforehand.)
+-- @Content-Length@ will be cleared if it was set. (We can't process the
+-- stream in O(1) space if the length is known beforehand.)
 --
 -- The wrapped handler will be run to completion, and then the 'Response'
 -- that's contained within the 'Snap' monad state will be passed to
 -- 'finishWith' to prevent further processing.
 --
-withCompression :: Snap a   -- ^ the web handler to run
-                -> Snap ()
+withCompression :: MonadSnap m
+                => m a   -- ^ the web handler to run
+                -> m ()
 withCompression = withCompression' compressibleMimeTypes
 
 
 ------------------------------------------------------------------------------
 -- | The same as 'withCompression', with control over which MIME types to
 -- compress.
-withCompression' :: Set ByteString
+withCompression' :: MonadSnap m
+                 => Set ByteString
                     -- ^ set of compressible MIME types
-                 -> Snap a
+                 -> m a
                     -- ^ the web handler to run
-                 -> Snap ()
+                 -> m ()
 withCompression' mimeTable action = do
     _    <- action
     resp <- getResponse
@@ -93,7 +95,6 @@
     getResponse >>= finishWith
 
   where
-    chkAcceptEncoding :: Snap ()
     chkAcceptEncoding = do
         req <- getRequest
         debug $ "checking accept-encoding"
@@ -133,7 +134,7 @@
 
 
 ------------------------------------------------------------------------------
-gzipCompression :: ByteString -> Snap ()
+gzipCompression :: MonadSnap m => ByteString -> m ()
 gzipCompression ce = modifyResponse f
   where
     f = setHeader "Content-Encoding" ce .
@@ -143,7 +144,7 @@
 
 
 ------------------------------------------------------------------------------
-compressCompression :: ByteString -> Snap ()
+compressCompression :: MonadSnap m => ByteString -> m ()
 compressCompression ce = modifyResponse f
   where
     f = setHeader "Content-Encoding" ce .
@@ -153,83 +154,88 @@
 
 
 ------------------------------------------------------------------------------
-gcompress :: forall a . Enumerator a -> Enumerator a
+-- FIXME: use zlib-bindings
+gcompress :: forall a . Enumerator ByteString IO a
+          -> Enumerator ByteString IO  a
 gcompress = compressEnumerator GZip.compress
 
 
 ------------------------------------------------------------------------------
-ccompress :: forall a . Enumerator a -> Enumerator a
+ccompress :: forall a . Enumerator ByteString IO a
+          -> Enumerator ByteString IO a
 ccompress = compressEnumerator Zlib.compress
 
 
 ------------------------------------------------------------------------------
 compressEnumerator :: forall a .
                       (L.ByteString -> L.ByteString)
-                   -> Enumerator a
-                   -> Enumerator a
-compressEnumerator compFunc enum iteratee = do
-    writeEnd <- newChan
-    readEnd  <- newChan
-    tid      <- forkIO $ threadProc readEnd writeEnd
+                   -> Enumerator ByteString IO a
+                   -> Enumerator ByteString IO a
+compressEnumerator compFunc enum origStep = do
+    writeEnd <- liftIO $ newChan
+    readEnd  <- liftIO $ newChan
+    tid      <- liftIO $ forkIO $ threadProc readEnd writeEnd
 
-    enum (IterateeG $ f readEnd writeEnd tid iteratee)
+    enum (f readEnd writeEnd tid origStep)
 
   where
     --------------------------------------------------------------------------
-    streamFinished :: Stream -> Bool
-    streamFinished (EOF _)   = True
-    streamFinished (Chunk _) = False
+    streamFinished :: Stream ByteString -> Bool
+    streamFinished EOF        = True
+    streamFinished (Chunks _) = False
 
 
     --------------------------------------------------------------------------
-    consumeSomeOutput :: Chan Stream
-                      -> Iteratee IO a
-                      -> IO (Iteratee IO a)
-    consumeSomeOutput writeEnd iter = do
-        e <- isEmptyChan writeEnd
+    consumeSomeOutput :: Chan (Either SomeException (Stream ByteString))
+                      -> Step ByteString IO a
+                      -> Iteratee ByteString IO (Step ByteString IO a)
+    consumeSomeOutput writeEnd step = do
+        e <- lift $ isEmptyChan writeEnd
         if e
-          then return iter
+          then return step
           else do
-            ch <- readChan writeEnd
-
-            iter' <- liftM liftI $ runIter iter ch
-            consumeSomeOutput writeEnd iter'
-
+            ech <- lift $ readChan writeEnd
+            either throwError
+                   (\ch -> do
+                        step' <- checkDone (\k -> lift $ runIteratee $ k ch)
+                                           step
+                        consumeSomeOutput writeEnd step')
+                   ech
 
     --------------------------------------------------------------------------
-    consumeRest :: Chan Stream
-                -> Iteratee IO a
-                -> IO (IterV IO a)
-    consumeRest writeEnd iter = do
-        ch <- readChan writeEnd
-
-        iv <- runIter iter ch
-        if (streamFinished ch)
-           then return iv
-           else consumeRest writeEnd $ liftI iv
-
+    consumeRest :: Chan (Either SomeException (Stream ByteString))
+                -> Step ByteString IO a
+                -> Iteratee ByteString IO a
+    consumeRest writeEnd step = do
+        ech <- lift $ readChan writeEnd
+        either throwError
+               (\ch -> do
+                   step' <- checkDone (\k -> lift $ runIteratee $ k ch) step
+                   if (streamFinished ch)
+                      then returnI step'
+                      else consumeRest writeEnd step')
+               ech
 
     --------------------------------------------------------------------------
-    f readEnd writeEnd tid i (EOF Nothing) = do
-        writeChan readEnd Nothing
-        x <- consumeRest writeEnd i
-        killThread tid
-        return x
-
-    f _ _ tid _ (EOF (Just e)) = do
-        killThread tid
-        return $ Cont undefined (Just e)
+    f _ _ _ (Error e) = Error e
+    f _ _ _ (Yield x _) = Yield x EOF
+    f readEnd writeEnd tid st@(Continue k) = Continue $ \ch ->
+        case ch of
+          EOF -> do
+            lift $ writeChan readEnd Nothing
+            x <- consumeRest writeEnd st
+            lift $ killThread tid
+            return x
 
-    f readEnd writeEnd tid i (Chunk s') = do
-        let s = unWrap s'
-        writeChan readEnd $ Just s
-        i' <- consumeSomeOutput writeEnd i
-        return $ Cont (IterateeG $ f readEnd writeEnd tid i') Nothing
+          (Chunks xs) -> do
+            mapM_ (lift . writeChan readEnd . Just) xs
+            step' <- consumeSomeOutput writeEnd (Continue k)
+            returnI $ f readEnd writeEnd tid step'
 
 
     --------------------------------------------------------------------------
     threadProc :: Chan (Maybe ByteString)
-               -> Chan Stream
+               -> Chan (Either SomeException (Stream ByteString))
                -> IO ()
     threadProc readEnd writeEnd = do
         stream <- getChanContents readEnd
@@ -238,14 +244,15 @@
         let output = L.toChunks $ compFunc bs
 
         runIt output `catch` \(e::SomeException) ->
-            writeChan writeEnd $ EOF (Just $ Err $ show e)
+            writeChan writeEnd $ Left e
 
       where
         runIt (x:xs) = do
             writeChan writeEnd (toChunk x) >> runIt xs
 
         runIt []     = do
-            writeChan writeEnd $ EOF Nothing
+            writeChan writeEnd $ Right EOF
+
     --------------------------------------------------------------------------
     streamToChunks []            = []
     streamToChunks (Nothing:_)   = []
@@ -253,7 +260,7 @@
 
 
     --------------------------------------------------------------------------
-    toChunk = Chunk . WrapBS
+    toChunk = Right . Chunks . (:[])
 
 
 ------------------------------------------------------------------------------
diff --git a/test/snap-core-testsuite.cabal b/test/snap-core-testsuite.cabal
--- a/test/snap-core-testsuite.cabal
+++ b/test/snap-core-testsuite.cabal
@@ -33,10 +33,9 @@
     dlist >= 0.5 && < 0.6,
     filepath,
     HUnit >= 1.2 && < 2,
-    iteratee >= 0.3.1 && < 0.4,
-    ListLike >= 1 && < 2,
+    enumerator == 0.4.*,
     MonadCatchIO-transformers >= 0.2 && < 0.3,
-    monads-fd,
+    monads-fd <0.2,
     old-locale,
     old-time,
     parallel >= 2.2 && <2.3,
@@ -45,10 +44,10 @@
     test-framework >= 0.3.1 && <0.4,
     test-framework-hunit >= 0.2.5 && < 0.3,
     test-framework-quickcheck2 >= 0.2.6 && < 0.3,
-    text >= 0.10 && <0.11,
+    text >= 0.11 && <0.12,
     time,
     transformers,
-    unix-compat,
+    unix-compat == 0.2.*,
     zlib
     
   ghc-options: -O2 -Wall -fhpc -fwarn-tabs -funbox-strict-fields -threaded
diff --git a/test/suite/Snap/Internal/Http/Types/Tests.hs b/test/suite/Snap/Internal/Http/Types/Tests.hs
--- a/test/suite/Snap/Internal/Http/Types/Tests.hs
+++ b/test/suite/Snap/Internal/Http/Types/Tests.hs
@@ -5,10 +5,10 @@
 
 import           Control.Monad
 import           Control.Parallel.Strategies
+import qualified Data.ByteString.Char8 as S
 import           Data.ByteString.Char8 (ByteString)
 import           Data.ByteString.Lazy.Char8 ()
 import           Data.IORef
-import           Data.Iteratee (stream2stream, run)
 import           Data.List (sort)
 import qualified Data.Map as Map
 import           Data.Time.Calendar
@@ -20,8 +20,9 @@
 import           Text.Regex.Posix
 
 import           Snap.Internal.Http.Types
-import           Snap.Iteratee (enumBS, fromWrap)
+import           Snap.Iteratee
 
+
 tests :: [Test]
 tests = [ testTypes
         , testUrlDecode
@@ -31,7 +32,7 @@
 
 mkRq :: IO Request
 mkRq = do
-    enum <- newIORef (SomeEnumerator return)
+    enum <- newIORef (SomeEnumerator $ enumBS "")
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False Map.empty
                  enum Nothing GET (1,1) [] "" "/" "/" "/" "" Map.empty
 
@@ -93,8 +94,9 @@
     assertEqual "response status reason" "bogus" $ rspStatusReason resp
     assertEqual "content-length" (Just 4) $ rspContentLength resp
     -- run response body
-    bd <- (rspBodyToEnum $ rspBody resp) stream2stream >>= run
-    assertEqual "response body" "PING" (fromWrap bd)
+    let benum = rspBodyToEnum $ rspBody resp
+    bd <- runIteratee consume >>= run_ . benum
+    assertEqual "response body" "PING" $ S.concat bd
 
     let !_ = show GET
     let !_ = GET == POST
diff --git a/test/suite/Snap/Internal/Routing/Tests.hs b/test/suite/Snap/Internal/Routing/Tests.hs
--- a/test/suite/Snap/Internal/Routing/Tests.hs
+++ b/test/suite/Snap/Internal/Routing/Tests.hs
@@ -63,7 +63,7 @@
 
 mkRequest :: ByteString -> IO Request
 mkRequest uri = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False Map.empty
                      enum Nothing GET (1,1) [] "" uri "/"
@@ -72,7 +72,7 @@
 go :: Snap a -> ByteString -> IO a
 go m s = do
     req <- mkRequest s
-    run $ evalSnap m (const $ return ()) req
+    run_ $ evalSnap m (const $ return ()) req
 
 
 routes :: Snap ByteString
diff --git a/test/suite/Snap/Iteratee/Tests.hs b/test/suite/Snap/Iteratee/Tests.hs
--- a/test/suite/Snap/Iteratee/Tests.hs
+++ b/test/suite/Snap/Iteratee/Tests.hs
@@ -1,39 +1,42 @@
 {-# LANGUAGE BangPatterns         #-}
 {-# LANGUAGE OverloadedStrings    #-}
+{-# LANGUAGE PackageImports       #-}
 {-# LANGUAGE ScopedTypeVariables  #-}
 {-# OPTIONS_GHC -fno-warn-orphans #-}
 
 module Snap.Iteratee.Tests
   ( tests ) where
 
-import qualified Control.Exception as E
-import           Control.Exception hiding (try, assert)
-import           Control.Monad
-import           Control.Monad.Identity
-import qualified Data.ByteString as S
-import qualified Data.ByteString.Lazy.Char8 as L
-import           Data.Monoid
-import           Data.Int
-import           Data.IORef
-import           Data.Iteratee.WrappedByteString
-import           Data.Word
-import           Prelude hiding (drop, take)
-import           Test.Framework 
-import           Test.Framework.Providers.QuickCheck2
-import           Test.QuickCheck
-import qualified Test.QuickCheck.Monadic as QC
-import           Test.QuickCheck.Monadic hiding (run)
-import           Test.Framework.Providers.HUnit
-import qualified Test.HUnit as H
+import qualified   Control.Exception as E
+import             Control.Exception hiding (try, assert)
+import             Control.Monad
+import "monads-fd" Control.Monad.Identity
+import             Data.ByteString (ByteString)
+import qualified   Data.ByteString as S
+import qualified   Data.ByteString.Lazy.Char8 as L
+import             Data.Int
+import             Data.Maybe
+import             Prelude hiding (head, drop, take)
+import             Test.Framework
+import             Test.Framework.Providers.QuickCheck2
+import             Test.QuickCheck
+import qualified   Test.QuickCheck.Monadic as QC
+import             Test.QuickCheck.Monadic hiding (run)
+import             Test.Framework.Providers.HUnit
+import qualified   Test.HUnit as H
 
-import           Snap.Iteratee
-import           Snap.Test.Common ()
+import             Snap.Iteratee
+import             Snap.Test.Common ()
 
+import Snap.Internal.Iteratee.Debug
 
 liftQ :: forall a m . (Monad m) => m a -> PropertyM m a
 liftQ = QC.run
 
 
+throwErr :: String -> Iteratee a IO b
+throwErr = throwError . AssertionFailed
+
 expectException :: IO a -> PropertyM IO ()
 expectException m = do
     e <- liftQ $ E.try m
@@ -45,12 +48,6 @@
 tests :: [Test]
 tests = [ testEnumBS
         , testEnumLBS
-        , testBuffer
-        , testBuffer2
-        , testBuffer3
-        , testBuffer4
-        , testBufferChain
-        , testBufferChainEscape
         , testUnsafeBuffer
         , testUnsafeBuffer2
         , testUnsafeBuffer3
@@ -67,340 +64,346 @@
         ]
 
 testEnumBS :: Test
-testEnumBS = testProperty "enumBS" prop
+testEnumBS = testProperty "iteratee/enumBS" prop
   where
     prop :: S.ByteString -> Bool
-    prop s = (S.concat $ L.toChunks $ fromWrap $ runIdentity (run iter)) == s
+    prop s = (S.concat $ runIdentity (run_ iter)) == s
       where
-        iter = runIdentity $ enumBS s stream2stream
+        iter = runIdentity $ liftM (enumBS s) $ runIteratee consume
 
 testEnumLBS :: Test
-testEnumLBS = testProperty "enumLBS" prop
+testEnumLBS = testProperty "iteratee/enumLBS" prop
   where
     prop :: L.ByteString -> Bool
-    prop s = fromWrap (runIdentity (run iter)) == s
+    prop s = L.fromChunks (runIdentity (run_ iter)) == s
       where
-        iter = runIdentity $ enumLBS s stream2stream
+        iter = runIdentity $ liftM (enumLBS s) $ runIteratee consume
 
 
-testBuffer :: Test
-testBuffer = testProperty "testBuffer" $
-             monadicIO $ forAllM arbitrary prop
+copyingConsume :: Iteratee ByteString IO L.ByteString
+copyingConsume = f []
   where
-    prop s = do
-        pre (s /= L.empty)
-
-        (i,_) <- liftQ $ bufferIteratee stream2stream
-        iter  <- liftQ $ enumLBS s' i
-        x     <- liftQ $ run iter
-
-        QC.assert $ fromWrap x == s'
-      where
-        s' = L.take 20000 $ L.cycle s
+    f l = do
+        mbX <- head
+        maybe (return $ L.fromChunks $ reverse l)
+              (\x -> let !z = S.copy x
+                     in z `seq` f (z : l))
+              mbX
 
 
-testBuffer2 :: Test
-testBuffer2 = testCase "testBuffer2" prop
-  where
-    prop = do
-        (i,_) <- bufferIteratee $ drop 4 >> stream2stream
-
-        s <- enumLBS "abcdefgh" i >>= run >>= return . fromWrap
-        H.assertEqual "s == 'efgh'" "efgh" s
+bufferAndRun :: Iteratee ByteString IO a -> L.ByteString -> IO a
+bufferAndRun ii s = do
+    i <- unsafeBufferIteratee ii >>= runIteratee
+    run_ $ enumLBS s i
 
 
-testBuffer3 :: Test
-testBuffer3 = testProperty "testBuffer3" $
-              monadicIO $ forAllM arbitrary prop
+testUnsafeBuffer :: Test
+testUnsafeBuffer = testProperty "iteratee/testUnsafeBuffer" $
+                   monadicIO $ forAllM arbitrary prop
   where
     prop s = do
-        pre (s /= L.empty)
-        (i,_) <- liftQ $ bufferIteratee (ss >>= \x -> drop 1 >> return x)
-        iter  <- liftQ $ enumLBS s' i
-        x     <- liftQ $ run iter
+        pre $ s /= L.empty
+        x <- liftQ $ bufferAndRun copyingConsume s'
+        assert $ x == s'
 
-        QC.assert $ fromWrap x == (L.take 19999 s')
       where
         s' = L.take 20000 $ L.cycle s
-        ss = joinI $ take 19999 stream2stream
 
 
-testBuffer4 :: Test
-testBuffer4 = testProperty "testBuffer4" $
-              monadicIO $ forAllM arbitrary prop
+testUnsafeBuffer2 :: Test
+testUnsafeBuffer2 = testCase "iteratee/testUnsafeBuffer2" prop
   where
-    prop s = do
-        (i,_) <- liftQ $ bufferIteratee (stream2stream >> throwErr (Err "foo"))
-        i'    <- liftQ $ enumLBS s i
-        expectException $ run i'
-
-        (j,_) <- liftQ $ bufferIteratee (throwErr (Err "foo") >> stream2stream)
-        j'    <- liftQ $ enumLBS s j
-        expectException $ run j'
-        
-        k <- liftQ $ enumErr "foo" j
-        expectException $ run k
+    prop = do
+        i <- unsafeBufferIteratee (drop 4 >> copyingConsume) >>= runIteratee
+        s <- run_ $ enumLBS "abcdefgh" i
+        H.assertEqual "s == 'efgh'" "efgh" s
 
 
-testBufferChain :: Test
-testBufferChain = testProperty "testBufferChain" $
-                  monadicIO $ forAllM arbitrary prop
+testUnsafeBuffer3 :: Test
+testUnsafeBuffer3 = testProperty "iteratee/testUnsafeBuffer3" $
+                    monadicIO $ forAllM arbitrary prop
   where
     prop s = do
-        pre (s /= L.empty)
+        pre $ s /= L.empty
 
-        (j,_) <- liftQ $ bufferIteratee stream2stream
-        (i,_) <- liftQ $ bufferIteratee j
-        iter  <- liftQ $ enumLBS s' i
-        x     <- liftQ $ run iter
+        ss <- liftQ $ runIteratee copyingConsume >>=
+                      return . joinI . take 19999
 
-        QC.assert $ fromWrap x == s'
+        x <- liftQ $ bufferAndRun (ss >>= \x -> drop 1 >> return x) s'
+
+        let y = L.take 19999 s'
+        if x /= y
+         then liftQ $ do
+             putStrLn $ "FAILED!!!!!"
+             putStrLn $ "length x = " ++ show (L.length x)
+             putStrLn $ "length y = " ++ show (L.length y)
+             diff x y
+             putStrLn $ "input was " ++ show s
+         else return ()
+
+        assert $ x == y
       where
         s' = L.take 20000 $ L.cycle s
 
 
-testBufferChainEscape :: Test
-testBufferChainEscape = testProperty "testBufferChainEscape" $
-                        monadicIO $ forAllM arbitrary prop
-  where
-    prop s = do
-        pre (s /= L.empty)
-
-        (j,esc) <- liftQ $ bufferIteratee stream2stream
-        (i,_)   <- liftQ $ bufferIteratee j
-
-        liftQ $ writeIORef esc True
-
-        iter    <- liftQ $ enumLBS s' i
-        x       <- liftQ $ run iter
+    diff a b = d a b (0::Int)
+    d a b n = do
+        case ma of
+          Nothing -> if mb /= Nothing
+                       then do
+                         let Just (y,_) = mb
+                         putStrLn $ "differ at byte " ++ show n
+                         putStrLn $ "a=Nothing, b=" ++ show y
+                       else return ()
 
-        QC.assert $ fromWrap x == s'
+          Just (x,rest1) ->
+            if isNothing mb
+              then do
+                putStrLn $ "differ at byte " ++ show n
+                putStrLn $ "a=" ++ show x ++ ", b=Nothing"
+              else do
+                let Just (y,rest2) = mb
+                if x /= y
+                   then do
+                     putStrLn $ "differ at byte " ++ show n
+                     putStrLn $ "a=" ++ show x ++ ", b=" ++ show y
+                     let r1 = L.take 6 rest1
+                     let r2 = L.take 6 rest2
+                     putStrLn $ "next few bytes a = " ++ show r1
+                     putStrLn $ "next few bytes b = " ++ show r2
+                   else
+                     d rest1 rest2 (n+1)
       where
-        s' = L.take 20000 $ L.cycle s
+        ma = L.uncons a
+        mb = L.uncons b
 
 
-copyingStream2stream :: Iteratee IO (WrappedByteString Word8)
-copyingStream2stream = IterateeG (step mempty)
-  where
-  step acc (Chunk (WrapBS ls))
-    | S.null ls = return $ Cont (IterateeG (step acc)) Nothing
-    | otherwise = do
-          let !ls' = S.copy ls
-          let !bs' = WrapBS $! ls'
-          return $ Cont (IterateeG (step (acc `mappend` bs')))
-                        Nothing
 
-  step acc str        = return $ Done acc str
-
+tub3Prop s = do
+    ss <- runIteratee copyingConsume >>=
+          return . joinI . take 19999
 
-bufferAndRun :: Iteratee IO a -> L.ByteString -> IO a
-bufferAndRun ii s = do
-    i    <- unsafeBufferIteratee ii
-    iter <- enumLBS s i
-    run iter
+    let it = (ss >>= \x -> drop 1 >> return x)
+    x <- bufferAndRun (iterateeDebugWrapper "foo" it) s'
 
+    let a = x
+    let b = L.take 19999 s'
+    let boo = (a == b)
+    putStrLn $ "equal? " ++ show boo
+    diff a b
 
-testUnsafeBuffer :: Test
-testUnsafeBuffer = testProperty "testUnsafeBuffer" $
-                   monadicIO $ forAllM arbitrary prop
   where
-    prop s = do
-        pre $ s /= L.empty
-        x <- liftQ $ bufferAndRun copyingStream2stream s'
-        assert $ fromWrap x == s'
+    diff a b = d a b (0::Int)
+    d a b n = do
+        case ma of
+          Nothing -> if mb /= Nothing
+                       then do
+                         let Just (y,_) = mb
+                         putStrLn $ "differ at byte " ++ show n
+                         putStrLn $ "a=Nothing, b=" ++ show y
+                       else return ()
 
+          Just (x,rest1) ->
+            if isNothing mb
+              then do
+                putStrLn $ "differ at byte " ++ show n
+                putStrLn $ "a=" ++ show x ++ ", b=Nothing"
+              else do
+                let Just (y,rest2) = mb
+                if x /= y
+                   then do
+                     putStrLn $ "differ at byte " ++ show n
+                     putStrLn $ "a=" ++ show x ++ ", b=" ++ show y
+                   else
+                     d rest1 rest2 (n+1)
       where
-        s' = L.take 20000 $ L.cycle s
+        ma = L.uncons a
+        mb = L.uncons b
 
+    s' = L.take 20000 $ L.cycle s
 
-testUnsafeBuffer2 :: Test
-testUnsafeBuffer2 = testCase "testUnsafeBuffer2" prop
-  where
-    prop = do
-        i <- unsafeBufferIteratee $ drop 4 >> copyingStream2stream
 
-        s <- enumLBS "abcdefgh" i >>= run >>= return . fromWrap
-        H.assertEqual "s == 'efgh'" "efgh" s
 
 
-testUnsafeBuffer3 :: Test
-testUnsafeBuffer3 = testProperty "testUnsafeBuffer3" $
-                    monadicIO $ forAllM arbitrary prop
-  where
-    prop s = do
-        pre $ s /= L.empty
-        x <- liftQ $ bufferAndRun (ss >>= \x -> drop 1 >> return x) s'
-
-        assert $ fromWrap x == (L.take 19999 s')
-      where
-        s' = L.take 20000 $ L.cycle s
-        ss = joinI $ take 19999 copyingStream2stream
-
-
 testUnsafeBuffer4 :: Test
-testUnsafeBuffer4 = testProperty "testUnsafeBuffer4" $
+testUnsafeBuffer4 = testProperty "iteratee/testUnsafeBuffer4" $
                     monadicIO $ forAllM arbitrary prop
   where
     prop s = do
-        i  <- liftQ $
-              unsafeBufferIteratee (copyingStream2stream >> throwErr (Err "foo"))
-        i' <- liftQ $ enumLBS s i
-        expectException $ run i'
+        i  <- liftQ (unsafeBufferIteratee (copyingConsume >> throwErr "foo") >>=
+                     runIteratee)
+        expectException $ run_ $ enumLBS s i
 
-        j  <- liftQ $
-              unsafeBufferIteratee (throwErr (Err "foo") >> copyingStream2stream)
-        j' <- liftQ $ enumLBS s j
-        expectException $ run j'
-        
-        k <- liftQ $ enumErr "foo" j
-        expectException $ run k
+        j  <- liftQ $ unsafeBufferIteratee (throwErr "foo" >> copyingConsume)
+                      >>= runIteratee
+        expectException $ run_ $ enumLBS s j
 
 
 testUnsafeBuffer5 :: Test
-testUnsafeBuffer5 = testProperty "testUnsafeBuffer5" $
+testUnsafeBuffer5 = testProperty "iteratee/testUnsafeBuffer5" $
                     monadicIO $ forAllM arbitrary prop
   where
     prop s = do
         pre $ s /= L.empty
-        x <- liftQ $ bufferAndRun copyingStream2stream s
-        assert $ fromWrap x == s
+        x <- liftQ $ bufferAndRun copyingConsume s
+        assert $ x == s
 
 
 testTakeExactly1 :: Test
-testTakeExactly1 = testProperty "short stream" $
+testTakeExactly1 = testProperty "iteratee/takeExactly1" $
                    monadicIO $ forAllM arbitrary prop
   where
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
-        expectException $ doIter >>= run >>= return . fromWrap
+        expectException $ doIter >>= run_
 
       where
-        doIter = enumLBS s (joinI (takeExactly (n+1) stream2stream))
+        doIter = runIteratee consume >>=
+                 runIteratee . joinI . takeExactly (n+1) >>=
+                 return . enumLBS s
 
         n = fromIntegral $ L.length s
 
 
 testTakeExactly2 :: Test
-testTakeExactly2 = testProperty "exact stream" $
+testTakeExactly2 = testProperty "iteratee/takeExactly2" $
                    monadicIO $ forAllM arbitrary prop
   where
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
-        e <- liftQ $ doIter >>= run >>= return . fromWrap
-        assert $ e == s
+        e <- liftQ $ doIter >>= run_
+        assert $ L.fromChunks e == s
 
       where
-        doIter = enumLBS s (joinI (takeExactly n stream2stream))
+        doIter = runIteratee consume >>=
+                 runIteratee . joinI . takeExactly n >>=
+                 return . enumLBS s
 
         n = fromIntegral $ L.length s
 
 
 testTakeExactly3 :: Test
-testTakeExactly3 = testProperty "long stream" $
+testTakeExactly3 = testProperty "iteratee/takeExactly3" $
                    monadicIO $ forAllM arbitrary prop
   where
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
-        e <- liftQ $ doIter >>= run >>= return . fromWrap
-        assert $ e == L.take (fromIntegral n) s
+        e <- liftQ $ doIter >>= run_
+        assert $ L.fromChunks e == L.take (fromIntegral n) s
 
       where
-        doIter = enumLBS s (joinI (takeExactly n stream2stream))
+        doIter = runIteratee consume >>=
+                 runIteratee . joinI . takeExactly n >>=
+                 return . enumLBS s
 
         n = fromIntegral $ L.length s
 
 
 testTakeNoMoreThan1 :: Test
-testTakeNoMoreThan1 = testProperty "takeNoMore: short stream" $
+testTakeNoMoreThan1 = testProperty "iteratee/takeNoMoreThan1" $
                       monadicIO $ forAllM arbitrary prop
   where
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
-        s' <- liftQ $ doIter >>= run >>= return . fromWrap
+        s' <- liftQ $ doIter >>= run_
 
-        assert $ s == s'
+        assert $ s == L.fromChunks s'
 
       where
-        doIter = enumLBS s (joinI (takeNoMoreThan (n+1) stream2stream))
+        doIter = do
+            step <- runIteratee consume >>=
+                    runIteratee . joinI . takeNoMoreThan (n+1)
+            return $ enumLBS s step
 
         n = fromIntegral $ L.length s
 
 
 testTakeNoMoreThan2 :: Test
-testTakeNoMoreThan2 = testProperty "takeNoMore: exact stream" $
+testTakeNoMoreThan2 = testProperty "iteratee/takeNoMoreThan2" $
                       monadicIO $ forAllM arbitrary prop
   where
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
-        e <- liftQ $ doIter >>= run >>= return . fromWrap
-        assert $ e == s
+        e <- liftQ $ doIter >>= run_
+        assert $ L.fromChunks e == s
 
       where
-        doIter = enumLBS (L.concat ["", s])
-                         (joinI (takeNoMoreThan n stream2stream))
+        doIter = do
+            step  <- runIteratee consume
+            step' <- runIteratee (joinI (takeNoMoreThan n step))
+            return $ enumLBS (L.concat ["", s]) step'
 
+
         n = fromIntegral $ L.length s
 
 
 testTakeNoMoreThan3 :: Test
-testTakeNoMoreThan3 = testProperty "takeNoMoreLong" $
+testTakeNoMoreThan3 = testProperty "iteratee/takeNoMoreThan3" $
                       monadicIO $ forAllM arbitrary prop
   where
     prop :: (Int64,L.ByteString) -> PropertyM IO ()
     prop (m,s) = do
-        v <- liftQ $ enumLBS "" (joinI (takeNoMoreThan 0 stream2stream)) >>= run
-        assert $ fromWrap v == ""
+        step1 <- liftQ $ runIteratee consume
+        step2 <- liftQ $ runIteratee $ joinI (takeNoMoreThan 0 step1)
+        v <- liftQ $ run_ $ enumLBS "" step2
+        assert $ S.concat v == ""
 
         if (L.null s || m == 0)
            then liftQ $ do
-                     !_ <- doIter >>= run
+                     !_ <- doIter >>= run_
                      return ()
-           else expectException $ doIter >>= run >>= return . fromWrap
+           else expectException $ doIter >>= run_ >>= return
 
-        
+
       where
-        doIter = enumLBS s (joinI (takeNoMoreThan (n-abs m) stream2stream))
+        doIter = do
+            step <- runIteratee consume
+            let i = joinI $ takeNoMoreThan (n-abs m) step
+            step' <- runIteratee i
+            return $ enumLBS s step'
+
         n = L.length s
 
 
 testCountBytes :: Test
-testCountBytes = testProperty "count bytes" $
+testCountBytes = testProperty "iteratee/countBytes" $
                  monadicIO $ forAllM arbitrary prop
   where
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
-        (!_,n1) <- f (countBytes (return ()))
-        (!_,n2) <- f (countBytes stream2stream)
+        (!_,n1) <- liftQ (runIteratee (countBytes (return ())) >>= f)
+        (!_,n2) <- liftQ (runIteratee (countBytes consume) >>= f)
 
         assert $ n1 == 0
         assert $ n2 == n
 
-        expectException $ g erriter
-        expectException $ enumEof erriter >>= run
-        
+        expectException $ (runIteratee erriter >>= f)
+        expectException $ (runIteratee erriter >>= run_ . enumEOF)
 
+
      where
-       erriter = countBytes $ throwErr $ Err "foo"
-       g iter = enumLBS s iter >>= run
-       f = liftQ . g
+       erriter = countBytes $ throwError $ AssertionFailed "foo"
+       f iter = run_ $ enumLBS s iter
        n = L.length s
 
 
 testCountBytes2 :: Test
-testCountBytes2 = testProperty "count bytes" $
+testCountBytes2 = testProperty "iteratee/countBytes2" $
                   monadicIO $ forAllM arbitrary prop
   where
     prop :: L.ByteString -> PropertyM IO ()
     prop s = do
         pre $ L.length s > 4
-        (n1,s') <- f iter
 
+        step    <- liftQ $ runIteratee iter
+        (n1,s') <- f step
+
         assert $ n1 == 4
-        assert $ fromWrap s' == L.drop 4 s
+        assert $ s' == L.drop 4 s
 
      where
-       f i = liftQ $ enumLBS s i >>= run
+       f i = liftQ $ run_ $ enumLBS s i
        iter = do
            (!_,m) <- countBytes $ drop' 4
-           x <- stream2stream
+           x <- liftM L.fromChunks consume
            return (m,x)
diff --git a/test/suite/Snap/Test/Common.hs b/test/suite/Snap/Test/Common.hs
--- a/test/suite/Snap/Test/Common.hs
+++ b/test/suite/Snap/Test/Common.hs
@@ -8,13 +8,8 @@
 import qualified Data.ByteString as S
 import qualified Data.ByteString.Lazy as L
 import           Data.ByteString.Internal (c2w)
-import           Data.Iteratee.WrappedByteString
-import           Data.Word
 import           Test.QuickCheck
 
-
-instance Show (WrappedByteString Word8) where
-    show (WrapBS s) = show s
 
 instance Arbitrary S.ByteString where
     arbitrary = liftM (S.pack . map c2w) arbitrary
diff --git a/test/suite/Snap/Types/Tests.hs b/test/suite/Snap/Types/Tests.hs
--- a/test/suite/Snap/Types/Tests.hs
+++ b/test/suite/Snap/Types/Tests.hs
@@ -1,35 +1,35 @@
-{-# LANGUAGE BangPatterns #-}
-{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE BangPatterns        #-}
+{-# LANGUAGE OverloadedStrings   #-}
+{-# LANGUAGE PackageImports      #-}
 {-# LANGUAGE ScopedTypeVariables #-}
 
 module Snap.Types.Tests
   ( tests ) where
 
-import           Control.Applicative
-import           Control.Concurrent.MVar
-import           Control.Exception (SomeException)
-import           Control.Monad
-import           Control.Monad.CatchIO
-import           Control.Monad.Trans (liftIO)
-import           Control.Parallel.Strategies
-import           Data.ByteString.Char8 (ByteString)
-import qualified Data.ByteString.Char8 as S
-import qualified Data.ByteString.Lazy.Char8 as L
-import           Data.IORef
-import           Data.Iteratee
-import           Data.Text ()
-import           Data.Text.Lazy ()
-import qualified Data.Map as Map
-import           Prelude hiding (catch)
-import           Test.Framework
-import           Test.Framework.Providers.HUnit
-import           Test.Framework.Providers.QuickCheck2
-import           Test.HUnit hiding (Test, path)
-
-import           Snap.Internal.Types
-import           Snap.Internal.Http.Types
-import           Snap.Iteratee
-import           Snap.Test.Common ()
+import             Control.Applicative
+import             Control.Concurrent.MVar
+import             Control.Exception (SomeException)
+import             Control.Monad
+import             Control.Monad.CatchIO
+import "monads-fd" Control.Monad.Trans (liftIO)
+import             Control.Parallel.Strategies
+import             Data.ByteString.Char8 (ByteString)
+import qualified   Data.ByteString.Char8 as S
+import qualified   Data.ByteString.Lazy.Char8 as L
+import             Data.IORef
+import             Data.Text ()
+import             Data.Text.Lazy ()
+import qualified   Data.Map as Map
+import             Prelude hiding (catch)
+import             Test.Framework
+import             Test.Framework.Providers.HUnit
+import             Test.Framework.Providers.QuickCheck2
+import             Test.HUnit hiding (Test, path)
+                   
+import             Snap.Internal.Types
+import             Snap.Internal.Http.Types
+import             Snap.Iteratee
+import             Snap.Test.Common ()
 
 
 tests :: [Test]
@@ -76,7 +76,7 @@
 
 mkRequest :: ByteString -> IO Request
 mkRequest uri = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "127.0.0.1" 999 "foo" 1000 "foo" False Map.empty
                      enum Nothing GET (1,1) [] "" uri "/"
@@ -84,7 +84,7 @@
 
 mkRequestQuery :: ByteString -> ByteString -> [ByteString] -> IO Request
 mkRequestQuery uri k v = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     let mp = Map.fromList [(k,v)]
     let q  = S.concat [k,"=", S.concat v]
@@ -96,7 +96,7 @@
 
 mkZomgRq :: IO Request
 mkZomgRq = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "127.0.0.1" 999 "foo" 1000 "foo" False Map.empty
                      enum Nothing GET (1,1) [] "" "/" "/" "/" "" Map.empty
@@ -112,14 +112,14 @@
 
 mkRqWithBody :: IO Request
 mkRqWithBody = do
-    enum <- newIORef $ SomeEnumerator (enumBS "zazzle")
+    enum <- newIORef $ SomeEnumerator (enumBS "zazzle" >==> enumEOF)
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False Map.empty
                  enum Nothing GET (1,1) [] "" "/" "/" "/" ""
                  Map.empty
 
 
 testCatchIO :: Test
-testCatchIO = testCase "catchIO" $ do
+testCatchIO = testCase "types/catchIO" $ do
     (_,rsp)  <- go f
     (_,rsp2) <- go g
 
@@ -139,19 +139,19 @@
 go :: Snap a -> IO (Request,Response)
 go m = do
     zomgRq <- mkZomgRq
-    run $ runSnap m (\x -> return $! (show x `using` rdeepseq) `seq` ()) zomgRq
+    run_ $ runSnap m (\x -> return $! (show x `using` rdeepseq) `seq` ()) zomgRq
 
 
 goIP :: Snap a -> IO (Request,Response)
 goIP m = do
     rq <- mkIpHeaderRq
-    run $ runSnap m (const $ return ()) rq
+    run_ $ runSnap m (const $ return ()) rq
 
 
 goPath :: ByteString -> Snap a -> IO (Request,Response)
 goPath s m = do
     rq <- mkRequest s
-    run $ runSnap m (const $ return ()) rq
+    run_ $ runSnap m (const $ return ()) rq
 
 
 goPathQuery :: ByteString
@@ -161,13 +161,13 @@
             -> IO (Request,Response)
 goPathQuery s k v m = do
     rq <- mkRequestQuery s k v
-    run $ runSnap m (const $ return ()) rq
+    run_ $ runSnap m (const $ return ()) rq
 
 
 goBody :: Snap a -> IO (Request,Response)
 goBody m = do
     rq <- mkRqWithBody
-    run $ runSnap m (const $ return ()) rq
+    run_ $ runSnap m (const $ return ()) rq
 
 
 testFail :: Test
@@ -182,7 +182,7 @@
 
 
 testAlternative :: Test
-testAlternative = testCase "alternative" $ do
+testAlternative = testCase "types/alternative" $ do
     (_,resp) <- go (pass <|> setFoo "Bar")
     assertEqual "foo present" (Just "Bar") $ getHeader "Foo" resp
 
@@ -203,13 +203,13 @@
 
 
 testEarlyTermination :: Test
-testEarlyTermination = testCase "early termination" $ do
+testEarlyTermination = testCase "types/earlyTermination" $ do
     (_,resp) <- go (finishWith sampleResponse >>= \_ -> setFoo "Bar")
     assertEqual "foo" (Just ["Quux"]) $ getHeaders "Foo" resp
 
 
 testRqBody :: Test
-testRqBody = testCase "request bodies" $ do
+testRqBody = testCase "types/requestBodies" $ do
     mvar1 <- newEmptyMVar
     mvar2 <- newEmptyMVar
 
@@ -232,11 +232,11 @@
         getRequestBody >>= liftIO . putMVar mvar1
         getRequestBody >>= liftIO . putMVar mvar2
 
-    g = transformRequestBody return
+    g = transformRequestBody returnI
 
 
 testTrivials :: Test
-testTrivials = testCase "trivial functions" $ do
+testTrivials = testCase "types/trivials" $ do
     (rq,rsp) <- go $ do
         req <- getRequest
         putRequest $ req { rqIsSecure=True }
@@ -275,13 +275,13 @@
 
 
 testMethod :: Test
-testMethod = testCase "method" $ do
+testMethod = testCase "types/method" $ do
    expect404 $ go (method POST $ return ())
    expectNo404 $ go (method GET $ return ())
 
 
 testDir :: Test
-testDir = testCase "dir" $ do
+testDir = testCase "types/dir" $ do
    expect404 $ goPath "foo/bar" (dir "zzz" $ return ())
    expectNo404 $ goPath "foo/bar" (dir "foo" $ return ())
    expect404 $ goPath "fooz/bar" (dir "foo" $ return ())
@@ -292,7 +292,7 @@
 
 
 testParam :: Test
-testParam = testCase "getParam" $ do
+testParam = testCase "types/getParam" $ do
     expect404 $ goPath "/foo" f
     expectNo404 $ goPathQuery "/foo" "param" ["foo"] f
   where
@@ -304,11 +304,13 @@
 
 
 getBody :: Response -> IO L.ByteString
-getBody r = liftM fromWrap ((rspBodyToEnum $ rspBody r) stream2stream >>= run)
+getBody r = do
+    let benum = rspBodyToEnum $ rspBody r
+    liftM L.fromChunks (runIteratee consume >>= run_ . benum)
 
 
 testWrites :: Test
-testWrites = testCase "writes" $ do
+testWrites = testCase "types/writes" $ do
     (_,r) <- go h
     b <- getBody r
     assertEqual "output functions" "Foo1Foo2Foo3" b
@@ -321,20 +323,20 @@
 
 
 testURLEncode1 :: Test
-testURLEncode1 = testCase "url encoding 1" $ do
+testURLEncode1 = testCase "types/urlEncoding1" $ do
     let b = urlEncode "the quick brown fox~#"
     assertEqual "url encoding 1" "the+quick+brown+fox%7e%23" b
     assertEqual "fail" Nothing $ urlDecode "%"
 
 
 testURLEncode2 :: Test
-testURLEncode2 = testProperty "url encoding 2" prop
+testURLEncode2 = testProperty "types/urlEncoding2" prop
   where
     prop s = (urlDecode $ urlEncode s) == Just s
 
 
 testDir2 :: Test
-testDir2 = testCase "dir2" $ do
+testDir2 = testCase "types/dir2" $ do
     (_,resp) <- goPath "foo/bar" f
     b <- getBody resp
     assertEqual "context path" "/foo/bar/" b
@@ -346,7 +348,7 @@
 
 
 testIpHeaderFilter :: Test
-testIpHeaderFilter = testCase "ipHeaderFilter" $ do
+testIpHeaderFilter = testCase "types/ipHeaderFilter" $ do
     (_,r) <- goIP f
     b <- getBody r
     assertEqual "ipHeaderFilter" "1.2.3.4" b
@@ -364,7 +366,7 @@
 
 
 testMZero404 :: Test
-testMZero404 = testCase "mzero 404" $ do
+testMZero404 = testCase "types/mzero404" $ do
     (_,r) <- go mzero
     let l = rspContentLength r
     b <- getBody r
@@ -373,9 +375,9 @@
 
 
 testEvalSnap :: Test
-testEvalSnap = testCase "evalSnap exception" $ do
+testEvalSnap = testCase "types/evalSnap-exception" $ do
     rq <- mkZomgRq
-    expectException (run $ evalSnap f
+    expectException (run_ $ evalSnap f
                                     (const $ return ())
                                     rq >> return ())
   where
@@ -387,7 +389,7 @@
 
 
 testLocalRequest :: Test
-testLocalRequest = testCase "localRequest" $ do
+testLocalRequest = testCase "types/localRequest" $ do
     rq1 <- mkZomgRq
     rq2 <- mkRequest "zzz/zz/z"
 
@@ -403,7 +405,7 @@
 
 
 testRedirect :: Test
-testRedirect = testCase "redirect" $ do
+testRedirect = testCase "types/redirect" $ do
     (_,rsp)  <- go (redirect "/foo/bar")
 
     assertEqual "redirect path" (Just "/foo/bar") $ getHeader "Location" rsp
diff --git a/test/suite/Snap/Util/FileServe/Tests.hs b/test/suite/Snap/Util/FileServe/Tests.hs
--- a/test/suite/Snap/Util/FileServe/Tests.hs
+++ b/test/suite/Snap/Util/FileServe/Tests.hs
@@ -38,20 +38,22 @@
 
 
 getBody :: Response -> IO L.ByteString
-getBody r = liftM fromWrap ((rspBodyToEnum $ rspBody r) stream2stream >>= run)
+getBody r = do
+    let benum = rspBodyToEnum $ rspBody r
+    liftM L.fromChunks (runIteratee consume >>= run_ . benum)
 
 
 go :: Snap a -> ByteString -> IO Response
 go m s = do
     rq <- mkRequest s
-    liftM snd (run $ runSnap m (const $ return ()) rq)
+    liftM snd (run_ $ runSnap m (const $ return ()) rq)
 
 
 goIfModifiedSince :: Snap a -> ByteString -> ByteString -> IO Response
 goIfModifiedSince m s lm = do
     rq <- mkRequest s
     let r = setHeader "if-modified-since" lm rq
-    liftM snd (run $ runSnap m (const $ return ()) r)
+    liftM snd (run_ $ runSnap m (const $ return ()) r)
 
 
 goIfRange :: Snap a -> ByteString -> (Int,Int) -> ByteString -> IO Response
@@ -61,7 +63,7 @@
             setHeader "Range"
                        (S.pack $ "bytes=" ++ show start ++ "-" ++ show end)
                        rq
-    liftM snd (run $ runSnap m (const $ return ()) r)
+    liftM snd (run_ $ runSnap m (const $ return ()) r)
 
 
 goRange :: Snap a -> ByteString -> (Int,Int) -> IO Response
@@ -70,7 +72,7 @@
     let rq = setHeader "Range"
                        (S.pack $ "bytes=" ++ show start ++ "-" ++ show end)
                        rq'
-    liftM snd (run $ runSnap m (const $ return ()) rq)
+    liftM snd (run_ $ runSnap m (const $ return ()) rq)
 
 
 goMultiRange :: Snap a -> ByteString -> (Int,Int) -> (Int,Int) -> IO Response
@@ -80,7 +82,7 @@
                        (S.pack $ "bytes=" ++ show start ++ "-" ++ show end
                                  ++ "," ++ show start2 ++ "-" ++ show end2)
                        rq'
-    liftM snd (run $ runSnap m (const $ return ()) rq)
+    liftM snd (run_ $ runSnap m (const $ return ()) rq)
 
 
 goRangePrefix :: Snap a -> ByteString -> Int -> IO Response
@@ -89,7 +91,7 @@
     let rq = setHeader "Range"
                        (S.pack $ "bytes=" ++ show start ++ "-")
                        rq'
-    liftM snd (run $ runSnap m (const $ return ()) rq)
+    liftM snd (run_ $ runSnap m (const $ return ()) rq)
 
 
 goRangeSuffix :: Snap a -> ByteString -> Int -> IO Response
@@ -98,12 +100,12 @@
     let rq = setHeader "Range"
                        (S.pack $ "bytes=-" ++ show end)
                        rq'
-    liftM snd (run $ runSnap m (const $ return ()) rq)
+    liftM snd (run_ $ runSnap m (const $ return ()) rq)
 
 
 mkRequest :: ByteString -> IO Request
 mkRequest uri = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False Map.empty
                      enum Nothing GET (1,1) [] "" uri "/"
                      (S.concat ["/",uri]) "" Map.empty
diff --git a/test/suite/Snap/Util/GZip/Tests.hs b/test/suite/Snap/Util/GZip/Tests.hs
--- a/test/suite/Snap/Util/GZip/Tests.hs
+++ b/test/suite/Snap/Util/GZip/Tests.hs
@@ -9,10 +9,11 @@
 import qualified Codec.Compression.GZip as GZip
 import qualified Codec.Compression.Zlib as Zlib
 import           Control.Exception hiding (assert)
+import           Control.Monad (liftM)
+import           Data.ByteString (ByteString)
 import qualified Data.ByteString.Lazy.Char8 as L
 import           Data.Digest.Pure.MD5
 import           Data.IORef
-import           Data.Iteratee
 import qualified Data.Map as Map
 import           Data.Serialize
 import           Test.Framework
@@ -30,6 +31,10 @@
 import           Snap.Util.GZip
 
 
+stream2stream
+  :: Iteratee ByteString IO L.ByteString
+stream2stream = liftM L.fromChunks consume
+
 ------------------------------------------------------------------------------
 tests :: [Test]
 tests = [ testIdentity1
@@ -42,8 +47,7 @@
         , testNopWhenContentEncodingSet
         , testCompositionDoesn'tExplode
         , testGzipLotsaChunks
-        , testBadHeaders
-        , testIterateeException ]
+        , testBadHeaders ]
 
 
 ------------------------------------------------------------------------------
@@ -74,7 +78,7 @@
 ------------------------------------------------------------------------------
 mkNoHeaders :: IO Request
 mkNoHeaders = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False emptyHdrs
                  enum Nothing GET (1,1) [] "" "/" "/" "/" "" Map.empty
@@ -82,14 +86,14 @@
 
 mkGzipRq :: IO Request
 mkGzipRq = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False gzipHdrs
                  enum Nothing GET (1,1) [] "" "/" "/" "/" "" Map.empty
 
 mkXGzipRq :: IO Request
 mkXGzipRq = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False xGzipHdrs
                  enum Nothing GET (1,1) [] "" "/" "/" "/" "" Map.empty
@@ -99,14 +103,14 @@
 ------------------------------------------------------------------------------
 mkCompressRq :: IO Request
 mkCompressRq = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False compressHdrs
                  enum Nothing GET (1,1) [] "" "/" "/" "/" "" Map.empty
 
 mkXCompressRq :: IO Request
 mkXCompressRq = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False xCompressHdrs
                  enum Nothing GET (1,1) [] "" "/" "/" "/" "" Map.empty
@@ -116,7 +120,7 @@
 ------------------------------------------------------------------------------
 mkBadRq :: IO Request
 mkBadRq = do
-    enum <- newIORef $ SomeEnumerator return
+    enum <- newIORef $ SomeEnumerator returnI
 
     return $ Request "foo" 80 "foo" 999 "foo" 1000 "foo" False badHdrs
                   enum Nothing GET (1,1) [] "" "/" "/" "/" "" Map.empty
@@ -132,7 +136,7 @@
 goGeneric :: IO Request -> Snap a -> IO (Request, Response)
 goGeneric mkRq m = do
     rq <- mkRq
-    run $! runSnap (seqSnap m) (const $ return ()) rq
+    run_ $! runSnap (seqSnap m) (const $ return ()) rq
 
 goGZip, goCompress, goXGZip     :: Snap a -> IO (Request,Response)
 goNoHeaders, goXCompress, goBad :: Snap a -> IO (Request,Response)
@@ -150,12 +154,6 @@
 
 
 ------------------------------------------------------------------------------
-textPlainErr :: L.ByteString -> Snap ()
-textPlainErr s = modifyResponse $
-                 setResponseBody (enumLBS s >. enumErr "blah") .
-                 setContentType "text/plain"
-
-
 textPlain :: L.ByteString -> Snap ()
 textPlain s = modifyResponse $
               setResponseBody (enumLBS s) .
@@ -185,7 +183,7 @@
         let body = rspBodyToEnum $ rspBody rsp
 
         c <- liftQ $
-             body stream2stream >>= run >>= return . fromWrap
+             runIteratee stream2stream >>= run_ . body
 
         assert $ s == c
 
@@ -206,7 +204,7 @@
         let body = rspBodyToEnum $ rspBody rsp
 
         c <- liftQ $
-             body stream2stream >>= run >>= return . fromWrap
+             runIteratee stream2stream >>= run_ . body
 
         assert $ s == c
 
@@ -223,7 +221,7 @@
         let body = rspBodyToEnum $ rspBody rsp
 
         c <- liftQ $
-             body stream2stream >>= run >>= return . fromWrap
+             runIteratee stream2stream >>= run_ . body
 
         let s1 = GZip.decompress c
         assert $ s == s1
@@ -242,7 +240,7 @@
         let body = rspBodyToEnum $ rspBody rsp
 
         c <- liftQ $
-              body stream2stream >>= run >>= return . fromWrap
+              runIteratee stream2stream >>= run_ . body
 
         let s' = Zlib.decompress c
         assert $ s == s'
@@ -258,7 +256,7 @@
         let body3 = rspBodyToEnum $ rspBody rsp3
 
         s3 <- liftQ $
-              body3 stream2stream >>= run >>= return . fromWrap
+              runIteratee stream2stream >>= run_ . body3
 
         assert $ s == s3
 
@@ -274,7 +272,7 @@
         let body = rspBodyToEnum $ rspBody rsp
 
         c <- liftQ $
-             body stream2stream >>= run >>= return . fromWrap
+             runIteratee stream2stream >>= run_ . body
 
         let s1 = GZip.decompress c
         assert $ s == s1
@@ -292,7 +290,7 @@
         let body2 = rspBodyToEnum $ rspBody rsp2
 
         c2 <- liftQ $
-              body2 stream2stream >>= run >>= return . fromWrap
+              runIteratee stream2stream >>= run_ . body2
 
         let s2 = Zlib.decompress c2
         assert $ s == s2
@@ -307,7 +305,7 @@
         (!_,!rsp) <- goBad (seqSnap $ withCompression $ textPlain s)
         let body = rspBodyToEnum $ rspBody rsp
 
-        body stream2stream >>= run >>= return . fromWrap
+        runIteratee stream2stream >>= run_ . body
 
 
 ------------------------------------------------------------------------------
@@ -346,7 +344,7 @@
         let body = rspBodyToEnum $ rspBody rsp
 
         c <- liftQ $
-             body stream2stream >>= run >>= return . fromWrap
+             runIteratee stream2stream >>= run_ . body
 
         let s1 = GZip.decompress c
         assert $ s == s1
@@ -361,7 +359,7 @@
         (!_,!rsp) <- goGZip (seqSnap $ withCompression $ textPlain s)
         let body = rspBodyToEnum $ rspBody rsp
 
-        c <- body stream2stream >>= run >>= return . fromWrap
+        c <- runIteratee stream2stream >>= run_ . body
 
         let s1 = GZip.decompress c
         H.assertBool "streams equal" $ s == s1
@@ -373,14 +371,3 @@
     frobnicate s = let s' = encode $ md5 $ L.fromChunks [s]
                    in (s:frobnicate s')
 
-
-------------------------------------------------------------------------------
-testIterateeException :: Test
-testIterateeException = testProperty "gzip/iterateeException" $
-                        monadicIO $ forAllM arbitrary prop
-  where
-    prop :: L.ByteString -> PropertyM IO ()
-    prop s = expectException $ do
-        (!_,!rsp) <- goGZip (seqSnap $ withCompression $ textPlainErr s)
-        let body = rspBodyToEnum $ rspBody rsp
-        body stream2stream >>= run >>= return . fromWrap
