Fix payload test bugs (#8)

* fix memory mgmt bug in status details strings

* remove flags

* allow server to specify status details
This commit is contained in:
Connor Clark 2016-05-25 13:11:30 -07:00
parent 2ad0465df6
commit 3746383976
12 changed files with 128 additions and 52 deletions

View File

@ -131,6 +131,10 @@ grpc_metadata_array** metadata_array_create(){
grpc_metadata_array **retval = malloc(sizeof(grpc_metadata_array*)); grpc_metadata_array **retval = malloc(sizeof(grpc_metadata_array*));
*retval = malloc(sizeof(grpc_metadata_array)); *retval = malloc(sizeof(grpc_metadata_array));
grpc_metadata_array_init(*retval); grpc_metadata_array_init(*retval);
#ifdef GRPC_HASKELL_DEBUG
printf("C wrapper: metadata_array_create debug: %p %p %p\n", retval, *retval,
(*retval)->metadata);
#endif
return retval; return retval;
} }
@ -299,7 +303,11 @@ void op_send_status_server(grpc_op *op_array, size_t i,
} }
grpc_status_code* create_status_code_ptr(){ grpc_status_code* create_status_code_ptr(){
return malloc(sizeof(grpc_status_code)); grpc_status_code* retval = malloc(sizeof(grpc_status_code));
#ifdef GRPC_HASKELL_DEBUG
printf("C wrapper: create_status_code_ptr debug: %p\n", retval);
#endif
return retval;
} }
grpc_status_code deref_status_code_ptr(grpc_status_code* p){ grpc_status_code deref_status_code_ptr(grpc_status_code* p){

View File

@ -47,6 +47,7 @@ GRPC
, runOps , runOps
, Op(..) , Op(..)
, OpRecvResult(..) , OpRecvResult(..)
, StatusDetails(..)
) where ) where

View File

@ -114,9 +114,10 @@ withClientCall client method host timeout f = do
data NormalRequestResult = NormalRequestResult data NormalRequestResult = NormalRequestResult
ByteString ByteString
MetadataMap --init metadata (Maybe MetadataMap) --init metadata
MetadataMap --trailing metadata MetadataMap --trailing metadata
C.StatusCode C.StatusCode
StatusDetails
deriving (Show, Eq) deriving (Show, Eq)
-- | Function for assembling call result when the 'MethodType' is 'Normal'. -- | Function for assembling call result when the 'MethodType' is 'Normal'.
@ -128,8 +129,14 @@ compileNormalRequestResults
-- core use cases easy. -- core use cases easy.
[OpRecvInitialMetadataResult m, [OpRecvInitialMetadataResult m,
OpRecvMessageResult body, OpRecvMessageResult body,
OpRecvStatusOnClientResult m2 status] OpRecvStatusOnClientResult m2 status details]
= NormalRequestResult body m m2 status = NormalRequestResult body (Just m) m2 status (StatusDetails details)
-- TODO: it seems registered request responses on the server
-- don't send initial metadata. Hence the 'Maybe'. Investigate.
compileNormalRequestResults
[OpRecvMessageResult body,
OpRecvStatusOnClientResult m2 status details]
= NormalRequestResult body Nothing m2 status (StatusDetails details)
compileNormalRequestResults _ = compileNormalRequestResults _ =
--TODO: impossible case should be enforced by more precise types. --TODO: impossible case should be enforced by more precise types.
error "non-normal request input to compileNormalRequestResults." error "non-normal request input to compileNormalRequestResults."

View File

