gRPC-haskell/src/Network/GRPC/LowLevel/Call.hs

116 lines
4.8 KiB
Haskell
Raw Normal View History

Begin safe low-level Haskell layer (#7) * grpc_server_request_call * basic slice functionality * rename function to emphasize side effects * add docs * ByteBuffer function bindings * replace unsafeCoerce with more specific function, add docs, tests. * add newtypes for Tag and Reserved void pointers * manually fix request_registered_call binding * use nocode keyword to fix Ptr () problems * decouple copying Slice from freeing slice * Add time ops * remove nocode decls * Start Op module, fix c2hs preprocessing order * metadata manipulation operations * metadata free function, test * helper functions for constructing ops of each type * bindings for op creation functions * finish up Op creation functions, implement Op destruction, add docs. * tweak documentation * rework Op creation functions to work with an array of ops, for ease of use with grpc_call_start_batch * forgot to change return types * wrap hook lines, fix types to op creation functions * implement part of the payload test * hideous, but working, end to end test * bindings for connectivity state checks, split test into two threads * various cleanup * rename Core to Unsafe for emphasis, clean up tests more * begin safe low-level facilities * begin completion queue and server stuff * Finish server start/stop, cq start/stop, add tests * facilities for safely executing op batches * reorganize LowLevel modules, begin explicit export list * client functionality, stub payload test, various refactors * tweak cabal file, add test * add more documentation * doc tweaks * begin refactor to improve CompletionQueue safety * export only thread-safe CQ functions, add registered call creation and other CQ utilities * begin refactor to use GRPCIO monad, fix missing push semaphore, fix mem leak in server calls * switch to explicit Either where needed * add crashing tests, continue fleshing out serverHandleNormalCall * fix haddock error, finish first draft of request handling function * reduce GHC warnings * non-registered client request helpers * initial request/response test working * don't pass tags around; generate where needed * server call bracket functions * correct order of semaphore acquisition and shutdown check * simple debug flag logging, simplify Call type * fix various registered method issues (but still not working) * cleanup * delete old code * remove old todo * use MetadataMap synonym pervasively * more comments * update TODOs * tweak safety caveat * docs tweaks * improve haddocks * add casts to eliminate clang warnings, remove unused function * update options to eliminate cabal warnings * remove outdated todo * remove unneeded exports from CompletionQueue * rename to GRPCIOCallError, re-add create/shutdown exports (needed for Server module) * newtypes for hosts and method names * more newtypes * more debug logging * Fix flag name collision * instrument uses of free * more debug * switch to STM for completion queue stuff * reduce warnings * more debugging, create/destroy call tests * refactor, fix failure cleanup for server call creation. More tests passing. * formatting tweaks
2016-05-24 22:34:50 +02:00
{-# LANGUAGE RecordWildCards #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
module Network.GRPC.LowLevel.Call where
import Control.Monad
import Data.String (IsString)
import Foreign.Marshal.Alloc (free)
import Foreign.Ptr (Ptr, castPtr)
import Foreign.Storable (peek)
import qualified Network.GRPC.Unsafe as C
import qualified Network.GRPC.Unsafe.Time as C
import qualified Network.GRPC.Unsafe.Metadata as C
import qualified Network.GRPC.Unsafe.ByteBuffer as C
import Network.GRPC.LowLevel.GRPC (grpcDebug)
-- | Models the four types of RPC call supported by gRPC. We currently only
-- support the first alternative, and only in a preliminary fashion.
data GRPCMethodType = Normal | ClientStreaming | ServerStreaming | BiDiStreaming
deriving (Show, Eq, Ord, Enum)
newtype MethodName = MethodName {unMethodName :: String}
deriving (Show, Eq, IsString)
newtype Host = Host {unHost :: String}
deriving (Show, Eq, IsString)
-- | Represents a registered method. Methods can optionally be registered in
-- order to make the C-level request/response code simpler.
-- Before making or awaiting a registered call, the
-- method must be registered with the client (see 'clientRegisterMethod') and
-- the server (see 'serverRegisterMethod').
-- Contains state for identifying that method in the underlying gRPC library.
data RegisteredMethod = RegisteredMethod {methodType :: GRPCMethodType,
methodName :: MethodName,
methodHost :: Host,
methodHandle :: C.CallHandle}
-- | Represents one GRPC call (i.e. request). This type is used on both the
-- client and server. Contains pointers to all the necessary C state needed to
-- send and respond to a call.
-- This is used to associate send/receive 'Op's with a request.
-- There are separate functions for creating these depending on whether the
-- method is registered and whether the call is on the client or server side.
data Call = ClientCall {internalCall :: C.Call}
| ServerCall
{internalCall :: C.Call,
requestMetadataRecv :: (Ptr C.MetadataArray),
optionalPayload :: Maybe (Ptr C.ByteBuffer),
parentPtr :: Maybe (Ptr C.Call),
callDetails :: Maybe (C.CallDetails),
-- ^ used on the server for non-registered calls
--, to identify the endpoint being used.
callDeadline :: Maybe C.CTimeSpecPtr
}
debugCall :: Call -> IO ()
#ifdef DEBUG
debugCall (ClientCall (C.Call ptr)) =
grpcDebug $ "debugCall: client call: " ++ (show ptr)
debugCall call@(ServerCall (C.Call ptr) _ _ _ _ _) = do
grpcDebug $ "debugCall: server call: " ++ (show ptr)
grpcDebug $ "debugCall: metadata ptr: " ++ show (requestMetadataRecv call)
metadataArr <- peek (requestMetadataRecv call)
metadata <- C.getAllMetadataArray metadataArr
grpcDebug $ "debugCall: metadata received: " ++ (show metadata)
forM_ (optionalPayload call) $ \payloadPtr -> do
grpcDebug $ "debugCall: payload ptr: " ++ show payloadPtr
payload <- peek payloadPtr
bs <- C.copyByteBufferToByteString payload
grpcDebug $ "debugCall: payload contents: " ++ show bs
forM_ (parentPtr call) $ \parentPtr' -> do
grpcDebug $ "debugCall: parent ptr: " ++ show parentPtr'
(C.Call parent) <- peek parentPtr'
grpcDebug $ "debugCall: parent: " ++ show parent
forM_ (callDetails call) $ \(C.CallDetails callDetailsPtr) -> do
grpcDebug $ "debugCall: callDetails ptr: " ++ show callDetailsPtr
--TODO: need functions for getting data out of call_details.
forM_ (callDeadline call) $ \timespecptr -> do
grpcDebug $ "debugCall: deadline ptr: " ++ show timespecptr
timespec <- peek timespecptr
grpcDebug $ "debugCall: deadline: " ++ show (C.timeSpec timespec)
#else
{-# INLINE debugCall #-}
debugCall = const $ return ()
#endif
-- | Destroys a 'Call'.
destroyCall :: Call -> IO ()
destroyCall ClientCall{..} = do
grpcDebug "Destroying client-side call object."
C.grpcCallDestroy internalCall
destroyCall call@ServerCall{..} = do
grpcDebug "destroyCall: entered."
debugCall call
grpcDebug $ "Destroying server-side call object: " ++ show internalCall
C.grpcCallDestroy internalCall
grpcDebug $ "destroying metadata array: " ++ show requestMetadataRecv
C.metadataArrayDestroy requestMetadataRecv
grpcDebug $ "destroying optional payload" ++ show optionalPayload
forM_ optionalPayload C.destroyReceivingByteBuffer
grpcDebug $ "freeing parentPtr: " ++ show parentPtr
forM_ parentPtr free
grpcDebug $ "destroying call details" ++ show callDetails
forM_ callDetails C.destroyCallDetails
grpcDebug $ "destroying deadline." ++ show callDeadline
forM_ callDeadline C.timespecDestroy
2016-05-26 01:26:28 +02:00
_nowarn_unused :: a
_nowarn_unused =
castPtr `undefined`
(peek :: Ptr Int -> IO Int) `undefined`
()