module Pipes.Aws.S3.Download
(
fromS3
, fromS3'
, fromS3WithManager
, ContentRange(..)
, S3DownloadError(..)
) where
import Control.Monad (unless)
import Control.Exception (Exception)
import qualified Data.ByteString as BS
import Data.ByteString (ByteString)
import Pipes
import Pipes.Safe
import Network.HTTP.Types
import Network.HTTP.Client
import Network.HTTP.Client.TLS
import qualified Aws
import qualified Aws.Core as Aws
import qualified Aws.S3 as S3
import Pipes.Aws.S3.Types
data ContentRange = ContentRange { firstBytePos, lastBytePos :: Int }
deriving (Eq, Ord, Show)
data S3DownloadError = S3DownloadError Bucket Object Status
deriving (Show)
instance Exception S3DownloadError
fromS3 :: MonadSafe m
=> Bucket -> Object
-> Maybe ContentRange
-> Producer BS.ByteString m ()
fromS3 bucket object range = do
cfg <- liftIO Aws.baseConfiguration
fromS3' cfg Aws.defServiceConfig bucket object range
fromS3' :: MonadSafe m
=> Aws.Configuration
-> S3.S3Configuration Aws.NormalQuery
-> Bucket -> Object
-> Maybe ContentRange
-> Producer BS.ByteString m ()
fromS3' cfg s3cfg bucket object range = do
mgr <- liftIO $ newManager tlsManagerSettings
fromS3WithManager mgr cfg s3cfg bucket object range
fromS3WithManager
:: MonadSafe m
=> Manager
-> Aws.Configuration
-> S3.S3Configuration Aws.NormalQuery
-> Bucket -> Object
-> Maybe ContentRange
-> Producer BS.ByteString m ()
fromS3WithManager mgr cfg s3cfg (Bucket bucket) (Object object) range = do
let getObj = (S3.getObject bucket object) { S3.goResponseContentRange = fmap (\(ContentRange a b) -> (a,b)) range }
req <- liftIO $ buildRequest cfg s3cfg getObj
Pipes.Safe.bracket (liftIO $ responseOpen req mgr) (liftIO . responseClose) $ \resp ->
if statusIsSuccessful (responseStatus resp)
then from $ brRead $ responseBody resp
else throwM $ S3DownloadError (Bucket bucket) (Object object) (responseStatus resp)
from :: MonadIO m => IO ByteString -> Producer ByteString m ()
from io = go
where
go = do
bs <- liftIO io
unless (BS.null bs) $ do
yield bs
go
buildRequest :: (MonadIO m, Aws.Transaction r a)
=> Aws.Configuration
-> Aws.ServiceConfiguration r Aws.NormalQuery
-> r
-> m Request
buildRequest cfg scfg req = do
let cred = Aws.credentials cfg
sigData <- liftIO $ Aws.signatureData Aws.Timestamp cred
let signed = Aws.signQuery req scfg sigData
liftIO $ Aws.queryToHttpRequest signed