@ -30,6 +30,7 @@ import Control.Concurrent.STM.TVar (TVar, newTVarIO, modifyTVar',
readTVar, writeTVar) readTVar, writeTVar)
import Control.Exception (bracket) import Control.Exception (bracket)
import Data.IORef (IORef, newIORef, atomicModifyIORef') import Data.IORef (IORef, newIORef, atomicModifyIORef')
import Data.List (intersperse)
import Foreign.Marshal.Alloc (malloc, free) import Foreign.Marshal.Alloc (malloc, free)
import Foreign.Ptr (nullPtr, plusPtr) import Foreign.Ptr (nullPtr, plusPtr)
import Foreign.Storable (peek) import Foreign.Storable (peek)
@ -188,7 +189,8 @@ startBatch :: CompletionQueue -> C.Call -> C.OpArray -> Int -> C.Tag
-> IO (Either GRPCIOError ()) -> IO (Either GRPCIOError ())
startBatch cq@CompletionQueue{..} call opArray opArraySize tag = startBatch cq@CompletionQueue{..} call opArray opArraySize tag =
withPermission Push cq $ fmap throwIfCallError $ do withPermission Push cq $ fmap throwIfCallError $ do
grpcDebug "startBatch: calling grpc_call_start_batch." grpcDebug $ "startBatch: calling grpc_call_start_batch with pointers: "
++ show call ++ " " ++ show opArray
res <- C.grpcCallStartBatch call opArray opArraySize tag C.reserved res <- C.grpcCallStartBatch call opArray opArraySize tag C.reserved
grpcDebug "startBatch: grpc_call_start_batch call returned." grpcDebug "startBatch: grpc_call_start_batch call returned."
return res return res
@ -230,6 +232,10 @@ channelCreateRegisteredCall :: C.Channel -> C.Call -> C.PropagationMask
channelCreateRegisteredCall channelCreateRegisteredCall
chan parent mask cq@CompletionQueue{..} handle deadline = chan parent mask cq@CompletionQueue{..} handle deadline =
withPermission Push cq $ do withPermission Push cq $ do
grpcDebug $ "channelCreateRegisteredCall: call with "
++ concat (intersperse " " [show chan, show parent, show mask,
show unsafeCQ, show handle,
show deadline])
call <- C.grpcChannelCreateRegisteredCall chan parent mask unsafeCQ call <- C.grpcChannelCreateRegisteredCall chan parent mask unsafeCQ
handle deadline C.reserved handle deadline C.reserved
return $ Right $ ClientCall call return $ Right $ ClientCall call

View File

