{-# LANGUAGE DeriveDataTypeable #-}
module Extism.PDK.HTTP where
import Data.ByteString as B
import Data.Word
import Extism.JSON (Nullable (..))
import qualified Extism.Manifest (HTTPRequest (..))
import Extism.PDK
import Extism.PDK.Bindings
import Extism.PDK.Memory
import Text.JSON (Result(..), decode, encode, makeObj)
import qualified Text.JSON.Generic
data Request = Request
{ Request -> String
url :: String,
:: [(String, String)],
Request -> String
method :: String
}
deriving (Text.JSON.Generic.Typeable, Typeable Request
Typeable Request =>
(forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Request -> c Request)
-> (forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Request)
-> (Request -> Constr)
-> (Request -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Request))
-> (forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Request))
-> ((forall b. Data b => b -> b) -> Request -> Request)
-> (forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Request -> r)
-> (forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Request -> r)
-> (forall u. (forall d. Data d => d -> u) -> Request -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> Request -> u)
-> (forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Request -> m Request)
-> (forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Request -> m Request)
-> (forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Request -> m Request)
-> Data Request
Request -> Constr
Request -> DataType
(forall b. Data b => b -> b) -> Request -> Request
forall a.
Typeable a =>
(forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> a -> c a)
-> (forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c a)
-> (a -> Constr)
-> (a -> DataType)
-> (forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c a))
-> (forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c a))
-> ((forall b. Data b => b -> b) -> a -> a)
-> (forall r r'.
(r -> r' -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall r r'.
(r' -> r -> r) -> r -> (forall d. Data d => d -> r') -> a -> r)
-> (forall u. (forall d. Data d => d -> u) -> a -> [u])
-> (forall u. Int -> (forall d. Data d => d -> u) -> a -> u)
-> (forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> (forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> a -> m a)
-> Data a
forall u. Int -> (forall d. Data d => d -> u) -> Request -> u
forall u. (forall d. Data d => d -> u) -> Request -> [u]
forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Request -> r
forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Request -> r
forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Request -> m Request
forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Request -> m Request
forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Request
forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Request -> c Request
forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Request)
forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Request)
$cgfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Request -> c Request
gfoldl :: forall (c :: * -> *).
(forall d b. Data d => c (d -> b) -> d -> c b)
-> (forall g. g -> c g) -> Request -> c Request
$cgunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Request
gunfold :: forall (c :: * -> *).
(forall b r. Data b => c (b -> r) -> c r)
-> (forall r. r -> c r) -> Constr -> c Request
$ctoConstr :: Request -> Constr
toConstr :: Request -> Constr
$cdataTypeOf :: Request -> DataType
dataTypeOf :: Request -> DataType
$cdataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Request)
dataCast1 :: forall (t :: * -> *) (c :: * -> *).
Typeable t =>
(forall d. Data d => c (t d)) -> Maybe (c Request)
$cdataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Request)
dataCast2 :: forall (t :: * -> * -> *) (c :: * -> *).
Typeable t =>
(forall d e. (Data d, Data e) => c (t d e)) -> Maybe (c Request)
$cgmapT :: (forall b. Data b => b -> b) -> Request -> Request
gmapT :: (forall b. Data b => b -> b) -> Request -> Request
$cgmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Request -> r
gmapQl :: forall r r'.
(r -> r' -> r)
-> r -> (forall d. Data d => d -> r') -> Request -> r
$cgmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Request -> r
gmapQr :: forall r r'.
(r' -> r -> r)
-> r -> (forall d. Data d => d -> r') -> Request -> r
$cgmapQ :: forall u. (forall d. Data d => d -> u) -> Request -> [u]
gmapQ :: forall u. (forall d. Data d => d -> u) -> Request -> [u]
$cgmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Request -> u
gmapQi :: forall u. Int -> (forall d. Data d => d -> u) -> Request -> u
$cgmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Request -> m Request
gmapM :: forall (m :: * -> *).
Monad m =>
(forall d. Data d => d -> m d) -> Request -> m Request
$cgmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Request -> m Request
gmapMp :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Request -> m Request
$cgmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Request -> m Request
gmapMo :: forall (m :: * -> *).
MonadPlus m =>
(forall d. Data d => d -> m d) -> Request -> m Request
Text.JSON.Generic.Data)
data Response = Response
{ Response -> Int
statusCode :: Int,
Response -> Memory
memory :: Memory
}
newRequest :: String -> Request
newRequest :: String -> Request
newRequest String
url =
String -> [(String, String)] -> String -> Request
Request String
url [] String
"GET"
withMethod :: String -> Request -> Request
withMethod :: String -> Request -> Request
withMethod String
meth Request
req =
Request
req {method = meth}
withHeaders :: [(String, String)] -> Request -> Request
[(String, String)]
h Request
req =
Request
req {headers = h}
responseMemory :: Response -> Memory
responseMemory :: Response -> Memory
responseMemory (Response Int
_ Memory
mem) = Memory
mem
responseByteString :: Response -> IO ByteString
responseByteString :: Response -> IO ByteString
responseByteString (Response Int
_ Memory
mem) = do
Either String ByteString
a <- Memory -> IO (Either String ByteString)
forall a. FromBytes a => Memory -> IO (Either String a)
load Memory
mem
case Either String ByteString
a of
Left String
e -> String -> IO ByteString
forall a. HasCallStack => String -> a
error String
e
Right ByteString
x -> ByteString -> IO ByteString
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return ByteString
x
responseString :: Response -> IO String
responseString :: Response -> IO String
responseString (Response Int
_ Memory
mem) = Memory -> IO String
loadString Memory
mem
responseJSON :: (Text.JSON.Generic.Data a) => Response -> IO (Either String a)
responseJSON :: forall a. Data a => Response -> IO (Either String a)
responseJSON (Response Int
_ Memory
mem) = do
Result JSValue
json <- String -> Result JSValue
forall a. JSON a => String -> Result a
decode (String -> Result JSValue) -> IO String -> IO (Result JSValue)
forall (f :: * -> *) a b. Functor f => (a -> b) -> f a -> f b
<$> Memory -> IO String
loadString Memory
mem
case Result JSValue
json of
Ok JSValue
json ->
case JSValue -> Result a
forall a. Data a => JSValue -> Result a
Text.JSON.Generic.fromJSON JSValue
json of
Ok a
x -> Either String a -> IO (Either String a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Either String a -> IO (Either String a))
-> Either String a -> IO (Either String a)
forall a b. (a -> b) -> a -> b
$ a -> Either String a
forall a b. b -> Either a b
Right a
x
Error String
msg -> Either String a -> IO (Either String a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String a
forall a b. a -> Either a b
Left String
msg)
Error String
msg -> Either String a -> IO (Either String a)
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (String -> Either String a
forall a b. a -> Either a b
Left String
msg)
response :: (FromBytes a) => Response -> IO (Either String a)
response :: forall a. FromBytes a => Response -> IO (Either String a)
response (Response Int
_ Memory
mem) = Memory -> IO (Either String a)
forall a. FromBytes a => Memory -> IO (Either String a)
load Memory
mem
sendRequestWithBody :: (ToBytes a) => Request -> a -> IO Response
sendRequestWithBody :: forall a. ToBytes a => Request -> a -> IO Response
sendRequestWithBody Request
req a
b = do
Memory
body <- a -> IO Memory
forall a. ToBytes a => a -> IO Memory
alloc a
b
let json :: String
json =
HTTPRequest -> String
forall a. JSON a => a -> String
encode
Extism.Manifest.HTTPRequest
{ url :: String
Extism.Manifest.url = Request -> String
url Request
req,
headers :: Nullable [(String, String)]
Extism.Manifest.headers = [(String, String)] -> Nullable [(String, String)]
forall a. a -> Nullable a
NotNull ([(String, String)] -> Nullable [(String, String)])
-> [(String, String)] -> Nullable [(String, String)]
forall a b. (a -> b) -> a -> b
$ Request -> [(String, String)]
headers Request
req,
method :: Nullable String
Extism.Manifest.method = String -> Nullable String
forall a. a -> Nullable a
NotNull (String -> Nullable String) -> String -> Nullable String
forall a b. (a -> b) -> a -> b
$ Request -> String
method Request
req
}
Memory
j <- String -> IO Memory
allocString String
json
MemoryOffset
res <- MemoryOffset -> MemoryOffset -> IO MemoryOffset
extismHTTPRequest (Memory -> MemoryOffset
memoryOffset Memory
j) (Memory -> MemoryOffset
memoryOffset Memory
body)
Memory -> IO ()
free Memory
j
Memory -> IO ()
free Memory
body
Int32
code <- IO Int32
extismHTTPStatusCode
if MemoryOffset
res MemoryOffset -> MemoryOffset -> Bool
forall a. Eq a => a -> a -> Bool
== MemoryOffset
0
then Response -> IO Response
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Memory -> Response
Response (Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
code) (MemoryOffset -> MemoryOffset -> Memory
Memory MemoryOffset
0 MemoryOffset
0))
else do
Memory
mem <- MemoryOffset -> IO Memory
findMemory MemoryOffset
res
Response -> IO Response
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Memory -> Response
Response (Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
code) Memory
mem)
sendRequest :: (ToBytes a) => Request -> Maybe a -> IO Response
sendRequest :: forall a. ToBytes a => Request -> Maybe a -> IO Response
sendRequest Request
req Maybe a
b =
let json :: String
json =
HTTPRequest -> String
forall a. JSON a => a -> String
encode
Extism.Manifest.HTTPRequest
{ url :: String
Extism.Manifest.url = Request -> String
url Request
req,
headers :: Nullable [(String, String)]
Extism.Manifest.headers = [(String, String)] -> Nullable [(String, String)]
forall a. a -> Nullable a
NotNull ([(String, String)] -> Nullable [(String, String)])
-> [(String, String)] -> Nullable [(String, String)]
forall a b. (a -> b) -> a -> b
$ Request -> [(String, String)]
headers Request
req,
method :: Nullable String
Extism.Manifest.method = String -> Nullable String
forall a. a -> Nullable a
NotNull (String -> Nullable String) -> String -> Nullable String
forall a b. (a -> b) -> a -> b
$ Request -> String
method Request
req
}
in let bodyMem :: IO Memory
bodyMem = case Maybe a
b of
Maybe a
Nothing -> Memory -> IO Memory
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Memory -> IO Memory) -> Memory -> IO Memory
forall a b. (a -> b) -> a -> b
$ MemoryOffset -> MemoryOffset -> Memory
Memory MemoryOffset
0 MemoryOffset
0
Just a
b -> a -> IO Memory
forall a. ToBytes a => a -> IO Memory
alloc a
b
in do
Memory
body <- IO Memory
bodyMem
Memory
j <- String -> IO Memory
allocString String
json
MemoryOffset
res <- MemoryOffset -> MemoryOffset -> IO MemoryOffset
extismHTTPRequest (Memory -> MemoryOffset
memoryOffset Memory
j) (Memory -> MemoryOffset
memoryOffset Memory
body)
Memory -> IO ()
free Memory
j
Memory -> IO ()
free Memory
body
Int32
code <- IO Int32
extismHTTPStatusCode
if MemoryOffset
res MemoryOffset -> MemoryOffset -> Bool
forall a. Eq a => a -> a -> Bool
== MemoryOffset
0
then Response -> IO Response
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Memory -> Response
Response (Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
code) (MemoryOffset -> MemoryOffset -> Memory
Memory MemoryOffset
0 MemoryOffset
0))
else do
MemoryOffset
len <- MemoryOffset -> IO MemoryOffset
extismLengthUnsafe MemoryOffset
res
Response -> IO Response
forall a. a -> IO a
forall (m :: * -> *) a. Monad m => a -> m a
return (Int -> Memory -> Response
Response (Int32 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral Int32
code) (MemoryOffset -> MemoryOffset -> Memory
Memory MemoryOffset
res MemoryOffset
len))