diff --git a/Network/Wai.hs b/Network/Wai.hs
--- a/Network/Wai.hs
+++ b/Network/Wai.hs
@@ -43,8 +43,10 @@
     , mkCIByteString
       -- ** Request header names
     , RequestHeader
+    , RequestHeaders
       -- ** Response header names
     , ResponseHeader
+    , ResponseHeaders
       -- ** Response status code
     , Status (..)
     , status200
@@ -57,17 +59,15 @@
     , status404
     , status405
     , status500
-      -- ** Response body
-    , ResponseBody (..)
-      -- ** Source
-    , Source (..)
-      -- * Enumerator
-    , Enumerator (..)
       -- * WAI interface
     , Request (..)
     , Response (..)
+    , ResponseEnumerator
+    , responseEnumerator
     , Application
     , Middleware
+      -- * Response body smart constructors
+    , responseLBS
     ) where
 
 import qualified Data.ByteString as B
@@ -76,6 +76,12 @@
 import Data.Char (toLower)
 import Data.String (IsString (..))
 import Data.Typeable (Typeable)
+import Data.Enumerator (Iteratee, ($$), joinI, run_)
+import qualified Data.Enumerator as E
+import Data.Enumerator.IO (enumFile)
+import Blaze.ByteString.Builder (Builder, fromByteString, fromLazyByteString)
+import Data.Data (Data)
+import Network.Socket (SockAddr)
 
 -- | HTTP request method. Since the HTTP protocol allows arbitrary request
 -- methods, we leave this open as a 'B.ByteString'. Please note the request
@@ -111,6 +117,7 @@
     { ciOriginal :: !B.ByteString
     , ciLowerCase :: !B.ByteString
     }
+    deriving (Data, Typeable)
 
 -- | Convert a regular bytestring to a case-insensitive bytestring.
 mkCIByteString :: B.ByteString -> CIByteString
@@ -118,6 +125,8 @@
 
 instance Show CIByteString where
     show = show . ciOriginal
+instance Read CIByteString where
+    readsPrec i = map (\(x, y) -> (mkCIByteString x, y)) . readsPrec i
 instance Eq CIByteString where
     x == y = ciLowerCase x == ciLowerCase y
 instance Ord CIByteString where
@@ -128,10 +137,12 @@
 -- | Headers sent from the client to the server. Note that this is a
 -- case-insensitive string, as the HTTP spec specifies.
 type RequestHeader = CIByteString
+type RequestHeaders = [(RequestHeader, B.ByteString)]
 
 -- | Headers sent from the server to the client. Note that this is a
 -- case-insensitive string, as the HTTP spec specifies.
 type ResponseHeader = CIByteString
+type ResponseHeaders = [(ResponseHeader, B.ByteString)]
 
 -- | HTTP status code; a combination of the integral code and a status message.
 -- Equality is determined solely on the basis of the integral code.
@@ -181,60 +192,6 @@
 status500 :: Status
 status500 = Status 500 $ B8.pack "Internal Server Error"
 
--- | This is a source for 'B.ByteString's. It is a function (wrapped in a
--- newtype) that will return Nothing if the data has been completely consumed,
--- or return the next 'B.ByteString' from the source along with a new 'Source'
--- to continue reading from.
---
--- Be certain not to reuse a 'Source'! It might work fine with some
--- implementations of 'Source', while causing bugs with others.
---
--- This datatype is used by WAI to represent a request body. We choose this
--- over an enumerator in that it gives the application power over control flow.
--- This not only makes it easier to use in many situations, but also allows
--- implementation of some features such as a backtracking parser which doesn't
--- read the entire body into memory.
-newtype Source = Source { runSource :: IO (Maybe (B.ByteString, Source)) }
-
--- | An enumerator is a data producer. It takes two arguments: a function to
--- enumerate over (the iteratee) and an accumulating parameter. As the
--- enumerator produces output, it calls the iteratee, thereby avoiding the need
--- to allocate large amounts of memory for storing the entire piece of data.
---
--- Normally in Haskell, we can achieve the same results with laziness. For
--- example, an inifinite list does not require inifinite memory storage; we
--- simply get away with thunks. However, when operating in the IO monad, we do
--- not have this luxury. There are other approaches, such as lazy I\/O. If you
--- would like to program in this manner, please see
--- "Network.Wai.Enumerator", in particular toLBS.
---
--- That said, let's address the details of this particular enumerator
--- implementation. You'll notice that the iteratee is a function that takes two
--- arguments and returns an 'Either' value. The second argument is simply the
--- piece of data generated by the enumerator. The 'Either' value at the end is
--- a means to alert the enumerator whether to continue or not. If it returns
--- 'Left', then the enumeration should cease. If it returns 'Right', it should
--- continue.
---
--- The accumulating parameter (a) has meaning only to the iteratee; the
--- enumerator simply passes it around. The enumerator itself also returns an
--- 'Either' value; a 'Right' means the enumerator ran to completion, while a
--- 'Left' indicates early termination was requested by the iteratee.
---
--- 'Enumerator's are not required to be resumable. That is to say, the
--- 'Enumerator' may only be called once. While this requirement puts a bit of a
--- strain on the caller in some situations, it saves a large amount of
--- complication- and thus performance- on the producer.
---
--- In WAI, an Enumerator is used to represent the response body. We have
--- specifically chosen one of the simplest representations of an enumerator to
--- avoid coding complication and performance overhead.
-newtype Enumerator = Enumerator { runEnumerator :: forall a.
-              (a -> B.ByteString -> IO (Either a a))
-                 -> a
-                 -> IO (Either a a)
-}
-
 -- | Information on the request sent by the client. This abstracts away the
 -- details of the underlying implementation.
 data Request = Request
