bloodhound-amazonka-auth-0.1.2.0: src/Database/Bloodhound/Auth/Amazonka/Internal.hs
{-# LANGUAGE DeriveDataTypeable #-}
module Database.Bloodhound.Auth.Amazonka.Internal where
-------------------------------------------------------------------------------
import Control.Applicative as A
import Control.Exception
import Data.Time.Clock
import Data.Typeable
import Network.AWS.Data.Body
import Network.AWS.Data.ByteString
import Network.AWS.Data.Path
import Network.AWS.Data.Query
import qualified Network.AWS.Data.Query as A
import Network.AWS.ElasticSearch (elasticSearch)
import Network.AWS.Types (AuthEnv, Region, sgRequest,
sgSign)
import qualified Network.AWS.Types as A
import Network.HTTP.Client
import Network.HTTP.Types.Method (Method, parseMethod)
import URI.ByteString
-------------------------------------------------------------------------------
-- | Request hook to install into your 'BHEnv'. Does not handle
-- streaming request bodies, which should not be an issue for
-- Bloodhound. The exception cases handled by 'EsAmazonkaAuthError'
-- are truly exceptional and should probably be thrown.
--
-- @
-- env <- newEnv region Discover
-- let auth = env ^. envAuth
-- let hook req = withAuth auth $ \ae ->
-- either (liftIO . throwIO) return =<< amazonkaAuthHook ae region req
-- mgr <- newManager tlsManagerSettings
-- let bhe = (mkBHEnv server mgr) { bhRequestHook = hook }
-- @
amazonkaAuthHook :: AuthEnv -> Region -> Request -> IO (Either EsAmazonkaAuthError Request)
amazonkaAuthHook ae reg req = amazonkaAuthHook' ae reg req A.<$> getCurrentTime
-------------------------------------------------------------------------------
amazonkaAuthHook' :: AuthEnv -> Region -> Request -> UTCTime -> Either EsAmazonkaAuthError Request
amazonkaAuthHook' ae reg req now = toReq <$> toAwsRequest req reg
where algo = sgSign (A._svcSigner elasticSearch)
toReq req' = restoreTimeout (decodePath (sgRequest (algo req' ae reg now)))
-- We decode the path because for some reason AWS ES actually
-- doesn't want the path url encoded. If you do, it will
-- expect double-encoding for the canonical uri. If you
-- double, it will expect triple and so-on.
decodePath x = x { path = urlDecode True (path x)}
restoreTimeout x = x { responseTimeout = responseTimeout req }
-------------------------------------------------------------------------------
toAwsRequest :: Request -> Region -> Either EsAmazonkaAuthError (A.Request a)
toAwsRequest r reg = do
meth <- either (const (Left badMethod)) Right (parseMethod bsMeth)
rqb <- toRQBody (requestBody r)
q <- toQS (queryString r)
return (A.Request { A._rqService = svc
, A._rqMethod = meth
, A._rqPath = rawPath (path r)
, A._rqQuery = q
, A._rqHeaders = requestHeaders r
, A._rqBody = rqb})
where bsMeth = method r
badMethod = InvalidStdMethod bsMeth
svc = elasticSearch { A._svcEndpoint = const endpoint}
endpoint = requestEndpoint r reg
-------------------------------------------------------------------------------
toQS :: ByteString -> Either EsAmazonkaAuthError A.QueryString
toQS bs = case parseRelativeRef laxURIParserOptions bs of
Right rr -> Right (go (rrQuery rr))
Left _ -> Left MalformedQueryString
where go q = QList [ QPair k (QValue (Just v)) | (k, v) <- queryPairs q]
-------------------------------------------------------------------------------
requestEndpoint :: Request -> Region -> A.Endpoint
requestEndpoint r reg = A.Endpoint { A._endpointHost = host r
, A._endpointSecure = secure r
, A._endpointPort = port r
, A._endpointScope = toBS reg}
-------------------------------------------------------------------------------
-- | These edge cases shouldn't come up in normal operation. The best
-- course of action is probably to throw these as an exception.
data EsAmazonkaAuthError = InvalidStdMethod Method
| StreamingBodyNotSupported
| MalformedQueryString deriving (Show, Eq, Typeable)
instance Exception EsAmazonkaAuthError
-------------------------------------------------------------------------------
toRQBody :: RequestBody -> Either EsAmazonkaAuthError RqBody
toRQBody (RequestBodyLBS b) = Right (toBody b)
toRQBody (RequestBodyBS b) = Right (toBody b)
toRQBody _ = Left StreamingBodyNotSupported