diff --git a/Network/HTTP/Proxy.hs b/Network/HTTP/Proxy.hs
--- a/Network/HTTP/Proxy.hs
+++ b/Network/HTTP/Proxy.hs
@@ -10,9 +10,11 @@
 -- License       : BSD3
 --
 -- History:
---   This code originated in Michael Snoyman's Warp package, was modified by
---   Stephen Blackheath to turn it into a HTTP/HTTP proxy and is now maintained
---   by Erik de Castro Lopo.
+--   This code originated in Michael Snoyman's Warp package when Warp was based
+--   on the Data.Source library. That code was modified by Stephen Blackheath
+--   to turn it into a HTTP/HTTP proxy and from there fell under the maintainership
+--   of Erik de Castro Lopo who then did the conversion from Data.Enumerator to
+--   Data.Conduit. During that conversion, Warp was again used as a reference.
 --
 ---------------------------------------------------------
 
@@ -43,6 +45,7 @@
 import qualified Data.ByteString as S
 import qualified Data.ByteString.Unsafe as SU
 import qualified Data.ByteString.Char8 as B
+import qualified Data.ByteString.Lazy as L
 
 import Network    ( PortID(..) )
 import Network.Socket
@@ -50,7 +53,7 @@
     , SocketType (Stream), listen, bindSocket, setSocketOption, maxListenQueue
     , SockAddr, SocketOption (ReuseAddr)
     , AddrInfo(..), AddrInfoFlag(..), defaultHints, getAddrInfo
-    , Socket, sClose, shutdown, ShutdownCmd(..), HostName, ServiceName, socket, connect
+    , Socket, sClose, HostName, ServiceName, socket, connect
     )
 import Network.BSD ( getProtocolNumber )
 import Network.Wai
@@ -62,33 +65,35 @@
     , fromException, AsyncException (ThreadKilled)
     , bracketOnError, IOException, throw
     )
-import Control.Concurrent (forkIO, ThreadId, killThread)
-
-import Data.Maybe (fromMaybe, isNothing)
-import qualified Network.HTTP.Enumerator as HE
+import Control.Concurrent (forkIO, killThread)
+import Data.Maybe (fromMaybe, isJust, isNothing)
+import qualified Network.HTTP.Conduit as HC
 
 import Data.Typeable (Typeable)
 
