{-# 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
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 forall a. Maybe a
Nothing forall a. Maybe a
Nothing forall a. Maybe a
Nothing forall a. Maybe a
Nothing forall a. Maybe a
Nothing forall a. Maybe a
Nothing forall a. Maybe a
Nothing forall a. Maybe a
Nothing forall a. Maybe a
Nothing 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
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 :: forall queryType.
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
..} = forall qt.
S3Query -> S3Configuration qt -> SignatureData -> SignedQuery
s3SignQuery S3Query {
                                   s3QMethod :: Method
s3QMethod = Method
Get
                                 , s3QBucket :: Maybe ByteString
s3QBucket = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Bucket -> ByteString
T.encodeUtf8 Bucket
goBucket
                                 , s3QObject :: Maybe ByteString
s3QObject = forall a. a -> Maybe a
Just forall a b. (a -> b) -> a -> b
$ Bucket -> ByteString
T.encodeUtf8 Bucket
goObjectName
                                 , s3QSubresources :: Query
s3QSubresources = forall a. QueryLike a => a -> Query
HTTP.toQuery [
                                                       (ByteString
"versionId" :: B8.ByteString,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goVersionId
                                                     , (ByteString
"response-content-type" :: B8.ByteString,) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseContentType
                                                     , (ByteString
"response-content-language",) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseContentLanguage
                                                     , (ByteString
"response-expires",) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseExpires
                                                     , (ByteString
"response-cache-control",) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseCacheControl
                                                     , (ByteString
"response-content-disposition",) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseContentDisposition
                                                     , (ByteString
"response-content-encoding",) forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goResponseContentEncoding
                                                     ]
                                 , s3QQuery :: Query
s3QQuery = []
                                 , s3QContentType :: Maybe ByteString
s3QContentType = forall a. Maybe a
Nothing
                                 , s3QContentMd5 :: Maybe (Digest MD5)
s3QContentMd5 = forall a. Maybe a
Nothing
                                 , s3QAmzHeaders :: RequestHeaders
s3QAmzHeaders = []
                                 , s3QOtherHeaders :: RequestHeaders
s3QOtherHeaders = forall a. [Maybe a] -> [a]
catMaybes [
                                                       forall {a} {a} {a}.
(IsString a, Show a, Show a) =>
(a, a) -> (a, ByteString)
decodeRange forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe (Int, Int)
goResponseContentRange
                                                     , (HeaderName
"if-match",) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bucket -> ByteString
T.encodeUtf8 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goIfMatch
                                                     , (HeaderName
"if-none-match",) forall b c a. (b -> c) -> (a -> b) -> a -> c
. Bucket -> ByteString
T.encodeUtf8 forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Maybe Bucket
goIfNoneMatch
                                                     ]
                                 , s3QRequestBody :: Maybe RequestBody
s3QRequestBody = forall a. Maybe a
Nothing
                                 }
      where decodeRange :: (a, a) -> (a, ByteString)
decodeRange (a
pos,a
len) = (a
"range",[ByteString] -> ByteString
B8.concat forall a b. (a -> b) -> a -> b
$ [ByteString
"bytes=", String -> ByteString
B8.pack (forall a. Show a => a -> String
show a
pos), ByteString
"-", String -> ByteString
B8.pack (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{} IORef (ResponseMetadata GetObjectResponse)
metadata Response (ConduitM () ByteString (ResourceT IO) ())
resp
        | Status
status forall a. Eq a => a -> a -> Bool
== Status
HTTP.status200 = do
            Response (ConduitM () ByteString (ResourceT IO) ())
rsp <- forall a.
HTTPResponseConsumer a
-> IORef S3Metadata -> HTTPResponseConsumer a
s3BinaryResponseConsumer forall (m :: * -> *) a. Monad m => a -> m a
return IORef (ResponseMetadata GetObjectResponse)
metadata Response (ConduitM () ByteString (ResourceT IO) ())
resp
            ObjectMetadata
om <- forall (m :: * -> *).
MonadThrow m =>
RequestHeaders -> m ObjectMetadata
parseObjectMetadata (forall body. Response body -> RequestHeaders
HTTP.responseHeaders Response (ConduitM () ByteString (ResourceT IO) ())
resp)
            forall (m :: * -> *) a. Monad m => a -> m a
return 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 = 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  = 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 <- forall (m :: * -> *) r. Monad m => ConduitT () Void m r -> m r
C.runConduit forall a b. (a -> b) -> a -> b
$ forall body. Response body -> body
HTTP.responseBody Response (ConduitM () ByteString (ResourceT IO) ())
x forall (m :: * -> *) a b c r.
Monad m =>
ConduitT a b m () -> ConduitT b c m r -> ConduitT a c m r
.| forall (m :: * -> *) a o. Monad m => ConduitT a o m [a]
CL.consume
        forall (m :: * -> *) a. Monad m => a -> m a
return 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
            }