packages feed

servant-haxl-client 0.1.0.0 → 0.2.0.0

raw patch · 15 files changed

+1230/−952 lines, 15 filesdep +case-insensitivedep +ghcjs-basePVP ok

version bump matches the API change (PVP)

Dependencies added: case-insensitive, ghcjs-base

API changes (from Hackage documentation)

- Servant.Client.Haxl: ConnectionError :: HttpException -> ServantError
- Servant.Client.Haxl: DecodeFailure :: String -> MediaType -> ByteString -> ServantError
- Servant.Client.Haxl: FailureResponse :: Status -> MediaType -> ByteString -> ServantError
- Servant.Client.Haxl: InvalidContentTypeHeader :: ByteString -> ByteString -> ServantError
- Servant.Client.Haxl: UnsupportedContentType :: MediaType -> ByteString -> ServantError
- Servant.Client.Haxl: [connectionError] :: ServantError -> HttpException
- Servant.Client.Haxl: [decodeError] :: ServantError -> String
- Servant.Client.Haxl: [responseBody] :: ServantError -> ByteString
- Servant.Client.Haxl: [responseContentTypeHeader] :: ServantError -> ByteString
- Servant.Client.Haxl: [responseContentType] :: ServantError -> MediaType
- Servant.Client.Haxl: [responseStatus] :: ServantError -> Status
- Servant.Client.Haxl: class HasClient layout where type family Client layout :: *
- Servant.Client.Haxl: client :: HasClient layout => Proxy layout -> BaseUrl -> Client layout
- Servant.Client.Haxl: clientWithRoute :: HasClient layout => Proxy layout -> Req -> BaseUrl -> Client layout
- Servant.Client.Haxl: data ServantError
- Servant.Client.Haxl: initServantClientState :: Int -> IO (State ServantRequest)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol capture, Servant.Common.Text.ToText a, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.Capture.Capture capture a Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol path, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (path Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol sym, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.MatrixParam.MatrixFlag sym Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol sym, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.QueryParam.QueryFlag sym Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.Header.Header sym a Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.MatrixParam.MatrixParam sym a Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.MatrixParam.MatrixParams sym a Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.QueryParam.QueryParam sym a Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.QueryParam.QueryParams sym a Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (Servant.API.ContentTypes.MimeRender ct a, Servant.Client.Haxl.HasClient sublayout) => Servant.Client.Haxl.HasClient (Servant.API.ReqBody.ReqBody (ct : cts) a Servant.API.Sub.:> sublayout)
- Servant.Client.Haxl: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls) => Servant.Client.Haxl.HasClient (Servant.API.Get.Get (ct : cts) (Servant.API.ResponseHeaders.Headers ls a))
- Servant.Client.Haxl: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls) => Servant.Client.Haxl.HasClient (Servant.API.Patch.Patch (ct : cts) (Servant.API.ResponseHeaders.Headers ls a))
- Servant.Client.Haxl: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls) => Servant.Client.Haxl.HasClient (Servant.API.Post.Post (ct : cts) (Servant.API.ResponseHeaders.Headers ls a))
- Servant.Client.Haxl: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls) => Servant.Client.Haxl.HasClient (Servant.API.Put.Put (ct : cts) (Servant.API.ResponseHeaders.Headers ls a))
- Servant.Client.Haxl: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls, cts' ~ (ct : cts)) => Servant.Client.Haxl.HasClient (Servant.API.Delete.Delete cts' (Servant.API.ResponseHeaders.Headers ls a))
- Servant.Client.Haxl: instance (Servant.API.ContentTypes.MimeUnrender ct a, cts' ~ (ct : cts)) => Servant.Client.Haxl.HasClient (Servant.API.Delete.Delete cts' a)
- Servant.Client.Haxl: instance (Servant.Client.Haxl.HasClient a, Servant.Client.Haxl.HasClient b) => Servant.Client.Haxl.HasClient (a Servant.API.Alternative.:<|> b)
- Servant.Client.Haxl: instance Servant.API.ContentTypes.MimeUnrender ct a => Servant.Client.Haxl.HasClient (Servant.API.Patch.Patch (ct : cts) a)
- Servant.Client.Haxl: instance Servant.API.ContentTypes.MimeUnrender ct a => Servant.Client.Haxl.HasClient (Servant.API.Post.Post (ct : cts) a)
- Servant.Client.Haxl: instance Servant.API.ContentTypes.MimeUnrender ct a => Servant.Client.Haxl.HasClient (Servant.API.Put.Put (ct : cts) a)
- Servant.Client.Haxl: instance Servant.API.ContentTypes.MimeUnrender ct result => Servant.Client.Haxl.HasClient (Servant.API.Get.Get (ct : cts) result)
- Servant.Client.Haxl: instance Servant.Client.Haxl.HasClient (Servant.API.Delete.Delete cts ())
- Servant.Client.Haxl: instance Servant.Client.Haxl.HasClient (Servant.API.Get.Get (ct : cts) ())
- Servant.Client.Haxl: instance Servant.Client.Haxl.HasClient (Servant.API.Patch.Patch (ct : cts) ())
- Servant.Client.Haxl: instance Servant.Client.Haxl.HasClient (Servant.API.Post.Post (ct : cts) ())
- Servant.Client.Haxl: instance Servant.Client.Haxl.HasClient (Servant.API.Put.Put (ct : cts) ())
- Servant.Client.Haxl: instance Servant.Client.Haxl.HasClient Servant.API.Raw.Raw
- Servant.Common.BaseUrl.Haxl: BaseUrl :: Scheme -> String -> Int -> BaseUrl
- Servant.Common.BaseUrl.Haxl: Http :: Scheme
- Servant.Common.BaseUrl.Haxl: Https :: Scheme
- Servant.Common.BaseUrl.Haxl: [baseUrlHost] :: BaseUrl -> String
- Servant.Common.BaseUrl.Haxl: [baseUrlPort] :: BaseUrl -> Int
- Servant.Common.BaseUrl.Haxl: [baseUrlScheme] :: BaseUrl -> Scheme
- Servant.Common.BaseUrl.Haxl: data BaseUrl
- Servant.Common.BaseUrl.Haxl: data Scheme
- Servant.Common.BaseUrl.Haxl: instance Data.Hashable.Class.Hashable Servant.Common.BaseUrl.Haxl.BaseUrl
- Servant.Common.BaseUrl.Haxl: instance Data.Hashable.Class.Hashable Servant.Common.BaseUrl.Haxl.Scheme
- Servant.Common.BaseUrl.Haxl: instance GHC.Classes.Eq Servant.Common.BaseUrl.Haxl.BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Classes.Eq Servant.Common.BaseUrl.Haxl.Scheme
- Servant.Common.BaseUrl.Haxl: instance GHC.Classes.Ord Servant.Common.BaseUrl.Haxl.BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Classes.Ord Servant.Common.BaseUrl.Haxl.Scheme
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Constructor Servant.Common.BaseUrl.Haxl.C1_0BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Constructor Servant.Common.BaseUrl.Haxl.C1_0Scheme
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Constructor Servant.Common.BaseUrl.Haxl.C1_1Scheme
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Datatype Servant.Common.BaseUrl.Haxl.D1BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Datatype Servant.Common.BaseUrl.Haxl.D1Scheme
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Generic Servant.Common.BaseUrl.Haxl.BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Generic Servant.Common.BaseUrl.Haxl.Scheme
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Selector Servant.Common.BaseUrl.Haxl.S1_0_0BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Selector Servant.Common.BaseUrl.Haxl.S1_0_1BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Generics.Selector Servant.Common.BaseUrl.Haxl.S1_0_2BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Show.Show Servant.Common.BaseUrl.Haxl.BaseUrl
- Servant.Common.BaseUrl.Haxl: instance GHC.Show.Show Servant.Common.BaseUrl.Haxl.Scheme
- Servant.Common.BaseUrl.Haxl: parseBaseUrl :: String -> Either String BaseUrl
- Servant.Common.BaseUrl.Haxl: showBaseUrl :: BaseUrl -> String
- Servant.Common.Req.Haxl: AllCodes :: WantedStatusCodes
- Servant.Common.Req.Haxl: ConnectionError :: HttpException -> ServantError
- Servant.Common.Req.Haxl: DecodeFailure :: String -> MediaType -> ByteString -> ServantError
- Servant.Common.Req.Haxl: FailureResponse :: Status -> MediaType -> ByteString -> ServantError
- Servant.Common.Req.Haxl: InvalidContentTypeHeader :: ByteString -> ByteString -> ServantError
- Servant.Common.Req.Haxl: Req :: String -> QueryText -> Maybe (ByteString, MediaType) -> [MediaType] -> [(String, Text)] -> Req
- Servant.Common.Req.Haxl: SelectCodes :: [Int] -> WantedStatusCodes
- Servant.Common.Req.Haxl: ServantRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl -> ServantRequest (Int, ByteString, MediaType, [Header], Response ByteString)
- Servant.Common.Req.Haxl: UnsupportedContentType :: MediaType -> ByteString -> ServantError
- Servant.Common.Req.Haxl: [connectionError] :: ServantError -> HttpException
- Servant.Common.Req.Haxl: [decodeError] :: ServantError -> String
- Servant.Common.Req.Haxl: [headers] :: Req -> [(String, Text)]
- Servant.Common.Req.Haxl: [qs] :: Req -> QueryText
- Servant.Common.Req.Haxl: [reqAccept] :: Req -> [MediaType]
- Servant.Common.Req.Haxl: [reqBody] :: Req -> Maybe (ByteString, MediaType)
- Servant.Common.Req.Haxl: [reqPath] :: Req -> String
- Servant.Common.Req.Haxl: [responseBody] :: ServantError -> ByteString
- Servant.Common.Req.Haxl: [responseContentTypeHeader] :: ServantError -> ByteString
- Servant.Common.Req.Haxl: [responseContentType] :: ServantError -> MediaType
- Servant.Common.Req.Haxl: [responseStatus] :: ServantError -> Status
- Servant.Common.Req.Haxl: addHeader :: ToText a => String -> a -> Req -> Req
- Servant.Common.Req.Haxl: appendToMatrixParams :: String -> Maybe String -> Req -> Req
- Servant.Common.Req.Haxl: appendToPath :: String -> Req -> Req
- Servant.Common.Req.Haxl: appendToQueryString :: Text -> Maybe Text -> Req -> Req
- Servant.Common.Req.Haxl: catchHttpException :: IO a -> IO (Either HttpException a)
- Servant.Common.Req.Haxl: data Req
- Servant.Common.Req.Haxl: data ServantError
- Servant.Common.Req.Haxl: data ServantRequest a
- Servant.Common.Req.Haxl: data WantedStatusCodes
- Servant.Common.Req.Haxl: defReq :: Req
- Servant.Common.Req.Haxl: displayHttpRequest :: Method -> String
- Servant.Common.Req.Haxl: instance Data.Hashable.Class.Hashable (Servant.Common.Req.Haxl.ServantRequest a)
- Servant.Common.Req.Haxl: instance Data.Hashable.Class.Hashable Servant.Common.Req.Haxl.Req
- Servant.Common.Req.Haxl: instance Data.Hashable.Class.Hashable Servant.Common.Req.Haxl.WantedStatusCodes
- Servant.Common.Req.Haxl: instance GHC.Classes.Eq (Servant.Common.Req.Haxl.ServantRequest a)
- Servant.Common.Req.Haxl: instance GHC.Classes.Eq Servant.Common.Req.Haxl.Req
- Servant.Common.Req.Haxl: instance GHC.Classes.Eq Servant.Common.Req.Haxl.WantedStatusCodes
- Servant.Common.Req.Haxl: instance GHC.Classes.Ord Servant.Common.Req.Haxl.Req
- Servant.Common.Req.Haxl: instance GHC.Classes.Ord Servant.Common.Req.Haxl.WantedStatusCodes
- Servant.Common.Req.Haxl: instance GHC.Exception.Exception Servant.Common.Req.Haxl.ServantError
- Servant.Common.Req.Haxl: instance GHC.Generics.Constructor Servant.Common.Req.Haxl.C1_0Req
- Servant.Common.Req.Haxl: instance GHC.Generics.Constructor Servant.Common.Req.Haxl.C1_0WantedStatusCodes
- Servant.Common.Req.Haxl: instance GHC.Generics.Constructor Servant.Common.Req.Haxl.C1_1WantedStatusCodes
- Servant.Common.Req.Haxl: instance GHC.Generics.Datatype Servant.Common.Req.Haxl.D1Req
- Servant.Common.Req.Haxl: instance GHC.Generics.Datatype Servant.Common.Req.Haxl.D1WantedStatusCodes
- Servant.Common.Req.Haxl: instance GHC.Generics.Generic Servant.Common.Req.Haxl.Req
- Servant.Common.Req.Haxl: instance GHC.Generics.Generic Servant.Common.Req.Haxl.WantedStatusCodes
- Servant.Common.Req.Haxl: instance GHC.Generics.Selector Servant.Common.Req.Haxl.S1_0_0Req
- Servant.Common.Req.Haxl: instance GHC.Generics.Selector Servant.Common.Req.Haxl.S1_0_1Req
- Servant.Common.Req.Haxl: instance GHC.Generics.Selector Servant.Common.Req.Haxl.S1_0_2Req
- Servant.Common.Req.Haxl: instance GHC.Generics.Selector Servant.Common.Req.Haxl.S1_0_3Req
- Servant.Common.Req.Haxl: instance GHC.Generics.Selector Servant.Common.Req.Haxl.S1_0_4Req
- Servant.Common.Req.Haxl: instance GHC.Show.Show (Servant.Common.Req.Haxl.ServantRequest a)
- Servant.Common.Req.Haxl: instance GHC.Show.Show Servant.Common.Req.Haxl.Req
- Servant.Common.Req.Haxl: instance GHC.Show.Show Servant.Common.Req.Haxl.ServantError
- Servant.Common.Req.Haxl: instance GHC.Show.Show Servant.Common.Req.Haxl.WantedStatusCodes
- Servant.Common.Req.Haxl: instance Haxl.Core.Show1.Show1 Servant.Common.Req.Haxl.ServantRequest
- Servant.Common.Req.Haxl: instance Haxl.Core.StateStore.StateKey Servant.Common.Req.Haxl.ServantRequest
- Servant.Common.Req.Haxl: instance Haxl.Core.Types.DataSource () Servant.Common.Req.Haxl.ServantRequest
- Servant.Common.Req.Haxl: instance Haxl.Core.Types.DataSourceName Servant.Common.Req.Haxl.ServantRequest
- Servant.Common.Req.Haxl: performRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () (Int, ByteString, MediaType, [Header], Response ByteString)
- Servant.Common.Req.Haxl: performRequestCT :: MimeUnrender ct result => Proxy ct -> Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () ([Header], result)
- Servant.Common.Req.Haxl: performRequestNoBody :: Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () ()
- Servant.Common.Req.Haxl: performRequest_ :: Manager -> Method -> Req -> WantedStatusCodes -> BaseUrl -> EitherT ServantError IO (Int, ByteString, MediaType, [Header], Response ByteString)
- Servant.Common.Req.Haxl: reqToRequest :: MonadThrow m => Req -> BaseUrl -> m Request
- Servant.Common.Req.Haxl: setRQBody :: ByteString -> MediaType -> Req -> Req
+ Servant.Haxl.Client: ConnectionError :: ServantConnectionError -> ServantError
+ Servant.Haxl.Client: DecodeFailure :: String -> MediaType -> ByteString -> ServantError
+ Servant.Haxl.Client: FailureResponse :: Status -> MediaType -> ByteString -> ServantError
+ Servant.Haxl.Client: InvalidContentTypeHeader :: ByteString -> ByteString -> ServantError
+ Servant.Haxl.Client: UnsupportedContentType :: MediaType -> ByteString -> ServantError
+ Servant.Haxl.Client: [connectionError] :: ServantError -> ServantConnectionError
+ Servant.Haxl.Client: [decodeError] :: ServantError -> String
+ Servant.Haxl.Client: [responseBody] :: ServantError -> ByteString
+ Servant.Haxl.Client: [responseContentTypeHeader] :: ServantError -> ByteString
+ Servant.Haxl.Client: [responseContentType] :: ServantError -> MediaType
+ Servant.Haxl.Client: [responseStatus] :: ServantError -> Status
+ Servant.Haxl.Client: class HasClient layout where type family Client layout :: *
+ Servant.Haxl.Client: client :: HasClient layout => Proxy layout -> BaseUrl -> Client layout
+ Servant.Haxl.Client: clientWithRoute :: HasClient layout => Proxy layout -> Req -> BaseUrl -> Client layout
+ Servant.Haxl.Client: data ServantConnectionError
+ Servant.Haxl.Client: data ServantError
+ Servant.Haxl.Client: data ServantResponse
+ Servant.Haxl.Client: initServantClientState :: Int -> IO (State ServantRequest)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol capture, Servant.Common.Text.ToText a, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.Capture.Capture capture a Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol path, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (path Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.Header.Header sym a Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.MatrixParam.MatrixParam sym a Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.MatrixParam.MatrixParams sym a Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.QueryParam.QueryParam sym a Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol sym, Servant.Common.Text.ToText a, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.QueryParam.QueryParams sym a Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol sym, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.MatrixParam.MatrixFlag sym Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (GHC.TypeLits.KnownSymbol sym, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.QueryParam.QueryFlag sym Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (Servant.API.ContentTypes.MimeRender ct a, Servant.Haxl.Client.HasClient sublayout) => Servant.Haxl.Client.HasClient (Servant.API.ReqBody.ReqBody (ct : cts) a Servant.API.Sub.:> sublayout)
+ Servant.Haxl.Client: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls) => Servant.Haxl.Client.HasClient (Servant.API.Get.Get (ct : cts) (Servant.API.ResponseHeaders.Headers ls a))
+ Servant.Haxl.Client: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls) => Servant.Haxl.Client.HasClient (Servant.API.Patch.Patch (ct : cts) (Servant.API.ResponseHeaders.Headers ls a))
+ Servant.Haxl.Client: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls) => Servant.Haxl.Client.HasClient (Servant.API.Post.Post (ct : cts) (Servant.API.ResponseHeaders.Headers ls a))
+ Servant.Haxl.Client: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls) => Servant.Haxl.Client.HasClient (Servant.API.Put.Put (ct : cts) (Servant.API.ResponseHeaders.Headers ls a))
+ Servant.Haxl.Client: instance (Servant.API.ContentTypes.MimeUnrender ct a, Servant.API.ResponseHeaders.BuildHeadersTo ls, cts' ~ (ct : cts)) => Servant.Haxl.Client.HasClient (Servant.API.Delete.Delete cts' (Servant.API.ResponseHeaders.Headers ls a))
+ Servant.Haxl.Client: instance (Servant.API.ContentTypes.MimeUnrender ct a, cts' ~ (ct : cts)) => Servant.Haxl.Client.HasClient (Servant.API.Delete.Delete cts' a)
+ Servant.Haxl.Client: instance (Servant.Haxl.Client.HasClient a, Servant.Haxl.Client.HasClient b) => Servant.Haxl.Client.HasClient (a Servant.API.Alternative.:<|> b)
+ Servant.Haxl.Client: instance Servant.API.ContentTypes.MimeUnrender ct a => Servant.Haxl.Client.HasClient (Servant.API.Patch.Patch (ct : cts) a)
+ Servant.Haxl.Client: instance Servant.API.ContentTypes.MimeUnrender ct a => Servant.Haxl.Client.HasClient (Servant.API.Post.Post (ct : cts) a)
+ Servant.Haxl.Client: instance Servant.API.ContentTypes.MimeUnrender ct a => Servant.Haxl.Client.HasClient (Servant.API.Put.Put (ct : cts) a)
+ Servant.Haxl.Client: instance Servant.API.ContentTypes.MimeUnrender ct result => Servant.Haxl.Client.HasClient (Servant.API.Get.Get (ct : cts) result)
+ Servant.Haxl.Client: instance Servant.Haxl.Client.HasClient (Servant.API.Delete.Delete cts ())
+ Servant.Haxl.Client: instance Servant.Haxl.Client.HasClient (Servant.API.Get.Get (ct : cts) ())
+ Servant.Haxl.Client: instance Servant.Haxl.Client.HasClient (Servant.API.Patch.Patch (ct : cts) ())
+ Servant.Haxl.Client: instance Servant.Haxl.Client.HasClient (Servant.API.Post.Post (ct : cts) ())
+ Servant.Haxl.Client: instance Servant.Haxl.Client.HasClient (Servant.API.Put.Put (ct : cts) ())
+ Servant.Haxl.Client: instance Servant.Haxl.Client.HasClient Servant.API.Raw.Raw
+ Servant.Haxl.Client.BaseUrl: BaseUrl :: Scheme -> String -> Int -> BaseUrl
+ Servant.Haxl.Client.BaseUrl: Http :: Scheme
+ Servant.Haxl.Client.BaseUrl: Https :: Scheme
+ Servant.Haxl.Client.BaseUrl: [baseUrlHost] :: BaseUrl -> String
+ Servant.Haxl.Client.BaseUrl: [baseUrlPort] :: BaseUrl -> Int
+ Servant.Haxl.Client.BaseUrl: [baseUrlScheme] :: BaseUrl -> Scheme
+ Servant.Haxl.Client.BaseUrl: data BaseUrl
+ Servant.Haxl.Client.BaseUrl: data Scheme
+ Servant.Haxl.Client.BaseUrl: parseBaseUrl :: String -> Either String BaseUrl
+ Servant.Haxl.Client.BaseUrl: showBaseUrl :: BaseUrl -> String
+ Servant.Haxl.Client.Internal: ServantRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl -> ServantRequest (Int, ByteString, MediaType, [Header], ServantResponse)
+ Servant.Haxl.Client.Internal: ServantResponse :: (Response ByteString) -> ServantResponse
+ Servant.Haxl.Client.Internal: data ServantRequest a
+ Servant.Haxl.Client.Internal: data ServantResponse
+ Servant.Haxl.Client.Internal: initServantClientState :: Int -> IO (State ServantRequest)
+ Servant.Haxl.Client.Internal: instance Data.Hashable.Class.Hashable (Servant.Haxl.Client.Internal.ServantRequest a)
+ Servant.Haxl.Client.Internal: instance GHC.Classes.Eq (Servant.Haxl.Client.Internal.ServantRequest a)
+ Servant.Haxl.Client.Internal: instance GHC.Show.Show (Servant.Haxl.Client.Internal.ServantRequest a)
+ Servant.Haxl.Client.Internal: instance GHC.Show.Show Servant.Haxl.Client.Internal.ServantResponse
+ Servant.Haxl.Client.Internal: instance Haxl.Core.Show1.Show1 Servant.Haxl.Client.Internal.ServantRequest
+ Servant.Haxl.Client.Internal: instance Haxl.Core.StateStore.StateKey Servant.Haxl.Client.Internal.ServantRequest
+ Servant.Haxl.Client.Internal: instance Haxl.Core.Types.DataSource () Servant.Haxl.Client.Internal.ServantRequest
+ Servant.Haxl.Client.Internal: instance Haxl.Core.Types.DataSourceName Servant.Haxl.Client.Internal.ServantRequest
+ Servant.Haxl.Client.Internal.Error: ServantConnectionError :: HttpException -> ServantConnectionError
+ Servant.Haxl.Client.Internal.Error: data ServantConnectionError
+ Servant.Haxl.Client.Internal.Error: instance GHC.Show.Show Servant.Haxl.Client.Internal.Error.ServantConnectionError
+ Servant.Haxl.Client.Req: addHeader :: ToText a => String -> a -> Req -> Req
+ Servant.Haxl.Client.Req: appendToMatrixParams :: String -> Maybe String -> Req -> Req
+ Servant.Haxl.Client.Req: appendToPath :: String -> Req -> Req
+ Servant.Haxl.Client.Req: appendToQueryString :: Text -> Maybe Text -> Req -> Req
+ Servant.Haxl.Client.Req: data Req
+ Servant.Haxl.Client.Req: data ServantConnectionError
+ Servant.Haxl.Client.Req: data ServantError
+ Servant.Haxl.Client.Req: data ServantResponse
+ Servant.Haxl.Client.Req: defReq :: Req
+ Servant.Haxl.Client.Req: displayHttpRequest :: Method -> String
+ Servant.Haxl.Client.Req: initServantClientState :: Int -> IO (State ServantRequest)
+ Servant.Haxl.Client.Req: performRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () (Int, ByteString, MediaType, [Header], ServantResponse)
+ Servant.Haxl.Client.Req: performRequestCT :: MimeUnrender ct result => Proxy ct -> Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () ([Header], result)
+ Servant.Haxl.Client.Req: performRequestNoBody :: Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () ()
+ Servant.Haxl.Client.Req: setRQBody :: ByteString -> MediaType -> Req -> Req
+ Servant.Haxl.Client.Types: AllCodes :: WantedStatusCodes
+ Servant.Haxl.Client.Types: BaseUrl :: Scheme -> String -> Int -> BaseUrl
+ Servant.Haxl.Client.Types: ConnectionError :: ServantConnectionError -> ServantError
+ Servant.Haxl.Client.Types: DecodeFailure :: String -> MediaType -> ByteString -> ServantError
+ Servant.Haxl.Client.Types: FailureResponse :: Status -> MediaType -> ByteString -> ServantError
+ Servant.Haxl.Client.Types: Http :: Scheme
+ Servant.Haxl.Client.Types: Https :: Scheme
+ Servant.Haxl.Client.Types: InvalidContentTypeHeader :: ByteString -> ByteString -> ServantError
+ Servant.Haxl.Client.Types: Req :: String -> QueryText -> Maybe (ByteString, MediaType) -> [MediaType] -> [(String, Text)] -> Req
+ Servant.Haxl.Client.Types: SelectCodes :: [Int] -> WantedStatusCodes
+ Servant.Haxl.Client.Types: UnsupportedContentType :: MediaType -> ByteString -> ServantError
+ Servant.Haxl.Client.Types: [baseUrlHost] :: BaseUrl -> String
+ Servant.Haxl.Client.Types: [baseUrlPort] :: BaseUrl -> Int
+ Servant.Haxl.Client.Types: [baseUrlScheme] :: BaseUrl -> Scheme
+ Servant.Haxl.Client.Types: [connectionError] :: ServantError -> ServantConnectionError
+ Servant.Haxl.Client.Types: [decodeError] :: ServantError -> String
+ Servant.Haxl.Client.Types: [headers] :: Req -> [(String, Text)]
+ Servant.Haxl.Client.Types: [qs] :: Req -> QueryText
+ Servant.Haxl.Client.Types: [reqAccept] :: Req -> [MediaType]
+ Servant.Haxl.Client.Types: [reqBody] :: Req -> Maybe (ByteString, MediaType)
+ Servant.Haxl.Client.Types: [reqPath] :: Req -> String
+ Servant.Haxl.Client.Types: [responseBody] :: ServantError -> ByteString
+ Servant.Haxl.Client.Types: [responseContentTypeHeader] :: ServantError -> ByteString
+ Servant.Haxl.Client.Types: [responseContentType] :: ServantError -> MediaType
+ Servant.Haxl.Client.Types: [responseStatus] :: ServantError -> Status
+ Servant.Haxl.Client.Types: data BaseUrl
+ Servant.Haxl.Client.Types: data Req
+ Servant.Haxl.Client.Types: data Scheme
+ Servant.Haxl.Client.Types: data ServantConnectionError
+ Servant.Haxl.Client.Types: data ServantError
+ Servant.Haxl.Client.Types: data WantedStatusCodes
+ Servant.Haxl.Client.Types: instance Data.Hashable.Class.Hashable Servant.Haxl.Client.Types.BaseUrl
+ Servant.Haxl.Client.Types: instance Data.Hashable.Class.Hashable Servant.Haxl.Client.Types.Req
+ Servant.Haxl.Client.Types: instance Data.Hashable.Class.Hashable Servant.Haxl.Client.Types.Scheme
+ Servant.Haxl.Client.Types: instance Data.Hashable.Class.Hashable Servant.Haxl.Client.Types.WantedStatusCodes
+ Servant.Haxl.Client.Types: instance GHC.Classes.Eq Servant.Haxl.Client.Types.BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Classes.Eq Servant.Haxl.Client.Types.Req
+ Servant.Haxl.Client.Types: instance GHC.Classes.Eq Servant.Haxl.Client.Types.Scheme
+ Servant.Haxl.Client.Types: instance GHC.Classes.Eq Servant.Haxl.Client.Types.WantedStatusCodes
+ Servant.Haxl.Client.Types: instance GHC.Classes.Ord Servant.Haxl.Client.Types.BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Classes.Ord Servant.Haxl.Client.Types.Req
+ Servant.Haxl.Client.Types: instance GHC.Classes.Ord Servant.Haxl.Client.Types.Scheme
+ Servant.Haxl.Client.Types: instance GHC.Classes.Ord Servant.Haxl.Client.Types.WantedStatusCodes
+ Servant.Haxl.Client.Types: instance GHC.Exception.Exception Servant.Haxl.Client.Types.ServantError
+ Servant.Haxl.Client.Types: instance GHC.Generics.Constructor Servant.Haxl.Client.Types.C1_0BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Generics.Constructor Servant.Haxl.Client.Types.C1_0Req
+ Servant.Haxl.Client.Types: instance GHC.Generics.Constructor Servant.Haxl.Client.Types.C1_0Scheme
+ Servant.Haxl.Client.Types: instance GHC.Generics.Constructor Servant.Haxl.Client.Types.C1_0WantedStatusCodes
+ Servant.Haxl.Client.Types: instance GHC.Generics.Constructor Servant.Haxl.Client.Types.C1_1Scheme
+ Servant.Haxl.Client.Types: instance GHC.Generics.Constructor Servant.Haxl.Client.Types.C1_1WantedStatusCodes
+ Servant.Haxl.Client.Types: instance GHC.Generics.Datatype Servant.Haxl.Client.Types.D1BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Generics.Datatype Servant.Haxl.Client.Types.D1Req
+ Servant.Haxl.Client.Types: instance GHC.Generics.Datatype Servant.Haxl.Client.Types.D1Scheme
+ Servant.Haxl.Client.Types: instance GHC.Generics.Datatype Servant.Haxl.Client.Types.D1WantedStatusCodes
+ Servant.Haxl.Client.Types: instance GHC.Generics.Generic Servant.Haxl.Client.Types.BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Generics.Generic Servant.Haxl.Client.Types.Req
+ Servant.Haxl.Client.Types: instance GHC.Generics.Generic Servant.Haxl.Client.Types.Scheme
+ Servant.Haxl.Client.Types: instance GHC.Generics.Generic Servant.Haxl.Client.Types.WantedStatusCodes
+ Servant.Haxl.Client.Types: instance GHC.Generics.Selector Servant.Haxl.Client.Types.S1_0_0BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Generics.Selector Servant.Haxl.Client.Types.S1_0_0Req
+ Servant.Haxl.Client.Types: instance GHC.Generics.Selector Servant.Haxl.Client.Types.S1_0_1BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Generics.Selector Servant.Haxl.Client.Types.S1_0_1Req
+ Servant.Haxl.Client.Types: instance GHC.Generics.Selector Servant.Haxl.Client.Types.S1_0_2BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Generics.Selector Servant.Haxl.Client.Types.S1_0_2Req
+ Servant.Haxl.Client.Types: instance GHC.Generics.Selector Servant.Haxl.Client.Types.S1_0_3Req
+ Servant.Haxl.Client.Types: instance GHC.Generics.Selector Servant.Haxl.Client.Types.S1_0_4Req
+ Servant.Haxl.Client.Types: instance GHC.Show.Show Servant.Haxl.Client.Types.BaseUrl
+ Servant.Haxl.Client.Types: instance GHC.Show.Show Servant.Haxl.Client.Types.Req
+ Servant.Haxl.Client.Types: instance GHC.Show.Show Servant.Haxl.Client.Types.Scheme
+ Servant.Haxl.Client.Types: instance GHC.Show.Show Servant.Haxl.Client.Types.ServantError
+ Servant.Haxl.Client.Types: instance GHC.Show.Show Servant.Haxl.Client.Types.WantedStatusCodes

