diff --git a/CHANGELOG.md b/CHANGELOG.md
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,12 @@
+[The latest version of this document is on GitHub.](https://github.com/haskell-servant/servant/blob/master/servant-client/CHANGELOG.md)
+[Changelog for `servant` package contains significant entries for all core packages.](https://github.com/haskell-servant/servant/blob/master/servant/CHANGELOG.md)
+
+0.12
+----
+
+- Factored out into `servant-client-core` all the functionality that was
+  independent of the `http-client` backend.
+
 0.11
 ----
 
diff --git a/servant-client.cabal b/servant-client.cabal
--- a/servant-client.cabal
+++ b/servant-client.cabal
@@ -1,5 +1,5 @@
 name:                servant-client
-version:             0.11
+version:             0.12
 synopsis: automatical derivation of querying functions for servant webservices
 description:
   This library lets you derive automatically Haskell functions that
@@ -12,11 +12,15 @@
 license-file:        LICENSE
 author:              Servant Contributors
 maintainer:          haskell-servant-maintainers@googlegroups.com
-copyright:           2014-2016 Zalora South East Asia Pte Ltd, Servant Contributors
-category:            Servant Web
+copyright:           2014-2016 Zalora South East Asia Pte Ltd, 2016-2017 Servant Contributors
+category:            Servant, Web
 build-type:          Simple
 cabal-version:       >=1.10
-tested-with:         GHC >= 7.8
+tested-with:
+  GHC==7.8.4
+  GHC==7.10.3
+  GHC==8.0.2
+  GHC==8.2.1
 homepage:            http://haskell-servant.readthedocs.org/
 Bug-reports:         http://github.com/haskell-servant/servant/issues
 extra-source-files:
@@ -30,39 +34,27 @@
 library
   exposed-modules:
     Servant.Client
-    Servant.Client.Generic
-    Servant.Client.Experimental.Auth
-    Servant.Common.BaseUrl
-    Servant.Common.BasicAuth
-    Servant.Common.Req
+    Servant.Client.Internal.HttpClient
   build-depends:
-      base                  >= 4.7      && < 4.10
+      base                  >= 4.7      && < 4.11
     , base-compat           >= 0.9.1    && < 0.10
+    , bytestring            >= 0.10     && < 0.11
     , aeson                 >= 0.7      && < 1.3
     , attoparsec            >= 0.12     && < 0.14
-    , base64-bytestring     >= 1.0.0.1  && < 1.1
-    , bytestring            >= 0.10     && < 0.11
-    , exceptions            >= 0.8      && < 0.9
-    , generics-sop          >= 0.1.0.0  && < 0.4
-    , http-api-data         >= 0.3.6    && < 0.4
-    , http-client           >= 0.4.18.1 && < 0.6
+    , containers            >= 0.5      && < 0.6
+    , http-client           >= 0.4.30   && < 0.6
     , http-client-tls       >= 0.2.2    && < 0.4
-    , http-media            >= 0.6.2    && < 0.7
-    , http-types            >= 0.8.6    && < 0.10
+    , http-media            >= 0.6.2    && < 0.8
+    , http-types            >= 0.8.6    && < 0.11
+    , exceptions            >= 0.8      && < 0.9
     , monad-control         >= 1.0.0.4  && < 1.1
-    , network-uri           >= 2.6      && < 2.7
-    , safe                  >= 0.3.9    && < 0.4
+    , mtl                   >= 2.1      && < 2.3
     , semigroupoids         >= 4.3      && < 5.3
-    , servant               == 0.11.*
-    , string-conversions    >= 0.3      && < 0.5
+    , servant-client-core   == 0.12.*
     , text                  >= 1.2      && < 1.3
     , transformers          >= 0.3      && < 0.6
     , transformers-base     >= 0.4.4    && < 0.5
     , transformers-compat   >= 0.4      && < 0.6
-    , mtl
-  if !impl(ghc >= 8.0)
-    build-depends:
-        semigroups          >=0.16.2.2 && <0.19
   hs-source-dirs: src
   default-language: Haskell2010
   ghc-options: -Wall
@@ -76,14 +68,16 @@
   default-language: Haskell2010
   hs-source-dirs: test
   main-is: Spec.hs
+  build-tool-depends:
+    hspec-discover:hspec-discover
   other-modules:
       Servant.ClientSpec
-    , Servant.Common.BaseUrlSpec
   build-depends:
       base == 4.*
     , aeson
     , base-compat
     , bytestring
+    , containers
     , deepseq
     , hspec == 2.*
     , http-api-data
@@ -96,7 +90,8 @@
     , QuickCheck >= 2.7
     , servant
     , servant-client
-    , servant-server == 0.11.*
+    , servant-client-core
+    , servant-server == 0.12.*
     , text
     , transformers
     , transformers-compat
diff --git a/src/Servant/Client.hs b/src/Servant/Client.hs
--- a/src/Servant/Client.hs
+++ b/src/Servant/Client.hs
@@ -1,498 +1,13 @@
-{-# LANGUAGE CPP                  #-}
-{-# LANGUAGE DataKinds            #-}
-{-# LANGUAGE FlexibleContexts     #-}
-{-# LANGUAGE FlexibleInstances    #-}
-{-# LANGUAGE InstanceSigs         #-}
-{-# LANGUAGE OverloadedStrings    #-}
-{-# LANGUAGE PolyKinds            #-}
-{-# LANGUAGE ScopedTypeVariables  #-}
-{-# LANGUAGE TypeFamilies         #-}
-{-# LANGUAGE TypeOperators        #-}
-{-# LANGUAGE UndecidableInstances #-}
-
-#include "overlapping-compat.h"
 -- | This module provides 'client' which can automatically generate
 -- querying functions for each endpoint just from the type representing your
 -- API.
 module Servant.Client
-  ( AuthClientData
-  , AuthenticateReq(..)
-  , client
-  , HasClient(..)
+  ( client
   , ClientM
   , runClientM
-  , ClientEnv (ClientEnv)
-  , mkAuthenticateReq
-  , ServantError(..)
-  , EmptyClient(..)
-  , module Servant.Common.BaseUrl
+  , ClientEnv(..)
+  , module Servant.Client.Core.Reexport
   ) where
 
-import           Data.ByteString.Lazy       (ByteString)
-import           Data.List
-import           Data.Proxy
-import           Data.String.Conversions
-import           Data.Text                  (unpack)
-import           GHC.TypeLits
-import           Network.HTTP.Client        (Response)
-import           Network.HTTP.Media
-import qualified Network.HTTP.Types         as H
-import qualified Network.HTTP.Types.Header  as HTTP
-import           Prelude ()
-import           Prelude.Compat
-import           Servant.API
-import           Servant.Client.Experimental.Auth
-import           Servant.Common.BaseUrl
-import           Servant.Common.BasicAuth
-import           Servant.Common.Req
-
--- * Accessing APIs as a Client
-
--- | 'client' allows you to produce operations to query an API from a client.
---
--- > type MyApi = "books" :> Get '[JSON] [Book] -- GET /books
--- >         :<|> "books" :> ReqBody '[JSON] Book :> Post '[JSON] Book -- POST /books
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > getAllBooks :: ClientM [Book]
--- > postNewBook :: Book -> ClientM Book
--- > (getAllBooks :<|> postNewBook) = client myApi
-client :: HasClient api => Proxy api -> Client api
-client p = clientWithRoute p defReq
-
--- | This class lets us define how each API combinator
--- influences the creation of an HTTP request. It's mostly
--- an internal class, you can just use 'client'.
-class HasClient api where
-  type Client api :: *
-  clientWithRoute :: Proxy api -> Req -> Client api
-
-
--- | A client querying function for @a ':<|>' b@ will actually hand you
---   one function for querying @a@ and another one for querying @b@,
---   stitching them together with ':<|>', which really is just like a pair.
---
--- > type MyApi = "books" :> Get '[JSON] [Book] -- GET /books
--- >         :<|> "books" :> ReqBody '[JSON] Book :> Post Book -- POST /books
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > getAllBooks :: ClientM [Book]
--- > postNewBook :: Book -> ClientM Book
--- > (getAllBooks :<|> postNewBook) = client myApi
-instance (HasClient a, HasClient b) => HasClient (a :<|> b) where
-  type Client (a :<|> b) = Client a :<|> Client b
-  clientWithRoute Proxy req =
-    clientWithRoute (Proxy :: Proxy a) req :<|>
-    clientWithRoute (Proxy :: Proxy b) req
-
--- | Singleton type representing a client for an empty API.
-data EmptyClient = EmptyClient deriving (Eq, Show, Bounded, Enum)
-
--- | The client for 'EmptyAPI' is simply 'EmptyClient'.
---
--- > type MyAPI = "books" :> Get '[JSON] [Book] -- GET /books
--- >         :<|> "nothing" :> EmptyAPI
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > getAllBooks :: ClientM [Book]
--- > (getAllBooks :<|> EmptyClient) = client myApi
-instance HasClient EmptyAPI where
-  type Client EmptyAPI = EmptyClient
-  clientWithRoute Proxy _ = EmptyClient
-
--- | If you use a 'Capture' in one of your endpoints in your API,
--- the corresponding querying function will automatically take
--- an additional argument of the type specified by your 'Capture'.
--- That function will take care of inserting a textual representation
--- of this value at the right place in the request path.
---
--- You can control how values for this type are turned into
--- text by specifying a 'ToHttpApiData' instance for your type.
---
--- Example:
---
--- > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > getBook :: Text -> ClientM Book
--- > getBook = client myApi
--- > -- then you can just use "getBook" to query that endpoint
-instance (KnownSymbol capture, ToHttpApiData a, HasClient api)
-      => HasClient (Capture capture a :> api) where
-
-  type Client (Capture capture a :> api) =
-    a -> Client api
-
-  clientWithRoute Proxy req val =
-    clientWithRoute (Proxy :: Proxy api)
-                    (appendToPath p req)
-
-    where p = unpack (toUrlPiece val)
-
--- | If you use a 'CaptureAll' in one of your endpoints in your API,
--- the corresponding querying function will automatically take an
--- additional argument of a list of the type specified by your
--- 'CaptureAll'. That function will take care of inserting a textual
--- representation of this value at the right place in the request
--- path.
---
--- You can control how these values are turned into text by specifying
--- a 'ToHttpApiData' instance of your type.
---
--- Example:
---
--- > type MyAPI = "src" :> CaptureAll Text -> Get '[JSON] SourceFile
--- >
--- > myApi :: Proxy
--- > myApi = Proxy
---
--- > getSourceFile :: [Text] -> ClientM SourceFile
--- > getSourceFile = client myApi
--- > -- then you can use "getSourceFile" to query that endpoint
-instance (KnownSymbol capture, ToHttpApiData a, HasClient sublayout)
-      => HasClient (CaptureAll capture a :> sublayout) where
-
-  type Client (CaptureAll capture a :> sublayout) =
-    [a] -> Client sublayout
-
-  clientWithRoute Proxy req vals =
-    clientWithRoute (Proxy :: Proxy sublayout)
-                    (foldl' (flip appendToPath) req ps)
-
-    where ps = map (unpack . toUrlPiece) vals
-
-instance OVERLAPPABLE_
-  -- Note [Non-Empty Content Types]
-  (MimeUnrender ct a, ReflectMethod method, cts' ~ (ct ': cts)
-  ) => HasClient (Verb method status cts' a) where
-  type Client (Verb method status cts' a) = ClientM a
-  clientWithRoute Proxy req = do
-    snd <$> performRequestCT (Proxy :: Proxy ct) method req
-      where method = reflectMethod (Proxy :: Proxy method)
-
-instance OVERLAPPING_
-  (ReflectMethod method) => HasClient (Verb method status cts NoContent) where
-  type Client (Verb method status cts NoContent)
-    = ClientM NoContent
-  clientWithRoute Proxy req = do
-    performRequestNoBody method req >> return NoContent
-      where method = reflectMethod (Proxy :: Proxy method)
-
-instance OVERLAPPING_
-  -- Note [Non-Empty Content Types]
-  ( MimeUnrender ct a, BuildHeadersTo ls, ReflectMethod method, cts' ~ (ct ': cts)
-  ) => HasClient (Verb method status cts' (Headers ls a)) where
-  type Client (Verb method status cts' (Headers ls a))
-    = ClientM (Headers ls a)
-  clientWithRoute Proxy req = do
-    let method = reflectMethod (Proxy :: Proxy method)
-    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) method req
-    return $ Headers { getResponse = resp
-                     , getHeadersHList = buildHeadersTo hdrs
-                     }
-
-instance OVERLAPPING_
-  ( BuildHeadersTo ls, ReflectMethod method
-  ) => HasClient (Verb method status cts (Headers ls NoContent)) where
-  type Client (Verb method status cts (Headers ls NoContent))
-    = ClientM (Headers ls NoContent)
-  clientWithRoute Proxy req = do
-    let method = reflectMethod (Proxy :: Proxy method)
-    hdrs <- performRequestNoBody method req
-    return $ Headers { getResponse = NoContent
-                     , getHeadersHList = buildHeadersTo hdrs
-                     }
-
-
--- | If you use a 'Header' in one of your endpoints in your API,
--- the corresponding querying function will automatically take
--- an additional argument of the type specified by your 'Header',
--- wrapped in Maybe.
---
--- That function will take care of encoding this argument as Text
--- in the request headers.
---
--- All you need is for your type to have a 'ToHttpApiData' instance.
---
--- Example:
---
--- > newtype Referer = Referer { referrer :: Text }
--- >   deriving (Eq, Show, Generic, ToHttpApiData)
--- >
--- >            -- GET /view-my-referer
--- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > viewReferer :: Maybe Referer -> ClientM Book
--- > viewReferer = client myApi
--- > -- then you can just use "viewRefer" to query that endpoint
--- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments
-instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
-      => HasClient (Header sym a :> api) where
-
-  type Client (Header sym a :> api) =
-    Maybe a -> Client api
-
-  clientWithRoute Proxy req mval =
-    clientWithRoute (Proxy :: Proxy api)
-                    (maybe req
-                           (\value -> Servant.Common.Req.addHeader hname value req)
-                           mval
-                    )
-
-    where hname = symbolVal (Proxy :: Proxy sym)
-
--- | Using a 'HttpVersion' combinator in your API doesn't affect the client
--- functions.
-instance HasClient api
-  => HasClient (HttpVersion :> api) where
-
-  type Client (HttpVersion :> api) =
-    Client api
-
-  clientWithRoute Proxy =
-    clientWithRoute (Proxy :: Proxy api)
-
--- | If you use a 'QueryParam' in one of your endpoints in your API,
--- the corresponding querying function will automatically take
--- an additional argument of the type specified by your 'QueryParam',
--- enclosed in Maybe.
---
--- If you give Nothing, nothing will be added to the query string.
---
--- If you give a non-'Nothing' value, this function will take care
--- of inserting a textual representation of this value in the query string.
---
--- You can control how values for your type are turned into
--- text by specifying a 'ToHttpApiData' instance for your type.
---
--- Example:
---
--- > type MyApi = "books" :> QueryParam "author" Text :> Get '[JSON] [Book]
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > getBooksBy :: Maybe Text -> ClientM [Book]
--- > getBooksBy = client myApi
--- > -- then you can just use "getBooksBy" to query that endpoint.
--- > -- 'getBooksBy Nothing' for all books
--- > -- 'getBooksBy (Just "Isaac Asimov")' to get all books by Isaac Asimov
-instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
-      => HasClient (QueryParam sym a :> api) where
-
-  type Client (QueryParam sym a :> api) =
-    Maybe a -> Client api
-
-  -- if mparam = Nothing, we don't add it to the query string
-  clientWithRoute Proxy req mparam =
-    clientWithRoute (Proxy :: Proxy api)
-                    (maybe req
-                           (flip (appendToQueryString pname) req . Just)
-                           mparamText
-                    )
-
-    where pname  = cs pname'
-          pname' = symbolVal (Proxy :: Proxy sym)
-          mparamText = fmap toQueryParam mparam
-
--- | If you use a 'QueryParams' in one of your endpoints in your API,
--- the corresponding querying function will automatically take
--- an additional argument, a list of values of the type specified
--- by your 'QueryParams'.
---
--- If you give an empty list, nothing will be added to the query string.
---
--- Otherwise, this function will take care
--- of inserting a textual representation of your values in the query string,
--- under the same query string parameter name.
---
--- You can control how values for your type are turned into
--- text by specifying a 'ToHttpApiData' instance for your type.
---
--- Example:
---
--- > type MyApi = "books" :> QueryParams "authors" Text :> Get '[JSON] [Book]
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > getBooksBy :: [Text] -> ClientM [Book]
--- > getBooksBy = client myApi
--- > -- then you can just use "getBooksBy" to query that endpoint.
--- > -- 'getBooksBy []' for all books
--- > -- 'getBooksBy ["Isaac Asimov", "Robert A. Heinlein"]'
--- > --   to get all books by Asimov and Heinlein
-instance (KnownSymbol sym, ToHttpApiData a, HasClient api)
-      => HasClient (QueryParams sym a :> api) where
-
-  type Client (QueryParams sym a :> api) =
-    [a] -> Client api
-
-  clientWithRoute Proxy req paramlist =
-    clientWithRoute (Proxy :: Proxy api)
-                    (foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just))
-                            req
-                            paramlist'
-                    )
-
-    where pname  = cs pname'
-          pname' = symbolVal (Proxy :: Proxy sym)
-          paramlist' = map (Just . toQueryParam) paramlist
-
--- | If you use a 'QueryFlag' in one of your endpoints in your API,
--- the corresponding querying function will automatically take
--- an additional 'Bool' argument.
---
--- If you give 'False', nothing will be added to the query string.
---
--- Otherwise, this function will insert a value-less query string
--- parameter under the name associated to your 'QueryFlag'.
---
--- Example:
---
--- > type MyApi = "books" :> QueryFlag "published" :> Get '[JSON] [Book]
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > getBooks :: Bool -> ClientM [Book]
--- > getBooks = client myApi
--- > -- then you can just use "getBooks" to query that endpoint.
--- > -- 'getBooksBy False' for all books
--- > -- 'getBooksBy True' to only get _already published_ books
-instance (KnownSymbol sym, HasClient api)
-      => HasClient (QueryFlag sym :> api) where
-
-  type Client (QueryFlag sym :> api) =
-    Bool -> Client api
-
-  clientWithRoute Proxy req flag =
-    clientWithRoute (Proxy :: Proxy api)
-                    (if flag
-                       then appendToQueryString paramname Nothing req
-                       else req
-                    )
-
-    where paramname = cs $ symbolVal (Proxy :: Proxy sym)
-
-
--- | Pick a 'Method' and specify where the server you want to query is. You get
--- back the full `Response`.
-instance HasClient Raw where
-  type Client Raw
-    = H.Method ->  ClientM (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)
-
-  clientWithRoute :: Proxy Raw -> Req -> Client Raw
-  clientWithRoute Proxy req httpMethod = do
-    performRequest httpMethod req
-
--- | If you use a 'ReqBody' in one of your endpoints in your API,
--- the corresponding querying function will automatically take
--- an additional argument of the type specified by your 'ReqBody'.
--- That function will take care of encoding this argument as JSON and
--- of using it as the request body.
---
--- All you need is for your type to have a 'ToJSON' instance.
---
--- Example:
---
--- > type MyApi = "books" :> ReqBody '[JSON] Book :> Post '[JSON] Book
--- >
--- > myApi :: Proxy MyApi
--- > myApi = Proxy
--- >
--- > addBook :: Book -> ClientM Book
--- > addBook = client myApi
--- > -- then you can just use "addBook" to query that endpoint
-instance (MimeRender ct a, HasClient api)
-      => HasClient (ReqBody (ct ': cts) a :> api) where
-
-  type Client (ReqBody (ct ': cts) a :> api) =
-    a -> Client api
-
-  clientWithRoute Proxy req body =
-    clientWithRoute (Proxy :: Proxy api)
-                    (let ctProxy = Proxy :: Proxy ct
-                     in setReqBodyLBS (mimeRender ctProxy body)
-                                  -- We use first contentType from the Accept list
-                                  (contentType ctProxy)
-                                  req
-                    )
-
--- | Make the querying function append @path@ to the request path.
-instance (KnownSymbol path, HasClient api) => HasClient (path :> api) where
-  type Client (path :> api) = Client api
-
-  clientWithRoute Proxy req =
-     clientWithRoute (Proxy :: Proxy api)
-                     (appendToPath p req)
-
-    where p = symbolVal (Proxy :: Proxy path)
-
-instance HasClient api => HasClient (Vault :> api) where
-  type Client (Vault :> api) = Client api
-
-  clientWithRoute Proxy req =
-    clientWithRoute (Proxy :: Proxy api) req
-
-instance HasClient api => HasClient (RemoteHost :> api) where
-  type Client (RemoteHost :> api) = Client api
-
-  clientWithRoute Proxy req =
-    clientWithRoute (Proxy :: Proxy api) req
-
-instance HasClient api => HasClient (IsSecure :> api) where
-  type Client (IsSecure :> api) = Client api
-
-  clientWithRoute Proxy req =
-    clientWithRoute (Proxy :: Proxy api) req
-
-instance HasClient subapi =>
-  HasClient (WithNamedContext name context subapi) where
-
-  type Client (WithNamedContext name context subapi) = Client subapi
-  clientWithRoute Proxy = clientWithRoute (Proxy :: Proxy subapi)
-
-instance ( HasClient api
-         ) => HasClient (AuthProtect tag :> api) where
-  type Client (AuthProtect tag :> api)
-    = AuthenticateReq (AuthProtect tag) -> Client api
-
-  clientWithRoute Proxy req (AuthenticateReq (val,func)) =
-    clientWithRoute (Proxy :: Proxy api) (func val req)
-
--- * Basic Authentication
-
-instance HasClient api => HasClient (BasicAuth realm usr :> api) where
-  type Client (BasicAuth realm usr :> api) = BasicAuthData -> Client api
-
-  clientWithRoute Proxy req val =
-    clientWithRoute (Proxy :: Proxy api) (basicAuthReq val req)
-
-
-{- Note [Non-Empty Content Types]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Rather than have
-
-   instance (..., cts' ~ (ct ': cts)) => ... cts' ...
-
-It may seem to make more sense to have:
-
-   instance (...) => ... (ct ': cts) ...
-
-But this means that if another instance exists that does *not* require
-non-empty lists, but is otherwise more specific, no instance will be overall
-more specific. This in turn generally means adding yet another instance (one
-for empty and one for non-empty lists).
--}
+import Servant.Client.Internal.HttpClient
+import Servant.Client.Core.Reexport
diff --git a/src/Servant/Client/Experimental/Auth.hs b/src/Servant/Client/Experimental/Auth.hs
deleted file mode 100644
--- a/src/Servant/Client/Experimental/Auth.hs
+++ /dev/null
@@ -1,36 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeSynonymInstances #-}
-{-# LANGUAGE TypeFamilies      #-}
-
--- | Authentication for clients
-
-module Servant.Client.Experimental.Auth (
-    AuthenticateReq(AuthenticateReq, unAuthReq)
-  , AuthClientData
-  , mkAuthenticateReq
-  ) where
-
-import Servant.Common.Req (Req)
-
--- | For a resource protected by authentication (e.g. AuthProtect), we need
--- to provide the client with some data used to add authentication data
--- to a request
---
--- NOTE: THIS API IS EXPERIMENTAL AND SUBJECT TO CHANGE
-type family AuthClientData a :: *
-
--- | For better type inference and to avoid usage of a data family, we newtype
--- wrap the combination of some 'AuthClientData' and a function to add authentication
--- data to a request
---
--- NOTE: THIS API IS EXPERIMENTAL AND SUBJECT TO CHANGE
-newtype AuthenticateReq a =
-  AuthenticateReq { unAuthReq :: (AuthClientData a, AuthClientData a -> Req -> Req) }
-
--- | Handy helper to avoid wrapping datatypes in tuples everywhere.
---
--- NOTE: THIS API IS EXPERIMENTAL AND SUBJECT TO CHANGE
-mkAuthenticateReq :: AuthClientData a
-                  -> (AuthClientData a -> Req -> Req)
-                  -> AuthenticateReq a
-mkAuthenticateReq val func = AuthenticateReq (val, func)
diff --git a/src/Servant/Client/Generic.hs b/src/Servant/Client/Generic.hs
deleted file mode 100644
--- a/src/Servant/Client/Generic.hs
+++ /dev/null
@@ -1,164 +0,0 @@
-{-# LANGUAGE CPP #-}
-{-# LANGUAGE DataKinds #-}
-{-# LANGUAGE DefaultSignatures #-}
-{-# LANGUAGE FlexibleContexts #-}
-{-# LANGUAGE FlexibleInstances #-}
-{-# LANGUAGE MultiParamTypeClasses #-}
-{-# LANGUAGE TypeFamilies #-}
-{-# LANGUAGE TypeOperators #-}
-{-# LANGUAGE UndecidableInstances #-}
-
-#include "overlapping-compat.h"
-
-module Servant.Client.Generic
-  ( ClientLike(..)
-  , genericMkClientL
-  , genericMkClientP
-  ) where
-
-import Generics.SOP   (Code, Generic, I(..), NP(..), NS(Z), SOP(..), to)
-import Servant.API    ((:<|>)(..))
-import Servant.Client (ClientM)
-
--- | This class allows us to match client structure with client functions
--- produced with 'client' without explicit pattern-matching.
---
--- The client structure needs a 'Generics.SOP.Generic' instance.
---
--- Example:
---
--- > type API
--- >     = "foo" :> Capture "x" Int :> Get '[JSON] Int
--- >  :<|> "bar" :> QueryParam "a" Char :> QueryParam "b" String :> Post '[JSON] [Int]
--- >  :<|> Capture "nested" Int :> NestedAPI
--- >
--- > type NestedAPI
--- >     = Get '[JSON] String
--- >  :<|> "baz" :> QueryParam "c" Char :> Post '[JSON] ()
--- >
--- > data APIClient = APIClient
--- >   { getFoo         :: Int -> ClientM Int
--- >   , postBar        :: Maybe Char -> Maybe String -> ClientM [Int]
--- >   , mkNestedClient :: Int -> NestedClient
--- >   } deriving GHC.Generic
--- >
--- > instance Generics.SOP.Generic APIClient
--- > instance (Client API ~ client) => ClientLike client APIClient
--- >
--- > data NestedClient = NestedClient
--- >  { getString :: ClientM String
--- >  , postBaz   :: Maybe Char -> ClientM ()
--- >  } deriving GHC.Generic
--- >
--- > instance Generics.SOP.Generic NestedClient
--- > instance (Client NestedAPI ~ client) => ClientLike client NestedClient
--- >
--- > mkAPIClient :: APIClient
--- > mkAPIClient = mkClient (client (Proxy :: Proxy API))
---
--- By default, left-nested alternatives are expanded:
---
--- > type API1
--- >     = "foo" :> Capture "x" Int :> Get '[JSON] Int
--- >  :<|> "bar" :> QueryParam "a" Char :> Post '[JSON] String
--- >
--- > type API2
--- >     = "baz" :> QueryParam "c" Char :> Post '[JSON] ()
--- >
--- > type API = API1 :<|> API2
--- >
--- > data APIClient = APIClient
--- >   { getFoo  :: Int -> ClientM Int
--- >   , postBar :: Maybe Char -> ClientM String
--- >   , postBaz :: Maybe Char -> ClientM ()
--- >   } deriving GHC.Generic
--- >
--- > instance Generics.SOP.Generic APIClient
--- > instance (Client API ~ client) => ClientLike client APIClient
--- >
--- > mkAPIClient :: APIClient
--- > mkAPIClient = mkClient (client (Proxy :: Proxy API))
---
--- If you want to define client for @API1@ as a separate data structure,
--- you can use 'genericMkClientP':
---
--- > data APIClient1 = APIClient1
--- >   { getFoo  :: Int -> ClientM Int
--- >   , postBar :: Maybe Char -> ClientM String
--- >   } deriving GHC.Generic
--- >
--- > instance Generics.SOP.Generic APIClient1
--- > instance (Client API1 ~ client) => ClientLike client APIClient1
--- >
--- > data APIClient = APIClient
--- >   { mkAPIClient1 :: APIClient1
--- >   , postBaz      :: Maybe Char -> ClientM ()
--- >   } deriving GHC.Generic
--- >
--- > instance Generics.SOP.Generic APIClient
--- > instance (Client API ~ client) => ClientLike client APIClient where
--- >   mkClient = genericMkClientP
--- >
--- > mkAPIClient :: APIClient
--- > mkAPIClient = mkClient (client (Proxy :: Proxy API))
-class ClientLike client custom where
-  mkClient :: client -> custom
-  default mkClient :: (Generic custom, Code custom ~ '[xs], GClientList client '[], GClientLikeL (ClientList client '[]) xs)
-    => client -> custom
-  mkClient = genericMkClientL
-
-instance ClientLike client custom
-      => ClientLike (a -> client) (a -> custom) where
-  mkClient c = mkClient . c
-
-instance ClientLike (ClientM a) (ClientM a) where
-  mkClient = id
-
--- | Match client structure with client functions, regarding left-nested API clients
--- as separate data structures.
-class GClientLikeP client xs where
-  gMkClientP :: client -> NP I xs
-
-instance (GClientLikeP b (y ': xs), ClientLike a x)
-      => GClientLikeP (a :<|> b) (x ': y ': xs) where
-  gMkClientP (a :<|> b) = I (mkClient a) :* gMkClientP b
-
-instance ClientLike a x => GClientLikeP a '[x] where
-  gMkClientP a = I (mkClient a) :* Nil
-
--- | Match client structure with client functions, expanding left-nested API clients
--- in the same structure.
-class GClientLikeL (xs :: [*]) (ys :: [*]) where
-  gMkClientL :: NP I xs -> NP I ys
-
-instance GClientLikeL '[] '[] where
-  gMkClientL Nil = Nil
-
-instance (ClientLike x y, GClientLikeL xs ys) => GClientLikeL (x ': xs) (y ': ys) where
-  gMkClientL (I x :* xs) = I (mkClient x) :* gMkClientL xs
-
-type family ClientList (client :: *) (acc :: [*]) :: [*] where
-  ClientList (a :<|> b) acc = ClientList a (ClientList b acc)
-  ClientList a acc = a ': acc
-
-class GClientList client (acc :: [*]) where
-  gClientList :: client -> NP I acc -> NP I (ClientList client acc)
-
-instance (GClientList b acc, GClientList a (ClientList b acc))
-  => GClientList (a :<|> b) acc where
-  gClientList (a :<|> b) acc = gClientList a (gClientList b acc)
-
-instance OVERLAPPABLE_ (ClientList client acc ~ (client ': acc))
-  => GClientList client acc where
-  gClientList c acc = I c :* acc
-
--- | Generate client structure from client type, expanding left-nested API (done by default).
-genericMkClientL :: (Generic custom, Code custom ~ '[xs], GClientList client '[], GClientLikeL (ClientList client '[]) xs)
-  => client -> custom
-genericMkClientL = to . SOP . Z . gMkClientL . flip gClientList Nil
-
--- | Generate client structure from client type, regarding left-nested API clients as separate data structures.
-genericMkClientP :: (Generic custom, Code custom ~ '[xs], GClientLikeP client xs)
-  => client -> custom
-genericMkClientP = to . SOP . Z . gMkClientP
-
diff --git a/src/Servant/Client/Internal/HttpClient.hs b/src/Servant/Client/Internal/HttpClient.hs
new file mode 100644
--- /dev/null
+++ b/src/Servant/Client/Internal/HttpClient.hs
@@ -0,0 +1,153 @@
+{-# LANGUAGE CPP                        #-}
+{-# LANGUAGE DeriveDataTypeable         #-}
+{-# LANGUAGE DeriveGeneric              #-}
+{-# LANGUAGE FlexibleContexts           #-}
+{-# LANGUAGE FlexibleInstances          #-}
+{-# LANGUAGE GeneralizedNewtypeDeriving #-}
+{-# LANGUAGE MultiParamTypeClasses      #-}
+{-# LANGUAGE OverloadedStrings          #-}
+{-# LANGUAGE ScopedTypeVariables        #-}
+{-# LANGUAGE TypeFamilies               #-}
+
+-- | @http-client@-based client requests executor
+module Servant.Client.Internal.HttpClient where
+
+
+import           Prelude                     ()
+import           Prelude.Compat
+
+import           Control.Exception
+import           Control.Monad
+import           Control.Monad.Base          (MonadBase (..))
+import           Control.Monad.Catch         (MonadCatch, MonadThrow)
+import           Control.Monad.Error.Class   (MonadError (..))
+import           Control.Monad.Reader
+import           Control.Monad.Trans.Control (MonadBaseControl (..))
+import           Control.Monad.Trans.Except
+import           Data.ByteString.Builder     (toLazyByteString)
+import qualified Data.ByteString.Lazy        as BSL
+import           Data.Foldable               (toList)
+import           Data.Functor.Alt            (Alt (..))
+import           Data.Monoid                 ((<>))
+import           Data.Proxy                  (Proxy (..))
+import           Data.Sequence               (fromList)
+import           Data.String                 (fromString)
+import qualified Data.Text                   as T
+import           GHC.Generics
+import           Network.HTTP.Media          (renderHeader)
+import           Network.HTTP.Types          (hContentType, renderQuery,
+                                              statusCode)
+import           Servant.Client.Core
+
+import qualified Network.HTTP.Client         as Client
+
+-- | The environment in which a request is run.
+data ClientEnv
+  = ClientEnv
+  { manager :: Client.Manager
+  , baseUrl :: BaseUrl
+  }
+
+-- | Generates a set of client functions for an API.
+--
+-- Example:
+--
+-- > type API = Capture "no" Int :> Get '[JSON] Int
+-- >        :<|> Get '[JSON] [Bool]
+-- >
+-- > api :: Proxy API
+-- > api = Proxy
+-- >
+-- > getInt :: Int -> ClientM Int
+-- > getBools :: ClientM [Bool]
+-- > getInt :<|> getBools = client api
+client :: HasClient ClientM api => Proxy api -> Client ClientM api
+client api = api `clientIn` (Proxy :: Proxy ClientM)
+
+-- | @ClientM@ is the monad in which client functions run. Contains the
+-- 'Client.Manager' and 'BaseUrl' used for requests in the reader environment.
+newtype ClientM a = ClientM
+  { runClientM' :: ReaderT ClientEnv (ExceptT ServantError IO) a }
+  deriving ( Functor, Applicative, Monad, MonadIO, Generic
+           , MonadReader ClientEnv, MonadError ServantError, MonadThrow
+           , MonadCatch)
+
+instance MonadBase IO ClientM where
+  liftBase = ClientM . liftBase
+
+instance MonadBaseControl IO ClientM where
+  type StM ClientM a = Either ServantError a
+
+  liftBaseWith f = ClientM (liftBaseWith (\g -> f (g . runClientM')))
+
+  restoreM st = ClientM (restoreM st)
+
+-- | Try clients in order, last error is preserved.
+instance Alt ClientM where
+  a <!> b = a `catchError` \_ -> b
+
+instance RunClient ClientM where
+  runRequest = performRequest
+  throwServantError = throwError
+  catchServantError = catchError
+
+instance ClientLike (ClientM a) (ClientM a) where
+  mkClient = id
+
+runClientM :: ClientM a -> ClientEnv -> IO (Either ServantError a)
+runClientM cm env = runExceptT $ (flip runReaderT env) $ runClientM' cm
+
+
+performRequest :: Request -> ClientM Response
+performRequest req = do
+  m <- asks manager
+  burl <- asks baseUrl
+  let request = requestToClientRequest burl req
+
+  eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request m
+  case eResponse of
+    Left err -> throwError $ err
+    Right response -> do
+      let status = Client.responseStatus response
+          status_code = statusCode status
+          ourResponse = clientResponseToReponse response
+      unless (status_code >= 200 && status_code < 300) $
+        throwError $ FailureResponse ourResponse
+      return ourResponse
+
+clientResponseToReponse :: Client.Response BSL.ByteString -> Response
+clientResponseToReponse r = Response
+  { responseStatusCode = Client.responseStatus r
+  , responseBody = Client.responseBody r
+  , responseHeaders = fromList $ Client.responseHeaders r
+  , responseHttpVersion = Client.responseVersion r
+  }
+
+requestToClientRequest :: BaseUrl -> Request -> Client.Request
+requestToClientRequest burl r = Client.defaultRequest
+  { Client.method = requestMethod r
+  , Client.host = fromString $ baseUrlHost burl
+  , Client.port = baseUrlPort burl
+  , Client.path = BSL.toStrict
+                $ fromString (baseUrlPath burl)
+               <> toLazyByteString (requestPath r)
+  , Client.queryString = renderQuery True . toList $ requestQueryString r
+  , Client.requestHeaders =
+      let orig = toList $ requestHeaders r
+      in maybe orig (: orig) contentTypeHdr
+  , Client.requestBody = body
+  , Client.secure = isSecure
+  }
+  where
+    (body, contentTypeHdr) = case requestBody r of
+      Nothing -> (Client.RequestBodyLBS "", Nothing)
+      Just (RequestBodyLBS body', typ)
+        -> (Client.RequestBodyLBS body', Just (hContentType, renderHeader typ))
+    isSecure = case baseUrlScheme burl of
+      Http -> False
+      Https -> True
+
+catchConnectionError :: IO a -> IO (Either ServantError a)
+catchConnectionError action =
+  catch (Right <$> action) $ \e ->
+    pure . Left . ConnectionError . T.pack $ show (e :: Client.HttpException)
diff --git a/src/Servant/Common/BaseUrl.hs b/src/Servant/Common/BaseUrl.hs
deleted file mode 100644
--- a/src/Servant/Common/BaseUrl.hs
+++ /dev/null
@@ -1,77 +0,0 @@
-{-# LANGUAGE DeriveDataTypeable #-}
-{-# LANGUAGE DeriveGeneric      #-}
-{-# LANGUAGE ViewPatterns       #-}
-module Servant.Common.BaseUrl (
-  -- * types
-    BaseUrl (..)
-  , InvalidBaseUrlException
-  , Scheme (..)
-  -- * functions
-  , parseBaseUrl
-  , showBaseUrl
-) where
-
-import           Control.Monad.Catch (Exception, MonadThrow, throwM)
-import           Data.List
-import           Data.Typeable
-import           GHC.Generics
-import           Network.URI hiding (path)
-import           Safe
-import           Text.Read
-
--- | URI scheme to use
-data Scheme =
-    Http  -- ^ http://
-  | Https -- ^ https://
-  deriving (Show, Eq, Ord, Generic)
-
--- | Simple data type to represent the target of HTTP requests
---   for servant's automatically-generated clients.
-data BaseUrl = BaseUrl
-  { baseUrlScheme :: Scheme -- ^ URI scheme to use
-  , baseUrlHost   :: String   -- ^ host (eg "haskell.org")
-  , baseUrlPort   :: Int      -- ^ port (eg 80)
-  , baseUrlPath   :: String   -- ^ path (eg "/a/b/c")
-  } deriving (Show, Ord, Generic)
-
-instance Eq BaseUrl where
-    BaseUrl a b c path == BaseUrl a' b' c' path'
-        = a == a' && b == b' && c == c' && s path == s path'
-        where s ('/':x) = x
-              s x       = x
-
-showBaseUrl :: BaseUrl -> String
-showBaseUrl (BaseUrl urlscheme host port path) =
-  schemeString ++ "//" ++ host ++ (portString </> path)
-    where
-      a </> b = if "/" `isPrefixOf` b || null b then a ++ b else a ++ '/':b
-      schemeString = case urlscheme of
-        Http  -> "http:"
-        Https -> "https:"
-      portString = case (urlscheme, port) of
-        (Http, 80) -> ""
-        (Https, 443) -> ""
-        _ -> ":" ++ show port
-
-data InvalidBaseUrlException = InvalidBaseUrlException String deriving (Show, Typeable)
-instance Exception InvalidBaseUrlException
-
-parseBaseUrl :: MonadThrow m => String -> m BaseUrl
-parseBaseUrl s = case parseURI (removeTrailingSlash s) of
-  -- This is a rather hacky implementation and should be replaced with something
-  -- implemented in attoparsec (which is already a dependency anyhow (via aeson)).
-  Just (URI "http:" (Just (URIAuth "" host (':' : (readMaybe -> Just port)))) path "" "") ->
-    return (BaseUrl Http host port path)
-  Just (URI "http:" (Just (URIAuth "" host "")) path "" "") ->
-    return (BaseUrl Http host 80 path)
-  Just (URI "https:" (Just (URIAuth "" host (':' : (readMaybe -> Just port)))) path "" "") ->
-    return (BaseUrl Https host port path)
-  Just (URI "https:" (Just (URIAuth "" host "")) path "" "") ->
-    return (BaseUrl Https host 443 path)
-  _ -> if "://" `isInfixOf` s
-    then throwM (InvalidBaseUrlException $ "Invalid base URL: " ++ s)
-    else parseBaseUrl ("http://" ++ s)
- where
-  removeTrailingSlash str = case lastMay str of
-    Just '/' -> init str
-    _ -> str
diff --git a/src/Servant/Common/BasicAuth.hs b/src/Servant/Common/BasicAuth.hs
deleted file mode 100644
--- a/src/Servant/Common/BasicAuth.hs
+++ /dev/null
@@ -1,21 +0,0 @@
-{-# LANGUAGE OverloadedStrings #-}
-{-# LANGUAGE TypeSynonymInstances #-}
-{-# LANGUAGE TypeFamilies      #-}
-
--- | Basic Authentication for clients
-
-module Servant.Common.BasicAuth (
-    basicAuthReq
-  ) where
-
-import Data.ByteString.Base64 (encode)
-import Data.Monoid ((<>))
-import Data.Text.Encoding (decodeUtf8)
-import Servant.Common.Req (addHeader, Req)
-import Servant.API.BasicAuth (BasicAuthData(BasicAuthData))
-
--- | Authenticate a request using Basic Authentication
-basicAuthReq :: BasicAuthData -> Req -> Req
-basicAuthReq (BasicAuthData user pass) req =
-    let authText = decodeUtf8 ("Basic " <> encode (user <> ":" <> pass))
-    in addHeader "Authorization" authText req
diff --git a/src/Servant/Common/Req.hs b/src/Servant/Common/Req.hs
deleted file mode 100644
--- a/src/Servant/Common/Req.hs
+++ /dev/null
@@ -1,285 +0,0 @@
-{-# LANGUAGE CPP                        #-}
-{-# LANGUAGE DeriveDataTypeable         #-}
-{-# LANGUAGE DeriveGeneric              #-}
-{-# LANGUAGE GeneralizedNewtypeDeriving #-}
-{-# LANGUAGE MultiParamTypeClasses      #-}
-{-# LANGUAGE TypeFamilies               #-}
-{-# LANGUAGE OverloadedStrings          #-}
-{-# LANGUAGE ScopedTypeVariables        #-}
-
-module Servant.Common.Req where
-
-import Prelude ()
-import Prelude.Compat
-
-import Control.Exception
-import Control.Monad
-import Control.Monad.Catch (MonadThrow, MonadCatch)
-import Data.Foldable (toList)
-import Data.Functor.Alt (Alt (..))
-import Data.Semigroup ((<>))
-
-import Control.Monad.Error.Class (MonadError(..))
-import Control.Monad.Trans.Except
-
-import GHC.Generics
-import Control.Monad.Base (MonadBase (..))
-import Control.Monad.IO.Class ()
-import Control.Monad.Reader
-import Control.Monad.Trans.Control (MonadBaseControl (..))
-import qualified Data.ByteString.Builder as BS
-import Data.ByteString.Lazy hiding (pack, filter, map, null, elem, any)
-import Data.String
-import Data.String.Conversions (cs)
-import Data.Proxy
-import Data.Text (Text)
-import Data.Text.Encoding
-import Data.Typeable
-import Network.HTTP.Media
-import Network.HTTP.Types
-import Network.HTTP.Client hiding (Proxy, path)
-import qualified Network.HTTP.Types.Header   as HTTP
-import Network.URI hiding (path)
-import Servant.API.ContentTypes
-import Servant.Common.BaseUrl
-
-import qualified Network.HTTP.Client as Client
-
-import Web.HttpApiData
-
-data ServantError
-  = FailureResponse
-    { failingRequest            :: UrlReq
-    , responseStatus            :: Status
-    , responseContentType       :: MediaType
-    , responseBody              :: ByteString
-    }
-  | DecodeFailure
-    { decodeError               :: String
-    , responseContentType       :: MediaType
-    , responseBody              :: ByteString
-    }
-  | UnsupportedContentType
-    { responseContentType       :: MediaType
-    , responseBody              :: ByteString
-    }
-  | InvalidContentTypeHeader
-    { responseContentTypeHeader :: ByteString
-    , responseBody              :: ByteString
-    }
-  | ConnectionError
-    { connectionError           :: SomeException
-    }
-  deriving (Show, Typeable)
-
-instance Eq ServantError where
-  FailureResponse _ a b c == FailureResponse _ x y z =
-    (a, b, c) == (x, y, z)
-  DecodeFailure a b c == DecodeFailure x y z =
-    (a, b, c) == (x, y, z)
-  UnsupportedContentType a b == UnsupportedContentType x y =
-    (a, b) == (x, y)
-  InvalidContentTypeHeader a b == InvalidContentTypeHeader x y =
-    (a, b) == (x, y)
-  ConnectionError a == ConnectionError x =
-    show a == show x
-  _ == _ = False
-
-instance Exception ServantError
-
-data UrlReq = UrlReq BaseUrl Req
-
-instance Show UrlReq where
-  show (UrlReq url req) = showBaseUrl url ++ path ++ "?" ++ show (qs req)
-    where
-      path = cs (BS.toLazyByteString (reqPath req))
-
-data Req = Req
-  { reqPath   :: BS.Builder
-  , qs        :: QueryText
-  , reqBody   :: Maybe (RequestBody, MediaType)
-  , reqAccept :: [MediaType]
-  , headers   :: [(String, Text)]
-  }
-
-defReq :: Req
-defReq = Req "" [] Nothing [] []
-
-appendToPath :: String -> Req -> Req
-appendToPath p req =
-  req { reqPath = reqPath req <> "/" <> toEncodedUrlPiece p }
-
-appendToQueryString :: Text       -- ^ param name
-                    -> Maybe Text -- ^ param value
-                    -> Req
-                    -> Req
-appendToQueryString pname pvalue req =
-  req { qs = qs req ++ [(pname, pvalue)]
-      }
-
-addHeader :: ToHttpApiData a => String -> a -> Req -> Req
-addHeader name val req = req { headers = headers req
-                                      ++ [(name, decodeUtf8 (toHeader val))]
-                             }
-
--- | Set body and media type of the request being constructed.
---
--- The body is set to the given bytestring using the 'RequestBodyLBS'
--- constructor.
---
-{-# DEPRECATED setRQBody "Use setReqBodyLBS instead" #-}
-setRQBody :: ByteString -> MediaType -> Req -> Req
-setRQBody = setReqBodyLBS
-
--- | Set body and media type of the request being constructed.
---
--- The body is set to the given bytestring using the 'RequestBodyLBS'
--- constructor.
---
--- @since 0.9.2.0
---
-setReqBodyLBS :: ByteString -> MediaType -> Req -> Req
-setReqBodyLBS b t req = req { reqBody = Just (RequestBodyLBS b, t) }
-
--- | Set body and media type of the request being constructed.
---
--- @since 0.9.2.0
---
-setReqBody :: RequestBody -> MediaType -> Req -> Req
-setReqBody b t req = req { reqBody = Just (b, t) }
-
-reqToRequest :: (Functor m, MonadThrow m) => Req -> BaseUrl -> m Request
-reqToRequest req (BaseUrl reqScheme reqHost reqPort path) =
-    setheaders . setAccept . setrqb . setQS <$> parseRequest url
-
-  where url = show $ nullURI { uriScheme = case reqScheme of
-                                  Http  -> "http:"
-                                  Https -> "https:"
-                             , uriAuthority = Just $
-                                 URIAuth { uriUserInfo = ""
-                                         , uriRegName = reqHost
-                                         , uriPort = ":" ++ show reqPort
-                                         }
-                             , uriPath = fullPath
-                             }
-        fullPath = path ++ cs (BS.toLazyByteString (reqPath req))
-
-        setrqb r = case reqBody req of
-                     Nothing -> r
-                     Just (b,t) -> r { requestBody = b
-                                     , requestHeaders = requestHeaders r
-                                                     ++ [(hContentType, cs . show $ t)] }
-        setQS = setQueryString $ queryTextToQuery (qs req)
-        setheaders r = r { requestHeaders = requestHeaders r
-                                         <> fmap toProperHeader (headers req) }
-        setAccept r = r { requestHeaders = filter ((/= "Accept") . fst) (requestHeaders r)
-                                        <> [("Accept", renderHeader $ reqAccept req)
-                                              | not . null . reqAccept $ req] }
-        toProperHeader (name, val) =
-          (fromString name, encodeUtf8 val)
-
-#if !MIN_VERSION_http_client(0,4,30)
--- 'parseRequest' is introduced in http-client-0.4.30
--- it differs from 'parseUrl', by not throwing exceptions on non-2xx http statuses
---
--- See for implementations:
--- http://hackage.haskell.org/package/http-client-0.4.30/docs/src/Network-HTTP-Client-Request.html#parseRequest
--- http://hackage.haskell.org/package/http-client-0.5.0/docs/src/Network-HTTP-Client-Request.html#parseRequest
-parseRequest :: MonadThrow m => String -> m Request
-parseRequest url = liftM disableStatusCheck (parseUrl url)
-  where
-    disableStatusCheck req = req { checkStatus = \ _status _headers _cookies -> Nothing }
-#endif
-
-
--- * performing requests
-
-displayHttpRequest :: Method -> String
-displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"
-
-data ClientEnv
-  = ClientEnv
-  { manager :: Manager
-  , baseUrl :: BaseUrl
-  }
-
-
--- | @ClientM@ is the monad in which client functions run. Contains the
--- 'Manager' and 'BaseUrl' used for requests in the reader environment.
-
-newtype ClientM a = ClientM { runClientM' :: ReaderT ClientEnv (ExceptT ServantError IO) a }
-                    deriving ( Functor, Applicative, Monad, MonadIO, Generic
-                             , MonadReader ClientEnv
-                             , MonadError ServantError
-                             , MonadThrow, MonadCatch
-                             )
-
-instance MonadBase IO ClientM where
-  liftBase = ClientM . liftBase
-
-instance MonadBaseControl IO ClientM where
-  type StM ClientM a = Either ServantError a
-
-  -- liftBaseWith :: (RunInBase ClientM IO -> IO a) -> ClientM a
-  liftBaseWith f = ClientM (liftBaseWith (\g -> f (g . runClientM')))
-
-  -- restoreM :: StM ClientM a -> ClientM a
-  restoreM st = ClientM (restoreM st)
-
--- | Try clients in order, last error is preserved.
-instance Alt ClientM where
-  a <!> b = a `catchError` \_ -> b
-
-runClientM :: ClientM a -> ClientEnv -> IO (Either ServantError a)
-runClientM cm env = runExceptT $ (flip runReaderT env) $ runClientM' cm
-
-
-performRequest :: Method -> Req
-               -> ClientM ( Int, ByteString, MediaType
-                          , [HTTP.Header], Response ByteString)
-performRequest reqMethod req = do
-  m <- asks manager
-  reqHost <- asks baseUrl
-  partialRequest <- liftIO $ reqToRequest req reqHost
-
-  let request = partialRequest { Client.method = reqMethod }
-
-  eResponse <- liftIO $ catchConnectionError $ Client.httpLbs request m
-  case eResponse of
-    Left err ->
-      throwError . ConnectionError $ SomeException err
-
-    Right response -> do
-      let status = Client.responseStatus response
-          body = Client.responseBody response
-          hdrs = Client.responseHeaders response
-          status_code = statusCode status
-      ct <- case lookup "Content-Type" $ Client.responseHeaders response of
-                 Nothing -> pure $ "application"//"octet-stream"
-                 Just t -> case parseAccept t of
-                   Nothing -> throwError $ InvalidContentTypeHeader (cs t) body
-                   Just t' -> pure t'
-      unless (status_code >= 200 && status_code < 300) $
-        throwError $ FailureResponse (UrlReq reqHost req) status ct body
-      return (status_code, body, ct, hdrs, response)
-
-performRequestCT :: MimeUnrender ct result => Proxy ct -> Method -> Req
-    -> ClientM ([HTTP.Header], result)
-performRequestCT ct reqMethod req = do
-  let acceptCTS = contentTypes ct
-  (_status, respBody, respCT, hdrs, _response) <-
-    performRequest reqMethod (req { reqAccept = toList acceptCTS })
-  unless (any (matches respCT) acceptCTS) $ throwError $ UnsupportedContentType respCT respBody
-  case mimeUnrender ct respBody of
-    Left err -> throwError $ DecodeFailure err respCT respBody
-    Right val -> return (hdrs, val)
-
-performRequestNoBody :: Method -> Req -> ClientM [HTTP.Header]
-performRequestNoBody reqMethod req = do
-  (_status, _body, _ct, hdrs, _response) <- performRequest reqMethod req
-  return hdrs
-
-catchConnectionError :: IO a -> IO (Either ServantError a)
-catchConnectionError action =
-  catch (Right <$> action) $ \e ->
-    pure . Left . ConnectionError $ SomeException (e :: HttpException)
diff --git a/test/Servant/ClientSpec.hs b/test/Servant/ClientSpec.hs
--- a/test/Servant/ClientSpec.hs
+++ b/test/Servant/ClientSpec.hs
@@ -24,40 +24,57 @@
 {-# OPTIONS_GHC -fno-warn-name-shadowing #-}
 
 #include "overlapping-compat.h"
-module Servant.ClientSpec where
+module Servant.ClientSpec (spec) where
 
-import           Control.Arrow              (left)
-import           Control.Concurrent         (forkIO, killThread, ThreadId)
-import           Control.Exception          (bracket)
-import           Control.Monad.Error.Class  (throwError )
+import           Prelude                                    ()
+import           Prelude.Compat
+
+import           Control.Arrow                              (left)
+import           Control.Concurrent                         (ThreadId, forkIO,
+                                                             killThread)
+import           Control.Exception                          (bracket)
+import           Control.Monad.Error.Class                  (throwError)
 import           Data.Aeson
-import qualified Data.ByteString.Lazy       as BS
-import           Data.Char                  (chr, isPrint)
-import           Data.Foldable              (forM_)
-import           Data.Monoid                hiding (getLast)
+import           Data.Char                                  (chr, isPrint)
+import           Data.Foldable                              (forM_)
+import           Data.Monoid                                hiding (getLast)
 import           Data.Proxy
-import qualified Generics.SOP               as SOP
-import           GHC.Generics               (Generic)
-import qualified Network.HTTP.Client        as C
-import           Network.HTTP.Media
-import qualified Network.HTTP.Types         as HTTP
+import qualified Generics.SOP                               as SOP
+import           GHC.Generics                               (Generic)
+import qualified Network.HTTP.Client                        as C
+import qualified Network.HTTP.Types                         as HTTP
 import           Network.Socket
-import           Network.Wai                (Request, requestHeaders, responseLBS)
+import qualified Network.Wai                                as Wai
 import           Network.Wai.Handler.Warp
-import           Prelude ()
-import           Prelude.Compat
-import           System.IO.Unsafe           (unsafePerformIO)
-import           Test.HUnit
+import           System.IO.Unsafe                           (unsafePerformIO)
 import           Test.Hspec
 import           Test.Hspec.QuickCheck
+import           Test.HUnit
 import           Test.QuickCheck
-import           Web.FormUrlEncoded         (FromForm, ToForm)
+import           Web.FormUrlEncoded                         (FromForm, ToForm)
 
-import           Servant.API
+import           Servant.API                                ((:<|>) ((:<|>)),
+                                                             (:>), AuthProtect,
+                                                             BasicAuth,
+                                                             BasicAuthData (..),
+                                                             Capture,
+                                                             CaptureAll, Delete,
+                                                             DeleteNoContent,
+                                                             EmptyAPI, addHeader,
+                                                             FormUrlEncoded,
+                                                             Get, Header,
+                                                             Headers, JSON,
+                                                             NoContent (NoContent),
+                                                             Post, Put, Raw,
+                                                             QueryFlag,
+                                                             QueryParam,
+                                                             QueryParams,
+                                                             ReqBody,
+                                                             getHeaders)
 import           Servant.API.Internal.Test.ComprehensiveAPI
 import           Servant.Client
-import           Servant.Client.Generic
-import qualified Servant.Common.Req         as SCR
+import qualified Servant.Client.Core.Internal.Request as Req
+import qualified Servant.Client.Core.Internal.Auth as Auth
 import           Servant.Server
 import           Servant.Server.Experimental.Auth
 
@@ -75,25 +92,28 @@
 
 -- * test data types
 
-data Person = Person {
-  name :: String,
-  age :: Integer
- }
-  deriving (Eq, Show, Generic)
+data Person = Person
+  { _name :: String
+  , _age  :: Integer
+  } deriving (Eq, Show, Generic)
 
 instance ToJSON Person
 instance FromJSON Person
 
-instance ToForm Person where
-instance FromForm Person where
+instance ToForm Person
+instance FromForm Person
 
 alice :: Person
 alice = Person "Alice" 42
 
+carol :: Person
+carol = Person "Carol" 17
+
 type TestHeaders = '[Header "X-Example1" Int, Header "X-Example2" String]
 
 type Api =
-       "get" :> Get '[JSON] Person
+  Get '[JSON] Person
+  :<|> "get" :> Get '[JSON] Person
   :<|> "deleteEmpty" :> DeleteNoContent '[JSON] NoContent
   :<|> "capture" :> Capture "name" String :> Get '[JSON,FormUrlEncoded] Person
   :<|> "captureAll" :> CaptureAll "names" String :> Get '[JSON] [Person]
@@ -116,24 +136,24 @@
 api :: Proxy Api
 api = Proxy
 
-getGet          :: SCR.ClientM Person
-getDeleteEmpty  :: SCR.ClientM NoContent
-getCapture      :: String -> SCR.ClientM Person
-getCaptureAll   :: [String] -> SCR.ClientM [Person]
-getBody         :: Person -> SCR.ClientM Person
-getQueryParam   :: Maybe String -> SCR.ClientM Person
-getQueryParams  :: [String] -> SCR.ClientM [Person]
-getQueryFlag    :: Bool -> SCR.ClientM Bool
-getRawSuccess :: HTTP.Method
-  -> SCR.ClientM (Int, BS.ByteString, MediaType, [HTTP.Header], C.Response BS.ByteString)
-getRawFailure   :: HTTP.Method
-  -> SCR.ClientM (Int, BS.ByteString, MediaType, [HTTP.Header], C.Response BS.ByteString)
+getRoot         :: ClientM Person
+getGet          :: ClientM Person
+getDeleteEmpty  :: ClientM NoContent
+getCapture      :: String -> ClientM Person
+getCaptureAll   :: [String] -> ClientM [Person]
+getBody         :: Person -> ClientM Person
+getQueryParam   :: Maybe String -> ClientM Person
+getQueryParams  :: [String] -> ClientM [Person]
+getQueryFlag    :: Bool -> ClientM Bool
+getRawSuccess   :: HTTP.Method -> ClientM Response
+getRawFailure   :: HTTP.Method -> ClientM Response
 getMultiple     :: String -> Maybe Int -> Bool -> [(String, [Rational])]
-  -> SCR.ClientM (String, Maybe Int, Bool, [(String, [Rational])])
-getRespHeaders  :: SCR.ClientM (Headers TestHeaders Bool)
-getDeleteContentType :: SCR.ClientM NoContent
+  -> ClientM (String, Maybe Int, Bool, [(String, [Rational])])
+getRespHeaders  :: ClientM (Headers TestHeaders Bool)
+getDeleteContentType :: ClientM NoContent
 
-getGet
+getRoot
+  :<|> getGet
   :<|> getDeleteEmpty
   :<|> getCapture
   :<|> getCaptureAll
@@ -150,7 +170,8 @@
 
 server :: Application
 server = serve api (
-       return alice
+       return carol
+  :<|> return alice
   :<|> return NoContent
   :<|> (\ name -> return $ Person name 0)
   :<|> (\ names -> return (zipWith Person names [0..]))
@@ -161,8 +182,8 @@
                    Nothing -> throwError $ ServantErr 400 "missing parameter" "" [])
   :<|> (\ names -> return (zipWith Person names [0..]))
   :<|> return
-  :<|> (Tagged $ \ _request respond -> respond $ responseLBS HTTP.ok200 [] "rawSuccess")
-  :<|> (Tagged $ \ _request respond -> respond $ responseLBS HTTP.badRequest400 [] "rawFailure")
+  :<|> (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.ok200 [] "rawSuccess")
+  :<|> (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.badRequest400 [] "rawFailure")
   :<|> (\ a b c d -> return (a, b, c, d))
   :<|> (return $ addHeader 1729 $ addHeader "eg2" True)
   :<|> return NoContent
@@ -178,9 +199,9 @@
 
 failServer :: Application
 failServer = serve failApi (
-       (Tagged $ \ _request respond -> respond $ responseLBS HTTP.ok200 [] "")
-  :<|> (\ _capture -> Tagged $ \_request respond -> respond $ responseLBS HTTP.ok200 [("content-type", "application/json")] "")
-  :<|> (Tagged $ \_request respond -> respond $ responseLBS HTTP.ok200 [("content-type", "fooooo")] "")
+       (Tagged $ \ _request respond -> respond $ Wai.responseLBS HTTP.ok200 [] "")
+  :<|> (\ _capture -> Tagged $ \_request respond -> respond $ Wai.responseLBS HTTP.ok200 [("content-type", "application/json")] "")
+  :<|> (Tagged $ \_request respond -> respond $ Wai.responseLBS HTTP.ok200 [("content-type", "fooooo")] "")
  )
 
 -- * basic auth stuff
@@ -214,16 +235,16 @@
 genAuthAPI = Proxy
 
 type instance AuthServerData (AuthProtect "auth-tag") = ()
-type instance AuthClientData (AuthProtect "auth-tag") = ()
+type instance Auth.AuthClientData (AuthProtect "auth-tag") = ()
 
-genAuthHandler :: AuthHandler Request ()
+genAuthHandler :: AuthHandler Wai.Request ()
 genAuthHandler =
-  let handler req = case lookup "AuthHeader" (requestHeaders req) of
+  let handler req = case lookup "AuthHeader" (Wai.requestHeaders req) of
         Nothing -> throwError (err401 { errBody = "Missing auth header" })
         Just _ -> return ()
   in mkAuthHandler handler
 
-genAuthServerContext :: Context '[ AuthHandler Request () ]
+genAuthServerContext :: Context '[ AuthHandler Wai.Request () ]
 genAuthServerContext = genAuthHandler :. EmptyContext
 
 genAuthServer :: Application
@@ -236,11 +257,11 @@
  :<|> Capture "foo" String :> NestedAPI1
 
 data GenericClient = GenericClient
-  { getSqr          :: Maybe Int -> SCR.ClientM Int
+  { getSqr          :: Maybe Int -> ClientM Int
   , mkNestedClient1 :: String -> NestedClient1
   } deriving Generic
 instance SOP.Generic GenericClient
-instance (Client GenericClientAPI ~ client) => ClientLike client GenericClient
+instance (Client ClientM GenericClientAPI ~ client) => ClientLike client GenericClient
 
 type NestedAPI1
     = QueryParam "int" Int :> NestedAPI2
@@ -248,21 +269,21 @@
 
 data NestedClient1 = NestedClient1
   { mkNestedClient2 :: Maybe Int -> NestedClient2
-  , idChar          :: Maybe Char -> SCR.ClientM Char
+  , idChar          :: Maybe Char -> ClientM Char
   } deriving Generic
 instance SOP.Generic NestedClient1
-instance (Client NestedAPI1 ~ client) => ClientLike client NestedClient1
+instance (Client ClientM NestedAPI1 ~ client) => ClientLike client NestedClient1
 
 type NestedAPI2
     = "sum"  :> Capture "first" Int :> Capture "second" Int :> Get '[JSON] Int
  :<|> "void" :> Post '[JSON] ()
 
 data NestedClient2 = NestedClient2
-  { getSum    :: Int -> Int -> SCR.ClientM Int
-  , doNothing :: SCR.ClientM ()
+  { getSum    :: Int -> Int -> ClientM Int
+  , doNothing :: ClientM ()
   } deriving Generic
 instance SOP.Generic NestedClient2
-instance (Client NestedAPI2 ~ client) => ClientLike client NestedClient2
+instance (Client ClientM NestedAPI2 ~ client) => ClientLike client NestedClient2
 
 genericClientServer :: Application
 genericClientServer = serve (Proxy :: Proxy GenericClientAPI) (
@@ -276,67 +297,72 @@
     nestedServer1 _str = nestedServer2 :<|> (maybe (throwError $ ServantErr 400 "missing parameter" "" []) return)
     nestedServer2 _int = (\ x y -> return (x + y)) :<|> return ()
 
-{-# NOINLINE manager #-}
-manager :: C.Manager
-manager = unsafePerformIO $ C.newManager C.defaultManagerSettings
+{-# NOINLINE manager' #-}
+manager' :: C.Manager
+manager' = unsafePerformIO $ C.newManager C.defaultManagerSettings
 
+runClient :: ClientM a -> BaseUrl -> IO (Either ServantError a)
+runClient x baseUrl' = runClientM x (ClientEnv manager' baseUrl')
+
 sucessSpec :: Spec
 sucessSpec = beforeAll (startWaiApp server) $ afterAll endWaiApp $ do
+    it "Servant.API.Get root" $ \(_, baseUrl) -> do
+      left show <$> runClient getRoot baseUrl  `shouldReturn` Right carol
 
     it "Servant.API.Get" $ \(_, baseUrl) -> do
-      (left show <$> (runClientM getGet  (ClientEnv manager baseUrl)))  `shouldReturn` Right alice
+      left show <$> runClient getGet baseUrl  `shouldReturn` Right alice
 
     describe "Servant.API.Delete" $ do
       it "allows empty content type" $ \(_, baseUrl) -> do
-        (left show <$> (runClientM getDeleteEmpty (ClientEnv manager baseUrl))) `shouldReturn` Right NoContent
+        left show <$> runClient getDeleteEmpty baseUrl `shouldReturn` Right NoContent
 
       it "allows content type" $ \(_, baseUrl) -> do
-        (left show <$> (runClientM getDeleteContentType (ClientEnv manager baseUrl))) `shouldReturn` Right NoContent
+        left show <$> runClient getDeleteContentType baseUrl `shouldReturn` Right NoContent
 
     it "Servant.API.Capture" $ \(_, baseUrl) -> do
-      (left show <$> (runClientM (getCapture "Paula") (ClientEnv manager baseUrl))) `shouldReturn` Right (Person "Paula" 0)
+      left show <$> runClient (getCapture "Paula") baseUrl `shouldReturn` Right (Person "Paula" 0)
 
     it "Servant.API.CaptureAll" $ \(_, baseUrl) -> do
       let expected = [(Person "Paula" 0), (Person "Peta" 1)]
-      (left show <$> (runClientM (getCaptureAll ["Paula", "Peta"]) (ClientEnv  manager baseUrl))) `shouldReturn` Right expected
+      left show <$> runClient (getCaptureAll ["Paula", "Peta"]) baseUrl `shouldReturn` Right expected
 
     it "Servant.API.ReqBody" $ \(_, baseUrl) -> do
       let p = Person "Clara" 42
-      (left show <$> runClientM (getBody p) (ClientEnv manager baseUrl)) `shouldReturn` Right p
+      left show <$> runClient (getBody p) baseUrl `shouldReturn` Right p
 
     it "Servant.API.QueryParam" $ \(_, baseUrl) -> do
-      left show <$> runClientM (getQueryParam (Just "alice")) (ClientEnv manager baseUrl)  `shouldReturn` Right alice
-      Left FailureResponse{..} <- runClientM (getQueryParam (Just "bob")) (ClientEnv manager baseUrl)
-      responseStatus `shouldBe` HTTP.Status 400 "bob not found"
+      left show <$> runClient (getQueryParam (Just "alice")) baseUrl `shouldReturn` Right alice
+      Left (FailureResponse r) <- runClient (getQueryParam (Just "bob")) baseUrl
+      responseStatusCode r `shouldBe` HTTP.Status 400 "bob not found"
 
     it "Servant.API.QueryParam.QueryParams" $ \(_, baseUrl) -> do
-      (left show <$> runClientM (getQueryParams []) (ClientEnv manager baseUrl)) `shouldReturn` Right []
-      (left show <$> runClientM (getQueryParams ["alice", "bob"]) (ClientEnv manager baseUrl))
+      left show <$> runClient (getQueryParams []) baseUrl `shouldReturn` Right []
+      left show <$> runClient (getQueryParams ["alice", "bob"]) baseUrl
         `shouldReturn` Right [Person "alice" 0, Person "bob" 1]
 
     context "Servant.API.QueryParam.QueryFlag" $
       forM_ [False, True] $ \ flag -> it (show flag) $ \(_, baseUrl) -> do
-        (left show <$> runClientM (getQueryFlag flag) (ClientEnv manager baseUrl)) `shouldReturn` Right flag
+        left show <$> runClient (getQueryFlag flag) baseUrl `shouldReturn` Right flag
 
     it "Servant.API.Raw on success" $ \(_, baseUrl) -> do
-      res <- runClientM (getRawSuccess HTTP.methodGet) (ClientEnv manager baseUrl)
+      res <- runClient (getRawSuccess HTTP.methodGet) baseUrl
       case res of
         Left e -> assertFailure $ show e
-        Right (code, body, ct, _, response) -> do
-          (code, body, ct) `shouldBe` (200, "rawSuccess", "application"//"octet-stream")
-          C.responseBody response `shouldBe` body
-          C.responseStatus response `shouldBe` HTTP.ok200
+        Right r -> do
+          responseStatusCode r `shouldBe` HTTP.status200
+          responseBody r `shouldBe` "rawSuccess"
 
     it "Servant.API.Raw should return a Left in case of failure" $ \(_, baseUrl) -> do
-      res <- runClientM (getRawFailure HTTP.methodGet) (ClientEnv manager baseUrl)
+      res <- runClient (getRawFailure HTTP.methodGet) baseUrl
       case res of
         Right _ -> assertFailure "expected Left, but got Right"
-        Left e -> do
-          Servant.Client.responseStatus e `shouldBe` HTTP.status400
-          Servant.Client.responseBody e `shouldBe` "rawFailure"
+        Left (FailureResponse r) -> do
+          responseStatusCode r `shouldBe` HTTP.status400
+          responseBody r `shouldBe` "rawFailure"
+        Left e -> assertFailure $ "expected FailureResponse, but got " ++ show e
 
     it "Returns headers appropriately" $ \(_, baseUrl) -> do
-      res <- runClientM getRespHeaders (ClientEnv manager baseUrl)
+      res <- runClient getRespHeaders baseUrl
       case res of
         Left e -> assertFailure $ show e
         Right val -> getHeaders val `shouldBe` [("X-Example1", "1729"), ("X-Example2", "eg2")]
@@ -345,7 +371,7 @@
       it "works for a combination of Capture, QueryParam, QueryFlag and ReqBody" $ \(_, baseUrl) ->
         property $ forAllShrink pathGen shrink $ \(NonEmpty cap) num flag body ->
           ioProperty $ do
-            result <- left show <$> runClientM (getMultiple cap num flag body) (ClientEnv manager baseUrl)
+            result <- left show <$> runClient (getMultiple cap num flag body) baseUrl
             return $
               result === Right (cap, num, flag, body)
 
@@ -357,10 +383,10 @@
     let test :: (WrappedApi, String) -> Spec
         test (WrappedApi api, desc) =
           it desc $ bracket (startWaiApp $ serveW api) endWaiApp $ \(_, baseUrl) -> do
-            let getResponse :: SCR.ClientM ()
+            let getResponse :: ClientM ()
                 getResponse = client api
-            Left FailureResponse{..} <- runClientM getResponse (ClientEnv manager baseUrl)
-            responseStatus `shouldBe` (HTTP.Status 500 "error message")
+            Left (FailureResponse r) <- runClient getResponse baseUrl
+            responseStatusCode r `shouldBe` (HTTP.Status 500 "error message")
     in mapM_ test $
         (WrappedApi (Proxy :: Proxy (Delete '[JSON] ())), "Delete") :
         (WrappedApi (Proxy :: Proxy (Get '[JSON] ())), "Get") :
@@ -373,43 +399,43 @@
 
     context "client returns errors appropriately" $ do
       it "reports FailureResponse" $ \(_, baseUrl) -> do
-        let (_ :<|> getDeleteEmpty :<|> _) = client api
-        Left res <- runClientM getDeleteEmpty (ClientEnv manager baseUrl)
+        let (_ :<|> _ :<|> getDeleteEmpty :<|> _) = client api
+        Left res <- runClient getDeleteEmpty baseUrl
         case res of
-          FailureResponse _ (HTTP.Status 404 "Not Found") _ _ -> return ()
+          FailureResponse r | responseStatusCode r == HTTP.status404 -> return ()
           _ -> fail $ "expected 404 response, but got " <> show res
 
       it "reports DecodeFailure" $ \(_, baseUrl) -> do
-        let (_ :<|> _ :<|> getCapture :<|> _) = client api
-        Left res <- runClientM (getCapture "foo") (ClientEnv manager baseUrl)
+        let (_ :<|> _ :<|> _ :<|> getCapture :<|> _) = client api
+        Left res <- runClient (getCapture "foo") baseUrl
         case res of
-          DecodeFailure _ ("application/json") _ -> return ()
+          DecodeFailure _ _ -> return ()
           _ -> fail $ "expected DecodeFailure, but got " <> show res
 
       it "reports ConnectionError" $ \_ -> do
         let (getGetWrongHost :<|> _) = client api
-        Left res <- runClientM getGetWrongHost (ClientEnv manager (BaseUrl Http "127.0.0.1" 19872 ""))
+        Left res <- runClient getGetWrongHost (BaseUrl Http "127.0.0.1" 19872 "")
         case res of
           ConnectionError _ -> return ()
           _ -> fail $ "expected ConnectionError, but got " <> show res
 
       it "reports UnsupportedContentType" $ \(_, baseUrl) -> do
-        let (getGet :<|> _ ) = client api
-        Left res <- runClientM getGet (ClientEnv manager baseUrl)
+        let (_ :<|> getGet :<|> _ ) = client api
+        Left res <- runClient getGet baseUrl
         case res of
           UnsupportedContentType ("application/octet-stream") _ -> return ()
           _ -> fail $ "expected UnsupportedContentType, but got " <> show res
 
       it "reports InvalidContentTypeHeader" $ \(_, baseUrl) -> do
-        let (_ :<|> _ :<|> _ :<|> _ :<|> getBody :<|> _) = client api
-        Left res <- runClientM (getBody alice) (ClientEnv manager baseUrl)
+        let (_ :<|> _ :<|> _ :<|> _ :<|> _ :<|> getBody :<|> _) = client api
+        Left res <- runClient (getBody alice) baseUrl
         case res of
-          InvalidContentTypeHeader "fooooo" _ -> return ()
+          InvalidContentTypeHeader _ -> return ()
           _ -> fail $ "expected InvalidContentTypeHeader, but got " <> show res
 
 data WrappedApi where
   WrappedApi :: (HasServer (api :: *) '[], Server api ~ Handler a,
-                 HasClient api, Client api ~ SCR.ClientM ()) =>
+                 HasClient ClientM api, Client ClientM api ~ ClientM ()) =>
     Proxy api -> WrappedApi
 
 basicAuthSpec :: Spec
@@ -419,15 +445,15 @@
     it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
       let getBasic = client basicAuthAPI
       let basicAuthData = BasicAuthData "servant" "server"
-      (left show <$> runClientM (getBasic basicAuthData) (ClientEnv  manager baseUrl)) `shouldReturn` Right alice
+      left show <$> runClient (getBasic basicAuthData) baseUrl `shouldReturn` Right alice
 
   context "Authentication is rejected when requests are not authenticated properly" $ do
 
     it "Authenticates a BasicAuth protected server appropriately" $ \(_,baseUrl) -> do
       let getBasic = client basicAuthAPI
       let basicAuthData = BasicAuthData "not" "password"
-      Left FailureResponse{..} <- runClientM (getBasic basicAuthData) (ClientEnv manager baseUrl)
-      responseStatus `shouldBe` HTTP.Status 403 "Forbidden"
+      Left (FailureResponse r) <- runClient (getBasic basicAuthData) baseUrl
+      responseStatusCode r `shouldBe` HTTP.Status 403 "Forbidden"
 
 genAuthSpec :: Spec
 genAuthSpec = beforeAll (startWaiApp genAuthServer) $ afterAll endWaiApp $ do
@@ -435,16 +461,16 @@
 
     it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
       let getProtected = client genAuthAPI
-      let authRequest = mkAuthenticateReq () (\_ req ->  SCR.addHeader "AuthHeader" ("cool" :: String) req)
-      (left show <$> runClientM (getProtected authRequest) (ClientEnv manager baseUrl)) `shouldReturn` Right alice
+      let authRequest = Auth.mkAuthenticatedRequest () (\_ req -> Req.addHeader "AuthHeader" ("cool" :: String) req)
+      left show <$> runClient (getProtected authRequest) baseUrl `shouldReturn` Right alice
 
   context "Authentication is rejected when requests are not authenticated properly" $ do
 
     it "Authenticates a AuthProtect protected server appropriately" $ \(_, baseUrl) -> do
       let getProtected = client genAuthAPI
-      let authRequest = mkAuthenticateReq () (\_ req ->  SCR.addHeader "Wrong" ("header" :: String) req)
-      Left FailureResponse{..} <- runClientM (getProtected authRequest) (ClientEnv manager baseUrl)
-      responseStatus `shouldBe` (HTTP.Status 401 "Unauthorized")
+      let authRequest = Auth.mkAuthenticatedRequest () (\_ req -> Req.addHeader "Wrong" ("header" :: String) req)
+      Left (FailureResponse r) <- runClient (getProtected authRequest) baseUrl
+      responseStatusCode r `shouldBe` (HTTP.Status 401 "Unauthorized")
 
 genericClientSpec :: Spec
 genericClientSpec = beforeAll (startWaiApp genericClientServer) $ afterAll endWaiApp $ do
@@ -454,13 +480,13 @@
         NestedClient1{..} = mkNestedClient1 "example"
         NestedClient2{..} = mkNestedClient2 (Just 42)
 
-    it "works for top-level client function" $ \(_, baseUrl) -> do
-      (left show <$> (runClientM (getSqr (Just 5)) (ClientEnv manager baseUrl))) `shouldReturn` Right 25
+    it "works for top-level client inClientM function" $ \(_, baseUrl) -> do
+      left show <$> runClient (getSqr (Just 5)) baseUrl `shouldReturn` Right 25
 
     it "works for nested clients" $ \(_, baseUrl) -> do
-      (left show <$> (runClientM (idChar (Just 'c')) (ClientEnv manager baseUrl))) `shouldReturn` Right 'c'
-      (left show <$> (runClientM (getSum 3 4) (ClientEnv manager baseUrl))) `shouldReturn` Right 7
-      (left show <$> (runClientM doNothing (ClientEnv manager baseUrl))) `shouldReturn` Right ()
+      left show <$> runClient (idChar (Just 'c')) baseUrl `shouldReturn` Right 'c'
+      left show <$> runClient (getSum 3 4) baseUrl `shouldReturn` Right 7
+      left show <$> runClient doNothing baseUrl `shouldReturn` Right ()
 
 -- * utils
 
diff --git a/test/Servant/Common/BaseUrlSpec.hs b/test/Servant/Common/BaseUrlSpec.hs
deleted file mode 100644
--- a/test/Servant/Common/BaseUrlSpec.hs
+++ /dev/null
@@ -1,83 +0,0 @@
-{-# OPTIONS_GHC -fno-warn-orphans #-}
-module Servant.Common.BaseUrlSpec where
-
-import           Control.DeepSeq
-import           Prelude ()
-import           Prelude.Compat
-import           Test.Hspec
-import           Test.QuickCheck
-
-import           Servant.Common.BaseUrl
-
-spec :: Spec
-spec = do
-  let parse = parseBaseUrl :: String -> Maybe BaseUrl
-  describe "showBaseUrl" $ do
-    it "shows a BaseUrl" $ do
-      showBaseUrl (BaseUrl Http "foo.com" 80 "") `shouldBe` "http://foo.com"
-    it "shows a https BaseUrl" $ do
-      showBaseUrl (BaseUrl Https "foo.com" 443 "") `shouldBe` "https://foo.com"
-    it "shows the path of a BaseUrl" $ do
-      showBaseUrl (BaseUrl Http "foo.com" 80 "api") `shouldBe` "http://foo.com/api"
-    it "shows the path of an https BaseUrl" $ do
-      showBaseUrl (BaseUrl Https "foo.com" 443 "api") `shouldBe` "https://foo.com/api"
-    it "handles leading slashes in path" $ do
-      showBaseUrl (BaseUrl Https "foo.com" 443 "/api") `shouldBe` "https://foo.com/api"
-
-  describe "httpBaseUrl" $ do
-    it "allows to construct default http BaseUrls" $ do
-      BaseUrl Http "bar" 80 "" `shouldBe` BaseUrl Http "bar" 80 ""
-
-  describe "parseBaseUrl" $ do
-    it "is total" $ do
-      property $ \ string ->
-        deepseq (fmap show (parse string )) True
-
-    it "is the inverse of showBaseUrl" $ do
-      property $ \ baseUrl -> counterexample (showBaseUrl baseUrl) $
-        parse (showBaseUrl baseUrl) === Just baseUrl
-
-    context "trailing slashes" $ do
-      it "allows trailing slashes" $ do
-        parse "foo.com/" `shouldBe` Just (BaseUrl Http "foo.com" 80 "")
-
-      it "allows trailing slashes in paths" $ do
-        parse "foo.com/api/" `shouldBe` Just (BaseUrl Http "foo.com" 80 "api")
-
-    context "urls without scheme" $ do
-      it "assumes http" $ do
-        parse "foo.com" `shouldBe` Just (BaseUrl Http "foo.com" 80 "")
-
-      it "allows port numbers" $ do
-        parse "foo.com:8080" `shouldBe` Just (BaseUrl Http "foo.com" 8080 "")
-
-    it "can parse paths" $ do
-      parse "http://foo.com/api" `shouldBe` Just (BaseUrl Http "foo.com" 80 "api")
-
-    it "rejects ftp urls" $ do
-      parse "ftp://foo.com" `shouldBe` Nothing
-
-instance Arbitrary BaseUrl where
-  arbitrary = BaseUrl <$>
-    elements [Http, Https] <*>
-    hostNameGen <*>
-    portGen <*>
-    pathGen
-   where
-    letters = ['a' .. 'z'] ++ ['A' .. 'Z']
-    -- this does not perfectly mirror the url standard, but I hope it's good
-    -- enough.
-    hostNameGen = do
-      first <- elements letters
-      middle <- listOf1 $ elements (letters ++ ['0' .. '9'] ++ ['.', '-'])
-      last' <- elements letters
-      return (first : middle ++ [last'])
-    portGen = frequency $
-      (1, return 80) :
-      (1, return 443) :
-      (1, choose (1, 20000)) :
-      []
-    pathGen = listOf1 . elements $ letters
-
-isLeft :: Either a b -> Bool
-isLeft = either (const True) (const False)
