{-# LANGUAGE CPP #-}

module Aws.S3.Commands.GetObject
where

import           Aws.Core
import           Aws.S3.Core
import           Control.Applicative
import           Control.Monad.Trans.Resource (ResourceT)
import           Data.ByteString.Char8 ({- IsString -})
import qualified Data.ByteString.Char8 as B8
import qualified Data.ByteString.Lazy  as L
import qualified Data.Conduit          as C
import           Data.Conduit ((.|))
import qualified Data.Conduit.List     as CL
import           Data.Maybe
import qualified Data.Text             as T
import qualified Data.Text.Encoding    as T
import           Prelude
import qualified Network.HTTP.Conduit  as HTTP
import qualified Network.HTTP.Types    as HTTP

data GetObject
    = GetObject {
        GetObject -> Bucket
goBucket :: Bucket
      , GetObject -> Bucket
goObjectName :: Object
      , GetObject -> Maybe Bucket
goVersionId :: Maybe T.Text
      , GetObject -> Maybe Bucket
goResponseContentType :: Maybe T.Text
      , GetObject -> Maybe Bucket
goResponseContentLanguage :: Maybe T.Text
      , GetObject -> Maybe Bucket
goResponseExpires :: Maybe T.Text
      , GetObject -> Maybe Bucket
goResponseCacheControl :: Maybe T.Text
      , GetObject -> Maybe Bucket
goResponseContentDisposition :: Maybe T.Text
      , GetObject -> Maybe Bucket
goResponseContentEncoding :: Maybe T.Text
      , GetObject -> Maybe (Int, Int)
goResponseContentRange :: Maybe (Int,Int)
      , GetObject -> Maybe Bucket
goIfMatch :: Maybe T.Text
      -- ^ Return the object only if its entity tag (ETag, which is an md5sum of the content) is the same as the one specified; otherwise, catch a 'StatusCodeException' with a status of 412 precondition failed.
      , GetObject -> Maybe Bucket
goIfNoneMatch :: Maybe T.Text
      -- ^ Return the object only if its entity tag (ETag, which is an md5sum of the content) is different from the one specified; otherwise, catch a 'StatusCodeException' with a status of 304 not modified.
      }
  deriving (Int -> GetObject -> ShowS
[GetObject] -> ShowS
GetObject -> String
(Int -> GetObject -> ShowS)
-> (GetObject -> String)
-> ([GetObject] -> ShowS)
-> Show GetObject
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GetObject] -> ShowS
$cshowList :: [GetObject] -> ShowS
show :: GetObject -> String
$cshow :: GetObject -> String
showsPrec :: Int -> GetObject -> ShowS
$cshowsPrec :: Int -> GetObject -> ShowS
Show)

getObject :: Bucket -> T.Text -> GetObject
getObject :: Bucket -> Bucket -> GetObject
getObject Bucket
b Bucket
o = Bucket
-> Bucket
-> Maybe Bucket
-> Maybe Bucket
-> Maybe Bucket
-> Maybe Bucket
-> Maybe Bucket
-> Maybe Bucket
-> Maybe Bucket
-> Maybe (Int, Int)
-> Maybe Bucket
-> Maybe Bucket
-> GetObject
GetObject Bucket
b Bucket
o Maybe Bucket
forall a. Maybe a
Nothing Maybe Bucket
forall a. Maybe a
Nothing Maybe Bucket
forall a. Maybe a
Nothing Maybe Bucket
forall a. Maybe a
Nothing Maybe Bucket
forall a. Maybe a
Nothing Maybe Bucket
forall a. Maybe a
Nothing Maybe Bucket
forall a. Maybe a
Nothing Maybe (Int, Int)
forall a. Maybe a
Nothing Maybe Bucket
forall a. Maybe a
Nothing Maybe Bucket
forall a. Maybe a
Nothing

data GetObjectResponse
    = GetObjectResponse {
        GetObjectResponse -> ObjectMetadata
gorMetadata :: ObjectMetadata,
        GetObjectResponse
-> Response (ConduitM () ByteString (ResourceT IO) ())
gorResponse :: HTTP.Response (C.ConduitM () B8.ByteString (ResourceT IO) ())
      }