@@ -252,45 +209,35 @@
   ,  requestHeaders :: [(RequestHeader, B.ByteString)]
   -- ^ Was this request made over an SSL connection?
   ,  isSecure       :: Bool
-  ,  requestBody    :: Source
   -- ^ Log the given line in some method; how this is accomplished is
   -- server-dependant.
   ,  errorHandler   :: String -> IO ()
   -- | The client\'s host information.
-  ,  remoteHost     :: B.ByteString
+  ,  remoteHost     :: SockAddr
   }
   deriving Typeable
 
--- | The response body returned to the server from the application. We provide
--- three separate constructors as optimizations:
---
--- * 'ResponseEnumerator' is the most general type, allowing constant-memory
--- production of a response, even in the presence of interleaved I\/O actions.
---
--- * 'ResponseFile' serves a static file from the filesystem. Many servers use
--- a sendfile system call to optimize this type of serving, making this a huge
--- performance gain.
---
--- * 'ResponseLBS'. Often times, we wish to return a response that includes no
--- interleaved I\/O. In this case, we can use Haskell's natural laziness to our
--- advantage, and represent the response as a lazy bytestring.
-data ResponseBody = ResponseFile FilePath
-                  | ResponseEnumerator Enumerator
-                  | ResponseLBS L.ByteString
+data Response
+    = ResponseFile Status ResponseHeaders FilePath
+    | ResponseBuilder Status ResponseHeaders Builder
+    | ResponseEnumerator (forall a. ResponseEnumerator a)
   deriving Typeable
 
-data Response = Response
-  { status          :: Status
-  , responseHeaders :: [(ResponseHeader, B.ByteString)]
-  -- | A common optimization is to use the sendfile system call when sending
-  -- files from the disk. This datatype facilitates this optimization; if
-  -- 'Left' is returned, the server will send the file from the disk by
-  -- whatever means it wishes. If 'Right', it will call the 'Enumerator'.
-  , responseBody    :: ResponseBody
-  }
-  deriving Typeable
+type ResponseEnumerator a =
+    (Status -> ResponseHeaders -> Iteratee Builder IO a) -> IO a
 
-type Application = Request -> IO Response
+responseEnumerator :: Response -> ResponseEnumerator a
+responseEnumerator (ResponseEnumerator e) f = e f
+responseEnumerator (ResponseFile s h fp) f =
+    run_ $ enumFile fp $$ joinI $ E.map fromByteString $$ f s h
+responseEnumerator (ResponseBuilder s h b) f = run_ $ do
+    E.yield () $ E.Chunks [b]
+    f s h
+
+responseLBS :: Status -> ResponseHeaders -> L.ByteString -> Response
+responseLBS s h = ResponseBuilder s h . fromLazyByteString
+
+type Application = Request -> Iteratee B.ByteString IO Response
 
 -- | Middleware is a component that sits between the server and application. It
 -- can do such tasks as GZIP encoding or response caching. What follows is the
