forked from aristidb/aws
/
Aws.hs
166 lines (146 loc) · 5.61 KB
/
Aws.hs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
{-# LANGUAGE FlexibleContexts, OverloadedStrings #-}
module Aws.Aws
where
import Aws.Credentials
import Aws.Http
import Aws.Query
import Aws.Response
import Aws.S3.Info
import Aws.Ses.Info
import Aws.Signature
import Aws.SimpleDb.Info
import Aws.Sqs.Info
import Aws.Transaction
import Control.Applicative
import Control.Monad.Trans (liftIO)
import Data.Attempt (attemptIO)
import Data.Conduit (runResourceT)
import Data.IORef
import Data.Monoid
import System.IO (stderr)
import qualified Control.Exception as E
import qualified Data.ByteString as B
import qualified Data.Text as T
import qualified Data.Text.IO as T
import qualified Network.HTTP.Conduit as HTTP
data LogLevel
= Debug
| Info
| Warning
| Error
deriving (Show, Eq, Ord)
data Configuration
= Configuration {
timeInfo :: TimeInfo
, credentials :: Credentials
, sdbInfo :: SdbInfo
, sdbInfoUri :: SdbInfo
, s3Info :: S3Info
, s3InfoUri :: S3Info
, sqsInfo :: SqsInfo
, sqsInfoUri :: SqsInfo
, sesInfo :: SesInfo
, sesInfoUri :: SesInfo
, logger :: LogLevel -> T.Text -> IO ()
}
defaultLog :: LogLevel -> LogLevel -> T.Text -> IO ()
defaultLog minLevel lev t | lev >= minLevel = T.hPutStrLn stderr $ T.concat [T.pack $ show lev, ": ", t]
| otherwise = return ()
class ConfigurationFetch a where
configurationFetch :: Configuration -> a
configurationFetchUri :: Configuration -> a
configurationFetchUri = configurationFetch
instance ConfigurationFetch () where
configurationFetch _ = ()
instance ConfigurationFetch SdbInfo where
configurationFetch = sdbInfo
configurationFetchUri = sdbInfoUri
instance ConfigurationFetch S3Info where
configurationFetch = s3Info
configurationFetchUri = s3InfoUri
instance ConfigurationFetch SqsInfo where
configurationFetch = sqsInfo
configurationFetchUri = sqsInfoUri
instance ConfigurationFetch SesInfo where
configurationFetch = sesInfo
configurationFetchUri = sesInfoUri
baseConfiguration :: IO Configuration
baseConfiguration = do
Just cr <- loadCredentialsDefault
return Configuration {
timeInfo = Timestamp
, credentials = cr
, sdbInfo = sdbHttpsPost sdbUsEast
, sdbInfoUri = sdbHttpsGet sdbUsEast
, s3Info = s3 HTTP s3EndpointUsClassic False
, s3InfoUri = s3 HTTP s3EndpointUsClassic True
, sqsInfo = sqs HTTP sqsEndpointUsClassic False
, sqsInfoUri = sqs HTTP sqsEndpointUsClassic True
, sesInfo = sesHttpsPost sesUsEast
, sesInfoUri = sesHttpsGet sesUsEast
, logger = defaultLog Warning
}
-- TODO: better error handling when credentials cannot be loaded
debugConfiguration :: IO Configuration
debugConfiguration = do
c <- baseConfiguration
return c {
sdbInfo = sdbHttpPost sdbUsEast
, sdbInfoUri = sdbHttpGet sdbUsEast
, logger = defaultLog Debug
}
aws :: (Transaction r a
, ConfigurationFetch (Info r))
=> Configuration -> HTTP.Manager -> r -> IO (Response (ResponseMetadata a) a)
aws = unsafeAws
awsRef :: (Transaction r a
, ConfigurationFetch (Info r))
=> Configuration -> HTTP.Manager -> IORef (ResponseMetadata a) -> r -> IO a
awsRef = unsafeAwsRef
simpleAws :: (Transaction r a
, ConfigurationFetch (Info r))
=> Configuration -> r -> IO (Response (ResponseMetadata a) a)
simpleAws cfg request = HTTP.withManager $ \manager -> liftIO $ aws cfg manager request
simpleAwsRef :: (Transaction r a
, ConfigurationFetch (Info r))
=> Configuration -> IORef (ResponseMetadata a) -> r -> IO a
simpleAwsRef cfg metadataRef request = HTTP.withManager $ \manager -> liftIO $ awsRef cfg manager metadataRef request
unsafeAws
:: (ResponseConsumer r a,
Monoid (ResponseMetadata a),
SignQuery r,
ConfigurationFetch (Info r)) =>
Configuration -> HTTP.Manager -> r -> IO (Response (ResponseMetadata a) a)
unsafeAws cfg manager request = do
metadataRef <- newIORef mempty
resp <- attemptIO (id :: E.SomeException -> E.SomeException) $
unsafeAwsRef cfg manager metadataRef request
metadata <- readIORef metadataRef
return $ Response metadata resp
unsafeAwsRef
:: (ResponseConsumer r a,
Monoid (ResponseMetadata a),
SignQuery r,
ConfigurationFetch (Info r)) =>
Configuration -> HTTP.Manager -> IORef (ResponseMetadata a) -> r -> IO a
unsafeAwsRef cfg manager metadataRef request = do
sd <- signatureData <$> timeInfo <*> credentials $ cfg
let info = configurationFetch cfg
let q = signQuery request info sd
logger cfg Debug $ T.pack $ "String to sign: " ++ show (sqStringToSign q)
let httpRequest = queryToHttpRequest q
resp <- runResourceT $ do
HTTP.Response status _ headers body <- HTTP.http httpRequest manager
responseConsumer request metadataRef status headers body
return resp
awsUri :: (SignQuery request
, ConfigurationFetch (Info request))
=> Configuration -> request -> IO B.ByteString
awsUri cfg request = do
let ti = timeInfo cfg
cr = credentials cfg
info = configurationFetchUri cfg
sd <- signatureData ti cr
let q = signQuery request info sd
logger cfg Debug $ T.pack $ "String to sign: " ++ show (sqStringToSign q)
return $ queryToUri q