data GetObjectMemoryResponse
    = GetObjectMemoryResponse ObjectMetadata (HTTP.Response L.ByteString)
    deriving (Int -> GetObjectMemoryResponse -> ShowS
[GetObjectMemoryResponse] -> ShowS
GetObjectMemoryResponse -> String
(Int -> GetObjectMemoryResponse -> ShowS)
-> (GetObjectMemoryResponse -> String)
-> ([GetObjectMemoryResponse] -> ShowS)
-> Show GetObjectMemoryResponse
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
showList :: [GetObjectMemoryResponse] -> ShowS
$cshowList :: [GetObjectMemoryResponse] -> ShowS
show :: GetObjectMemoryResponse -> String
$cshow :: GetObjectMemoryResponse -> String
showsPrec :: Int -> GetObjectMemoryResponse -> ShowS
$cshowsPrec :: Int -> GetObjectMemoryResponse -> ShowS
Show)

-- | ServiceConfiguration: 'S3Configuration'
instance SignQuery GetObject where
    type ServiceConfiguration GetObject = S3Configuration
    signQuery :: GetObject
-> ServiceConfiguration GetObject queryType
-> SignatureData
-> SignedQuery
signQuery GetObject {Maybe (Int, Int)
Maybe Bucket
Bucket
goIfNoneMatch :: Maybe Bucket
goIfMatch :: Maybe Bucket
goResponseContentRange :: Maybe (Int, Int)
goResponseContentEncoding :: Maybe Bucket
goResponseContentDisposition :: Maybe Bucket
goResponseCacheControl :: Maybe Bucket
goResponseExpires :: Maybe Bucket
goResponseContentLanguage :: Maybe Bucket
goResponseContentType :: Maybe Bucket
goVersionId :: Maybe Bucket
goObjectName :: Bucket
goBucket :: Bucket
goIfNoneMatch :: GetObject -> Maybe Bucket
goIfMatch :: GetObject -> Maybe Bucket
goResponseContentRange :: GetObject -> Maybe (Int, Int)
goResponseContentEncoding :: GetObject -> Maybe Bucket
goResponseContentDisposition :: GetObject -> Maybe Bucket
goResponseCacheControl :: GetObject -> Maybe Bucket
goResponseExpires :: GetObject -> Maybe Bucket
goResponseContentLanguage :: GetObject -> Maybe Bucket
goResponseContentType :: GetObject -> Maybe Bucket
goVersionId :: GetObject -> Maybe Bucket
goObjectName :: GetObject -> Bucket
goBucket :: GetObject -> Bucket
..} = S3Query
-> S3Configuration queryType -> SignatureData -> SignedQuery
forall qt.
S3Query -> S3Configuration qt -> SignatureData -> SignedQuery
s3SignQuery S3Query :: Method
-> Maybe ByteString
-> Maybe ByteString
-> Query
-> Query
-> Maybe ByteString
-> Maybe (Digest MD5)
-> RequestHeaders
-> RequestHeaders
-> Maybe RequestBody
-> S3Query
S3Query {
                                   s3QMethod :: Method
s3QMethod = Method
Get
                                 , s3QBucket :: Maybe ByteString
s3QBucket = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Bucket -> ByteString
T.encodeUtf8 Bucket
goBucket
                                 , s3QObject :: Maybe ByteString
s3QObject = ByteString -> Maybe ByteString
forall a. a -> Maybe a
Just (ByteString -> Maybe ByteString) -> ByteString -> Maybe ByteString
forall a b. (a -> b) -> a -> b
$ Bucket -> ByteString
T.encodeUtf8 Bucket
goObjectName
                                 , s3QSubresources :: Query
s3QSubresources = [Maybe (ByteString, Bucket)] -> Query
forall a. QueryLike a => a -> Query
HTTP.toQuery [
                                                       (ByteString
"versionId" :: B8.ByteString,) (Bucket -> (ByteString, Bucket))
-> Maybe Bucket -> Maybe (ByteString, Bucket)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goVersionId
                                                     , (ByteString
"response-content-type" :: B8.ByteString,) (Bucket -> (ByteString, Bucket))
-> Maybe Bucket -> Maybe (ByteString, Bucket)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseContentType
                                                     , (ByteString
"response-content-language",) (Bucket -> (ByteString, Bucket))
-> Maybe Bucket -> Maybe (ByteString, Bucket)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseContentLanguage
                                                     , (ByteString
"response-expires",) (Bucket -> (ByteString, Bucket))
-> Maybe Bucket -> Maybe (ByteString, Bucket)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseExpires
                                                     , (ByteString
"response-cache-control",) (Bucket -> (ByteString, Bucket))
-> Maybe Bucket -> Maybe (ByteString, Bucket)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseCacheControl
                                                     , (ByteString
"response-content-disposition",) (Bucket -> (ByteString, Bucket))
-> Maybe Bucket -> Maybe (ByteString, Bucket)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseContentDisposition
                                                     , (ByteString
"response-content-encoding",) (Bucket -> (ByteString, Bucket))
-> Maybe Bucket -> Maybe (ByteString, Bucket)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseContentEncoding
                                                     ]
                                 , s3QQuery :: Query
s3QQuery = []
                                 , s3QContentType :: Maybe ByteString
s3QContentType = Maybe ByteString
forall a. Maybe a
Nothing
                                 , s3QContentMd5 :: Maybe (Digest MD5)
s3QContentMd5 = Maybe (Digest MD5)
forall a. Maybe a
Nothing
                                 , s3QAmzHeaders :: RequestHeaders
s3QAmzHeaders = []
                                 , s3QOtherHeaders :: RequestHeaders
s3QOtherHeaders = [Maybe (HeaderName, ByteString)] -> RequestHeaders
forall a. [Maybe a] -> [a]
catMaybes [
                                                       (Int, Int) -> (HeaderName, ByteString)
forall a a a.
(IsString a, Show a, Show a) =>
(a, a) -> (a, ByteString)
decodeRange ((Int, Int) -> (HeaderName, ByteString))
-> Maybe (Int, Int) -> Maybe (HeaderName, ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe (Int, Int)
goResponseContentRange
                                                     , (HeaderName
"if-match",) (ByteString -> (HeaderName, ByteString))
-> (Bucket -> ByteString) -> Bucket -> (HeaderName, ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bucket -> ByteString
T.encodeUtf8 (Bucket -> (HeaderName, ByteString))
-> Maybe Bucket -> Maybe (HeaderName, ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goIfMatch
                                                     , (HeaderName
"if-none-match",) (ByteString -> (HeaderName, ByteString))
-> (Bucket -> ByteString) -> Bucket -> (HeaderName, ByteString)
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bucket -> ByteString
T.encodeUtf8 (Bucket -> (HeaderName, ByteString))
-> Maybe Bucket -> Maybe (HeaderName, ByteString)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goIfNoneMatch
                                                     ]
                                 , s3QRequestBody :: Maybe RequestBody
s3QRequestBody = Maybe RequestBody
forall a. Maybe a
Nothing
                                 }
      where decodeRange :: (a, a) -> (a, ByteString)
decodeRange (a
pos,a
len) = (a
"range",[ByteString] -> ByteString
B8.concat ([ByteString] -> ByteString) -> [ByteString] -> ByteString
forall a b. (a -> b) -> a -> b
$ [ByteString
"bytes=", String -> ByteString
B8.pack (a -> String
forall a. Show a => a -> String
show a
pos), ByteString
"-", String -> ByteString
B8.pack (a -> String
forall a. Show a => a -> String
show a
len)])

instance ResponseConsumer GetObject GetObjectResponse where
    type ResponseMetadata GetObjectResponse = S3Metadata
    responseConsumer :: Request
-> GetObject
-> IORef (ResponseMetadata GetObjectResponse)
-> HTTPResponseConsumer GetObjectResponse
responseConsumer Request
httpReq GetObject{Maybe (Int, Int)
Maybe Bucket
Bucket
goIfNoneMatch :: Maybe Bucket
goIfMatch :: Maybe Bucket
goResponseContentRange :: Maybe (Int, Int)
goResponseContentEncoding :: Maybe Bucket
goResponseContentDisposition :: Maybe Bucket
goResponseCacheControl :: Maybe Bucket
goResponseExpires :: Maybe Bucket
goResponseContentLanguage :: Maybe Bucket
goResponseContentType :: Maybe Bucket
goVersionId :: Maybe Bucket
goObjectName :: Bucket
goBucket :: Bucket
goIfNoneMatch :: GetObject -> Maybe Bucket
goIfMatch :: GetObject -> Maybe Bucket
goResponseContentRange :: GetObject -> Maybe (Int, Int)
goResponseContentEncoding :: GetObject -> Maybe Bucket
goResponseContentDisposition :: GetObject -> Maybe Bucket
goResponseCacheControl :: GetObject -> Maybe Bucket
goResponseExpires :: GetObject -> Maybe Bucket
goResponseContentLanguage :: GetObject -> Maybe Bucket
goResponseContentType :: GetObject -> Maybe Bucket
goVersionId :: GetObject -> Maybe Bucket
goObjectName :: GetObject -> Bucket
goBucket :: GetObject -> Bucket
..} IORef (ResponseMetadata GetObjectResponse)
metadata Response (ConduitM () ByteString (ResourceT IO) ())
resp
        | Status
status Status -> Status -> Bool
forall a. Eq a => a -> a -> Bool
== Status
HTTP.status200 = do
            Response (ConduitM () ByteString (ResourceT IO) ())
rsp <- HTTPResponseConsumer
  (Response (ConduitM () ByteString (ResourceT IO) ()))
-> IORef S3Metadata
-> HTTPResponseConsumer
     (Response (ConduitM () ByteString (ResourceT IO) ()))
forall a.
HTTPResponseConsumer a
-> IORef S3Metadata -> HTTPResponseConsumer a
s3BinaryResponseConsumer HTTPResponseConsumer
  (Response (ConduitM () ByteString (ResourceT IO) ()))
forall (m :: * -> *) a. Monad m => a -> m a
return IORef (ResponseMetadata GetObjectResponse)
IORef S3Metadata
metadata Response (ConduitM () ByteString (ResourceT IO) ())
resp
            ObjectMetadata
om <- RequestHeaders -> ResourceT IO ObjectMetadata
forall (m :: * -> *).
MonadThrow m =>
RequestHeaders -> m ObjectMetadata
parseObjectMetadata (Response (ConduitM () ByteString (ResourceT IO) ())
-> RequestHeaders
forall body. Response body -> RequestHeaders
HTTP.responseHeaders Response (ConduitM () ByteString (ResourceT IO) ())
resp)
            GetObjectResponse -> ResourceT IO GetObjectResponse
forall (m :: * -> *) a. Monad m => a -> m a
return (GetObjectResponse -> ResourceT IO GetObjectResponse)
-> GetObjectResponse -> ResourceT IO GetObjectResponse
forall a b. (a -> b) -> a -> b
$ ObjectMetadata
-> Response (ConduitM () ByteString (ResourceT IO) ())
-> GetObjectResponse
GetObjectResponse ObjectMetadata
om Response (ConduitM () ByteString (ResourceT IO) ())
rsp
        | Bool
otherwise = Request -> HTTPResponseConsumer GetObjectResponse
forall (m :: * -> *) a.
MonadThrow m =>
Request -> Response (ConduitM () ByteString m ()) -> m a
throwStatusCodeException Request
httpReq Response (ConduitM () ByteString (ResourceT IO) ())
resp
      where
        status :: Status
status  = Response (ConduitM () ByteString (ResourceT IO) ()) -> Status
forall body. Response body -> Status
HTTP.responseStatus    Response (ConduitM () ByteString (ResourceT IO) ())
resp

instance Transaction GetObject GetObjectResponse

instance AsMemoryResponse GetObjectResponse where
    type MemoryResponse GetObjectResponse = GetObjectMemoryResponse
    loadToMemory :: GetObjectResponse
-> ResourceT IO (MemoryResponse GetObjectResponse)
loadToMemory (GetObjectResponse ObjectMetadata
om Response (ConduitM () ByteString (ResourceT IO) ())
x) = do
        [ByteString]
bss <- ConduitT () Void (ResourceT IO) [ByteString]
-> ResourceT IO [ByteString]
forall (m :: * -> *) r. Monad m => ConduitT () Void m r -> m r
C.runConduit (ConduitT () Void (ResourceT IO) [ByteString]
 -> ResourceT IO [ByteString])
-> ConduitT () Void (ResourceT IO) [ByteString]
-> ResourceT IO [ByteString]
forall a b. (a -> b) -> a -> b
$ Response (ConduitM () ByteString (ResourceT IO) ())
-> ConduitM () ByteString (ResourceT IO) ()
forall body. Response body -> body
HTTP.responseBody Response (ConduitM () ByteString (ResourceT IO) ())
x ConduitM () ByteString (ResourceT IO) ()
-> ConduitM ByteString Void (ResourceT IO) [ByteString]
-> ConduitT () Void (ResourceT IO) [ByteString]
forall (m :: * -> *) a b c r.
Monad m =>
ConduitM a b m () -> ConduitM b c m r -> ConduitM a c m r
.| ConduitM ByteString Void (ResourceT IO) [ByteString]
forall (m :: * -> *) a o. Monad m => ConduitT a o m [a]
CL.consume
        GetObjectMemoryResponse -> ResourceT IO GetObjectMemoryResponse
forall (m :: * -> *) a. Monad m => a -> m a
return (GetObjectMemoryResponse -> ResourceT IO GetObjectMemoryResponse)
-> GetObjectMemoryResponse -> ResourceT IO GetObjectMemoryResponse
forall a b. (a -> b) -> a -> b
$ ObjectMetadata -> Response ByteString -> GetObjectMemoryResponse
GetObjectMemoryResponse ObjectMetadata
om Response (ConduitM () ByteString (ResourceT IO) ())
x
            { responseBody :: ByteString
HTTP.responseBody = [ByteString] -> ByteString
L.fromChunks [ByteString]
bss
            }