diff --git a/Network/Wai/Enumerator.hs b/Network/Wai/Enumerator.hs
deleted file mode 100644
--- a/Network/Wai/Enumerator.hs
+++ /dev/null
@@ -1,141 +0,0 @@
-{-# LANGUAGE Rank2Types #-}
--- | A collection of utility functions for dealing with 'Enumerator's.
-module Network.Wai.Enumerator
-    ( -- * Utilities
-      mapE
-      -- * Conversions
-    , -- ** Lazy byte strings
-      toLBS
-    , fromLBS
-    , fromLBS'
-      -- ** Source
-    , toSource
-      -- ** Handle
-    , fromHandle
-    , fromHandleFinally
-      -- ** FilePath
-    , fromFile
-    , fromFileFinally
-    , fromTempFile
-    , fromResponseBody
-    ) where
-
-import Network.Wai (Enumerator (..), Source (..), ResponseBody (..))
-import qualified Network.Wai.Source as Source
-import qualified Data.ByteString.Lazy as L
-import qualified Data.ByteString as B
-import System.Directory (removeFile)
-import System.IO (withBinaryFile, IOMode (ReadMode), Handle, hIsEOF)
-import Data.ByteString.Lazy.Internal (defaultChunkSize)
-import Control.Concurrent (forkIO)
-import Control.Concurrent.MVar
-import Control.Exception
-import Control.Monad ((<=<))
-
--- | Performs a specified conversion on each 'B.ByteString' output by an
--- enumerator.
-mapE :: (B.ByteString -> B.ByteString) -> Enumerator -> Enumerator
-mapE f (Enumerator e) = Enumerator $ \iter -> e (iter' iter) where
-    iter' iter a = iter a . f
-
--- | This uses 'unsafeInterleaveIO' to lazily read from an enumerator. All
--- normal lazy I/O warnings apply. In addition, since it is based on
--- 'toSource', please observe all precautions for that function.
-toLBS :: Enumerator -> IO L.ByteString
-toLBS = Source.toLBS <=< toSource
-
--- | This function safely converts a lazy bytestring into an enumerator.
-fromLBS :: L.ByteString -> Enumerator
-fromLBS lbs = Enumerator $ \iter a0 -> helper iter a0 $ L.toChunks lbs where
-    helper _ a [] = return $ Right a
-    helper iter a (x:xs) = do
-        ea <- iter a x
-        case ea of
-            Left a' -> return $ Left a'
-            Right a' -> helper iter a' xs
-
--- | Same as 'fromLBS', but the lazy bytestring is in the IO monad. This allows
--- you to lazily read a file into memory, perform some mapping on the data and
--- convert it into an enumerator.
-fromLBS' :: IO L.ByteString -> Enumerator
-fromLBS' lbs' = Enumerator $ \iter a0 -> lbs' >>= \lbs ->
-    runEnumerator (fromLBS lbs) iter a0
-
--- | This function uses another thread to convert an 'Enumerator' to a
--- 'Source'. In essence, this allows you to write code which \"pulls\" instead
--- of code which is pushed to. While this can be a useful technique, some
--- caveats apply:
---
--- * It will be more resource heavy than using the 'Enumerator' directly.
---
--- * You *must* consume all input. If you do not, then the other thread will be
--- deadlocked.
-toSource :: Enumerator -> IO Source
-toSource (Enumerator e) = do
-    buff <- newEmptyMVar
-    _ <- forkIO $ e (helper buff) () >> putMVar buff Nothing
-    return $ source buff
-      where
-        helper :: MVar (Maybe B.ByteString)
-               -> ()
-               -> B.ByteString
-               -> IO (Either () ())
-        helper buff _ bs = do
-            putMVar buff $ Just bs
-            return $ Right ()
-        source :: MVar (Maybe B.ByteString)
-               -> Source
-        source mmbs = Source $ do
-            mbs <- takeMVar mmbs
-            case mbs of
-                Nothing -> do
-                    -- By putting Nothing back in, the source can be called
-                    -- again without causing a deadlock.
-                    putMVar mmbs Nothing
-                    return Nothing
-                Just bs -> return $ Just (bs, source mmbs)
-
--- | Read a chunk of data from the given 'Handle' at a time. We use
--- 'defaultChunkSize' from the bytestring package to determine the largest
--- chunk to take.
-fromHandle :: Handle -> Enumerator
-fromHandle h = Enumerator $ \iter a -> do
-    eof <- hIsEOF h
-    if eof
-        then return $ Right a
-        else do
-            bs <- B.hGet h defaultChunkSize
-            ea' <- iter a bs
-            case ea' of
-                Left a' -> return $ Left a'
-                Right a' -> runEnumerator (fromHandle h) iter a'
-
--- | Wrapper around fromHandle to perform an action after EOF or an exception.
-fromHandleFinally :: Handle -> IO a -> Enumerator
-fromHandleFinally h onEOF = Enumerator $ \iter a0 ->
-                            finally (runEnumerator (fromHandle h) iter a0)
-                                    onEOF
-
--- | A little wrapper around 'fromHandle' which first opens a file for reading.
-fromFile :: FilePath -> Enumerator
-fromFile fp = Enumerator $ \iter a0 -> withBinaryFile fp ReadMode $ \h ->
-    runEnumerator (fromHandle h) iter a0
-
--- | Wrapper around fromFile to perform an action after the file is closed.
-fromFileFinally :: FilePath -> IO a -> Enumerator
-fromFileFinally fp onClose = Enumerator $ \iter a0 ->
-                             finally (runEnumerator (fromFile fp) iter a0)
-                                     onClose
-
--- | Enumerator to read and remove a file. Being based on 'fromFileFinally', it
--- ensures the file is removed, even in the presence of exceptions.
-fromTempFile :: FilePath -> Enumerator
-fromTempFile fp = fromFileFinally fp $ removeFile fp
-
--- | Since the response body is defined as a 'ResponseBody', this function
--- simply reduces the whole value to an enumerator. This can be convenient for
--- server implementations not optimizing file sending.
-fromResponseBody :: ResponseBody -> Enumerator
-fromResponseBody (ResponseEnumerator e) = e
-fromResponseBody (ResponseLBS lbs) = fromLBS lbs
-fromResponseBody (ResponseFile fp) = fromFile fp
diff --git a/Network/Wai/Source.hs b/Network/Wai/Source.hs
deleted file mode 100644
--- a/Network/Wai/Source.hs
+++ /dev/null
@@ -1,46 +0,0 @@
-module Network.Wai.Source
-    (
-    -- * Conversions
-      toEnumerator
-    , toLBS
-    , fromLBS
-    ) where
-
-import Network.Wai
-import qualified Data.ByteString.Lazy as L
-import System.IO.Unsafe (unsafeInterleaveIO)
-
--- | This function safely converts a 'Source' (where you pull data) to an
--- 'Enumerator' (which pushes the data to you). There should be no significant
--- performance impact from its use, and it uses no unsafe functions.
-toEnumerator :: Source -> Enumerator
-toEnumerator source0 = Enumerator $ helper source0 where
-    helper source iter a = do
-        next <- runSource source
-        case next of
-            Nothing -> return $ Right a
-            Just (bs, source') -> do
-                res <- iter a bs
-                case res of
-                    Left a' -> return $ Left a'
-                    Right a' -> helper source' iter a'
-
--- | Uses lazy I\/O (via 'unsafeInterleaveIO') to provide a lazy interface to
--- the given 'Source'. Normal lazy I\/O warnings apply.
-toLBS :: Source -> IO L.ByteString
-toLBS source0 = L.fromChunks `fmap` helper source0 where
-    helper source = unsafeInterleaveIO $ do
-        next <- runSource source
-        case next of
-            Nothing -> return []
-            Just (bs, source') -> do
-                rest <- helper source'
-                return $ bs : rest
-
--- | Convert a lazy bytestring to a 'Source'. This operation does not request lazy I\/O.
-fromLBS :: L.ByteString -> Source
-fromLBS =
-    go . L.toChunks
-  where
-    go [] = Source $ return Nothing
-    go (x:xs) = Source $ return $ Just (x, go xs)
diff --git a/wai.cabal b/wai.cabal
--- a/wai.cabal
+++ b/wai.cabal
@@ -1,5 +1,5 @@
 Name:                wai
-Version:             0.2.2.1
+Version:             0.3.0
 Synopsis:            Web Application Interface.
 Description:         Provides a common protocol for communication between web aplications and web servers.
 License:             BSD3
@@ -17,10 +17,10 @@
     location:        git://github.com/snoyberg/wai.git
 
 Library
-  Build-Depends:     base >= 3 && < 5,
-                     bytestring >= 0.9 && < 0.10,
-                     directory >= 1.0 && < 1.2
+  Build-Depends:     base                   >= 3        && < 5
+                   , bytestring             >= 0.9      && < 0.10
+                   , blaze-builder          >= 0.2      && < 0.3
+                   , enumerator             >= 0.4      && < 0.5
+                   , network                >= 2.2      && < 2.4
   Exposed-modules:   Network.Wai
-                     Network.Wai.Enumerator
-                     Network.Wai.Source
   ghc-options:       -Wall