-import Data.Enumerator hiding (filter, head, foldl', map)
-import qualified Data.Enumerator as E
-import qualified Data.Enumerator.List as EL
-import qualified Data.Enumerator.Binary as EB
-
+import Control.Monad.Trans.Resource (ResourceT, runResourceT, with)
+import qualified Data.Conduit as C
+import qualified Data.Conduit.Binary as CB
+import qualified Data.Conduit.List as CL
+import Data.Conduit.Blaze (builderToByteString)
+import Control.Exception.Lifted (throwIO)
+import Blaze.ByteString.Builder.HTTP
+    (chunkedTransferEncoding, chunkedTransferTerminator)
 import Blaze.ByteString.Builder
-    (copyByteString, Builder, toByteString, fromByteString)
+    (copyByteString, Builder, toLazyByteString, toByteStringIO)
 import Blaze.ByteString.Builder.Char8 (fromChar, fromShow)
-import Data.Monoid (mappend)
+import Data.Monoid (mappend, mempty)
 
 import Control.Monad.IO.Class (liftIO, MonadIO)
 import Control.Monad.Trans.Class (lift)
 import qualified Network.HTTP.Proxy.Timeout as T
+import Data.Word (Word8)
 import Data.List (delete, foldl')
-import Control.Monad (forever, when)
+import Control.Monad (forever, when, void)
 import qualified Network.HTTP.Types as H
 import qualified Data.CaseInsensitive as CI
 import System.IO (hPutStrLn, stderr)
-import Numeric (readDec)
-import Data.Int (Int64)
+import Network.HTTP.Proxy.ReadInt (readInt64)
 
 #if WINDOWS
 import Control.Concurrent (threadDelay)
@@ -96,9 +101,50 @@
 import Network.Socket (withSocketsDo)
 #endif
 
-bindPort :: Int         -- ^ Port
-         -> String      -- ^ Bind interface or "*" for all
-         -> IO Socket
+#if 0
+import Data.Version (showVersion)
+import qualified Paths_httpProxy
+
+httpProxyVersion :: String
+httpProxyVersion = showVersion Paths_warp.version
+#endif
+
+-- |
+--
+-- In order to provide slowloris protection, Warp provides timeout handlers. We
+-- follow these rules:
+--
+-- * A timeout is created when a connection is opened.
+--
+-- * When all request headers are read, the timeout is tickled.
+--
+-- * Every time at least 2048 bytes of the request body are read, the timeout
+--   is tickled.
+--
+-- * The timeout is paused while executing user code. This will apply to both
+--   the application itself, and a ResponseSource response. The timeout is
+--   resumed as soon as we return from user code.
+--
+-- * Every time data is successfully sent to the client, the timeout is tickled.
+data Connection = Connection
+    { connSendMany :: [B.ByteString] -> IO ()
+    , connSendAll  :: B.ByteString -> IO ()
+    , connSendFile :: FilePath -> Integer -> Integer -> IO () -> IO () -- ^ offset, length
+    , connClose    :: IO ()
+    , connRecv     :: IO B.ByteString
+    }
+
+socketConnection :: Socket -> Connection
+socketConnection s = Connection
+    { connSendMany = Sock.sendMany s
+    , connSendAll = Sock.sendAll s
+    , connSendFile = error "connSendFile : Cannot send a file here."
+    , connClose = sClose s
+    , connRecv = Sock.recv s bytesPerRead
+    }
+
+
+bindPort :: Int -> String -> IO Socket
 bindPort p s = do
     let hints = defaultHints { addrFlags = [AI_PASSIVE
                                          , AI_NUMERICSERV
@@ -113,7 +159,7 @@
         addr = if null addrs' then head addrs else head addrs'
     bracketOnError
         (Network.Socket.socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
-        sCloseX
+        sClose
         (\sock -> do
             setSocketOption sock ReuseAddr 1
             bindSocket sock (addrAddress addr)
@@ -131,7 +177,7 @@
 #if WINDOWS
 runProxySettings set = withSocketsDo $ do
     var <- MV.newMVar Nothing
-    let clean = MV.modifyMVar_ var $ \s -> maybe (return ()) sCloseX s >> return Nothing
+    let clean = MV.modifyMVar_ var $ \s -> maybe (return ()) sClose s >> return Nothing
     _ <- forkIO $ bracket
         (bindPort (proxyPort set) (proxyHost set))
         (const clean)
@@ -143,7 +189,7 @@
 runProxySettings set =
     bracket
         (bindPort (proxyPort set) (proxyHost set))
-        sCloseX
+        sClose
         (runSettingsSocket set)
 #endif
 
@@ -151,239 +197,71 @@
 
 
 runSettingsSocket :: Settings -> Socket -> IO ()
-runSettingsSocket set sock = do
+runSettingsSocket set sock =
+    runSettingsConnection set getter
+  where
+    getter = do
+        (conn, sa) <- accept sock
+        return (socketConnection conn, sa)
+
+runSettingsConnection :: Settings -> IO (Connection, SockAddr) -> IO ()
+runSettingsConnection set getConn = do
     let onE = proxyOnException set
-        reqRM = proxyRequestModifier set
         port = proxyPort set
     tm <- T.initialize $ proxyTimeout set * 1000000
-    mgr <- HE.newManager
+    mgr <- HC.newManager HC.def
     forever $ do
-        (conn, sa) <- accept sock
+        (conn, addr) <- getConn
         _ <- forkIO $ do
             th <- T.registerKillThread tm
-            serveConnection th tm onE reqRM port conn sa mgr
+            serveConnection set th tm onE port conn addr mgr
             T.cancel th
         return ()
 
-verboseSockets :: Bool
-verboseSockets = False
-
-sCloseX :: Socket -> IO ()
-sCloseX s = do
-    when verboseSockets $ putStrLn ("close " ++ show s)
-    sClose s
-
-shutdownX :: Socket -> ShutdownCmd -> IO ()
-shutdownX s ShutdownReceive = do
-    when verboseSockets $ putStrLn ("shutdown " ++ show s ++ " ShutdownReceive")
-    shutdown s ShutdownReceive
-
-shutdownX s ShutdownSend = do
-    when verboseSockets $ putStrLn ("shutdown " ++ show s ++ " ShutdownSend")
-    shutdown s ShutdownSend
-
-shutdownX s ShutdownBoth = do
-    when verboseSockets $ putStrLn ("shutdown " ++ show s ++ " ShutdownBoth")
-    shutdown s ShutdownBoth
-
-mkHeaders :: Monad m
-          => H.HttpVersion
-          -> H.Status
-          -> H.ResponseHeaders
-          -> E.Enumerator ByteString m b
-mkHeaders ver s hrs =
-    E.enumList 1 [toByteString $ headers ver s hrs False]
-
-
-serveConnection :: T.Handle
+serveConnection :: Settings
+                -> T.Handle
                 -> T.Manager
                 -> (SomeException -> IO ())
-                -> (Request -> IO Request)
-                -> Port -> Socket -> SockAddr
-                -> HE.Manager
+                -> Port
+                -> Connection -> SockAddr
+                -> HC.Manager
                 -> IO ()
-serveConnection th tm onException requestMod port conn remoteHost' mgr = do
-      mExtraSocket <- E.run_ (fromClient $$ serveConnection')
-          `finally` sCloseX conn
-      case mExtraSocket of
-          Just (tid, s) -> do
-              killThread tid
-              sCloseX s
-          Nothing -> return ()
-    `catch` onException
+serveConnection settings th tm onException port conn remoteHost' mgr =
+    catch
+        (finally
+          (runResourceT serveConnection')
+          (connClose conn))
+        onException
   where
-    fromClient = enumSocket th bytesPerRead conn
-
-    serveConnection' :: E.Iteratee ByteString IO (Maybe (ThreadId, Socket))
+    serveConnection' :: ResourceT IO ()
     serveConnection' = do
-        req <- parseRequest port remoteHost'
+        fromClient <- C.bufferSource $ C.Source $ return $ connSource conn th
+        serveConnection'' fromClient
+
+    serveConnection'' fromClient = do
+        req <- parseRequest port remoteHost' fromClient
         --liftIO $ print $ requestHeaders req
         case req of
             _ | requestMethod req `elem` [ "GET", "POST" ] ->
-                liftIO (requestMod req) >>= proxyPlain
+                liftIO (proxyRequestModifier settings req)
+                        >>= proxyPlain th conn mgr
+                        >>= \keepAlive -> when keepAlive $ serveConnection'' fromClient
             _ | requestMethod req == "CONNECT" ->
                 case B.split ':' (rawPathInfo req) of
-                    [h, p] -> proxyConnect th tm onException conn h (readDecimal $ B.unpack p) req
+                    [h, p] -> proxyConnect th tm conn h (readInt p) req
+                                >>= \keepAlive -> when keepAlive $ serveConnection'' fromClient
                     _      -> failRequest th conn req "Bad request" ("Bad request '" `mappend` rawPathInfo req `mappend` "'.")
-            _ | otherwise ->
+                                >>= \keepAlive -> when keepAlive $ serveConnection'' fromClient
+            _ ->
                 failRequest th conn req "Unknown request" ("Unknown request '" `mappend` rawPathInfo req `mappend` "'.")
-
-    proxyPlain :: Request -> E.Iteratee ByteString IO (Maybe (ThreadId, Socket))
-    proxyPlain req = do
-        let urlStr = "http://" `mappend` serverName req
-                               `mappend` rawPathInfo req
-                               `mappend` rawQueryString req
-            close =
-                let hasClose hdrs = (== "close") . CI.mk <$> lookup "connection" hdrs
-                    mClose = hasClose (requestHeaders req)
-                    -- RFC2616: Connection defaults to Close in HTTP/1.0 and Keep-Alive in HTTP/1.1
-                    defaultClose = httpVersion req == H.HttpVersion 1 0
-                in  fromMaybe defaultClose mClose
-            outHdrs = [(n,v) | (n,v) <- requestHeaders req, n /= "Host"]
-        liftIO $ putStrLn $ B.unpack (requestMethod req) ++ " " ++ B.unpack urlStr
-        let contentLength = if requestMethod req == "GET"
-             then 0
-             else readDecimal . B.unpack . fromMaybe "0" . lookup "content-length" . requestHeaders $ req
-
-        url <-
-            (\u -> u { HE.method = requestMethod req,
-                       HE.requestHeaders = outHdrs,
-                       HE.rawBody = True,
-                       HE.requestBody = HE.RequestBodyEnum contentLength
-                                            $ joinE (enumIteratee contentLength lazyTakeMax)
-                                            $ EL.map fromByteString
-                                            })
-                <$> lift (HE.semiParseUrl (B.unpack urlStr))
-
-        close' <- E.run_ $ HE.http url (handleHttpReply close) mgr
-        if close'
-            then return Nothing
-            else serveConnection'
-      where
-        handleHttpReply close status hdrs = do
-            let remoteClose = isNothing ("content-length" `lookup` hdrs)
-                close' = close || remoteClose
-                hdrs' = [(n, v) | (n, v) <- hdrs, n `notElem`
-                             ["connection", "proxy-connection"]
-                        ]
-                          ++ [("Connection", if close' then "Close" else "Keep-Alive")]
-            mkHeaders (httpVersion req) status hdrs' $$ iterSocket th conn close
-            return remoteClose
-
--- Create an Enumerator from an Iteratee.
--- The first parameter is the number of bytes to be pulled from the Iteratee.
--- The second is an Iteratee that can pull the data from the source in chunks.
--- The return value is an Enumerator that operates inside the Iteratee monad.
-enumIteratee :: MonadIO m => Int64
-             -> (Int -> Iteratee ByteString m ByteString)
-             -> Enumerator ByteString (Iteratee ByteString m) c
-enumIteratee maxlen takeMax = inner 0
-  where
-    blockLen = 32768
-    inner count (Continue k)
-        | count >= maxlen = k (Chunks [])
-        | count + blockLen <= maxlen = do
-                  bs <- lift $ takeMax $ fromIntegral blockLen
-                  if B.null bs
-                      then k EOF
-                      else k (Chunks [bs]) >>== inner (count + blength bs)
-        | otherwise = do
-                  bs <- lift $ takeMax $ fromIntegral (maxlen - count)
-                  if B.null bs
-                      then k EOF
-                      else k (Chunks [bs]) >>== inner (count + blength bs)
-    inner _ step = returnI step
-    blength = fromIntegral . B.length
-
--- Take up to maxlen bytes of data as lazily as possible. Avoid splitting and
--- joining ByteStrings as much as possible.
-lazyTakeMax :: Monad m => Int -> Iteratee ByteString m ByteString
-lazyTakeMax maxlen | maxlen <= 0 = return B.empty
-lazyTakeMax maxlen = continue step
-  where
-    step (Chunks []) = continue step
-
-    step (Chunks (x:xs))
-        | B.length x < maxlen = yield x (Chunks xs)
-        | otherwise =
-                let (start, extra) = B.splitAt maxlen x
-                in yield start (Chunks (extra:xs))
-
-    step EOF = yield B.empty EOF
-
---------------------------------------------------------------------------------
-
-failRequest :: T.Handle -> Socket -> Request -> ByteString -> ByteString -> E.Iteratee ByteString IO (Maybe (ThreadId, Socket))
-failRequest th conn req headerMsg bodyMsg = do
-        EB.isolate 0 =$
-            E.enumList 1 [bodyMsg] $$
-            mkHeaders (httpVersion req) status [("Content-Length", B.pack . show . B.length $ bodyMsg)] $$
-            iterSocket th conn True
-        return Nothing
-      where
-        status = H.status500 { H.statusMessage = headerMsg }
-
-proxyConnect :: T.Handle -> T.Manager -> (SomeException -> IO ()) -> Socket -> ByteString -> Int -> Request -> E.Iteratee ByteString IO (Maybe (ThreadId, Socket))
-proxyConnect th tm onException conn host prt req = do
-        liftIO $ putStrLn $ B.unpack (requestMethod req) ++ " " ++ B.unpack host ++ ":" ++ show prt
-        mHandles <- liftIO $ do
-            s <- connectTo (B.unpack host) (PortNumber . fromIntegral $ prt)
-            let eh = enumSocket th 65536 s
-                ih = iterSocket th s True
-            return $ Right (s, eh, ih)
-          `catch` \(exc :: IOException) ->
-            return $ Left $ "Unable to connect: " `mappend` B.pack (show exc)
-        case mHandles of
-            Right (s, eh, ih) -> do
-                tid <- liftIO $ forkIO $ do
-                    wrTh <- T.registerKillThread tm
-                    E.run_ (eh $$ mkHeaders (httpVersion req) H.statusOK [] $$ iterSocket wrTh conn True)
-                        `catch` onException
-                    T.cancel wrTh
-                ih
-                return (Just (tid, s))
-            Left errorMsg ->
-                failRequest th conn req errorMsg ("PROXY FAILURE\r\n" `mappend` errorMsg)
-
-connectTo :: HostName   -- Hostname
-          -> PortID     -- Port Identifier
-          -> IO Socket  -- Connected Socket
-connectTo hostname (Service serv) = connect' hostname serv
-connectTo hostname (PortNumber port) = connect' hostname (show port)
-connectTo _ (UnixSocket _) = error "Cannot connect to a UnixSocket"
-
-connect' :: HostName -> ServiceName -> IO Socket
-connect' host serv = do
-    proto <- getProtocolNumber "tcp"
-    let hints = defaultHints { addrFlags = [AI_ADDRCONFIG]
-                             , addrProtocol = proto
-                             , addrSocketType = Stream }
-    addrs <- getAddrInfo (Just hints) (Just host) (Just serv)
-    firstSuccessful $ map tryToConnect addrs
-  where
-  tryToConnect addr =
-    bracketOnError
-    (socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
-    sClose  -- only done if there's an error
-    (\sock -> do
-        connect sock (addrAddress addr)
-        return sock
-        )
-
--- Returns the first action from a list which does not throw an exception.
--- If all the actions throw exceptions (and the list of actions is not empty),
--- the last exception is thrown.
-firstSuccessful :: [IO a] -> IO a
-firstSuccessful [] = error "firstSuccessful: empty list"
-firstSuccessful (p:ps) = catch p $ \e ->
-    case ps of
-        [] -> throw (e :: IOException)
-        _  -> firstSuccessful ps
+                                >>= \keepAlive -> when keepAlive $ serveConnection'' fromClient
 
-parseRequest :: Port -> SockAddr -> E.Iteratee S.ByteString IO Request
-parseRequest port remoteHost' = do
-    headers' <- takeHeaders
-    parseRequest' port headers' remoteHost'
+parseRequest :: Port -> SockAddr
+             -> C.BufferedSource IO S.ByteString
+             -> ResourceT IO Request
+parseRequest port remoteHost' src = do
+    headers' <- src C.$$ takeHeaders
+    parseRequest' port headers' remoteHost' src
 
 -- FIXME come up with good values here
 bytesPerRead, maxTotalHeaderLength :: Int
@@ -395,7 +273,6 @@
     | BadFirstLine String
     | NonHttp
     | IncompleteHeaders
-    | ConnectionClosedByPeer
     | OverLargeHeader
     deriving (Show, Typeable, Eq)
 instance Exception InvalidRequest
@@ -404,41 +281,54 @@
 parseRequest' :: Port
               -> [ByteString]
               -> SockAddr
-              -> E.Iteratee S.ByteString IO Request
-parseRequest' _ [] _ = E.throwError $ NotEnoughLines []
-parseRequest' port (firstLine:otherLines) remoteHost' = do
+              -> C.BufferedSource IO S.ByteString
+              -> ResourceT IO Request
+parseRequest' _ [] _ _ = throwIO $ NotEnoughLines []
+parseRequest' port (firstLine:otherLines) remoteHost' src = do
     (method, rpath', gets, httpversion) <- parseFirst firstLine
-    let (host',rpath) =
-            if S.null rpath'
-                then ("","/")
-                else if "http://" `S.isPrefixOf` rpath'
-                         then S.breakByte 47 $ S.drop 7 rpath' -- '/'
-                         else ("", rpath')
+    let (host',rpath)
+            | S.null rpath' = ("", "/")
+            | "http://" `S.isPrefixOf` rpath' = S.breakByte 47 $ S.drop 7 rpath'
+            | otherwise = ("", rpath')
     let heads = map parseHeaderNoAttr otherLines
-    let host = case (host', lookup "host" heads) of
-            ("", Just h) -> h
-            (h, _)       -> h
+    let host = fromMaybe host' $ lookup "host" heads
+    let len =
+            case lookup "content-length" heads of
+                Nothing -> 0
+                Just bs -> readInt bs
+    let serverName' = takeUntil 58 host -- ':'
+    -- FIXME isolate takes an Integer instead of Int or Int64. If this is a
+    -- performance penalty, we may need our own version.
+    rbody <- C.prepareSource $
+        if len == 0
+            then mempty
+            else src C.$= CB.isolate len
     return Request
-                { requestMethod = method
-                , httpVersion = httpversion
-                , pathInfo = H.decodePathSegments rpath
-                , rawPathInfo = rpath
-                , rawQueryString = gets
-                , queryString = H.parseQuery gets
-                -- NOTE! The meaning of serverName is different from what it is in standard
-                -- WAI, in the following way:
-                -- 1. Here it includes the port number. Otherwise there is no way to obtain it.
-                -- 2. Where 'Host:' is also supplied, we prefer the URI host, since it has the.
-                --    (WAI gives preference to the 'Host:' header.)
-                , serverName = host  -- serverName'
-                , serverPort = port
-                , requestHeaders = heads
-                , isSecure = False
-                , remoteHost = remoteHost'
-                }
+            { requestMethod = method
+            , httpVersion = httpversion
+            , pathInfo = H.decodePathSegments rpath
+            , rawPathInfo = rpath
+            , rawQueryString = gets
+            , queryString = H.parseQuery gets
+            , serverName = serverName'
+            , serverPort = port
+            , requestHeaders = heads
+            , isSecure = False
+            , remoteHost = remoteHost'
+            , requestBody = C.Source $ return rbody
+            , vault = mempty
+            }
 
+
+takeUntil :: Word8 -> ByteString -> ByteString
+takeUntil c bs =
+    case S.elemIndex c bs of
+       Just !idx -> SU.unsafeTake idx bs
+       Nothing -> bs
+{-# INLINE takeUntil #-}
+
 parseFirst :: ByteString
-           -> E.Iteratee S.ByteString IO (ByteString, ByteString, ByteString, H.HttpVersion)
+           -> ResourceT IO (ByteString, ByteString, ByteString, H.HttpVersion)
 parseFirst s =
     case S.split 32 s of  -- ' '
         [method, query, http'] -> do
@@ -450,8 +340,8 @@
                                 "1.1" -> H.http11
                                 _ -> H.http10
                     in return (method, rpath, qstring, hv)
-               else E.throwError NonHttp
-        _ -> E.throwError $ BadFirstLine $ B.unpack s
+               else throwIO NonHttp
+        _ -> throwIO $ BadFirstLine $ B.unpack s
 {-# INLINE parseFirst #-} -- FIXME is this inline necessary? the function is only called from one place and not exported
 
 httpBuilder, spaceBuilder, newlineBuilder, transferEncodingBuilder
@@ -482,11 +372,97 @@
 
 responseHeaderToBuilder :: Builder -> H.Header -> Builder
 responseHeaderToBuilder b (x, y) = b
-    `mappend` copyByteString (CI.original x)
-    `mappend` colonSpaceBuilder
-    `mappend` copyByteString y
-    `mappend` newlineBuilder
+  `mappend` copyByteString (CI.original x)
+  `mappend` colonSpaceBuilder
+  `mappend` copyByteString y
+  `mappend` newlineBuilder
 
+checkPersist :: Request -> Bool
+checkPersist req
+    | ver == H.http11 = checkPersist11 conn
+    | otherwise       = checkPersist10 conn
+  where
+    ver = httpVersion req
+    conn = lookup "connection" $ requestHeaders req
+    checkPersist11 (Just x)
+        | CI.foldCase x == "close"      = False
+    checkPersist11 _                    = True
+    checkPersist10 (Just x)
+        | CI.foldCase x == "keep-alive" = True
+    checkPersist10 _                    = False
+
+isChunked :: H.HttpVersion -> Bool
+isChunked = (==) H.http11
+
+hasBody :: H.Status -> Request -> Bool
+hasBody s req = s /= H.Status 204 "" && s /= H.status304 &&
+                H.statusCode s >= 200 && requestMethod req /= "HEAD"
+
+sendResponse :: T.Handle
+             -> Request -> Connection -> Response -> ResourceT IO Bool
+sendResponse th req conn r = sendResponse' r
+  where
+    version = httpVersion req
+    isPersist = checkPersist req
+    isChunked' = isChunked version
+    needsChunked hs = isChunked' && not (hasLength hs)
+    isKeepAlive hs = isPersist && (isChunked' || hasLength hs)
+    hasLength hs = isJust $ lookup "content-length" hs
+
+    sendResponse' :: Response -> ResourceT IO Bool
+    sendResponse' ResponseFile{} = error "Proxy cannot send a file."
+
+    sendResponse' (ResponseBuilder s hs b)
+        | hasBody s req = liftIO $ do
+              toByteStringIO (\bs -> do
+                connSendAll conn bs
+                T.tickle th) body
+              return (isKeepAlive hs)
+        | otherwise = liftIO $ do
+            connSendMany conn
+                $ L.toChunks
+                $ toLazyByteString
+                $ headers' False
+            T.tickle th
+            return isPersist
+      where
+        headers' = headers version s hs
+        needsChunked' = needsChunked hs
+        body = if needsChunked'
+                  then headers' needsChunked'
+                       `mappend` chunkedTransferEncoding b
+                       `mappend` chunkedTransferTerminator
+                  else headers' False `mappend` b
+
+    sendResponse' (ResponseSource s hs body) =
+        response
+      where
+        headers' = headers version s hs
+        -- FIXME perhaps alloca a buffer per thread and reuse that in all
+        -- functions below. Should lessen greatly the GC burden (I hope)
+        response
+            | not (hasBody s req) = liftIO $ do
+                connSendMany conn
+                   $ L.toChunks $ toLazyByteString
+                   $ headers' False
+                T.tickle th
+                return (checkPersist req)
+            | otherwise = do
+                let src =
+                        CL.sourceList [headers' needsChunked'] `mappend`
+                        (if needsChunked' then body C.$= chunk else body)
+                src C.$$ builderToByteString C.=$ connSink conn th
+                return $ isKeepAlive hs
+        needsChunked' = needsChunked hs
+        chunk :: C.Conduit Builder IO Builder
+        chunk = C.Conduit $ return C.PreparedConduit
+            { C.conduitPush = push
+            , C.conduitClose = close
+            }
+
+        push x = return $ C.Producing [chunkedTransferEncoding x]
+        close = return [chunkedTransferTerminator]
+
 parseHeaderNoAttr :: ByteString -> H.Header
 parseHeaderNoAttr s =
     let (k, rest) = S.breakByte 58 s -- ':'
@@ -497,52 +473,40 @@
                    else rest
      in (CI.mk k, rest')
 
-enumSocket :: T.Handle -> Int -> Socket -> E.Enumerator ByteString IO a
-enumSocket th len sock =
-    inner
-  where
-    inner (E.Continue k) = do
-        bs <- liftIO $ Sock.recv sock len
-        liftIO $ T.tickle th
+connSource :: Connection -> T.Handle -> C.PreparedSource IO ByteString
+connSource Connection { connRecv = recv } th = C.PreparedSource
+    { C.sourcePull = do
+        bs <- liftIO recv
         if S.null bs
-            then do
-                liftIO $
-                    shutdownX sock ShutdownReceive
-                  `catch` \(exc :: IOException) ->
-                    putStrLn $ "couldn't shutdown read side of " ++ show sock ++ ": " ++ show exc
-                E.continue k
-            else k (E.Chunks [bs]) >>== inner
-    inner step = E.returnI step
------- The functions below are not warp-specific and could be split out into a
---separate package.
+            then return C.Closed
+            else do
+                when (S.length bs >= 2048) $ liftIO $ T.tickle th
+                return (C.Open bs)
+    , C.sourceClose = return ()
+    }
 
-iterSocket :: MonadIO m
-           => T.Handle
-           -> Socket
-           -> Bool  -- ^ To close
-           -> E.Iteratee B.ByteString m ()
-iterSocket th sock toClose =
-    E.continue step
+-- | Use 'connSendAll' to send this data while respecting timeout rules.
+connSink :: Connection -> T.Handle -> C.Sink B.ByteString IO ()
+connSink Connection { connSendAll = send } th = C.Sink $ return C.SinkData
+    { C.sinkPush = push
+    , C.sinkClose = close
+    }
   where
+    close = liftIO (T.resume th)
+    push x = do
+        liftIO $ T.resume th
+        liftIO $ send x
+        liftIO $ T.pause th
+        return C.Processing
     -- We pause timeouts before passing control back to user code. This ensures
-    -- that a timeout will only ever be executed when the proxy is in control. We
+    -- that a timeout will only ever be executed when Warp is in control. We
     -- also make sure to resume the timeout after the completion of user code
     -- so that we can kill idle connections.
-    step E.EOF = do
-        liftIO $ T.resume th
-        when toClose $
-            liftIO $
-                shutdownX sock ShutdownSend
-              `catch` \(exc :: IOException) ->
-                putStrLn $ "couldn't shutdown send side of " ++ show sock ++ ": " ++ show exc
-        E.yield () E.EOF
-    step (E.Chunks []) = E.continue step
-    step (E.Chunks xs) = do
-        liftIO $ T.resume th
-        liftIO $ Sock.sendMany sock xs
-        liftIO $ T.pause th
-        E.continue step
 
+------ The functions below are not warp-specific and could be split out into a
+--separate package.
+
+
 -- | Various proxy server settings. This is purposely kept as an abstract data
 -- type so that new settings can be added without breaking backwards
 -- compatibility. In order to create a 'Settings' value, use 'defaultSettings'
@@ -578,66 +542,60 @@
     go' (Just ThreadKilled) = False
     go' _ = True
 
-takeHeaders :: E.Iteratee ByteString IO [ByteString]
-takeHeaders = do
-  !x <- forceHead ConnectionClosedByPeer
-  takeHeaders' 0 id id x
-
-{-# INLINE takeHeaders #-}
+type BSEndo = ByteString -> ByteString
+type BSEndoList = [ByteString] -> [ByteString]
 
-takeHeaders' :: Int
-             -> ([ByteString] -> [ByteString])
-             -> ([ByteString] -> [ByteString])
-             -> ByteString
-             -> E.Iteratee S.ByteString IO [ByteString]
-takeHeaders' !len _ _ _ | len > maxTotalHeaderLength = E.throwError OverLargeHeader
-takeHeaders' !len !lines !prepend !bs = do
-  let !bsLen = {-# SCC "takeHeaders'.bsLen" #-} S.length bs
-      !mnl = {-# SCC "takeHeaders'.mnl" #-} S.elemIndex 10 bs
-  case mnl of
-       -- no newline.  prepend entire bs to next line
-       !Nothing -> {-# SCC "takeHeaders'.noNewline" #-} do
-         let !len' = len + bsLen
-         !more <- forceHead IncompleteHeaders
-         takeHeaders' len' lines (prepend . (:) bs) more
-       Just !nl -> {-# SCC "takeHeaders'.newline" #-} do
-         let !end = nl
-             !start = nl + 1
-             !line = {-# SCC "takeHeaders'.line" #-}
-                     if end > 0
-                        -- line data included in this chunk
-                        then S.concat $! prepend [SU.unsafeTake (checkCR bs end) bs]
-                        --then S.concat $! prepend [SU.unsafeTake (end-1) bs]
-                        -- no line data in this chunk (all in prepend, or empty line)
-                        else S.concat $! prepend []
-         if S.null line
-            -- no more headers
-            then {-# SCC "takeHeaders'.noMoreHeaders" #-} do
-              let !lines' = {-# SCC "takeHeaders'.noMoreHeaders.lines'" #-} lines []
-              if start < bsLen
-                 then {-# SCC "takeHeaders'.noMoreHeaders.yield" #-} do
-                   let !rest = {-# SCC "takeHeaders'.noMoreHeaders.yield.rest" #-} SU.unsafeDrop start bs
-                   E.yield lines' $! E.Chunks [rest]
-                 else return lines'
+data THStatus = THStatus
+    {-# UNPACK #-} !Int -- running total byte count
+    BSEndoList -- previously parsed lines
+    BSEndo -- bytestrings to be prepended
 
-            -- more headers
-            else {-# SCC "takeHeaders'.moreHeaders" #-} do
-              let !len' = len + start
-                  !lines' = {-# SCC "takeHeaders.lines'" #-} lines . (:) line
-              !more <- {-# SCC "takeHeaders'.more" #-}
-                       if start < bsLen
-                          then return $! SU.unsafeDrop start bs
-                          else forceHead IncompleteHeaders
-              {-# SCC "takeHeaders'.takeMore" #-} takeHeaders' len' lines' id more
-{-# INLINE takeHeaders' #-}
+takeHeaders :: C.Sink ByteString IO [ByteString]
+takeHeaders =
+    C.sinkState (THStatus 0 id id) takeHeadersPush close
+  where
+    close _ = throwIO IncompleteHeaders
+{-# INLINE takeHeaders #-}
 
-forceHead :: InvalidRequest -> E.Iteratee ByteString IO ByteString
-forceHead err = do
-  !mx <- EL.head
-  case mx of
-       !Nothing -> E.throwError err
-       Just !x -> return x
-{-# INLINE forceHead #-}
+takeHeadersPush :: THStatus
+                -> ByteString
+                -> ResourceT IO (THStatus, C.SinkResult ByteString [ByteString])
+takeHeadersPush (THStatus len _ _ ) _
+    | len > maxTotalHeaderLength = throwIO OverLargeHeader
+takeHeadersPush (THStatus len lines prepend) bs =
+    case mnl of
+        -- no newline.  prepend entire bs to next line
+        Nothing -> do
+            let len' = len + bsLen
+            return (THStatus len' lines (prepend . S.append bs), C.Processing)
+        Just nl -> do
+            let end = nl
+                start = nl + 1
+                line = prepend (if end > 0
+                                    then SU.unsafeTake (checkCR bs end) bs
+                                    else S.empty)
+            if S.null line
+                -- no more headers
+                then do
+                    let lines' = lines []
+                    if start < bsLen
+                        then do
+                            let rest = SU.unsafeDrop start bs
+                            return (undefined, C.Done (Just rest) lines')
+                        else return (undefined, C.Done Nothing lines')
+                -- more headers
+                else do
+                    let len' = len + start
+                        lines' = lines . (:) line
+                    if start < bsLen
+                        then do
+                            let more = SU.unsafeDrop start bs
+                            takeHeadersPush (THStatus len' lines' id) more
+                        else return (THStatus len' lines' id, C.Processing)
+  where
+    bsLen = S.length bs
+    mnl = S.elemIndex 10 bs
+{-# INLINE takeHeadersPush #-}
 
 checkCR :: ByteString -> Int -> Int
 checkCR bs pos =
@@ -647,6 +605,11 @@
         else pos
 {-# INLINE checkCR #-}
 
+readInt :: Integral a => ByteString -> a
+readInt bs = fromIntegral $ readInt64 bs
+{-# INLINE readInt #-}
+
+
 serverHeader :: H.RequestHeaders -> H.RequestHeaders
 serverHeader hdrs = case lookup key hdrs of
     Nothing  -> server : hdrs
@@ -657,10 +620,120 @@
     server = (key, ver)
     servers svr = (key, S.concat [svr, " ", ver])
 
+--------------------------------------------------------------------------------
 
-readDecimal :: Num a => String -> a
-readDecimal s =
-    case readDec s of
-      [] -> 0
-      (x, _):_ -> x
+proxyPlain :: T.Handle -> Connection -> HC.Manager -> Request -> ResourceT IO Bool
+proxyPlain th conn mgr req = do
+        let urlStr = "http://" `mappend` serverName req
+                               `mappend` rawPathInfo req
+                               `mappend` rawQueryString req
+            close =
+                let hasClose hdrs = (== "close") . CI.mk <$> lookup "connection" hdrs
+                    mClose = hasClose (requestHeaders req)
+                    -- RFC2616: Connection defaults to Close in HTTP/1.0 and Keep-Alive in HTTP/1.1
+                    defaultClose = httpVersion req == H.HttpVersion 1 0
+                in  fromMaybe defaultClose mClose
+            outHdrs = [(n,v) | (n,v) <- requestHeaders req, n /= "Host"]
+        liftIO $ putStrLn $ B.unpack (requestMethod req) ++ " " ++ B.unpack urlStr
+        let contentLength = if requestMethod req == "GET"
+             then 0
+             else readInt . fromMaybe "0" . lookup "content-length" . requestHeaders $ req
+
+        url <-
+            (\u -> u { HC.method = requestMethod req,
+                       HC.requestHeaders = outHdrs,
+                       HC.rawBody = True,
+                       HC.requestBody = HC.RequestBodySource contentLength
+                                            $ fmap copyByteString
+                                            $ requestBody req
+                                            })
+                <$> lift (HC.parseUrl (B.unpack urlStr))
+
+        HC.Response sc rh bodySource <- HC.http url mgr
+        close' <- handleHttpReply close sc rh
+        bodySource C.$$ connSink conn th
+        return $ not close'
+      where
+        handleHttpReply close status hdrs = do
+            let remoteClose = isNothing ("content-length" `lookup` hdrs)
+                close' = close || remoteClose
+                hdrs' = [(n, v) | (n, v) <- hdrs, n `notElem`
+                             ["connection", "proxy-connection"]
+                        ]
+                          ++ [("Connection", if close' then "Close" else "Keep-Alive")]
+            liftIO $ connSendMany conn $ L.toChunks $ toLazyByteString $
+                            headers (httpVersion req) status hdrs' False
+            return remoteClose
+
+failRequest :: T.Handle -> Connection -> Request -> ByteString -> ByteString -> ResourceT IO Bool
+failRequest th conn req headerMsg bodyMsg =
+    sendResponse th req conn $ ResponseBuilder status hdrs $ copyByteString bodyMsg
+  where
+    hdrs = [("Content-Length", B.pack . show . B.length $ bodyMsg)]
+    status = H.status500 { H.statusMessage = headerMsg }
+
+
+proxyConnect :: T.Handle -> T.Manager -> Connection -> ByteString -> Int -> Request -> ResourceT IO Bool
+proxyConnect th tm conn host prt req = do
+        liftIO $ putStrLn $ B.unpack (requestMethod req) ++ " " ++ B.unpack host ++ ":" ++ show prt
+        eConn <- liftIO $ do
+            usock <- socketConnection `fmap` connectTo (B.unpack host) (PortNumber . fromIntegral $ prt)
+            return $ Right usock
+          `catch` \(exc :: IOException) ->
+            return $ Left $ "Unable to connect: " `mappend` B.pack (show exc)
+        case eConn of
+            Right uconn -> do
+                fromUpstream <- C.bufferSource $ C.Source $ return $ connSource uconn th
+                liftIO $
+                    connSendMany conn $ L.toChunks $ toLazyByteString
+                                      $ headers (httpVersion req) H.statusOK [] False
+                void $ with (forkIO $ do
+                            wrTh <- T.registerKillThread tm
+                            runResourceT (fromUpstream C.$$ connSink conn wrTh)
+                            T.cancel wrTh
+                        ) killThread
+                fromClient <- C.bufferSource $ C.Source $ return $ connSource conn th
+                fromClient C.$$ connSink uconn th
+                return False
+            Left errorMsg ->
+                failRequest th conn req errorMsg ("PROXY FAILURE\r\n" `mappend` errorMsg)
+
+
+connectTo :: HostName   -- Hostname
+          -> PortID     -- Port Identifier
+          -> IO Socket  -- Connected Socket
+connectTo hostname (Service serv) = connect' hostname serv
+connectTo hostname (PortNumber port) = connect' hostname (show port)
+connectTo _ (UnixSocket _) = error "Cannot connect to a UnixSocket"
+
+connect' :: HostName -> ServiceName -> IO Socket
+connect' host serv = do
+    proto <- getProtocolNumber "tcp"
+    let hints = defaultHints { addrFlags = [AI_ADDRCONFIG]
+                             , addrProtocol = proto
+                             , addrSocketType = Stream }
+    addrs <- getAddrInfo (Just hints) (Just host) (Just serv)
+    firstSuccessful $ map tryToConnect addrs
+  where
+  tryToConnect addr =
+    bracketOnError
+    (socket (addrFamily addr) (addrSocketType addr) (addrProtocol addr))
+    sClose  -- only done if there's an error
+    (\sock -> do
+        connect sock (addrAddress addr)
+        return sock
+        )
+
+-- Returns the first action from a list which does not throw an exception.
+-- If all the actions throw exceptions (and the list of actions is not empty),
+-- the last exception is thrown.
+firstSuccessful :: [IO a] -> IO a
+firstSuccessful [] = error "firstSuccessful: empty list"
+firstSuccessful (p:ps) = catch p $ \e ->
+    case ps of
+        [] -> throw (e :: IOException)
+        _  -> firstSuccessful ps
+
+
+
 
diff --git a/Network/HTTP/Proxy/ReadInt.hs b/Network/HTTP/Proxy/ReadInt.hs
new file mode 100644
--- /dev/null
+++ b/Network/HTTP/Proxy/ReadInt.hs
@@ -0,0 +1,58 @@
+{-# LANGUAGE OverloadedStrings, MagicHash, BangPatterns  #-}
+
+-- Copyright     : Erik de Castro Lopo <erikd@mega-nerd.com>
+-- License       : BSD3
+
+module Network.HTTP.Proxy.ReadInt (readInt64) where
+
+-- This function lives in its own file because the MagicHash pragma interacts
+-- poorly with the CPP pragma.
+
+import Data.ByteString (ByteString)
+import Data.Int (Int64)
+import GHC.Prim
+import GHC.Types
+
+import qualified Data.ByteString.Char8 as B
+import qualified Data.Char as C
+
+-- This function is used to parse the Content-Length field of HTTP headers and
+-- is a performance hot spot. It should only be replaced with something
+-- significantly and provably faster.
+--
+-- It needs to be able work correctly on 32 bit CPUs for file sizes > 2G so we
+-- use Int64 here and then make a generic 'readInt' that allows conversion to
+-- Int and Integer.
+
+readInt64 :: ByteString -> Int64
+readInt64 bs =
+        B.foldl' (\i c -> i * 10 + fromIntegral (mhDigitToInt c)) 0
+             $ B.takeWhile C.isDigit bs
+{- NOINLINE readInt64MH #-}
+
+data Table = Table !Addr#
+
+{- NOINLINE mhDigitToInt #-}
+mhDigitToInt :: Char -> Int
+mhDigitToInt (C# i) = I# (word2Int# $ indexWord8OffAddr# addr (ord# i))
+  where
+    !(Table addr) = table
+    table :: Table
+    table = Table
+        "\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x01\x02\x03\x04\x05\x06\x07\x08\x09\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\
+        \\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"#
+
diff --git a/http-proxy.cabal b/http-proxy.cabal
--- a/http-proxy.cabal
+++ b/http-proxy.cabal
@@ -1,13 +1,14 @@
 Name:           http-proxy
-Version:        0.0.5
+Version:        0.0.6
 License:        BSD3
 License-file:   LICENSE
 Author:         Michael Snoyman, Stephen Blackheath, Erik de Castro Lopo
 Maintainer:     erikd@mega-nerd.com
 Homepage:       https://github.com/erikd/http-proxy
+Bug-reports:    https://github.com/erikd/http-proxy/issues
 Category:       Web
 Build-Type:     Simple
-Cabal-Version:  >=1.6
+Cabal-Version:  >=1.8
 Stability:      Experimental
 
 Synopsis:       A library for writing HTTP and HTTPS proxies
@@ -15,7 +16,7 @@
 Description:
   http-proxy is a library for writing HTTP and HTTPS proxies.
   .
-  Use of the enumerator library provides file streaming via the proxy in both
+  Use of the Conduit library provides file streaming via the proxy in both
   directions. Memory usage of the proxy scales linearly with the number of
   simultaneous connections and is independent of the size of the files being
   uploaded or downloaded.
@@ -30,13 +31,16 @@
 Library
   Build-Depends:     base                          >= 3        && < 5
                    , bytestring                    >= 0.9.1.4  && < 0.10
-                   , wai                           >= 0.4      && < 0.5
-                   , enumerator                    >= 0.4.8    && < 0.5
-                   , http-enumerator               >= 0.7.2.2  && < 0.8
+                   , wai                           >= 1.0      && < 1.1
+                   , conduit
+                   , http-conduit                  >= 1.2      && < 1.3
                    , transformers                  >= 0.2.2    && < 0.3
                    , blaze-builder                 >= 0.2.1.4  && < 0.4
                    , http-types                    >= 0.6      && < 0.7
-                   , case-insensitive              >= 0.2
+                   , case-insensitive              >= 0.4      && < 0.5
+                   , lifted-base                   >= 0.1      && < 0.2
+                   , blaze-builder-conduit         >= 0.0      && < 0.1
+                   , ghc-prim
   if flag(network-bytestring)
       build-depends: network               >= 2.2.1.5 && < 2.2.3
                    , network-bytestring    >= 0.1.3   && < 0.1.4
@@ -44,6 +48,7 @@
       build-depends: network               >= 2.3     && < 2.4
   Exposed-modules:   Network.HTTP.Proxy
   Other-modules:     Network.HTTP.Proxy.Timeout
+                     Network.HTTP.Proxy.ReadInt
 
   ghc-options:       -Wall
   if os(windows)