@ -1,4 +1,5 @@
{-# LANGUAGE RecordWildCards #-} {-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Network.GRPC.LowLevel.Op where module Network.GRPC.LowLevel.Op where
@ -6,10 +7,12 @@ import Control.Exception
import qualified Data.ByteString as B import qualified Data.ByteString as B
import qualified Data.Map.Strict as M import qualified Data.Map.Strict as M
import Data.Maybe (catMaybes) import Data.Maybe (catMaybes)
import Data.String (IsString)
import Foreign.C.String (CString)
import Foreign.C.Types (CInt) import Foreign.C.Types (CInt)
import Foreign.Marshal.Alloc (malloc, free) import Foreign.Marshal.Alloc (malloc, mallocBytes, free)
import Foreign.Ptr (Ptr) import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (peek) import Foreign.Storable (peek, poke)
import qualified Network.GRPC.Unsafe as C import qualified Network.GRPC.Unsafe as C
import qualified Network.GRPC.Unsafe.Metadata as C import qualified Network.GRPC.Unsafe.Metadata as C
import qualified Network.GRPC.Unsafe.ByteBuffer as C import qualified Network.GRPC.Unsafe.ByteBuffer as C
@ -21,13 +24,15 @@ import Network.GRPC.LowLevel.Call
type MetadataMap = M.Map B.ByteString B.ByteString type MetadataMap = M.Map B.ByteString B.ByteString
newtype StatusDetails = StatusDetails B.ByteString deriving (Show, Eq, IsString)
-- | Sum describing all possible send and receive operations that can be batched -- | Sum describing all possible send and receive operations that can be batched
-- and executed by gRPC. Usually these are processed in a handful of -- and executed by gRPC. Usually these are processed in a handful of
-- combinations depending on the 'MethodType' of the call being run. -- combinations depending on the 'MethodType' of the call being run.
data Op = OpSendInitialMetadata MetadataMap data Op = OpSendInitialMetadata MetadataMap
| OpSendMessage B.ByteString | OpSendMessage B.ByteString
| OpSendCloseFromClient | OpSendCloseFromClient
| OpSendStatusFromServer MetadataMap C.StatusCode --TODO: Issue #6 | OpSendStatusFromServer MetadataMap C.StatusCode StatusDetails
| OpRecvInitialMetadata | OpRecvInitialMetadata
| OpRecvMessage | OpRecvMessage
| OpRecvStatusOnClient | OpRecvStatusOnClient
@ -42,10 +47,19 @@ data OpContext =
| OpSendMessageContext C.ByteBuffer | OpSendMessageContext C.ByteBuffer
| OpSendCloseFromClientContext | OpSendCloseFromClientContext
| OpSendStatusFromServerContext C.MetadataKeyValPtr Int C.StatusCode | OpSendStatusFromServerContext C.MetadataKeyValPtr Int C.StatusCode
B.ByteString
| OpRecvInitialMetadataContext (Ptr C.MetadataArray) | OpRecvInitialMetadataContext (Ptr C.MetadataArray)
| OpRecvMessageContext (Ptr C.ByteBuffer) | OpRecvMessageContext (Ptr C.ByteBuffer)
| OpRecvStatusOnClientContext (Ptr C.MetadataArray) (Ptr C.StatusCode) | OpRecvStatusOnClientContext (Ptr C.MetadataArray) (Ptr C.StatusCode)
(Ptr CString)
| OpRecvCloseOnServerContext (Ptr CInt) | OpRecvCloseOnServerContext (Ptr CInt)
deriving Show
-- | Length we pass to gRPC for receiving status details
-- when processing 'OpRecvStatusOnClient'. It appears that gRPC actually ignores
-- this length and reallocates a longer string if necessary.
defaultStatusStringLen :: Int
defaultStatusStringLen = 128
-- | Allocates and initializes the 'Opcontext' corresponding to the given 'Op'. -- | Allocates and initializes the 'Opcontext' corresponding to the given 'Op'.
createOpContext :: Op -> IO OpContext createOpContext :: Op -> IO OpContext
@ -56,19 +70,23 @@ createOpContext (OpSendInitialMetadata m) =
createOpContext (OpSendMessage bs) = createOpContext (OpSendMessage bs) =
fmap OpSendMessageContext (C.createByteBuffer bs) fmap OpSendMessageContext (C.createByteBuffer bs)
createOpContext (OpSendCloseFromClient) = return OpSendCloseFromClientContext createOpContext (OpSendCloseFromClient) = return OpSendCloseFromClientContext
createOpContext (OpSendStatusFromServer m code) = createOpContext (OpSendStatusFromServer m code (StatusDetails str)) =
OpSendStatusFromServerContext OpSendStatusFromServerContext
<$> C.createMetadata m <$> C.createMetadata m
<*> return (M.size m) <*> return (M.size m)
<*> return code <*> return code
<*> return str
createOpContext OpRecvInitialMetadata = createOpContext OpRecvInitialMetadata =
fmap OpRecvInitialMetadataContext C.metadataArrayCreate fmap OpRecvInitialMetadataContext C.metadataArrayCreate
createOpContext OpRecvMessage = createOpContext OpRecvMessage =
fmap OpRecvMessageContext C.createReceivingByteBuffer fmap OpRecvMessageContext C.createReceivingByteBuffer
createOpContext OpRecvStatusOnClient = createOpContext OpRecvStatusOnClient = do
OpRecvStatusOnClientContext pmetadata <- C.metadataArrayCreate
<$> C.metadataArrayCreate pstatus <- C.createStatusCodePtr
<*> C.createStatusCodePtr pstr <- malloc
cstring <- mallocBytes defaultStatusStringLen
poke pstr cstring
return $ OpRecvStatusOnClientContext pmetadata pstatus pstr
createOpContext OpRecvCloseOnServer = createOpContext OpRecvCloseOnServer =
fmap OpRecvCloseOnServerContext $ malloc fmap OpRecvCloseOnServerContext $ malloc
@ -81,15 +99,15 @@ setOpArray arr i (OpSendMessageContext bb) =
C.opSendMessage arr i bb C.opSendMessage arr i bb
setOpArray arr i OpSendCloseFromClientContext = setOpArray arr i OpSendCloseFromClientContext =
C.opSendCloseClient arr i C.opSendCloseClient arr i
setOpArray arr i (OpSendStatusFromServerContext kvs l code) = setOpArray arr i (OpSendStatusFromServerContext kvs l code details) =
C.opSendStatusServer arr i l kvs code "" --TODO: Issue #6 B.useAsCString details $ \cstr ->
C.opSendStatusServer arr i l kvs code cstr
setOpArray arr i (OpRecvInitialMetadataContext pmetadata) = setOpArray arr i (OpRecvInitialMetadataContext pmetadata) =
C.opRecvInitialMetadata arr i pmetadata C.opRecvInitialMetadata arr i pmetadata
setOpArray arr i (OpRecvMessageContext pbb) = setOpArray arr i (OpRecvMessageContext pbb) =
C.opRecvMessage arr i pbb C.opRecvMessage arr i pbb
setOpArray arr i (OpRecvStatusOnClientContext pmetadata pstatus) = do setOpArray arr i (OpRecvStatusOnClientContext pmetadata pstatus pstr) = do
pCString <- malloc --TODO: Issue #6 C.opRecvStatusClient arr i pmetadata pstatus pstr defaultStatusStringLen
C.opRecvStatusClient arr i pmetadata pstatus pCString 0
setOpArray arr i (OpRecvCloseOnServerContext pcancelled) = do setOpArray arr i (OpRecvCloseOnServerContext pcancelled) = do
C.opRecvCloseServer arr i pcancelled C.opRecvCloseServer arr i pcancelled
@ -98,15 +116,18 @@ freeOpContext :: OpContext -> IO ()
freeOpContext (OpSendInitialMetadataContext m _) = C.metadataFree m freeOpContext (OpSendInitialMetadataContext m _) = C.metadataFree m
freeOpContext (OpSendMessageContext bb) = C.grpcByteBufferDestroy bb freeOpContext (OpSendMessageContext bb) = C.grpcByteBufferDestroy bb
freeOpContext OpSendCloseFromClientContext = return () freeOpContext OpSendCloseFromClientContext = return ()
freeOpContext (OpSendStatusFromServerContext metadata _ _) = freeOpContext (OpSendStatusFromServerContext metadata _ _ _) =
C.metadataFree metadata C.metadataFree metadata
freeOpContext (OpRecvInitialMetadataContext metadata) = freeOpContext (OpRecvInitialMetadataContext metadata) =
C.metadataArrayDestroy metadata C.metadataArrayDestroy metadata
freeOpContext (OpRecvMessageContext pbb) = freeOpContext (OpRecvMessageContext pbb) =
C.destroyReceivingByteBuffer pbb C.destroyReceivingByteBuffer pbb
freeOpContext (OpRecvStatusOnClientContext metadata pcode) = freeOpContext (OpRecvStatusOnClientContext metadata pcode pstr) = do
C.metadataArrayDestroy metadata C.metadataArrayDestroy metadata
>> C.destroyStatusCodePtr pcode C.destroyStatusCodePtr pcode
str <- peek pstr
free str
free pstr
freeOpContext (OpRecvCloseOnServerContext pcancelled) = freeOpContext (OpRecvCloseOnServerContext pcancelled) =
grpcDebug ("freeOpContext: freeing pcancelled: " ++ show pcancelled) grpcDebug ("freeOpContext: freeing pcancelled: " ++ show pcancelled)
>> free pcancelled >> free pcancelled
@ -125,7 +146,7 @@ withOpArray n = bracket (C.opArrayCreate n)
data OpRecvResult = data OpRecvResult =
OpRecvInitialMetadataResult MetadataMap OpRecvInitialMetadataResult MetadataMap
| OpRecvMessageResult B.ByteString | OpRecvMessageResult B.ByteString
| OpRecvStatusOnClientResult MetadataMap C.StatusCode | OpRecvStatusOnClientResult MetadataMap C.StatusCode B.ByteString
| OpRecvCloseOnServerResult Bool -- ^ True if call was cancelled. | OpRecvCloseOnServerResult Bool -- ^ True if call was cancelled.
deriving (Eq, Show) deriving (Eq, Show)
@ -145,12 +166,14 @@ resultFromOpContext (OpRecvMessageContext pbb) = do
bs <- C.copyByteBufferToByteString bb bs <- C.copyByteBufferToByteString bb
grpcDebug "resultFromOpContext: bb copied." grpcDebug "resultFromOpContext: bb copied."
return $ Just $ OpRecvMessageResult bs return $ Just $ OpRecvMessageResult bs
resultFromOpContext (OpRecvStatusOnClientContext pmetadata pcode) = do resultFromOpContext (OpRecvStatusOnClientContext pmetadata pcode pstr) = do
grpcDebug "resultFromOpContext: OpRecvStatusOnClientContext" grpcDebug "resultFromOpContext: OpRecvStatusOnClientContext"
metadata <- peek pmetadata metadata <- peek pmetadata
metadataMap <- C.getAllMetadataArray metadata metadataMap <- C.getAllMetadataArray metadata
code <- C.derefStatusCodePtr pcode code <- C.derefStatusCodePtr pcode
return $ Just $ OpRecvStatusOnClientResult metadataMap code cstr <- peek pstr
statusInfo <- B.packCString cstr
return $ Just $ OpRecvStatusOnClientResult metadataMap code statusInfo
resultFromOpContext (OpRecvCloseOnServerContext pcancelled) = do resultFromOpContext (OpRecvCloseOnServerContext pcancelled) = do
grpcDebug "resultFromOpContext: OpRecvCloseOnServerContext" grpcDebug "resultFromOpContext: OpRecvCloseOnServerContext"
cancelled <- fmap (\x -> if x > 0 then True else False) cancelled <- fmap (\x -> if x > 0 then True else False)
@ -185,7 +208,7 @@ runOps call cq ops timeLimit =
withOpArray l $ \opArray -> do withOpArray l $ \opArray -> do
grpcDebug "runOps: created op array." grpcDebug "runOps: created op array."
withOpContexts ops $ \contexts -> do withOpContexts ops $ \contexts -> do
grpcDebug "runOps: allocated op contexts." grpcDebug $ "runOps: allocated op contexts: " ++ show contexts
sequence_ $ zipWith (setOpArray opArray) [0..l-1] contexts sequence_ $ zipWith (setOpArray opArray) [0..l-1] contexts
tag <- newTag cq tag <- newTag cq
callError <- startBatch cq (internalCall call) opArray l tag callError <- startBatch cq (internalCall call) opArray l tag

View File

@ -2,6 +2,7 @@
module Network.GRPC.LowLevel.Server where module Network.GRPC.LowLevel.Server where
import Control.Concurrent (threadDelay)
import Control.Exception (bracket, finally) import Control.Exception (bracket, finally)
import Control.Monad import Control.Monad
import Data.ByteString (ByteString) import Data.ByteString (ByteString)
@ -159,11 +160,12 @@ serverOpsGetNormalCall initMetadata =
serverOpsSendNormalResponse :: ByteString serverOpsSendNormalResponse :: ByteString
-> MetadataMap -> MetadataMap
-> C.StatusCode -> C.StatusCode
-> StatusDetails
-> [Op] -> [Op]
serverOpsSendNormalResponse body metadata code = serverOpsSendNormalResponse body metadata code details =
[OpRecvCloseOnServer, [OpRecvCloseOnServer,
OpSendMessage body, OpSendMessage body,
OpSendStatusFromServer metadata code] OpSendStatusFromServer metadata code details]
serverOpsSendNormalRegisteredResponse :: ByteString serverOpsSendNormalRegisteredResponse :: ByteString
-> MetadataMap -> MetadataMap
@ -171,12 +173,14 @@ serverOpsSendNormalRegisteredResponse :: ByteString
-> MetadataMap -> MetadataMap
-- ^ trailing metadata -- ^ trailing metadata
-> C.StatusCode -> C.StatusCode
-> StatusDetails
-> [Op] -> [Op]
serverOpsSendNormalRegisteredResponse body initMetadata trailingMeta code = serverOpsSendNormalRegisteredResponse
body initMetadata trailingMeta code details =
[OpSendInitialMetadata initMetadata, [OpSendInitialMetadata initMetadata,
OpRecvCloseOnServer, OpRecvCloseOnServer,
OpSendMessage body, OpSendMessage body,
OpSendStatusFromServer trailingMeta code] OpSendStatusFromServer trailingMeta code details]
-- TODO: we will want to replace this with some more general concept that also -- TODO: we will want to replace this with some more general concept that also
-- works with streaming calls in the future. -- works with streaming calls in the future.
@ -189,7 +193,8 @@ serverHandleNormalRegisteredCall :: Server
-> (ByteString -> MetadataMap -> (ByteString -> MetadataMap
-> IO (ByteString, -> IO (ByteString,
MetadataMap, MetadataMap,
MetadataMap)) MetadataMap,
StatusDetails))
-- ^ Handler function takes a request body and -- ^ Handler function takes a request body and
-- metadata and returns a response body and -- metadata and returns a response body and
-- metadata. -- metadata.
@ -209,10 +214,10 @@ serverHandleNormalRegisteredCall s@Server{..} rm timeLimit initMetadata f = do
requestBody <- C.copyByteBufferToByteString payload requestBody <- C.copyByteBufferToByteString payload
metadataArray <- peek $ requestMetadataRecv call metadataArray <- peek $ requestMetadataRecv call
metadata <- C.getAllMetadataArray metadataArray metadata <- C.getAllMetadataArray metadataArray
(respBody, initMeta, trailingMeta) <- f requestBody metadata (respBody, initMeta, trailingMeta, details) <- f requestBody metadata
let status = C.GrpcStatusOk let status = C.GrpcStatusOk
let respOps = serverOpsSendNormalRegisteredResponse let respOps = serverOpsSendNormalRegisteredResponse
respBody initMeta trailingMeta status respBody initMeta trailingMeta status details
respOpsResults <- runOps call serverCQ respOps timeLimit respOpsResults <- runOps call serverCQ respOps timeLimit
grpcDebug "serverHandleNormalRegisteredCall: finished response ops." grpcDebug "serverHandleNormalRegisteredCall: finished response ops."
case respOpsResults of case respOpsResults of
@ -226,7 +231,7 @@ serverHandleNormalCall :: Server -> TimeoutSeconds
-> MetadataMap -> MetadataMap
-- ^ Initial metadata. -- ^ Initial metadata.
-> (ByteString -> MetadataMap -> (ByteString -> MetadataMap
-> IO (ByteString, MetadataMap)) -> IO (ByteString, MetadataMap, StatusDetails))
-- ^ Handler function takes a request body and -- ^ Handler function takes a request body and
-- metadata and returns a response body and metadata. -- metadata and returns a response body and metadata.
-> IO (Either GRPCIOError ()) -> IO (Either GRPCIOError ())
@ -239,9 +244,10 @@ serverHandleNormalCall s@Server{..} timeLimit initMetadata f = do
Left x -> return $ Left x Left x -> return $ Left x
Right [OpRecvMessageResult body] -> do Right [OpRecvMessageResult body] -> do
--TODO: we need to get client metadata --TODO: we need to get client metadata
(respBody, respMetadata) <- f body M.empty (respBody, respMetadata, details) <- f body M.empty
let status = C.GrpcStatusOk let status = C.GrpcStatusOk
let respOps = serverOpsSendNormalResponse respBody respMetadata status let respOps = serverOpsSendNormalResponse
respBody respMetadata status details
respOpsResults <- runOps call serverCQ respOps timeLimit respOpsResults <- runOps call serverCQ respOps timeLimit
case respOpsResults of case respOpsResults of
Left x -> do grpcDebug "serverHandleNormalCall: resp failed." Left x -> do grpcDebug "serverHandleNormalCall: resp failed."

View File

@ -1,3 +1,5 @@
{-# LANGUAGE StandaloneDeriving #-}
module Network.GRPC.Unsafe where module Network.GRPC.Unsafe where
import Control.Monad import Control.Monad
@ -20,9 +22,13 @@ import Network.GRPC.Unsafe.Constants
{#pointer *grpc_completion_queue as CompletionQueue newtype #} {#pointer *grpc_completion_queue as CompletionQueue newtype #}
deriving instance Show CompletionQueue
-- | Represents a connection to a server. Created on the client side. -- | Represents a connection to a server. Created on the client side.
{#pointer *grpc_channel as Channel newtype #} {#pointer *grpc_channel as Channel newtype #}
deriving instance Show Channel
-- | Represents a server. Created on the server side. -- | Represents a server. Created on the server side.
{#pointer *grpc_server as Server newtype #} {#pointer *grpc_server as Server newtype #}
@ -30,13 +36,11 @@ import Network.GRPC.Unsafe.Constants
-- type is abstract; we have no access to its fields. -- type is abstract; we have no access to its fields.
{#pointer *grpc_call as Call newtype #} {#pointer *grpc_call as Call newtype #}
instance Show Call where deriving instance Show Call
show (Call ptr) = show ptr
{#pointer *grpc_call_details as CallDetails newtype #} {#pointer *grpc_call_details as CallDetails newtype #}
instance Show CallDetails where deriving instance Show CallDetails
show (CallDetails ptr) = show ptr
{#fun create_call_details as ^ {} -> `CallDetails'#} {#fun create_call_details as ^ {} -> `CallDetails'#}
{#fun destroy_call_details as ^ {`CallDetails'} -> `()'#} {#fun destroy_call_details as ^ {`CallDetails'} -> `()'#}

View File

@ -1,3 +1,5 @@
{-# LANGUAGE StandaloneDeriving #-}
module Network.GRPC.Unsafe.ByteBuffer where module Network.GRPC.Unsafe.ByteBuffer where
#include <grpc/grpc.h> #include <grpc/grpc.h>
@ -21,6 +23,8 @@ import Foreign.Storable
-- Must be destroyed manually with 'grpcByteBufferDestroy'. -- Must be destroyed manually with 'grpcByteBufferDestroy'.
{#pointer *grpc_byte_buffer as ByteBuffer newtype #} {#pointer *grpc_byte_buffer as ByteBuffer newtype #}
deriving instance Show ByteBuffer
--Trivial Storable instance because 'ByteBuffer' type is a pointer. --Trivial Storable instance because 'ByteBuffer' type is a pointer.
instance Storable ByteBuffer where instance Storable ByteBuffer where
sizeOf (ByteBuffer r) = sizeOf r sizeOf (ByteBuffer r) = sizeOf r

View File

@ -1,3 +1,5 @@
{-# LANGUAGE StandaloneDeriving #-}
module Network.GRPC.Unsafe.Metadata where module Network.GRPC.Unsafe.Metadata where
import Control.Exception import Control.Exception
@ -18,6 +20,8 @@ import Foreign.Storable
-- is intended to be used when sending metadata. -- is intended to be used when sending metadata.
{#pointer *grpc_metadata as MetadataKeyValPtr newtype#} {#pointer *grpc_metadata as MetadataKeyValPtr newtype#}
deriving instance Show MetadataKeyValPtr
-- | Represents a pointer to a grpc_metadata_array. Must be destroyed with -- | Represents a pointer to a grpc_metadata_array. Must be destroyed with
-- 'metadataArrayDestroy'. This type is intended for receiving metadata. -- 'metadataArrayDestroy'. This type is intended for receiving metadata.
-- This can be populated by passing it to e.g. 'grpcServerRequestCall'. -- This can be populated by passing it to e.g. 'grpcServerRequestCall'.
@ -25,6 +29,8 @@ import Foreign.Storable
-- and length from this type. -- and length from this type.
{#pointer *grpc_metadata_array as MetadataArray newtype#} {#pointer *grpc_metadata_array as MetadataArray newtype#}
deriving instance Show MetadataArray
{#fun metadata_array_get_metadata as ^ {#fun metadata_array_get_metadata as ^
{`MetadataArray'} -> `MetadataKeyValPtr'#} {`MetadataArray'} -> `MetadataKeyValPtr'#}

View File

@ -1,3 +1,5 @@
{-# LANGUAGE StandaloneDeriving #-}
module Network.GRPC.Unsafe.Op where module Network.GRPC.Unsafe.Op where
import Control.Exception import Control.Exception
@ -34,6 +36,8 @@ import Foreign.Ptr
-- 'opArrayDestroy'. -- 'opArrayDestroy'.
{#pointer *grpc_op as OpArray newtype #} {#pointer *grpc_op as OpArray newtype #}
deriving instance Show OpArray
-- | Creates an empty 'OpArray' with space for the given number of ops. -- | Creates an empty 'OpArray' with space for the given number of ops.
{#fun op_array_create as ^ {`Int'} -> `OpArray'#} {#fun op_array_create as ^ {`Int'} -> `OpArray'#}
@ -98,5 +102,5 @@ withOpArray n f = bracket (opArrayCreate n) (flip opArrayDestroy n) f
-- Metadata and string are copied when creating the op, and can be safely -- Metadata and string are copied when creating the op, and can be safely
-- destroyed immediately after calling this function. -- destroyed immediately after calling this function.
{#fun op_send_status_server as ^ {#fun op_send_status_server as ^
{`OpArray', `Int', `Int', `MetadataKeyValPtr', `StatusCode', `String'} {`OpArray', `Int', `Int', `MetadataKeyValPtr', `StatusCode', `CString'}
-> `()'#} -> `()'#}

View File

@ -17,10 +17,10 @@ lowLevelTests = testGroup "Unit tests of low-level Haskell library"
, testClientCreateDestroy , testClientCreateDestroy
, testWithServerCall , testWithServerCall
, testWithClientCall , testWithClientCall
--, testPayloadLowLevel --TODO: currently crashing from free on unalloced ptr , testPayloadLowLevel
--, testClientRequestNoServer --TODO: succeeds when no other tests run. , testClientRequestNoServer
, testServerAwaitNoClient , testServerAwaitNoClient
--, testPayloadLowLevelUnregistered --TODO: succeeds when no other tests run. , testPayloadLowLevelUnregistered
] ]
dummyMeta :: M.Map ByteString ByteString dummyMeta :: M.Map ByteString ByteString
@ -53,7 +53,9 @@ testPayloadLowLevelServer grpc = do
withServer grpc conf $ \server -> do withServer grpc conf $ \server -> do
let method = head (registeredMethods server) let method = head (registeredMethods server)
result <- serverHandleNormalRegisteredCall server method 11 M.empty $ result <- serverHandleNormalRegisteredCall server method 11 M.empty $
\reqBody reqMeta -> return ("reply test", dummyMeta, dummyMeta) \reqBody reqMeta ->
return ("reply test", dummyMeta, dummyMeta,
StatusDetails "details string")
case result of case result of
Left err -> error $ show err Left err -> error $ show err
Right _ -> return () Right _ -> return ()
@ -66,7 +68,8 @@ testPayloadLowLevelClient grpc =
reqResult <- clientRegisteredRequest client method 10 "Hello!" M.empty reqResult <- clientRegisteredRequest client method 10 "Hello!" M.empty
case reqResult of case reqResult of
Left x -> error $ "Client got error: " ++ show x Left x -> error $ "Client got error: " ++ show x
Right (NormalRequestResult respBody initMeta trailingMeta respCode) -> do Right (NormalRequestResult respBody initMeta trailingMeta respCode details) -> do
details @?= "details string"
respBody @?= "reply test" respBody @?= "reply test"
respCode @?= GrpcStatusOk respCode @?= GrpcStatusOk
@ -76,15 +79,18 @@ testPayloadLowLevelClientUnregistered grpc = do
reqResult <- clientRequest client "/foo" "localhost" 10 "Hello!" M.empty reqResult <- clientRequest client "/foo" "localhost" 10 "Hello!" M.empty
case reqResult of case reqResult of
Left x -> error $ "Client got error: " ++ show x Left x -> error $ "Client got error: " ++ show x
Right (NormalRequestResult respBody initMeta trailingMeta respCode) -> do Right (NormalRequestResult
respBody initMeta trailingMeta respCode details) -> do
respBody @?= "reply test" respBody @?= "reply test"
respCode @?= GrpcStatusOk respCode @?= GrpcStatusOk
details @?= "details string"
testPayloadLowLevelServerUnregistered :: GRPC -> IO () testPayloadLowLevelServerUnregistered :: GRPC -> IO ()
testPayloadLowLevelServerUnregistered grpc = do testPayloadLowLevelServerUnregistered grpc = do
withServer grpc (ServerConfig "localhost" 50051 []) $ \server -> do withServer grpc (ServerConfig "localhost" 50051 []) $ \server -> do
result <- serverHandleNormalCall server 11 M.empty $ result <- serverHandleNormalCall server 11 M.empty $
\reqBody reqMeta -> return ("reply test", M.empty) \reqBody reqMeta -> return ("reply test", M.empty,
StatusDetails "details string")
case result of case result of
Left x -> error $ show x Left x -> error $ show x
Right _ -> return () Right _ -> return ()
@ -104,7 +110,7 @@ testServerAwaitNoClient = testCase "server wait times out when no client " $ do
withServer grpc conf $ \server -> do withServer grpc conf $ \server -> do
let method = head (registeredMethods server) let method = head (registeredMethods server)
result <- serverHandleNormalRegisteredCall server method 1 M.empty $ result <- serverHandleNormalRegisteredCall server method 1 M.empty $
\_ _ -> return ("", M.empty, M.empty) \_ _ -> return ("", M.empty, M.empty, StatusDetails "details")
result @?= Left GRPCIOTimeout result @?= Left GRPCIOTimeout
testServerUnregisteredAwaitNoClient :: TestTree testServerUnregisteredAwaitNoClient :: TestTree
@ -114,7 +120,7 @@ testServerUnregisteredAwaitNoClient =
let conf = ServerConfig "localhost" 50051 [] let conf = ServerConfig "localhost" 50051 []
withServer grpc conf $ \server -> do withServer grpc conf $ \server -> do
result <- serverHandleNormalCall server 10 M.empty $ result <- serverHandleNormalCall server 10 M.empty $
\_ _ -> return ("", M.empty) \_ _ -> return ("", M.empty, StatusDetails "")
case result of case result of
Left err -> error $ show err Left err -> error $ show err
Right _ -> return () Right _ -> return ()

View File

@ -165,8 +165,9 @@ testPayloadServer = do
cancelledPtr <- malloc cancelledPtr <- malloc
opRecvCloseServer respOps 0 cancelledPtr opRecvCloseServer respOps 0 cancelledPtr
opSendMessage respOps 1 respbb opSendMessage respOps 1 respbb
opSendStatusServer respOps 2 0 (MetadataKeyValPtr nullPtr) B.useAsCString "ok" $ \detailsStr ->
GrpcStatusOk "ok" opSendStatusServer respOps 2 0 (MetadataKeyValPtr nullPtr)
GrpcStatusOk detailsStr
serverCall <- peek serverCallPtr serverCall <- peek serverCallPtr
respBatchError <- grpcCallStartBatch serverCall respOps 3 respBatchError <- grpcCallStartBatch serverCall respOps 3
(tag 103) reserved (tag 103) reserved