diff --git a/LICENSE b/LICENSE
new file mode 100644
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,25 @@
+The following license covers this documentation, and the source code, except
+where otherwise indicated.
+
+Copyright 2012, Myles C. Maxfield. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+* Redistributions of source code must retain the above copyright notice, this
+  list of conditions and the following disclaimer.
+
+* Redistributions in binary form must reproduce the above copyright notice,
+  this list of conditions and the following disclaimer in the documentation
+  and/or other materials provided with the distribution.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS "AS IS" AND ANY EXPRESS OR
+IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+EVENT SHALL THE COPYRIGHT HOLDERS BE LIABLE FOR ANY DIRECT, INDIRECT,
+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
+OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/Network/HTTP/Conduit/Browser2.hs b/Network/HTTP/Conduit/Browser2.hs
new file mode 100644
--- /dev/null
+++ b/Network/HTTP/Conduit/Browser2.hs
@@ -0,0 +1,391 @@
+{-# LANGUAGE CPP #-}
+-- | This module is designed to work similarly to the Network.Browser module in the HTTP package.
+-- The idea is that there are two new types defined: 'BrowserState' and 'BrowserAction'. The
+-- purpose of this module is to make it easy to describe a browsing session, including navigating
+-- to multiple pages, and have things like cookie jar updates work as expected as you browse
+-- around.
+--
+-- BrowserAction is a monad that handles all your browser-related activities. This monad is
+-- actually implemented as a specialization of the State monad, over the BrowserState type. The
+-- BrowserState type has various bits of information that a web browser keeps, such as a current
+-- cookie jar, the number of times to retry a request on failure, HTTP proxy information, etc. In
+-- the BrowserAction monad, there is one BrowserState at any given time, and you can modify it by
+-- using the convenience functions in this module.
+--
+-- A special kind of modification of the current browser state is the action of making a HTTP
+-- request. This will do the request according to the params in the current BrowserState, as well
+-- as modifying the current state with, for example, an updated cookie jar.
+--
+-- To use this module, you would bind together a series of BrowserActions (This simulates the user
+-- clicking on links or using a settings dialogue etc.) to describe your browsing session. When
+-- you've described your session, you call 'browse' on your top-level BrowserAction to actually
+-- convert your actions into the ResourceT IO monad.
+--
+-- Note that the module will be renamed to Network.HTTP.Conduit.Browser a month or so after release.
+-- This is to give users migration time without name clashes.
+--
+-- Here is an example program:
+--
+-- > import qualified Data.ByteString as B
+-- > import qualified Data.ByteString.Lazy as LB
+-- > import qualified Data.ByteString.UTF8 as UB
+-- > import           Data.Conduit
+-- > import           Network.HTTP.Conduit
+-- > import           Network.HTTP.Conduit.Browser
+-- > 
+-- > -- The web request to log in to a service
+-- > req1 :: IO (Request (ResourceT IO))
+-- > req1 = do
+-- >   req <- parseUrl "http://www.myurl.com/login.php"
+-- >   return $ urlEncodedBody [ (UB.fromString "name", UB.fromString "litherum")
+-- >                           , (UB.fromString "pass", UB.fromString "S33kRe7")
+-- >                           ] req
+-- > 
+-- > -- Once authenticated, run this request
+-- > req2 :: IO (Request m')
+-- > req2 = parseUrl "http://www.myurl.com/main.php"
+-- > 
+-- > -- Bind two BrowserActions together
+-- > action :: Request (ResourceT IO) -> Request (ResourceT IO) -> BrowserAction (Response LB.ByteString)
+-- > action r1 r2 = do
+-- >   _ <- makeRequestLbs r1
+-- >   makeRequestLbs r2
+-- > 
+-- > main :: IO ()
+-- > main = do
+-- >   man <- newManager def
+-- >   r1 <- req1
+-- >   r2 <- req2
+-- >   out <- runResourceT $ browse man $ action r1 r2
+-- >   putStrLn $ UB.toString $ B.concat $ LB.toChunks $ responseBody out
+
+module Network.HTTP.Conduit.Browser2
+    ( BrowserState
+    , BrowserAction
+    , browse
+    , makeRequest
+    , makeRequestLbs
+    , defaultState
+    , getBrowserState
+    , setBrowserState
+    , withBrowserState
+    , getMaxRedirects
+    , setMaxRedirects
+    , withMaxRedirects
+    , getMaxRetryCount
+    , setMaxRetryCount
+    , withMaxRetryCount
+    , getTimeout
+    , setTimeout
+    , withTimeout
+    , getAuthorities
+    , setAuthorities
+    , withAuthorities
+    , getCookieFilter
+    , setCookieFilter
+    , withCookieFilter
+    , getCookieJar
+    , setCookieJar
+    , withCookieJar
+    , getCurrentProxy
+    , setCurrentProxy
+    , withCurrentProxy
+    , getOverrideHeaders
+    , setOverrideHeaders
+    , withOverrideHeaders
+    , insertOverrideHeader
+    , deleteOverrideHeader
+    , withOverrideHeader
+    , getUserAgent
+    , setUserAgent
+    , withUserAgent
+    , getManager
+    , setManager
+    )
+  where
+
+import qualified Data.ByteString as BS
+import qualified Data.ByteString.Lazy as L
+import Control.Monad.State
+import Control.Exception
+import qualified Control.Exception.Lifted as LE
+import Data.Conduit
+#if !MIN_VERSION_base(4,6,0)
+import Prelude hiding (catch)
+#endif
+import qualified Network.HTTP.Types as HT
+import qualified Network.HTTP.Types.Header as HT
+import Data.Time.Clock (getCurrentTime, UTCTime)
+import Data.CaseInsensitive (mk)
+import Data.ByteString.UTF8 (fromString)
+import Data.List (partition)
+import Web.Cookie (parseSetCookie)
+import Data.Maybe (catMaybes, fromMaybe)
+import qualified Data.Map as Map
+
+import Network.HTTP.Conduit
+
+data BrowserState = BrowserState
+  { maxRedirects        :: Maybe Int
+  , maxRetryCount       :: Int
+  , timeout             :: Maybe Int
+  , authorities         :: Request (ResourceT IO) -> Maybe (BS.ByteString, BS.ByteString)
+  , cookieFilter        :: Request (ResourceT IO) -> Cookie -> IO Bool
+  , cookieJar           :: CookieJar
+  , currentProxy        :: Maybe Proxy
+  , overrideHeaders     :: Map.Map HT.HeaderName BS.ByteString
+  , manager             :: Manager
+  } 
+
+defaultState :: Manager -> BrowserState
+defaultState m = BrowserState { maxRedirects = Nothing
+                              , maxRetryCount = 1
+                              , timeout = Nothing
+                              , authorities = \ _ -> Nothing
+                              , cookieFilter = \ _ _ -> return True
+                              , cookieJar = def
+                              , currentProxy = Nothing
+                              , overrideHeaders = Map.singleton HT.hUserAgent (fromString "http-conduit")
+                              , manager = m
+                              }
+
+type BrowserAction = StateT BrowserState (ResourceT IO)
+
+-- | Do the browser action with the given manager
+browse :: Manager -> BrowserAction a -> ResourceT IO a
+browse m act = evalStateT act (defaultState m)
+
+-- | Make a request, using all the state in the current BrowserState
+makeRequest :: Request (ResourceT IO) -> BrowserAction (Response (ResumableSource (ResourceT IO) BS.ByteString))
+makeRequest request = do
+  BrowserState
+    { maxRetryCount = max_retry_count
+    , maxRedirects = max_redirects
+    , timeout = time_out
+    , currentProxy  = current_proxy
+    , overrideHeaders = override_headers
+    } <- get
+  retryHelper (applyOverrideHeaders override_headers $
+    request { redirectCount = 0
+            , proxy = maybe (proxy request) Just current_proxy
+            , checkStatus = \ _ _ -> Nothing
+            , responseTimeout = maybe (responseTimeout request) Just time_out
+            }) max_retry_count (fromMaybe (redirectCount request) max_redirects) Nothing
+  where retryHelper request' retry_count max_redirects e
+          | retry_count == 0 = case e of
+            Just e' -> throw e'
+            Nothing -> throw TooManyRetries
+          | otherwise = do
+              resp <- LE.catch (if max_redirects==0
+                                  then (\(_,a,_) -> a) `fmap` performRequest request'
+                                  else runRedirectionChain request' max_redirects [])
+                (\ e' -> retryHelper request' (retry_count - 1) max_redirects (Just (e' :: HttpException)))
+              let code = HT.statusCode $ responseStatus resp
+              if code < 200 || code >= 300
+                then retryHelper request' (retry_count - 1) max_redirects (Just $ StatusCodeException (responseStatus resp) (responseHeaders resp))
+                else return resp
+        performRequest request' = do
+              s@(BrowserState { manager = manager'
+                              , authorities = auths
+                              , cookieJar = cookie_jar
+                              , cookieFilter = cookie_filter
+                              }) <- get
+              now <- liftIO getCurrentTime
+              let (request'', cookie_jar') = insertCookiesIntoRequest
+                                              (applyAuthorities auths request')
+                                              (evictExpiredCookies cookie_jar now) now
+              res <- lift $ http request'' manager'
+              (cookie_jar'', response) <- liftIO $ updateMyCookieJar res request'' now cookie_jar' cookie_filter
+              put $ s {cookieJar = cookie_jar''}
+              return (request'', res, response)
+        runRedirectionChain request' redirect_count ress
+          | redirect_count == (-1) = throw . TooManyRedirects =<< mapM (liftIO . runResourceT . lbsResponse) ress
+          | otherwise = do
+              (request'', res, response) <- performRequest request'
+              let code = HT.statusCode (responseStatus response)
+              if code >= 300 && code < 400
+                then do request''' <- case getRedirectedRequest request'' (responseHeaders response) code of
+                            Just a -> return a
+                            Nothing -> throw . UnparseableRedirect =<< (liftIO $ runResourceT $ lbsResponse response)
+                        runRedirectionChain request''' (redirect_count - 1) (res:ress)
+                else return res
+        applyAuthorities auths request' = case auths request' of
+          Just (user, pass) -> applyBasicAuth user pass request'
+          Nothing -> request'
+
+makeRequestLbs :: Request (ResourceT IO) -> BrowserAction (Response L.ByteString)
+makeRequestLbs = liftIO . runResourceT . lbsResponse <=< makeRequest
+
+applyOverrideHeaders :: Map.Map HT.HeaderName BS.ByteString -> Request a -> Request a
+applyOverrideHeaders ov request' = request' {requestHeaders = x $ requestHeaders request'}
+  where x r = Map.toList $ Map.union ov (Map.fromList r)
+
+updateMyCookieJar :: Response a -> Request (ResourceT IO) -> UTCTime -> CookieJar -> (Request (ResourceT IO) -> Cookie -> IO Bool) -> IO (CookieJar, Response a)
+updateMyCookieJar response request' now cookie_jar cookie_filter = do
+  filtered_cookies <- filterM (cookie_filter request') $ catMaybes $ map (\ sc -> generateCookie sc request' now True) set_cookies
+  return (cookieJar' filtered_cookies, response {responseHeaders = other_headers})
+  where (set_cookie_headers, other_headers) = partition ((== (mk $ fromString "Set-Cookie")) . fst) $ responseHeaders response
+        set_cookie_data = map snd set_cookie_headers
+        set_cookies = map parseSetCookie set_cookie_data
+        cookieJar' = foldl (\ cj c -> insertCheckedCookie c cj True) cookie_jar
+
+-- | You can save and restore the state at will
+getBrowserState    :: BrowserAction BrowserState
+getBrowserState = get
+setBrowserState    :: BrowserState -> BrowserAction ()
+setBrowserState = put
+withBrowserState   :: BrowserState -> BrowserAction a -> BrowserAction a
+withBrowserState s a = do
+  current <- get
+  put s
+  out <- a
+  put current
+  return out
+
+-- | The number of redirects to allow.
+-- if Nothing uses Request's 'redirectCount'
+getMaxRedirects    :: BrowserAction (Maybe Int)
+getMaxRedirects    = get >>= \ a -> return $ maxRedirects a
+setMaxRedirects    :: Maybe Int -> BrowserAction ()
+setMaxRedirects  b = get >>= \ a -> put a {maxRedirects = b}
+withMaxRedirects   :: Maybe Int -> BrowserAction a -> BrowserAction a
+withMaxRedirects a b = do
+  current <- getMaxRedirects
+  setMaxRedirects a
+  out <- b
+  setMaxRedirects current
+  return out
+-- | The number of times to retry a failed connection
+getMaxRetryCount   :: BrowserAction Int
+getMaxRetryCount   = get >>= \ a -> return $ maxRetryCount a
+setMaxRetryCount   :: Int -> BrowserAction ()
+setMaxRetryCount b = get >>= \ a -> put a {maxRetryCount = b}
+withMaxRetryCount  :: Int -> BrowserAction a -> BrowserAction a
+withMaxRetryCount a b = do
+  current <- getMaxRetryCount
+  setMaxRetryCount a
+  out <- b
+  setMaxRetryCount current
+  return out
+-- | Number of microseconds to wait for a response.
+-- if Nothing uses Request's 'responseTimeout'
+getTimeout         :: BrowserAction (Maybe Int)
+getTimeout         = get >>= \ a -> return $ timeout a
+setTimeout         :: Maybe Int -> BrowserAction ()
+setTimeout       b = get >>= \ a -> put a {timeout = b}
+withTimeout        :: Maybe Int -> BrowserAction a -> BrowserAction a
+withTimeout    a b = do
+  current <- getTimeout
+  setTimeout a
+  out <- b
+  setTimeout current
+  return out
+-- | A user-provided function that provides optional authorities.
+-- This function gets run on all requests before they get sent out.
+-- The output of this function is applied to the request.
+getAuthorities     :: BrowserAction (Request (ResourceT IO) -> Maybe (BS.ByteString, BS.ByteString))
+getAuthorities     = get >>= \ a -> return $ authorities a
+setAuthorities     :: (Request (ResourceT IO) -> Maybe (BS.ByteString, BS.ByteString)) -> BrowserAction ()
+setAuthorities   b = get >>= \ a -> put a {authorities = b}
+withAuthorities    :: (Request (ResourceT IO) -> Maybe (BS.ByteString, BS.ByteString)) -> BrowserAction a -> BrowserAction a
+withAuthorities a b = do
+  current <- getAuthorities
+  setAuthorities a
+  out <- b
+  setAuthorities current
+  return out
+-- | Each new Set-Cookie the browser encounters will pass through this filter.
+-- Only cookies that pass the filter (and are already valid) will be allowed into the cookie jar
+getCookieFilter    :: BrowserAction (Request (ResourceT IO) -> Cookie -> IO Bool)
+getCookieFilter    = get >>= \ a -> return $ cookieFilter a
+setCookieFilter    :: (Request (ResourceT IO) -> Cookie -> IO Bool) -> BrowserAction ()
+setCookieFilter  b = get >>= \ a -> put a {cookieFilter = b}
+withCookieFilter   :: (Request (ResourceT IO) -> Cookie -> IO Bool) -> BrowserAction a -> BrowserAction a
+withCookieFilter a b = do
+  current <- getCookieFilter
+  setCookieFilter a
+  out <- b
+  setCookieFilter current
+  return out
+-- | All the cookies!
+getCookieJar       :: BrowserAction CookieJar
+getCookieJar       = get >>= \ a -> return $ cookieJar a
+setCookieJar       :: CookieJar -> BrowserAction ()
+setCookieJar     b = get >>= \ a -> put a {cookieJar = b}
+withCookieJar      :: CookieJar -> BrowserAction a -> BrowserAction a
+withCookieJar a b = do
+  current <- getCookieJar
+  setCookieJar a
+  out <- b
+  setCookieJar current
+  return out
+-- | An optional proxy to send all requests through
+-- if Nothing uses Request's 'proxy'
+getCurrentProxy    :: BrowserAction (Maybe Proxy)
+getCurrentProxy    = get >>= \ a -> return $ currentProxy a
+setCurrentProxy    :: Maybe Proxy -> BrowserAction ()
+setCurrentProxy  b = get >>= \ a -> put a {currentProxy = b}
+withCurrentProxy   :: Maybe Proxy -> BrowserAction a -> BrowserAction a
+withCurrentProxy a b = do
+  current <- getCurrentProxy
+  setCurrentProxy a
+  out <- b
+  setCurrentProxy current
+  return out
+-- | Specifies Headers that should be added to 'Request',
+-- these will override Headers already specified in 'requestHeaders'.
+--
+-- > do insertOverrideHeader ("User-Agent", "http-conduit")
+-- >    insertOverrideHeader ("Connection", "keep-alive")
+-- >    makeRequest def{requestHeaders = [("User-Agent", "another agent"), ("Accept", "everything/digestible")]}
+-- > > User-Agent: http-conduit
+-- > > Accept: everything/digestible
+-- > > Connection: keep-alive
+getOverrideHeaders :: BrowserAction HT.RequestHeaders
+getOverrideHeaders = get >>= \ a -> return $ Map.toList $ overrideHeaders a
+setOverrideHeaders :: HT.RequestHeaders -> BrowserAction ()
+setOverrideHeaders b = do
+    current_user_agent <- getUserAgent
+    get >>= \ a -> put a {overrideHeaders = Map.fromList b}
+    setUserAgent current_user_agent
+withOverrideHeaders:: HT.RequestHeaders -> BrowserAction a -> BrowserAction a
+withOverrideHeaders a b = do
+  current <- getOverrideHeaders
+  setOverrideHeaders a
+  out <- b
+  setOverrideHeaders current
+  return out
+insertOverrideHeader :: HT.Header -> BrowserAction ()
+insertOverrideHeader (b, c) = get >>= \ a -> put a {overrideHeaders = Map.insert b c (overrideHeaders a)}
+deleteOverrideHeader :: HT.HeaderName -> BrowserAction ()
+deleteOverrideHeader b = get >>= \ a -> put a {overrideHeaders = Map.delete b (overrideHeaders a)}
+withOverrideHeader :: HT.Header -> BrowserAction a -> BrowserAction a
+withOverrideHeader a b = do
+  current <- getOverrideHeaders
+  insertOverrideHeader a
+  out <- b
+  setOverrideHeaders current
+  return out
+-- | What string to report our user-agent as.
+-- if Nothing will not send user-agent unless one is specified in 'Request'
+--
+-- > getUserAgent = lookup hUserAgent overrideHeaders
+-- > setUserAgent a = insertOverrideHeader (hUserAgent, a)
+getUserAgent       :: BrowserAction (Maybe BS.ByteString)
+getUserAgent       = get >>= \ a -> return $ Map.lookup HT.hUserAgent (overrideHeaders a)
+setUserAgent       :: Maybe BS.ByteString -> BrowserAction ()
+setUserAgent Nothing = deleteOverrideHeader HT.hUserAgent
+setUserAgent (Just b) = insertOverrideHeader (HT.hUserAgent, b)
+withUserAgent      :: Maybe BS.ByteString -> BrowserAction () -> BrowserAction ()
+withUserAgent a b = do
+  current <- getOverrideHeaders
+  setUserAgent a
+  out <- b
+  setOverrideHeaders current
+  return out
+
+-- | The active manager, managing the connection pool
+getManager         :: BrowserAction Manager
+getManager         = get >>= \ a -> return $ manager a
+setManager         :: Manager -> BrowserAction ()
+setManager       b = get >>= \ a -> put a {manager = b}
diff --git a/Setup.lhs b/Setup.lhs
new file mode 100644
--- /dev/null
+++ b/Setup.lhs
@@ -0,0 +1,8 @@
+#!/usr/bin/env runhaskell
+
+> module Main where
+> import Distribution.Simple
+> import System.Cmd (system)
+
+> main :: IO ()
+> main = defaultMain
diff --git a/http-conduit-browser.cabal b/http-conduit-browser.cabal
new file mode 100644
--- /dev/null
+++ b/http-conduit-browser.cabal
@@ -0,0 +1,73 @@
+name:            http-conduit-browser
+version:         1.6.1
+license:         BSD3
+license-file:    LICENSE
+author:          Myles C. Maxfield <myles.maxfield@gmail.com>
+maintainer:      Myles C. Maxfield <myles.maxfield@gmail.com>
+synopsis:        Browser interface to the http-conduit package
+description:
+    This package creates a monad representing things that browsers do,
+    letting you elegantly describe a browsing session. This package wraps
+    the http-conduit package by Michael Snoyman. Note that the module will
+    be renamed to Network.HTTP.Conduit.Browser a month or so after release.
+    This is to give users migration time without name clashes.
+category:        Web, Conduit
+stability:       Stable
+cabal-version:   >= 1.8
+build-type:      Simple
+homepage:        https://github.com/litherum/http-conduit/tree/http-conduit-browser
+extra-source-files: test/main.hs
+
+flag network-bytestring
+  default: False
+
+library
+    build-depends: base                  >= 4       && < 5
+                 , http-conduit          >= 1.6.1   && < 1.7
+                 , data-default
+                 , cookie
+                 , utf8-string
+                 , case-insensitive
+                 , time
+                 , http-types
+                 , conduit
+                 , lifted-base
+                 , mtl
+                 , bytestring
+                 , containers
+    exposed-modules: Network.HTTP.Conduit.Browser2
+    ghc-options:     -Wall
+
+test-suite test
+    main-is: test/main.hs
+    type: exitcode-stdio-1.0
+    hs-source-dirs: ., test
+
+    ghc-options:   -Wall
+    build-depends: base >= 4 && < 5
+                 , HUnit
+                 , hspec >= 1.3
+                 , http-conduit
+                 , blaze-builder
+                 , bytestring
+                 , text
+                 , data-default
+                 , conduit
+                 , case-insensitive
+                 , containers
+                 , utf8-string
+                 , transformers
+                 , resourcet
+                 , network-conduit
+                 , lifted-base
+                 , http-types
+                 , base64-bytestring
+                 , cookie
+                 , time
+                 , mtl
+                 , warp
+                 , wai
+
+source-repository head
+  type:     git
+  location: git://github.com/litherum/http-conduit.git
diff --git a/test/main.hs b/test/main.hs
new file mode 100644
--- /dev/null
+++ b/test/main.hs
@@ -0,0 +1,269 @@
+{-# LANGUAGE OverloadedStrings #-}
+{-# LANGUAGE ScopedTypeVariables #-}
+import Test.Hspec
+import qualified Data.ByteString as S
+import Network.Wai hiding (requestBody)
+import Network.Wai.Handler.Warp (run)
+import Network.HTTP.Conduit
+import Network.HTTP.Conduit.Browser2
+import Data.ByteString.Base64 (encode)
+import Control.Concurrent (forkIO, killThread)
+import Network.HTTP.Types
+import Control.Exception.Lifted (try)
+import Data.ByteString.UTF8 (fromString)
+import Data.CaseInsensitive (mk)
+import qualified Data.ByteString.Lazy as L
+import Data.IORef
+import Control.Monad.IO.Class (liftIO)
+
+-- TODO tests for responseTimeout/Browser.timeout.
+
+strictToLazy :: S.ByteString -> L.ByteString
+strictToLazy = L.fromChunks . replicate 1
+
+lazyToStrict :: L.ByteString -> S.ByteString
+lazyToStrict = S.concat . L.toChunks
+
+dummy :: S.ByteString
+dummy = "dummy"
+
+user :: S.ByteString
+user = "user"
+
+pass :: S.ByteString
+pass = "pass"
+
+failure :: L.ByteString
+failure = "failure"
+
+success :: L.ByteString
+success = "success"
+
+appWithSideEffect :: IORef Bool -> Application
+appWithSideEffect ref _ = liftIO $ do
+    v <- readIORef ref
+    writeIORef ref $ not v
+    if v
+        then return $ responseLBS status500 [] failure
+        else return $ responseLBS status200 [] success
+
+app :: Application
+app req =
+    case pathInfo req of
+        [] -> return $ responseLBS status200 [] "homepage"
+        ["cookies"] -> return $ responseLBS status200 [tastyCookie] "cookies"
+        ["print-cookies"] -> return $ responseLBS status200 [] $ getHeader "Cookie"
+        ["useragent"] -> return $ responseLBS status200 [] $ getHeader "User-Agent"
+        ["accept"] -> return $ responseLBS status200 [] $ getHeader "Accept"
+        ["authorities"] -> return $ responseLBS status200 [] $ getHeader "Authorization"
+        ["redir1"] -> return $ responseLBS temporaryRedirect307 [redir2] L.empty
+        ["redir2"] -> return $ responseLBS temporaryRedirect307 [redir3] L.empty
+        ["redir3"] -> return $ responseLBS status200 [] $ strictToLazy dummy
+        _ -> return $ responseLBS status404 [] "not found"
+
+    where tastyCookie = (mk (fromString "Set-Cookie"), fromString "flavor=chocolate-chip;")
+          getHeader s = strictToLazy $ case lookup s $ Network.Wai.requestHeaders req of
+                            Just a -> a
+                            Nothing -> S.empty
+          redir2 = (mk (fromString "Location"), fromString "/redir2")
+          redir3 = (mk (fromString "Location"), fromString "/redir3")
+
+main :: IO ()
+main = do
+    ref <- newIORef True
+    hspec $ do
+        describe "browser" $ do
+            it "cookie jar works" $ do
+                tid <- forkIO $ run 3011 app
+                request1 <- parseUrl "http://127.0.0.1:3011/cookies"
+                request2 <- parseUrl "http://127.0.0.1:3011/print-cookies"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        _ <- makeRequestLbs request1
+                        makeRequestLbs request2
+                killThread tid
+                if (lazyToStrict $ responseBody elbs) /= fromString "flavor=chocolate-chip"
+                     then error "Should have gotten the cookie back!"
+                     else return ()
+            it "cookie filter can deny cookies" $ do
+                tid <- forkIO $ run 3011 app
+                request1 <- parseUrl "http://127.0.0.1:3011/cookies"
+                request2 <- parseUrl "http://127.0.0.1:3011/print-cookies"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        setCookieFilter $ const $ const $ return False
+                        _ <- makeRequestLbs request1
+                        makeRequestLbs request2
+                killThread tid
+                if (lazyToStrict $ responseBody elbs) /= S.empty
+                     then error "Shouldn't have gotten the cookie back!"
+                     else return ()
+            it "can save and load cookie jar" $ do
+                tid <- forkIO $ run 3011 app
+                request1 <- parseUrl "http://127.0.0.1:3011/cookies"
+                request2 <- parseUrl "http://127.0.0.1:3011/print-cookies"
+                (elbs1, elbs2) <- withManager $ \manager -> do
+                    browse manager $ do
+                        _ <- makeRequestLbs request1
+                        cookie_jar <- getCookieJar
+                        setCookieJar def
+                        elbs1 <- makeRequestLbs request2
+                        setCookieJar cookie_jar
+                        elbs2 <- makeRequestLbs request2
+                        return (elbs1, elbs2)
+                killThread tid
+                if (((lazyToStrict $ responseBody elbs1) /= S.empty) ||
+                    ((lazyToStrict $ responseBody elbs2) /= fromString "flavor=chocolate-chip"))
+                     then error "Cookie jar got garbled up!"
+                     else return ()
+            it "user agent sets correctly" $ do
+                tid <- forkIO $ run 3012 app
+                request <- parseUrl "http://127.0.0.1:3012/useragent"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        setUserAgent $ Just $ fromString "abcd"
+                        makeRequestLbs request
+                killThread tid
+                if (lazyToStrict $ responseBody elbs) /= fromString "abcd"
+                     then error "Should have gotten the user agent back!"
+                     else return ()
+            it "user agent overrides" $ do
+                tid <- forkIO $ run 3012 app
+                request <- parseUrl "http://127.0.0.1:3012/useragent"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        setUserAgent $ Just $ fromString "abcd"
+                        makeRequestLbs request{Network.HTTP.Conduit.requestHeaders = [(hUserAgent, "bwahaha")]}
+                killThread tid
+                let a = lazyToStrict $ responseBody elbs
+                if a == fromString "abcd"
+                     then return ()
+                     else if a == fromString "bwahaha"
+                            then error "Should have overwriten request's own header!"
+                            else error $ "Some kind of magic happened, User-Agent: \"" ++ show a ++ "\"."
+            it "zeroes overrideHeaders" $ do
+                tid <- forkIO $ run 3012 app
+                request <- parseUrl "http://127.0.0.1:3012/useragent"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        setUserAgent Nothing
+                        setOverrideHeaders []
+                        makeRequestLbs request{Network.HTTP.Conduit.requestHeaders = [(hUserAgent, "bwahaha")]}
+                killThread tid
+                if (lazyToStrict $ responseBody elbs) /= fromString "bwahaha"
+                     then error "Shouldn't have deleted user-agent!"
+                     else return ()
+            it "setting overrideheaders doesn't unset useragent" $ do
+                tid <- forkIO $ run 3012 app
+                request <- parseUrl "http://127.0.0.1:3012/useragent"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        setUserAgent $ Just "abcd"
+                        setOverrideHeaders []
+                        makeRequestLbs request{Network.HTTP.Conduit.requestHeaders = [(hUserAgent, "bwahaha")]}
+                killThread tid
+                if (lazyToStrict $ responseBody elbs) /= fromString "abcd"
+                     then error "Should have overrided user-agent!"
+                     else return ()
+            it "doesn't override additional headers" $ do
+                tid <- forkIO $ run 3012 app
+                request <- parseUrl "http://127.0.0.1:3012/accept"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        insertOverrideHeader ("User-Agent", "http-conduit")
+                        insertOverrideHeader ("Connection", "keep-alive")
+                        makeRequestLbs request{Network.HTTP.Conduit.requestHeaders = [("User-Agent", "another agent"), ("Accept", "everything/digestible")]}
+                killThread tid
+                if (lazyToStrict $ responseBody elbs) /= fromString "everything/digestible"
+                     then error "Shouldn't have deleted Accept header!"
+                     else return ()
+            it "authorities get set correctly" $ do
+                tid <- forkIO $ run 3013 app
+                request <- parseUrl "http://127.0.0.1:3013/authorities"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        setAuthorities $ const $ Just (user, pass)
+                        makeRequestLbs request
+                killThread tid
+                if (lazyToStrict $ responseBody elbs) /= (fromString "Basic " `S.append` (encode $ user `S.append` ":" `S.append` pass))
+                     then error "Authorities didn't get set correctly!"
+                     else return ()
+            it "can follow redirects" $ do
+                tid <- forkIO $ run 3014 app
+                request <- parseUrl "http://127.0.0.1:3014/redir1"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        setMaxRedirects $ Just 2
+                        makeRequestLbs request
+                killThread tid
+                if (lazyToStrict $ responseBody elbs) /= dummy
+                     then error "Should be able to follow 2 redirects"
+                     else return ()
+            it "max redirects fails correctly" $ do
+                tid <- forkIO $ run 3015 app
+                request <- parseUrl "http://127.0.0.1:3015/redir1"
+                elbs <- try $ withManager $ \manager -> do
+                    browse manager $ do
+                        setMaxRedirects $ Just 1
+                        makeRequestLbs request
+                killThread tid
+                case elbs of
+                     Left (TooManyRedirects _) -> return ()
+                     _ -> error "Shouldn't have followed all those redirects!"
+            it "Retry fails correctly when it is too low" $ do
+                writeIORef ref True
+                tid <- forkIO $ run 3016 $ appWithSideEffect ref
+                request <- parseUrl "http://127.0.0.1:3016/"
+                elbs <- try $ withManager $ \manager -> do
+                    browse manager $ do
+                        setMaxRetryCount 1
+                        makeRequestLbs request
+                killThread tid
+                case elbs of
+                     Left (StatusCodeException _ _) -> return ()
+                     _ -> error "1 redirect shouldn't be enough!"
+            it "Makes multiple retries" $ do
+                writeIORef ref True
+                tid <- forkIO $ run 3017 $ appWithSideEffect ref
+                request <- parseUrl "http://127.0.0.1:3017/"
+                elbs <- withManager $ \manager -> do
+                    browse manager $ do
+                        setMaxRetryCount 2
+                        makeRequestLbs request
+                killThread tid
+                if responseBody elbs /= success
+                     then error "Didn't retry failed request"
+                     else return ()
+            it "throws statusCodeException, when maxRedirects=0" $ do
+                tid <- forkIO $ run 3015 app
+                request <- parseUrl "http://127.0.0.1:3015/redir1"
+                elbs <- try $ withManager $ \manager -> do
+                    browse manager $ do
+                        setMaxRedirects $ Just 0
+                        makeRequestLbs request
+                killThread tid
+                case elbs of
+                     Left StatusCodeException{} -> return ()
+                     _ -> error "Should've thrown StatusCodeException!"
+            it "doesn't override redirectCount when maxRedirects=Nothing" $ do
+                tid <- forkIO $ run 3015 app
+                request <- parseUrl "http://127.0.0.1:3015/redir1"
+                elbs <- try $ withManager $ \manager -> do
+                    browse manager $ do
+                        setMaxRedirects Nothing
+                        makeRequestLbs request{redirectCount = 0}
+                killThread tid
+                case elbs of
+                     Left StatusCodeException{} -> return ()
+                     _ -> error "redirectCount /= 0!"
+            it "overrides redirectCount when maxRedirects/=Nothing" $ do
+                tid <- forkIO $ run 3015 app
+                request <- parseUrl "http://127.0.0.1:3015/redir1"
+                elbs <- try $ withManager $ \manager -> do
+                    browse manager $ do
+                        setMaxRedirects $ Just 0
+                        makeRequestLbs request{redirectCount = 10}
+                killThread tid
+                case elbs of
+                     Left StatusCodeException{} -> return ()
+                     _ -> error "redirectCount should be 0!"