Files

LICENSE view
@@ -1,4 +1,5 @@-Copyright (c) 2014, Zalora South East Asia Pte Ltd+Copyright (c) 2016, Will Fancher+Portions 2014-2016, Zalora South East Asia Pte Ltd  All rights reserved. @@ -13,7 +14,7 @@       disclaimer in the documentation and/or other materials provided       with the distribution. -    * Neither the name of Zalora South East Asia Pte Ltd nor the names of other+    * Neither the name of Will Fancher nor the names of other       contributors may be used to endorse or promote products derived       from this software without specific prior written permission. 
servant-haxl-client.cabal view
@@ -1,5 +1,5 @@ name:                servant-haxl-client-version:             0.1.0.0+version:             0.2.0.0 synopsis: automatical derivation of querying functions for servant webservices description:   This library lets you derive automatically Haskell functions that@@ -19,15 +19,22 @@ tested-with:         GHC >= 7.10 homepage:            http://github.com/ElvishJerricco/servant-haxl-client/ Bug-reports:         http://github.com/ElvishJerricco/servant-haxl-client/issues+-- Deal with https://github.com/haskell/cabal/issues/2544 / https://github.com/haskell/cabal/issues/367+extra-source-files: src-ghc/Servant/Haxl/Client/Internal.hs+                    src-ghc/Servant/Haxl/Client/Internal/Error.hs+                    src-ghcjs/Servant/Haxl/Client/Internal.hs+                    src-ghcjs/Servant/Haxl/Client/Internal/Error.hs+                    src/Servant/Haxl/Client.hs+                    src/Servant/Haxl/Client/BaseUrl.hs+                    src/Servant/Haxl/Client/Req.hs+                    src/Servant/Haxl/Client/Types.hs+ source-repository head   type: git   location: http://github.com/ElvishJerricco/servant-haxl-client.git  library-  exposed-modules:-    Servant.Client.Haxl-    Servant.Common.BaseUrl.Haxl-    Servant.Common.Req.Haxl+  hs-source-dirs: src   build-depends:       base >=4.8 && <5     , aeson@@ -38,8 +45,6 @@     , exceptions     , hashable     , haxl-    , http-client-    , http-client-tls     , http-media     , http-types     , network-uri >= 2.6@@ -48,9 +53,26 @@     , string-conversions     , text     , transformers-  hs-source-dirs: src+  if !impl(ghcjs)+    hs-source-dirs: src-ghc+    build-depends:+        http-client+      , http-client-tls+  else+    hs-source-dirs: src-ghcjs+    build-depends:+        ghcjs-base+      , case-insensitive+  exposed-modules:+    Servant.Haxl.Client+    Servant.Haxl.Client.BaseUrl+    Servant.Haxl.Client.Req+    Servant.Haxl.Client.Types+    Servant.Haxl.Client.Internal+    Servant.Haxl.Client.Internal.Error   default-language: Haskell2010   ghc-options: -Wall+  test-suite spec   type: exitcode-stdio-1.0
+ src-ghc/Servant/Haxl/Client/Internal.hs view
@@ -0,0 +1,145 @@+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE StandaloneDeriving    #-}+{-# LANGUAGE TypeFamilies          #-}++module Servant.Haxl.Client.Internal+  ( ServantResponse(..)+  , ServantRequest(..)+  , initServantClientState+  ) where++import           Control.Concurrent.Async+import           Control.Concurrent.QSem+import           Control.Exception+import           Control.Monad+import           Control.Monad.Catch                (MonadThrow)+import           Control.Monad.IO.Class+import           Control.Monad.Trans.Either+import           Data.ByteString.Lazy               hiding (elem, filter, map,+                                                     null, pack)+import           Data.Hashable+import           Data.String+import           Data.String.Conversions+import           Data.Text.Encoding+import           Haxl.Core                          hiding (Request, catch)+import           Network.HTTP.Client+import           Network.HTTP.Client.TLS+import           Network.HTTP.Media+import           Network.HTTP.Types+import qualified Network.HTTP.Types.Header          as HTTP+import           Network.URI+import           Servant.Haxl.Client.BaseUrl+import           Servant.Haxl.Client.Internal.Error+import           Servant.Haxl.Client.Types++import qualified Network.HTTP.Client                as Client++data ServantResponse = ServantResponse (Response ByteString) deriving Show++reqToRequest :: MonadThrow m => Req -> BaseUrl -> m Request+reqToRequest req (BaseUrl reqScheme reqHost reqPort) =+    (setheaders . setAccept . setrqb . setQS) <$> parseUrl url++  where url = show $ nullURI { uriScheme = case reqScheme of+                                  Http  -> "http:"+                                  Https -> "https:"+                             , uriAuthority = Just+                                 URIAuth { uriUserInfo = ""+                                         , uriRegName = reqHost+                                         , uriPort = ":" ++ show reqPort+                                         }+                             , uriPath = reqPath req+                             }++        setrqb r = case reqBody req of+                     Nothing -> r+                     Just (b,t) -> r { requestBody = RequestBodyLBS 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)+++performRequest_ :: Manager -> Method -> Req -> WantedStatusCodes -> BaseUrl+               -> EitherT ServantError IO ( Int, ByteString, MediaType+                                          , [HTTP.Header], ServantResponse)+performRequest_ manager reqMethod req wantedStatus reqHost = do+  partialRequest <- liftIO $ reqToRequest req reqHost++  let request = partialRequest { Client.method = reqMethod+                               , checkStatus = \ _status _headers _cookies -> Nothing+                               }++  eResponse <- liftIO $ catchHttpException $ Client.httpLbs request manager+  case eResponse of+    Left err ->+      left $ ConnectionError $ ServantConnectionError err++    Right response -> do+      let status = Client.responseStatus response+          body = Client.responseBody response+          hrds = 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 -> left $ InvalidContentTypeHeader (cs t) body+                   Just t' -> pure t'+      unless (wantedStatus `wants` status_code) $+        left $ FailureResponse status ct body+      return (status_code, body, ct, hrds, ServantResponse response)+      where+        wants AllCodes _ = True+        wants (SelectCodes codes) status_code = status_code `elem` codes++catchHttpException :: IO a -> IO (Either HttpException a)+catchHttpException action =+  catch (Right <$> action) (pure . Left)++data ServantRequest a where+  ServantRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl ->+    ServantRequest (Int, ByteString, MediaType, [HTTP.Header], ServantResponse)++deriving instance Show (ServantRequest a)+deriving instance Eq (ServantRequest a)++instance Show1 ServantRequest where+  show1 = show++instance Hashable (ServantRequest a) where+  hashWithSalt s (ServantRequest m r w h) = hashWithSalt s (m, r, w, h)++instance StateKey ServantRequest where+  data State ServantRequest = ServantRequestState Int Manager++instance DataSourceName ServantRequest where+  dataSourceName _ = "ServantRequest"++instance DataSource () ServantRequest where+  fetch (ServantRequestState numThreads manager) _ () requests = AsyncFetch $ \inner -> do+    sem <- newQSem numThreads+    asyncs <- mapM (handler sem) requests+    inner+    mapM_ wait asyncs+    where+      handler :: QSem -> BlockedFetch ServantRequest -> IO (Async ())+      handler sem (BlockedFetch (ServantRequest met req wantedStatus reqHost) rvar) =+        async $ bracket_ (waitQSem sem) (signalQSem sem) $ do+          e <- runEitherT $ performRequest_ manager met req wantedStatus reqHost+          case e of+            Left err -> putFailure rvar err+            Right a -> putSuccess rvar a+          return ()++initServantClientState :: Int -> IO (State ServantRequest)+initServantClientState numThreads =+  ServantRequestState numThreads <$> newManager tlsManagerSettings
+ src-ghc/Servant/Haxl/Client/Internal/Error.hs view
@@ -0,0 +1,7 @@+module Servant.Haxl.Client.Internal.Error+  ( ServantConnectionError(..)+  ) where++import           Network.HTTP.Client++data ServantConnectionError = ServantConnectionError HttpException deriving Show
+ src-ghcjs/Servant/Haxl/Client/Internal.hs view
@@ -0,0 +1,172 @@+{-# LANGUAGE CPP                      #-}+{-# LANGUAGE ForeignFunctionInterface #-}+{-# LANGUAGE GADTs                    #-}+{-# LANGUAGE JavaScriptFFI            #-}+{-# LANGUAGE MultiParamTypeClasses    #-}+{-# LANGUAGE OverloadedStrings        #-}+{-# LANGUAGE ScopedTypeVariables      #-}+{-# LANGUAGE StandaloneDeriving       #-}+{-# LANGUAGE TypeFamilies             #-}++module Servant.Haxl.Client.Internal+  ( ServantResponse(..)+  , ServantRequest(..)+  , initServantClientState+  ) where++import           Control.Concurrent.Async+import           Control.Concurrent.QSem+import           Control.Exception+import           Control.Monad+import           Control.Monad.IO.Class+import           Control.Monad.Trans.Either+import qualified Data.ByteString.Char8              as BS+import           Data.ByteString.Lazy               hiding (elem, filter, map,+                                                     null, pack)+import qualified Data.CaseInsensitive               as CI+import           Data.Hashable+import qualified Data.JSString                      as JSString+import           Data.JSString.Text+import           Data.Maybe+import           Data.String.Conversions+import           Data.Text                          (Text)+import qualified Data.Text                          as Text+import           Data.Text.Encoding+import           Haxl.Core                          hiding (Request, catch)+import qualified JavaScript.Web.XMLHttpRequest      as XHR+import           Network.HTTP.Media+import           Network.HTTP.Types+import qualified Network.HTTP.Types.Header          as HTTP+import           Network.URI+import           Servant.Haxl.Client.BaseUrl+import           Servant.Haxl.Client.Internal.Error+import           Servant.Haxl.Client.Types++-- foreign import javascript safe "new DataView($3,$1,$2)" js_dataView :: Int -> Int -> JSVal -> JSVal++data ServantResponse = ServantResponse (XHR.Response BS.ByteString)+instance Show ServantResponse where+  show (ServantResponse xhrResponse) = show $ XHR.contents xhrResponse++xhrMethodConversion :: Method -> XHR.Method+xhrMethodConversion method+  | method == methodGet    = XHR.GET+  | method == methodPost   = XHR.POST+  | method == methodPut    = XHR.PUT+  | method == methodDelete = XHR.DELETE+  | otherwise               = error "No such XHR method"++xhrRequestConversion :: XHR.Method -> Req -> BaseUrl -> XHR.Request+xhrRequestConversion method req (BaseUrl reqScheme reqHost reqPort) =+  setData XHR.Request+    { XHR.reqMethod           = method+    , XHR.reqURI              = JSString.pack . show $ nullURI+      { uriScheme = case reqScheme of+           Http  -> "http:"+           Https -> "https:"+      , uriAuthority = Just+          URIAuth { uriUserInfo = ""+                  , uriRegName = reqHost+                  , uriPort = ":" ++ show reqPort+                  }+      , uriPath = reqPath req+      , uriQuery = BS.unpack $ renderQuery True $ queryTextToQuery (qs req)+      }+    , XHR.reqLogin            = Nothing+    , XHR.reqHeaders          = (\(str, text) -> (JSString.pack str, textToJSString text))+                                <$> headers req+    , XHR.reqWithCredentials  = False+    , XHR.reqData             = XHR.NoData -- will be set by setData+    }+  where+    setData r = case reqBody req of+      Nothing          -> r+      Just (bs, media) -> do+        -- let (b, _, _) = fromByteString $ toStrict bs+        r { XHR.reqHeaders = XHR.reqHeaders r ++ [("Content-Type", JSString.pack . show $ media)]+          -- , XHR.reqData = XHR.TypedArrayData $ getUint8Array b+          -- Temporary workaround until I can solve this:+          -- http://stackoverflow.com/questions/35930517/sending-binary-data-with-ghcjs-xhr+          , XHR.reqData = XHR.StringData $ textToJSString $ decodeUtf8 $ toStrict bs+          }++performRequest_ :: Method -> Req -> WantedStatusCodes -> BaseUrl+               -> EitherT ServantError IO ( Int, ByteString, MediaType+                                          , [HTTP.Header], ServantResponse)+performRequest_ method req wantedStatus reqHost = do+  let xhrMethod = xhrMethodConversion method+  let xhrReq = xhrRequestConversion xhrMethod req reqHost+  eResponse <- liftIO $ catchXHRError $ XHR.xhrByteString xhrReq+  case eResponse of+    Left err ->+      left $ ConnectionError $ ServantConnectionError err+    Right response -> do+      let body = fromStrict $ fromMaybe "" (XHR.contents response)+          status_code = XHR.status response+      hrds <- liftIO $+          breakHeaders . textFromJSString <$> XHR.getAllResponseHeaders response+      ct <- case lookup "Content-Type" hrds of+                 Nothing -> pure $ "application"//"octet-stream"+                 Just t -> case parseAccept t of+                   Nothing -> left $ InvalidContentTypeHeader (cs t) body+                   Just t' -> pure t'+      unless (wantedStatus `wants` status_code) $+        left $ FailureResponse (Status status_code "") ct body+      return (status_code, body, ct, hrds, ServantResponse response)+      where+        wants AllCodes _ = True+        wants (SelectCodes codes) status_code = status_code `elem` codes++breakHeaders :: Text -> [HTTP.Header]+breakHeaders allHeaders = Text.splitOn "\r\n" allHeaders >>= breakHeader+  where+    breakHeader :: Text -> [HTTP.Header]+    breakHeader txHeader = header+      where+        (txName, txDataWithColon) = Text.breakOn ": " txHeader+        txData = Text.stripPrefix ": " txDataWithColon+        ciName = CI.mk $ encodeUtf8 txName+        bsData = encodeUtf8 <$> txData+        header = maybe [] (\d -> [(ciName, d)]) bsData++catchXHRError :: IO a -> IO (Either XHR.XHRError a)+catchXHRError action = catch (Right <$> action) (pure . Left)++data ServantRequest a where+  ServantRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl ->+    ServantRequest (Int, ByteString, MediaType, [HTTP.Header], ServantResponse)++deriving instance Show (ServantRequest a)+deriving instance Eq (ServantRequest a)++instance Show1 ServantRequest where+  show1 = show++instance Hashable (ServantRequest a) where+  hashWithSalt s (ServantRequest m r w h) = hashWithSalt s (m, r, w, h)++instance DataSourceName ServantRequest where+  dataSourceName _ = "ServantRequest"++instance StateKey ServantRequest where+  data State ServantRequest = ServantRequestState Int++instance DataSource () ServantRequest where+  fetch (ServantRequestState numThreads) _ () requests =+    AsyncFetch $ \inner -> do+      sem <- newQSem numThreads+      asyncs <- mapM (handler sem) requests+      inner+      mapM_ wait asyncs+      where+        handler :: QSem -> BlockedFetch ServantRequest -> IO (Async ())+        handler sem (BlockedFetch (ServantRequest met req wantedStatus reqHost) rvar) =+          async $ bracket_ (waitQSem sem) (signalQSem sem) $ do+            e <- runEitherT $ performRequest_ met req wantedStatus reqHost+            case e of+              Left err -> putFailure rvar err+              Right a -> putSuccess rvar a+            return ()++initServantClientState :: Int -> IO (State ServantRequest)+initServantClientState = return . ServantRequestState
+ src-ghcjs/Servant/Haxl/Client/Internal/Error.hs view
@@ -0,0 +1,7 @@+module Servant.Haxl.Client.Internal.Error+  ( ServantConnectionError(..)+  ) where++import           JavaScript.Web.XMLHttpRequest++data ServantConnectionError = ServantConnectionError XHRError deriving Show
− src/Servant/Client/Haxl.hs
@@ -1,622 +0,0 @@-{-# LANGUAGE CPP                  #-}-{-# LANGUAGE DataKinds            #-}-{-# LANGUAGE FlexibleContexts     #-}-{-# LANGUAGE FlexibleInstances    #-}-{-# LANGUAGE InstanceSigs         #-}-{-# LANGUAGE OverloadedStrings    #-}-{-# LANGUAGE ScopedTypeVariables  #-}-{-# LANGUAGE TypeFamilies         #-}-{-# LANGUAGE TypeOperators        #-}-{-# LANGUAGE UndecidableInstances #-}--- | This module provides 'client' which can automatically generate--- querying functions for each endpoint just from the type representing your--- API.-module Servant.Client.Haxl-  ( client-  , initServantClientState-  , HasClient(..)-  , ServantError(..)-  , module Servant.Common.BaseUrl.Haxl-  ) where--import           Control.Monad-import           Data.ByteString.Lazy       (ByteString)-import           Data.List-import           Data.Proxy-import           Data.String.Conversions-import           Data.Text                  (unpack)-import           GHC.TypeLits-import           Haxl.Core                  (GenHaxl, State)-import           Network.HTTP.Client hiding (Proxy)-import           Network.HTTP.Client.TLS-import           Network.HTTP.Media-import qualified Network.HTTP.Types         as H-import qualified Network.HTTP.Types.Header  as HTTP-import           Servant.API-import           Servant.Common.BaseUrl.Haxl-import           Servant.Common.Req.Haxl---- * 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 Book -- POST /books--- >--- > myApi :: Proxy MyApi--- > myApi = Proxy--- >--- > getAllBooks :: EitherT String IO [Book]--- > postNewBook :: Book -> EitherT String IO Book--- > (getAllBooks :<|> postNewBook) = client myApi host--- >   where host = BaseUrl Http "localhost" 8080-client :: HasClient layout => Proxy layout -> BaseUrl -> Client layout-client p = clientWithRoute p defReq--initServantClientState :: Int -> IO (State ServantRequest)-initServantClientState numThreads =-  ServantRequestState numThreads <$> newManager tlsManagerSettings---- | 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 layout where-  type Client layout :: *-  clientWithRoute :: Proxy layout -> Req -> BaseUrl -> Client layout--{-type Client layout = Client layout-}---- | 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 :: EitherT String IO [Book]--- > postNewBook :: Book -> EitherT String IO Book--- > (getAllBooks :<|> postNewBook) = client myApi host--- >   where host = BaseUrl Http "localhost" 8080-instance (HasClient a, HasClient b) => HasClient (a :<|> b) where-  type Client (a :<|> b) = Client a :<|> Client b-  clientWithRoute Proxy req baseurl =-    clientWithRoute (Proxy :: Proxy a) req baseurl :<|>-    clientWithRoute (Proxy :: Proxy b) req baseurl---- | 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 'ToText' instance for your type.------ Example:------ > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book--- >--- > myApi :: Proxy MyApi--- > myApi = Proxy--- >--- > getBook :: Text -> EitherT String IO Book--- > getBook = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- then you can just use "getBook" to query that endpoint-instance (KnownSymbol capture, ToText a, HasClient sublayout)-      => HasClient (Capture capture a :> sublayout) where--  type Client (Capture capture a :> sublayout) =-    a -> Client sublayout--  clientWithRoute Proxy req baseurl val =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (appendToPath p req)-                    baseurl--    where p = unpack (toText val)---- | If you have a 'Delete' endpoint in your API, the client--- side querying function that is created when calling 'client'--- will just require an argument that specifies the scheme, host--- and port to send the request to.-instance {-# OVERLAPPABLE #-}-  -- See https://downloads.haskell.org/~ghc/7.8.2/docs/html/users_guide/type-class-extensions.html#undecidable-instances-  (MimeUnrender ct a, cts' ~ (ct ': cts)) => HasClient (Delete cts' a) where-  type Client (Delete cts' a) = GenHaxl () a-  clientWithRoute Proxy req baseurl =-    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodDelete req (SelectCodes [200, 202]) baseurl---- | If you have a 'Delete xs ()' endpoint, the client expects a 204 No Content--- HTTP header.-instance {-# OVERLAPPING #-}-  HasClient (Delete cts ()) where-  type Client (Delete cts ()) = GenHaxl () ()-  clientWithRoute Proxy req baseurl =-    void $ performRequestNoBody H.methodDelete req (SelectCodes [204]) baseurl---- | If you have a 'Delete xs (Headers ls x)' endpoint, the client expects the--- corresponding headers.-instance {-# OVERLAPPING #-}-  -- See https://downloads.haskell.org/~ghc/7.8.2/docs/html/users_guide/type-class-extensions.html#undecidable-instances-  ( MimeUnrender ct a, BuildHeadersTo ls, cts' ~ (ct ': cts)-  ) => HasClient (Delete cts' (Headers ls a)) where-  type Client (Delete cts' (Headers ls a)) = GenHaxl () (Headers ls a)-  clientWithRoute Proxy req baseurl = do-    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodDelete req (SelectCodes [200, 202]) baseurl-    return Headers { getResponse = resp-                   , getHeadersHList = buildHeadersTo hdrs-                   }---- | If you have a 'Get' endpoint in your API, the client--- side querying function that is created when calling 'client'--- will just require an argument that specifies the scheme, host--- and port to send the request to.-instance {-# OVERLAPPABLE #-}-  (MimeUnrender ct result) => HasClient (Get (ct ': cts) result) where-  type Client (Get (ct ': cts) result) = GenHaxl () result-  clientWithRoute Proxy req baseurl =-    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodGet req (SelectCodes [200, 203]) baseurl---- | If you have a 'Get xs ()' endpoint, the client expects a 204 No Content--- HTTP status.-instance {-# OVERLAPPING #-}-  HasClient (Get (ct ': cts) ()) where-  type Client (Get (ct ': cts) ()) = GenHaxl () ()-  clientWithRoute Proxy req =-    performRequestNoBody H.methodGet req (SelectCodes [204])---- | If you have a 'Get xs (Headers ls x)' endpoint, the client expects the--- corresponding headers.-instance {-# OVERLAPPING #-}-  ( MimeUnrender ct a, BuildHeadersTo ls-  ) => HasClient (Get (ct ': cts) (Headers ls a)) where-  type Client (Get (ct ': cts) (Headers ls a)) = GenHaxl () (Headers ls a)-  clientWithRoute Proxy req baseurl = do-    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodGet req (SelectCodes [200, 203, 204]) baseurl-    return Headers { getResponse = resp-                   , 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 'ToText' instance.------ Example:------ > newtype Referer = Referer { referrer :: Text }--- >   deriving (Eq, Show, Generic, FromText, ToText)--- >--- >            -- GET /view-my-referer--- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer--- >--- > myApi :: Proxy MyApi--- > myApi = Proxy--- >--- > viewReferer :: Maybe Referer -> EitherT String IO Book--- > viewReferer = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- then you can just use "viewRefer" to query that endpoint--- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments-instance (KnownSymbol sym, ToText a, HasClient sublayout)-      => HasClient (Header sym a :> sublayout) where--  type Client (Header sym a :> sublayout) =-    Maybe a -> Client sublayout--  clientWithRoute Proxy req baseurl mval =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (maybe req-                           (\value -> Servant.Common.Req.Haxl.addHeader hname value req)-                           mval-                    )-                    baseurl--    where hname = symbolVal (Proxy :: Proxy sym)---- | If you have a 'Post' endpoint in your API, the client--- side querying function that is created when calling 'client'--- will just require an argument that specifies the scheme, host--- and port to send the request to.-instance {-# OVERLAPPABLE #-}-  (MimeUnrender ct a) => HasClient (Post (ct ': cts) a) where-  type Client (Post (ct ': cts) a) = GenHaxl () a-  clientWithRoute Proxy req baseurl =-    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPost req (SelectCodes [200, 201, 202]) baseurl---- | If you have a 'Post xs ()' endpoint, the client expects a 204 No Content--- HTTP header.-instance {-# OVERLAPPING #-}-  HasClient (Post (ct ': cts) ()) where-  type Client (Post (ct ': cts) ()) = GenHaxl () ()-  clientWithRoute Proxy req baseurl =-    void $ performRequestNoBody H.methodPost req (SelectCodes [204]) baseurl---- | If you have a 'Post xs (Headers ls x)' endpoint, the client expects the--- corresponding headers.-instance {-# OVERLAPPING #-}-  ( MimeUnrender ct a, BuildHeadersTo ls-  ) => HasClient (Post (ct ': cts) (Headers ls a)) where-  type Client (Post (ct ': cts) (Headers ls a)) = GenHaxl () (Headers ls a)-  clientWithRoute Proxy req baseurl = do-    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPost req (SelectCodes [200, 201, 202]) baseurl-    return Headers { getResponse = resp-                   , getHeadersHList = buildHeadersTo hdrs-                   }---- | If you have a 'Put' endpoint in your API, the client--- side querying function that is created when calling 'client'--- will just require an argument that specifies the scheme, host--- and port to send the request to.-instance {-# OVERLAPPABLE #-}-  (MimeUnrender ct a) => HasClient (Put (ct ': cts) a) where-  type Client (Put (ct ': cts) a) = GenHaxl () a-  clientWithRoute Proxy req baseurl =-    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPut req (SelectCodes [200,201]) baseurl---- | If you have a 'Put xs ()' endpoint, the client expects a 204 No Content--- HTTP header.-instance {-# OVERLAPPING #-}-  HasClient (Put (ct ': cts) ()) where-  type Client (Put (ct ': cts) ()) = GenHaxl () ()-  clientWithRoute Proxy req baseurl =-    void $ performRequestNoBody H.methodPut req (SelectCodes [204]) baseurl---- | If you have a 'Put xs (Headers ls x)' endpoint, the client expects the--- corresponding headers.-instance {-# OVERLAPPING #-}-  ( MimeUnrender ct a, BuildHeadersTo ls-  ) => HasClient (Put (ct ': cts) (Headers ls a)) where-  type Client (Put (ct ': cts) (Headers ls a)) = GenHaxl () (Headers ls a)-  clientWithRoute Proxy req baseurl = do-    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPut req (SelectCodes [200, 201]) baseurl-    return Headers { getResponse = resp-                   , getHeadersHList = buildHeadersTo hdrs-                   }---- | If you have a 'Patch' endpoint in your API, the client--- side querying function that is created when calling 'client'--- will just require an argument that specifies the scheme, host--- and port to send the request to.-instance {-# OVERLAPPABLE #-}-  (MimeUnrender ct a) => HasClient (Patch (ct ': cts) a) where-  type Client (Patch (ct ': cts) a) = GenHaxl () a-  clientWithRoute Proxy req baseurl =-    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPatch req (SelectCodes [200,201]) baseurl---- | If you have a 'Patch xs ()' endpoint, the client expects a 204 No Content--- HTTP header.-instance {-# OVERLAPPING #-}-  HasClient (Patch (ct ': cts) ()) where-  type Client (Patch (ct ': cts) ()) = GenHaxl () ()-  clientWithRoute Proxy req baseurl =-    void $ performRequestNoBody H.methodPatch req (SelectCodes [204]) baseurl---- | If you have a 'Patch xs (Headers ls x)' endpoint, the client expects the--- corresponding headers.-instance {-# OVERLAPPING #-}-  ( MimeUnrender ct a, BuildHeadersTo ls-  ) => HasClient (Patch (ct ': cts) (Headers ls a)) where-  type Client (Patch (ct ': cts) (Headers ls a)) = GenHaxl () (Headers ls a)-  clientWithRoute Proxy req baseurl = do-    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPatch req (SelectCodes [200, 201, 204]) baseurl-    return Headers { getResponse = resp-                   , getHeadersHList = buildHeadersTo hdrs-                   }---- | 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 'ToText' instance for your type.------ Example:------ > type MyApi = "books" :> QueryParam "author" Text :> Get '[JSON] [Book]--- >--- > myApi :: Proxy MyApi--- > myApi = Proxy--- >--- > getBooksBy :: Maybe Text -> EitherT String IO [Book]--- > getBooksBy = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- 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, ToText a, HasClient sublayout)-      => HasClient (QueryParam sym a :> sublayout) where--  type Client (QueryParam sym a :> sublayout) =-    Maybe a -> Client sublayout--  -- if mparam = Nothing, we don't add it to the query string-  clientWithRoute Proxy req baseurl mparam =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (maybe req-                           (flip (appendToQueryString pname) req . Just)-                           mparamText-                    )-                    baseurl--    where pname  = cs pname'-          pname' = symbolVal (Proxy :: Proxy sym)-          mparamText = fmap toText 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 'ToText' instance for your type.------ Example:------ > type MyApi = "books" :> QueryParams "authors" Text :> Get '[JSON] [Book]--- >--- > myApi :: Proxy MyApi--- > myApi = Proxy--- >--- > getBooksBy :: [Text] -> EitherT String IO [Book]--- > getBooksBy = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- 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, ToText a, HasClient sublayout)-      => HasClient (QueryParams sym a :> sublayout) where--  type Client (QueryParams sym a :> sublayout) =-    [a] -> Client sublayout--  clientWithRoute Proxy req baseurl paramlist =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just))-                            req-                            paramlist'-                    )-                    baseurl--    where pname  = cs pname'-          pname' = symbolVal (Proxy :: Proxy sym)-          paramlist' = map (Just . toText) 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 -> EitherT String IO [Book]--- > getBooks = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- 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 sublayout)-      => HasClient (QueryFlag sym :> sublayout) where--  type Client (QueryFlag sym :> sublayout) =-    Bool -> Client sublayout--  clientWithRoute Proxy req baseurl flag =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (if flag-                       then appendToQueryString paramname Nothing req-                       else req-                    )-                    baseurl--    where paramname = cs $ symbolVal (Proxy :: Proxy sym)---- | If you use a 'MatrixParam' in one of your endpoints in your API,--- the corresponding querying function will automatically take--- an additional argument of the type specified by your 'MatrixParam',--- 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 'ToText' instance for your type.------ Example:------ > type MyApi = "books" :> MatrixParam "author" Text :> Get '[JSON] [Book]--- >--- > myApi :: Proxy MyApi--- > myApi = Proxy--- >--- > getBooksBy :: Maybe Text -> EitherT String IO [Book]--- > getBooksBy = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- 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, ToText a, HasClient sublayout)-      => HasClient (MatrixParam sym a :> sublayout) where--  type Client (MatrixParam sym a :> sublayout) =-    Maybe a -> Client sublayout--  -- if mparam = Nothing, we don't add it to the query string-  clientWithRoute Proxy req baseurl mparam =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (maybe req-                           (flip (appendToMatrixParams pname . Just) req)-                           mparamText-                    )-                    baseurl--    where pname = symbolVal (Proxy :: Proxy sym)-          mparamText = fmap (cs . toText) mparam---- | If you use a 'MatrixParams' 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--- 'MatrixParams'.------ 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 path segment string, under the--- same matrix string parameter name.------ You can control how values for your type are turned into text by--- specifying a 'ToText' instance for your type.------ Example:------ > type MyApi = "books" :> MatrixParams "authors" Text :> Get '[JSON] [Book]--- >--- > myApi :: Proxy MyApi--- > myApi = Proxy--- >--- > getBooksBy :: [Text] -> EitherT String IO [Book]--- > getBooksBy = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- 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, ToText a, HasClient sublayout)-      => HasClient (MatrixParams sym a :> sublayout) where--  type Client (MatrixParams sym a :> sublayout) =-    [a] -> Client sublayout--  clientWithRoute Proxy req baseurl paramlist =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (foldl' (\ req' value -> maybe req' (flip (appendToMatrixParams pname) req' . Just . cs) value)-                            req-                            paramlist'-                    )-                    baseurl--    where pname  = cs pname'-          pname' = symbolVal (Proxy :: Proxy sym)-          paramlist' = map (Just . toText) paramlist---- | If you use a 'MatrixFlag' 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 path segment.------ Otherwise, this function will insert a value-less matrix parameter--- under the name associated to your 'MatrixFlag'.------ Example:------ > type MyApi = "books" :> MatrixFlag "published" :> Get '[JSON] [Book]--- >--- > myApi :: Proxy MyApi--- > myApi = Proxy--- >--- > getBooks :: Bool -> EitherT String IO [Book]--- > getBooks = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- 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 sublayout)-      => HasClient (MatrixFlag sym :> sublayout) where--  type Client (MatrixFlag sym :> sublayout) =-    Bool -> Client sublayout--  clientWithRoute Proxy req baseurl flag =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (if flag-                       then appendToMatrixParams paramname Nothing req-                       else req-                    )-                    baseurl--    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 -> GenHaxl () (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)--  clientWithRoute :: Proxy Raw -> Req -> BaseUrl -> Client Raw-  clientWithRoute Proxy req baseurl httpMethod = performRequest httpMethod req AllCodes baseurl---- | 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 -> EitherT String IO Book--- > addBook = client myApi host--- >   where host = BaseUrl Http "localhost" 8080--- > -- then you can just use "addBook" to query that endpoint-instance (MimeRender ct a, HasClient sublayout)-      => HasClient (ReqBody (ct ': cts) a :> sublayout) where--  type Client (ReqBody (ct ': cts) a :> sublayout) =-    a -> Client sublayout--  clientWithRoute Proxy req baseurl body =-    clientWithRoute (Proxy :: Proxy sublayout)-                    (let ctProxy = Proxy :: Proxy ct-                     in setRQBody (mimeRender ctProxy body)-                                  (contentType ctProxy)-                                  req-                    )-                    baseurl---- | Make the querying function append @path@ to the request path.-instance (KnownSymbol path, HasClient sublayout) => HasClient (path :> sublayout) where-  type Client (path :> sublayout) = Client sublayout--  clientWithRoute Proxy req =-     clientWithRoute (Proxy :: Proxy sublayout) (appendToPath p req)--    where p = symbolVal (Proxy :: Proxy path)
− src/Servant/Common/BaseUrl/Haxl.hs
@@ -1,63 +0,0 @@-{-# LANGUAGE DeriveGeneric #-}-{-# LANGUAGE ViewPatterns  #-}-module Servant.Common.BaseUrl.Haxl where--import           Data.Hashable-import           Data.List-import           GHC.Generics-import           Network.URI-import           Safe-import           Text.Read---- | URI scheme to use-data Scheme =-    Http  -- ^ http://-  | Https -- ^ https://-  deriving (Show, Eq, Ord, Generic)--instance Hashable Scheme where-  hashWithSalt s Http = hashWithSalt s (0 :: Int)-  hashWithSalt s Https = hashWithSalt s (1 :: Int)---- | 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)-  } deriving (Show, Eq, Ord, Generic)--instance Hashable BaseUrl where-  hashWithSalt s (BaseUrl sc h p) = hashWithSalt s (sc, h, p)--showBaseUrl :: BaseUrl -> String-showBaseUrl (BaseUrl urlscheme host port) =-  schemeString ++ "//" ++ host ++ portString-    where-      schemeString = case urlscheme of-        Http  -> "http:"-        Https -> "https:"-      portString = case (urlscheme, port) of-        (Http, 80) -> ""-        (Https, 443) -> ""-        _ -> ":" ++ show port--parseBaseUrl :: String -> Either String 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)))) "" "" "") ->-    Right (BaseUrl Http host port)-  Just (URI "http:" (Just (URIAuth "" host "")) "" "" "") ->-    Right (BaseUrl Http host 80)-  Just (URI "https:" (Just (URIAuth "" host (':' : (readMaybe -> Just port)))) "" "" "") ->-    Right (BaseUrl Https host port)-  Just (URI "https:" (Just (URIAuth "" host "")) "" "" "") ->-    Right (BaseUrl Https host 443)-  _ -> if "://" `isInfixOf` s-    then Left ("invalid base url: " ++ s)-    else parseBaseUrl ("http://" ++ s)- where-  removeTrailingSlash str = case lastMay str of-    Just '/' -> init str-    _ -> str
− src/Servant/Common/Req/Haxl.hs
@@ -1,241 +0,0 @@-{-# LANGUAGE CPP                   #-}-{-# LANGUAGE DeriveGeneric         #-}-{-# LANGUAGE GADTs                 #-}-{-# LANGUAGE MultiParamTypeClasses #-}-{-# LANGUAGE OverloadedStrings     #-}-{-# LANGUAGE ScopedTypeVariables   #-}-{-# LANGUAGE StandaloneDeriving    #-}-{-# LANGUAGE TypeFamilies          #-}-module Servant.Common.Req.Haxl where--import           Control.Concurrent.Async-import           Control.Concurrent.QSem-import           Control.Exception-import           Control.Monad-import           Control.Monad.Catch         (MonadThrow, throwM)-import           Control.Monad.IO.Class-import           Control.Monad.Trans.Either-import           Data.ByteString.Lazy        hiding (elem, filter, map, null,-                                              pack)-import           Data.Hashable-import           Data.Proxy-import           Data.String-import           Data.String.Conversions-import           Data.Text                   (Text)-import           Data.Text.Encoding-import           GHC.Generics-import           Haxl.Core                   hiding (Request, catch)-import           Network.HTTP.Client         hiding (Proxy)-import           Network.HTTP.Media-import           Network.HTTP.Types-import qualified Network.HTTP.Types.Header   as HTTP-import           Network.URI-import           Servant.API.ContentTypes-import           Servant.Common.BaseUrl.Haxl-import           Servant.Common.Text--import qualified Network.HTTP.Client         as Client--data ServantError-  = FailureResponse-    { responseStatus      :: Status-    , responseContentType :: MediaType-    , responseBody        :: ByteString-    }-  | DecodeFailure-    { decodeError         :: String-    , responseContentType :: MediaType-    , responseBody        :: ByteString-    }-  | UnsupportedContentType-    { responseContentType :: MediaType-    , responseBody        :: ByteString-    }-  | ConnectionError-    { connectionError :: HttpException-    }-  | InvalidContentTypeHeader-    { responseContentTypeHeader :: ByteString-    , responseBody              :: ByteString-    }-  deriving (Show)--instance Exception ServantError where--data Req = Req-  { reqPath   :: String-  , qs        :: QueryText-  , reqBody   :: Maybe (ByteString, MediaType)-  , reqAccept :: [MediaType]-  , headers   :: [(String, Text)]-  } deriving (Show, Eq, Ord, Generic)--instance Hashable Req where-  hashWithSalt s (Req p q b a h) = hashWithSalt s (p, q, bHash, aHash, h)-    where-      hashMediaType m = hashWithSalt s (show m)-      bHash = fmap (fmap hashMediaType) b-      aHash = fmap hashMediaType a--data WantedStatusCodes = AllCodes | SelectCodes [Int]-  deriving (Show, Eq, Ord, Generic)--instance Hashable WantedStatusCodes where-  hashWithSalt s AllCodes = hashWithSalt s (0::Int)-  hashWithSalt s (SelectCodes codes) = hashWithSalt s (1::Int, codes)--defReq :: Req-defReq = Req "" [] Nothing [] []--appendToPath :: String -> Req -> Req-appendToPath p req =-  req { reqPath = reqPath req ++ "/" ++ p }--appendToMatrixParams :: String-                     -> Maybe String-                     -> Req-                     -> Req-appendToMatrixParams pname pvalue req =-  req { reqPath = reqPath req ++ ";" ++ pname ++ maybe "" ("=" ++) pvalue }--appendToQueryString :: Text       -- ^ param name-                    -> Maybe Text -- ^ param value-                    -> Req-                    -> Req-appendToQueryString pname pvalue req =-  req { qs = qs req ++ [(pname, pvalue)]-      }--addHeader :: ToText a => String -> a -> Req -> Req-addHeader name val req = req { headers = headers req-                                      ++ [(name, toText val)]-                             }--setRQBody :: ByteString -> MediaType -> Req -> Req-setRQBody b t req = req { reqBody = Just (b, t) }--reqToRequest :: MonadThrow m => Req -> BaseUrl -> m Request-reqToRequest req (BaseUrl reqScheme reqHost reqPort) =-    (setheaders . setAccept . setrqb . setQS) <$> parseUrl url--  where url = show $ nullURI { uriScheme = case reqScheme of-                                  Http  -> "http:"-                                  Https -> "https:"-                             , uriAuthority = Just-                                 URIAuth { uriUserInfo = ""-                                         , uriRegName = reqHost-                                         , uriPort = ":" ++ show reqPort-                                         }-                             , uriPath = reqPath req-                             }--        setrqb r = case reqBody req of-                     Nothing -> r-                     Just (b,t) -> r { requestBody = RequestBodyLBS 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)----- * performing requests--displayHttpRequest :: Method -> String-displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"---performRequest_ :: Manager -> Method -> Req -> WantedStatusCodes -> BaseUrl-               -> EitherT ServantError IO ( Int, ByteString, MediaType-                                          , [HTTP.Header], Response ByteString)-performRequest_ manager reqMethod req wantedStatus reqHost = do-  partialRequest <- liftIO $ reqToRequest req reqHost--  let request = partialRequest { Client.method = reqMethod-                               , checkStatus = \ _status _headers _cookies -> Nothing-                               }--  eResponse <- liftIO $ catchHttpException $ Client.httpLbs request manager-  case eResponse of-    Left err ->-      left $ ConnectionError err--    Right response -> do-      let status = Client.responseStatus response-          body = Client.responseBody response-          hrds = 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 -> left $ InvalidContentTypeHeader (cs t) body-                   Just t' -> pure t'-      unless (wantedStatus `wants` status_code) $-        left $ FailureResponse status ct body-      return (status_code, body, ct, hrds, response)-      where-        wants AllCodes _ = True-        wants (SelectCodes codes) status_code = status_code `elem` codes--catchHttpException :: IO a -> IO (Either HttpException a)-catchHttpException action =-  catch (Right <$> action) (pure . Left)--data ServantRequest a where-  ServantRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl ->-    ServantRequest (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)--deriving instance Show (ServantRequest a)-deriving instance Eq (ServantRequest a)--instance Show1 ServantRequest where-  show1 = show--instance Hashable (ServantRequest a) where-  hashWithSalt s (ServantRequest m r w h) = hashWithSalt s (m, r, w, h)--instance StateKey ServantRequest where-  data State ServantRequest = ServantRequestState Int Manager--instance DataSourceName ServantRequest where-  dataSourceName _ = "ServantRequest"--instance DataSource () ServantRequest where-  fetch (ServantRequestState numThreads manager) _ () requests = AsyncFetch $ \inner -> do-    sem <- newQSem numThreads-    asyncs <- mapM (handler sem) requests-    inner-    mapM_ wait asyncs-    where-      handler :: QSem -> BlockedFetch ServantRequest -> IO (Async ())-      handler sem (BlockedFetch ((ServantRequest met req wantedStatus reqHost) :: ServantRequest a) rvar) =-        async $ bracket_ (waitQSem sem) (signalQSem sem) $ do-          e <- runEitherT $ performRequest_ manager met req wantedStatus reqHost-          case e of-            Left err -> putFailure rvar err-            Right a -> putSuccess rvar a-          return ()--performRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () (Int, ByteString, MediaType, [HTTP.Header], Response ByteString)-performRequest m r w h = dataFetch $ ServantRequest m r w h--performRequestCT :: MimeUnrender ct result =>-  Proxy ct -> Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () ([HTTP.Header], result)-performRequestCT ct reqMethod req wantedStatus reqHost = do-  let acceptCT = contentType ct-  (_status, respBody, respCT, hrds, _response) <--    performRequest reqMethod (req { reqAccept = [acceptCT] }) wantedStatus reqHost-  unless (matches respCT acceptCT) $ throwM $ UnsupportedContentType respCT respBody-  case mimeUnrender ct respBody of-    Left err -> throwM $ DecodeFailure err respCT respBody-    Right val -> return (hrds, val)--performRequestNoBody :: Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () ()-performRequestNoBody reqMethod req wantedStatus reqHost = do-  _ <- performRequest reqMethod req wantedStatus reqHost-  return ()
+ src/Servant/Haxl/Client.hs view
@@ -0,0 +1,619 @@+{-# LANGUAGE CPP                  #-}+{-# LANGUAGE DataKinds            #-}+{-# LANGUAGE FlexibleContexts     #-}+{-# LANGUAGE FlexibleInstances    #-}+{-# LANGUAGE InstanceSigs         #-}+{-# LANGUAGE OverloadedStrings    #-}+{-# LANGUAGE ScopedTypeVariables  #-}+{-# LANGUAGE TypeFamilies         #-}+{-# LANGUAGE TypeOperators        #-}+{-# LANGUAGE UndecidableInstances #-}+-- | This module provides 'client' which can automatically generate+-- querying functions for each endpoint just from the type representing your+-- API.+module Servant.Haxl.Client+  ( client+  , initServantClientState+  , HasClient(..)+  , ServantError(..)+  , ServantResponse+  , ServantConnectionError+  , module Servant.Haxl.Client.BaseUrl+  ) where++import           Control.Monad+import           Data.ByteString.Lazy       (ByteString)+import           Data.List+import           Data.Proxy+import           Data.String.Conversions+import           Data.Text                  (unpack)+import           GHC.TypeLits+import           Haxl.Core                  (GenHaxl)+import           Network.HTTP.Media+import qualified Network.HTTP.Types         as H+import qualified Network.HTTP.Types.Header  as HTTP+import           Servant.API+import           Servant.Haxl.Client.Types+import           Servant.Haxl.Client.Req+import           Servant.Haxl.Client.BaseUrl++-- * 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 Book -- POST /books+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getAllBooks :: EitherT String IO [Book]+-- > postNewBook :: Book -> EitherT String IO Book+-- > (getAllBooks :<|> postNewBook) = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+client :: HasClient layout => Proxy layout -> BaseUrl -> Client layout+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 layout where+  type Client layout :: *+  clientWithRoute :: Proxy layout -> Req -> BaseUrl -> Client layout++{-type Client layout = Client layout-}++-- | 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 :: EitherT String IO [Book]+-- > postNewBook :: Book -> EitherT String IO Book+-- > (getAllBooks :<|> postNewBook) = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+instance (HasClient a, HasClient b) => HasClient (a :<|> b) where+  type Client (a :<|> b) = Client a :<|> Client b+  clientWithRoute Proxy req baseurl =+    clientWithRoute (Proxy :: Proxy a) req baseurl :<|>+    clientWithRoute (Proxy :: Proxy b) req baseurl++-- | 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 'ToText' instance for your type.+--+-- Example:+--+-- > type MyApi = "books" :> Capture "isbn" Text :> Get '[JSON] Book+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBook :: Text -> EitherT String IO Book+-- > getBook = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- then you can just use "getBook" to query that endpoint+instance (KnownSymbol capture, ToText a, HasClient sublayout)+      => HasClient (Capture capture a :> sublayout) where++  type Client (Capture capture a :> sublayout) =+    a -> Client sublayout++  clientWithRoute Proxy req baseurl val =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (appendToPath p req)+                    baseurl++    where p = unpack (toText val)++-- | If you have a 'Delete' endpoint in your API, the client+-- side querying function that is created when calling 'client'+-- will just require an argument that specifies the scheme, host+-- and port to send the request to.+instance {-# OVERLAPPABLE #-}+  -- See https://downloads.haskell.org/~ghc/7.8.2/docs/html/users_guide/type-class-extensions.html#undecidable-instances+  (MimeUnrender ct a, cts' ~ (ct ': cts)) => HasClient (Delete cts' a) where+  type Client (Delete cts' a) = GenHaxl () a+  clientWithRoute Proxy req baseurl =+    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodDelete req (SelectCodes [200, 202]) baseurl++-- | If you have a 'Delete xs ()' endpoint, the client expects a 204 No Content+-- HTTP header.+instance {-# OVERLAPPING #-}+  HasClient (Delete cts ()) where+  type Client (Delete cts ()) = GenHaxl () ()+  clientWithRoute Proxy req baseurl =+    void $ performRequestNoBody H.methodDelete req (SelectCodes [204]) baseurl++-- | If you have a 'Delete xs (Headers ls x)' endpoint, the client expects the+-- corresponding headers.+instance {-# OVERLAPPING #-}+  -- See https://downloads.haskell.org/~ghc/7.8.2/docs/html/users_guide/type-class-extensions.html#undecidable-instances+  ( MimeUnrender ct a, BuildHeadersTo ls, cts' ~ (ct ': cts)+  ) => HasClient (Delete cts' (Headers ls a)) where+  type Client (Delete cts' (Headers ls a)) = GenHaxl () (Headers ls a)+  clientWithRoute Proxy req baseurl = do+    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodDelete req (SelectCodes [200, 202]) baseurl+    return Headers { getResponse = resp+                   , getHeadersHList = buildHeadersTo hdrs+                   }++-- | If you have a 'Get' endpoint in your API, the client+-- side querying function that is created when calling 'client'+-- will just require an argument that specifies the scheme, host+-- and port to send the request to.+instance {-# OVERLAPPABLE #-}+  (MimeUnrender ct result) => HasClient (Get (ct ': cts) result) where+  type Client (Get (ct ': cts) result) = GenHaxl () result+  clientWithRoute Proxy req baseurl =+    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodGet req (SelectCodes [200, 203]) baseurl++-- | If you have a 'Get xs ()' endpoint, the client expects a 204 No Content+-- HTTP status.+instance {-# OVERLAPPING #-}+  HasClient (Get (ct ': cts) ()) where+  type Client (Get (ct ': cts) ()) = GenHaxl () ()+  clientWithRoute Proxy req =+    performRequestNoBody H.methodGet req (SelectCodes [204])++-- | If you have a 'Get xs (Headers ls x)' endpoint, the client expects the+-- corresponding headers.+instance {-# OVERLAPPING #-}+  ( MimeUnrender ct a, BuildHeadersTo ls+  ) => HasClient (Get (ct ': cts) (Headers ls a)) where+  type Client (Get (ct ': cts) (Headers ls a)) = GenHaxl () (Headers ls a)+  clientWithRoute Proxy req baseurl = do+    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodGet req (SelectCodes [200, 203, 204]) baseurl+    return Headers { getResponse = resp+                   , 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 'ToText' instance.+--+-- Example:+--+-- > newtype Referer = Referer { referrer :: Text }+-- >   deriving (Eq, Show, Generic, FromText, ToText)+-- >+-- >            -- GET /view-my-referer+-- > type MyApi = "view-my-referer" :> Header "Referer" Referer :> Get '[JSON] Referer+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > viewReferer :: Maybe Referer -> EitherT String IO Book+-- > viewReferer = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- then you can just use "viewRefer" to query that endpoint+-- > -- specifying Nothing or e.g Just "http://haskell.org/" as arguments+instance (KnownSymbol sym, ToText a, HasClient sublayout)+      => HasClient (Header sym a :> sublayout) where++  type Client (Header sym a :> sublayout) =+    Maybe a -> Client sublayout++  clientWithRoute Proxy req baseurl mval =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (maybe req+                           (\value -> Servant.Haxl.Client.Req.addHeader hname value req)+                           mval+                    )+                    baseurl++    where hname = symbolVal (Proxy :: Proxy sym)++-- | If you have a 'Post' endpoint in your API, the client+-- side querying function that is created when calling 'client'+-- will just require an argument that specifies the scheme, host+-- and port to send the request to.+instance {-# OVERLAPPABLE #-}+  (MimeUnrender ct a) => HasClient (Post (ct ': cts) a) where+  type Client (Post (ct ': cts) a) = GenHaxl () a+  clientWithRoute Proxy req baseurl =+    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPost req (SelectCodes [200, 201, 202]) baseurl++-- | If you have a 'Post xs ()' endpoint, the client expects a 204 No Content+-- HTTP header.+instance {-# OVERLAPPING #-}+  HasClient (Post (ct ': cts) ()) where+  type Client (Post (ct ': cts) ()) = GenHaxl () ()+  clientWithRoute Proxy req baseurl =+    void $ performRequestNoBody H.methodPost req (SelectCodes [204]) baseurl++-- | If you have a 'Post xs (Headers ls x)' endpoint, the client expects the+-- corresponding headers.+instance {-# OVERLAPPING #-}+  ( MimeUnrender ct a, BuildHeadersTo ls+  ) => HasClient (Post (ct ': cts) (Headers ls a)) where+  type Client (Post (ct ': cts) (Headers ls a)) = GenHaxl () (Headers ls a)+  clientWithRoute Proxy req baseurl = do+    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPost req (SelectCodes [200, 201, 202]) baseurl+    return Headers { getResponse = resp+                   , getHeadersHList = buildHeadersTo hdrs+                   }++-- | If you have a 'Put' endpoint in your API, the client+-- side querying function that is created when calling 'client'+-- will just require an argument that specifies the scheme, host+-- and port to send the request to.+instance {-# OVERLAPPABLE #-}+  (MimeUnrender ct a) => HasClient (Put (ct ': cts) a) where+  type Client (Put (ct ': cts) a) = GenHaxl () a+  clientWithRoute Proxy req baseurl =+    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPut req (SelectCodes [200,201]) baseurl++-- | If you have a 'Put xs ()' endpoint, the client expects a 204 No Content+-- HTTP header.+instance {-# OVERLAPPING #-}+  HasClient (Put (ct ': cts) ()) where+  type Client (Put (ct ': cts) ()) = GenHaxl () ()+  clientWithRoute Proxy req baseurl =+    void $ performRequestNoBody H.methodPut req (SelectCodes [204]) baseurl++-- | If you have a 'Put xs (Headers ls x)' endpoint, the client expects the+-- corresponding headers.+instance {-# OVERLAPPING #-}+  ( MimeUnrender ct a, BuildHeadersTo ls+  ) => HasClient (Put (ct ': cts) (Headers ls a)) where+  type Client (Put (ct ': cts) (Headers ls a)) = GenHaxl () (Headers ls a)+  clientWithRoute Proxy req baseurl = do+    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPut req (SelectCodes [200, 201]) baseurl+    return Headers { getResponse = resp+                   , getHeadersHList = buildHeadersTo hdrs+                   }++-- | If you have a 'Patch' endpoint in your API, the client+-- side querying function that is created when calling 'client'+-- will just require an argument that specifies the scheme, host+-- and port to send the request to.+instance {-# OVERLAPPABLE #-}+  (MimeUnrender ct a) => HasClient (Patch (ct ': cts) a) where+  type Client (Patch (ct ': cts) a) = GenHaxl () a+  clientWithRoute Proxy req baseurl =+    snd <$> performRequestCT (Proxy :: Proxy ct) H.methodPatch req (SelectCodes [200,201]) baseurl++-- | If you have a 'Patch xs ()' endpoint, the client expects a 204 No Content+-- HTTP header.+instance {-# OVERLAPPING #-}+  HasClient (Patch (ct ': cts) ()) where+  type Client (Patch (ct ': cts) ()) = GenHaxl () ()+  clientWithRoute Proxy req baseurl =+    void $ performRequestNoBody H.methodPatch req (SelectCodes [204]) baseurl++-- | If you have a 'Patch xs (Headers ls x)' endpoint, the client expects the+-- corresponding headers.+instance {-# OVERLAPPING #-}+  ( MimeUnrender ct a, BuildHeadersTo ls+  ) => HasClient (Patch (ct ': cts) (Headers ls a)) where+  type Client (Patch (ct ': cts) (Headers ls a)) = GenHaxl () (Headers ls a)+  clientWithRoute Proxy req baseurl = do+    (hdrs, resp) <- performRequestCT (Proxy :: Proxy ct) H.methodPatch req (SelectCodes [200, 201, 204]) baseurl+    return Headers { getResponse = resp+                   , getHeadersHList = buildHeadersTo hdrs+                   }++-- | 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 'ToText' instance for your type.+--+-- Example:+--+-- > type MyApi = "books" :> QueryParam "author" Text :> Get '[JSON] [Book]+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBooksBy :: Maybe Text -> EitherT String IO [Book]+-- > getBooksBy = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- 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, ToText a, HasClient sublayout)+      => HasClient (QueryParam sym a :> sublayout) where++  type Client (QueryParam sym a :> sublayout) =+    Maybe a -> Client sublayout++  -- if mparam = Nothing, we don't add it to the query string+  clientWithRoute Proxy req baseurl mparam =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (maybe req+                           (flip (appendToQueryString pname) req . Just)+                           mparamText+                    )+                    baseurl++    where pname  = cs pname'+          pname' = symbolVal (Proxy :: Proxy sym)+          mparamText = fmap toText 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 'ToText' instance for your type.+--+-- Example:+--+-- > type MyApi = "books" :> QueryParams "authors" Text :> Get '[JSON] [Book]+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBooksBy :: [Text] -> EitherT String IO [Book]+-- > getBooksBy = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- 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, ToText a, HasClient sublayout)+      => HasClient (QueryParams sym a :> sublayout) where++  type Client (QueryParams sym a :> sublayout) =+    [a] -> Client sublayout++  clientWithRoute Proxy req baseurl paramlist =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (foldl' (\ req' -> maybe req' (flip (appendToQueryString pname) req' . Just))+                            req+                            paramlist'+                    )+                    baseurl++    where pname  = cs pname'+          pname' = symbolVal (Proxy :: Proxy sym)+          paramlist' = map (Just . toText) 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 -> EitherT String IO [Book]+-- > getBooks = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- 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 sublayout)+      => HasClient (QueryFlag sym :> sublayout) where++  type Client (QueryFlag sym :> sublayout) =+    Bool -> Client sublayout++  clientWithRoute Proxy req baseurl flag =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (if flag+                       then appendToQueryString paramname Nothing req+                       else req+                    )+                    baseurl++    where paramname = cs $ symbolVal (Proxy :: Proxy sym)++-- | If you use a 'MatrixParam' in one of your endpoints in your API,+-- the corresponding querying function will automatically take+-- an additional argument of the type specified by your 'MatrixParam',+-- 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 'ToText' instance for your type.+--+-- Example:+--+-- > type MyApi = "books" :> MatrixParam "author" Text :> Get '[JSON] [Book]+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBooksBy :: Maybe Text -> EitherT String IO [Book]+-- > getBooksBy = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- 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, ToText a, HasClient sublayout)+      => HasClient (MatrixParam sym a :> sublayout) where++  type Client (MatrixParam sym a :> sublayout) =+    Maybe a -> Client sublayout++  -- if mparam = Nothing, we don't add it to the query string+  clientWithRoute Proxy req baseurl mparam =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (maybe req+                           (flip (appendToMatrixParams pname . Just) req)+                           mparamText+                    )+                    baseurl++    where pname = symbolVal (Proxy :: Proxy sym)+          mparamText = fmap (cs . toText) mparam++-- | If you use a 'MatrixParams' 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+-- 'MatrixParams'.+--+-- 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 path segment string, under the+-- same matrix string parameter name.+--+-- You can control how values for your type are turned into text by+-- specifying a 'ToText' instance for your type.+--+-- Example:+--+-- > type MyApi = "books" :> MatrixParams "authors" Text :> Get '[JSON] [Book]+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBooksBy :: [Text] -> EitherT String IO [Book]+-- > getBooksBy = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- 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, ToText a, HasClient sublayout)+      => HasClient (MatrixParams sym a :> sublayout) where++  type Client (MatrixParams sym a :> sublayout) =+    [a] -> Client sublayout++  clientWithRoute Proxy req baseurl paramlist =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (foldl' (\ req' value -> maybe req' (flip (appendToMatrixParams pname) req' . Just . cs) value)+                            req+                            paramlist'+                    )+                    baseurl++    where pname  = cs pname'+          pname' = symbolVal (Proxy :: Proxy sym)+          paramlist' = map (Just . toText) paramlist++-- | If you use a 'MatrixFlag' 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 path segment.+--+-- Otherwise, this function will insert a value-less matrix parameter+-- under the name associated to your 'MatrixFlag'.+--+-- Example:+--+-- > type MyApi = "books" :> MatrixFlag "published" :> Get '[JSON] [Book]+-- >+-- > myApi :: Proxy MyApi+-- > myApi = Proxy+-- >+-- > getBooks :: Bool -> EitherT String IO [Book]+-- > getBooks = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- 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 sublayout)+      => HasClient (MatrixFlag sym :> sublayout) where++  type Client (MatrixFlag sym :> sublayout) =+    Bool -> Client sublayout++  clientWithRoute Proxy req baseurl flag =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (if flag+                       then appendToMatrixParams paramname Nothing req+                       else req+                    )+                    baseurl++    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 -> GenHaxl () (Int, ByteString, MediaType, [HTTP.Header], ServantResponse)++  clientWithRoute :: Proxy Raw -> Req -> BaseUrl -> Client Raw+  clientWithRoute Proxy req baseurl httpMethod = performRequest httpMethod req AllCodes baseurl++-- | 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 -> EitherT String IO Book+-- > addBook = client myApi host+-- >   where host = BaseUrl Http "localhost" 8080+-- > -- then you can just use "addBook" to query that endpoint+instance (MimeRender ct a, HasClient sublayout)+      => HasClient (ReqBody (ct ': cts) a :> sublayout) where++  type Client (ReqBody (ct ': cts) a :> sublayout) =+    a -> Client sublayout++  clientWithRoute Proxy req baseurl body =+    clientWithRoute (Proxy :: Proxy sublayout)+                    (let ctProxy = Proxy :: Proxy ct+                     in setRQBody (mimeRender ctProxy body)+                                  (contentType ctProxy)+                                  req+                    )+                    baseurl++-- | Make the querying function append @path@ to the request path.+instance (KnownSymbol path, HasClient sublayout) => HasClient (path :> sublayout) where+  type Client (path :> sublayout) = Client sublayout++  clientWithRoute Proxy req =+     clientWithRoute (Proxy :: Proxy sublayout) (appendToPath p req)++    where p = symbolVal (Proxy :: Proxy path)
+ src/Servant/Haxl/Client/BaseUrl.hs view
@@ -0,0 +1,45 @@+{-# LANGUAGE ViewPatterns #-}+module Servant.Haxl.Client.BaseUrl+  ( Scheme(..)+  , BaseUrl(..)+  , showBaseUrl+  , parseBaseUrl+  ) where++import           Data.List+import           Network.URI+import           Safe+import           Servant.Haxl.Client.Types+import           Text.Read++showBaseUrl :: BaseUrl -> String+showBaseUrl (BaseUrl urlscheme host port) =+  schemeString ++ "//" ++ host ++ portString+    where+      schemeString = case urlscheme of+        Http  -> "http:"+        Https -> "https:"+      portString = case (urlscheme, port) of+        (Http, 80) -> ""+        (Https, 443) -> ""+        _ -> ":" ++ show port++parseBaseUrl :: String -> Either String 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)))) "" "" "") ->+    Right (BaseUrl Http host port)+  Just (URI "http:" (Just (URIAuth "" host "")) "" "" "") ->+    Right (BaseUrl Http host 80)+  Just (URI "https:" (Just (URIAuth "" host (':' : (readMaybe -> Just port)))) "" "" "") ->+    Right (BaseUrl Https host port)+  Just (URI "https:" (Just (URIAuth "" host "")) "" "" "") ->+    Right (BaseUrl Https host 443)+  _ -> if "://" `isInfixOf` s+    then Left ("invalid base url: " ++ s)+    else parseBaseUrl ("http://" ++ s)+ where+  removeTrailingSlash str = case lastMay str of+    Just '/' -> init str+    _ -> str
+ src/Servant/Haxl/Client/Req.hs view
@@ -0,0 +1,95 @@+{-# LANGUAGE CPP                   #-}+{-# LANGUAGE GADTs                 #-}+{-# LANGUAGE MultiParamTypeClasses #-}+{-# LANGUAGE OverloadedStrings     #-}+{-# LANGUAGE ScopedTypeVariables   #-}+{-# LANGUAGE TypeFamilies          #-}+module Servant.Haxl.Client.Req+  ( Req+  , ServantError+  , ServantResponse+  , ServantConnectionError+  , defReq+  , appendToPath+  , appendToMatrixParams+  , appendToQueryString+  , addHeader+  , setRQBody+  , displayHttpRequest+  , initServantClientState+  , performRequest+  , performRequestCT+  , performRequestNoBody+  ) where++import           Control.Monad+import           Control.Monad.Catch+import           Data.ByteString.Lazy               hiding (elem, filter, map,+                                                     null, pack)+import           Data.Proxy+import           Data.String.Conversions+import           Data.Text                          (Text)+import           Haxl.Core                          hiding (Request, catch)+import           Network.HTTP.Media+import           Network.HTTP.Types+import qualified Network.HTTP.Types.Header          as HTTP+import           Servant.API.ContentTypes+import           Servant.Common.Text+import           Servant.Haxl.Client.BaseUrl+import           Servant.Haxl.Client.Internal+import           Servant.Haxl.Client.Internal.Error+import           Servant.Haxl.Client.Types++defReq :: Req+defReq = Req "" [] Nothing [] []++appendToPath :: String -> Req -> Req+appendToPath p req =+  req { reqPath = reqPath req ++ "/" ++ p }++appendToMatrixParams :: String+                     -> Maybe String+                     -> Req+                     -> Req+appendToMatrixParams pname pvalue req =+  req { reqPath = reqPath req ++ ";" ++ pname ++ maybe "" ("=" ++) pvalue }++appendToQueryString :: Text       -- ^ param name+                    -> Maybe Text -- ^ param value+                    -> Req+                    -> Req+appendToQueryString pname pvalue req =+  req { qs = qs req ++ [(pname, pvalue)]+      }++addHeader :: ToText a => String -> a -> Req -> Req+addHeader name val req = req { headers = headers req+                                      ++ [(name, toText val)]+                             }++setRQBody :: ByteString -> MediaType -> Req -> Req+setRQBody b t req = req { reqBody = Just (b, t) }++-- * performing requests++displayHttpRequest :: Method -> String+displayHttpRequest httpmethod = "HTTP " ++ cs httpmethod ++ " request"++performRequest :: Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () (Int, ByteString, MediaType, [HTTP.Header], ServantResponse)+performRequest m r w h = dataFetch $ ServantRequest m r w h++performRequestCT :: MimeUnrender ct result =>+  Proxy ct -> Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () ([HTTP.Header], result)+performRequestCT ct reqMethod req wantedStatus reqHost = do+  let acceptCT = contentType ct+  (_status, respBody, respCT, hrds, _response) <-+    performRequest reqMethod (req { reqAccept = [acceptCT] }) wantedStatus reqHost+  unless (matches respCT acceptCT) $ throwM $ UnsupportedContentType respCT respBody+  case mimeUnrender ct respBody of+    Left err -> throwM $ DecodeFailure err respCT respBody+    Right val -> return (hrds, val)++performRequestNoBody :: Method -> Req -> WantedStatusCodes -> BaseUrl -> GenHaxl () ()+performRequestNoBody reqMethod req wantedStatus reqHost = do+  _ <- performRequest reqMethod req wantedStatus reqHost+  return ()
+ src/Servant/Haxl/Client/Types.hs view
@@ -0,0 +1,88 @@+{-# LANGUAGE DeriveGeneric #-}++module Servant.Haxl.Client.Types+  ( ServantError(..)+  , ServantConnectionError+  , Req(..)+  , WantedStatusCodes(..)+  , Scheme(..)+  , BaseUrl(..)+  ) where++import           Control.Exception+import           Data.ByteString.Lazy+import           Data.Hashable+import           Data.Text+import           GHC.Generics+import           Network.HTTP.Media+import           Network.HTTP.Types+import           Servant.Haxl.Client.Internal.Error++data ServantError+  = FailureResponse+    { responseStatus      :: Status+    , responseContentType :: MediaType+    , responseBody        :: ByteString+    }+  | DecodeFailure+    { decodeError         :: String+    , responseContentType :: MediaType+    , responseBody        :: ByteString+    }+  | UnsupportedContentType+    { responseContentType :: MediaType+    , responseBody        :: ByteString+    }+  | ConnectionError+    { connectionError :: ServantConnectionError+    }+  | InvalidContentTypeHeader+    { responseContentTypeHeader :: ByteString+    , responseBody              :: ByteString+    }+  deriving (Show)++instance Exception ServantError where++data Req = Req+  { reqPath   :: String+  , qs        :: QueryText+  , reqBody   :: Maybe (ByteString, MediaType)+  , reqAccept :: [MediaType]+  , headers   :: [(String, Text)]+  } deriving (Show, Eq, Ord, Generic)++instance Hashable Req where+  hashWithSalt s (Req p q b a h) = hashWithSalt s (p, q, bHash, aHash, h)+    where+      hashMediaType m = hashWithSalt s (show m)+      bHash = fmap (fmap hashMediaType) b+      aHash = fmap hashMediaType a++data WantedStatusCodes = AllCodes | SelectCodes [Int]+  deriving (Show, Eq, Ord, Generic)++instance Hashable WantedStatusCodes where+  hashWithSalt s AllCodes = hashWithSalt s (0::Int)+  hashWithSalt s (SelectCodes codes) = hashWithSalt s (1::Int, codes)++-- | URI scheme to use+data Scheme =+    Http  -- ^ http://+  | Https -- ^ https://+  deriving (Show, Eq, Ord, Generic)++instance Hashable Scheme where+  hashWithSalt s Http = hashWithSalt s (0 :: Int)+  hashWithSalt s Https = hashWithSalt s (1 :: Int)++-- | 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)+  } deriving (Show, Eq, Ord, Generic)++instance Hashable BaseUrl where+  hashWithSalt s (BaseUrl sc h p) = hashWithSalt s (sc, h, p)
test/Servant/ClientSpec.hs view
@@ -12,25 +12,25 @@  module Servant.ClientSpec where -import qualified Control.Arrow              as Arrow+import qualified Control.Arrow                      as Arrow import           Control.Concurrent import           Control.Exception import           Control.Monad.Trans.Either import           Data.Aeson-import           Data.ByteString.Lazy       (ByteString)+import           Data.ByteString.Lazy               (ByteString) import           Data.Char-import           Data.Foldable              (forM_)+import           Data.Foldable                      (forM_) import           Data.Monoid import           Data.Proxy-import qualified Data.Text                  as T+import qualified Data.Text                          as T import           GHC.Generics-import           Haxl.Core                  hiding (try)-import qualified Network.HTTP.Client        as C+import           Haxl.Core                          hiding (try)+import qualified Network.HTTP.Client                as C import           Network.HTTP.Media-import           Network.HTTP.Types         hiding (Header)-import qualified Network.HTTP.Types         as HTTP+import           Network.HTTP.Types                 hiding (Header)+import qualified Network.HTTP.Types                 as HTTP import           Network.Socket-import           Network.Wai                hiding (Response)+import           Network.Wai                        hiding (Response) import           Network.Wai.Handler.Warp import           Test.Hspec import           Test.Hspec.QuickCheck@@ -38,7 +38,9 @@ import           Test.QuickCheck  import           Servant.API-import           Servant.Client.Haxl+import           Servant.Haxl.Client+import           Servant.Haxl.Client.Internal+import           Servant.Haxl.Client.Internal.Error import           Servant.Server  -- * test data types@@ -67,6 +69,7 @@         return $ Person (T.unpack n) (read $ T.unpack a)  deriving instance Eq ServantError+deriving instance Eq ServantConnectionError  instance Eq C.HttpException where   a == b = show a == show b@@ -167,8 +170,8 @@       getMatrixParam :: Maybe String -> GenHaxl () Person       getMatrixParams :: [String] -> GenHaxl () [Person]       getMatrixFlag :: Bool -> GenHaxl () Bool-      getRawSuccess :: Method -> GenHaxl () (Int, ByteString, MediaType, [HTTP.Header], C.Response ByteString)-      getRawFailure :: Method -> GenHaxl () (Int, ByteString, MediaType, [HTTP.Header], C.Response ByteString)+      getRawSuccess :: Method -> GenHaxl () (Int, ByteString, MediaType, [HTTP.Header], ServantResponse)+      getRawFailure :: Method -> GenHaxl () (Int, ByteString, MediaType, [HTTP.Header], ServantResponse)       getMultiple :: String -> Maybe Int -> Bool -> [(String, [Rational])] -> GenHaxl () (String, Maybe Int, Bool, [(String, [Rational])])       getRespHeaders :: GenHaxl () (Headers TestHeaders Bool)       getDeleteContentType :: GenHaxl () ()@@ -241,7 +244,7 @@       res <- testHaxl (getRawSuccess methodGet)       case res of         Left e -> assertFailure $ show e-        Right (code, body, ct, _, response) -> do+        Right (code, body, ct, _, (ServantResponse response)) -> do           (code, body, ct) `shouldBe` (200, "rawSuccess", "application"//"octet-stream")           C.responseBody response `shouldBe` body           C.responseStatus response `shouldBe` ok200@@ -250,7 +253,7 @@       res <- testHaxl (getRawFailure methodGet)       case res of         Left e -> assertFailure $ show e-        Right (code, body, ct, _, response) -> do+        Right (code, body, ct, _, (ServantResponse response)) -> do           (code, body, ct) `shouldBe` (400, "rawFailure", "application"//"octet-stream")           C.responseBody response `shouldBe` body           C.responseStatus response `shouldBe` badRequest400@@ -319,7 +322,7 @@       it "reports ConnectionError" $ do         Left res <- testHaxl getGetWrongHost         case res of-          ConnectionError (C.FailedConnectionException2 "127.0.0.1" 19872 False _) -> return ()+          ConnectionError (ServantConnectionError (C.FailedConnectionException2 "127.0.0.1" 19872 False _)) -> return ()           _ -> fail $ "expected ConnectionError, but got " <> show res        it "reports UnsupportedContentType" $ do
test/Servant/Common/BaseUrlSpec.hs view
@@ -6,7 +6,7 @@ import           Test.Hspec import           Test.QuickCheck -import           Servant.Common.BaseUrl.Haxl+import           Servant.Haxl.Client.BaseUrl  spec :: Spec spec = do