server/Thrift/Server/ProcessorCallback.hsc (85 lines of code) (raw):
-- Copyright (c) Facebook, Inc. and its affiliates.
module Thrift.Server.ProcessorCallback
( withProcessorCallback
, makeProcessorCallback
, deleteProcessorCallback
, ProcessorCallback
) where
import Control.Exception
import Data.ByteString (ByteString)
import qualified Data.ByteString.UTF8 as UTF8
import qualified Data.ByteString.Unsafe as BS
import Data.Typeable (typeOf)
import Foreign
import Foreign.C
import Thrift.Monad
import Thrift.Processor
import Thrift.Protocol.Id
#include <cpp/HaskellProcessor.h>
withProcessorCallback :: (Processor s)
=> (forall r . s r -> IO r) -- ^ handler to use
-> (FunPtr ProcessorCallback -> IO a)
-> IO a
withProcessorCallback handler =
bracket (makeProcessorCallback handler) deleteProcessorCallback
makeProcessorCallback :: (Processor s)
=> (forall r . s r -> IO r) -- ^ handler to use
-> IO (FunPtr ProcessorCallback)
makeProcessorCallback handler = do
counter <- newCounter
mkProcessorCallback $ handlerWrapper counter handler
deleteProcessorCallback :: FunPtr ProcessorCallback -> IO ()
deleteProcessorCallback = freeHaskellFunPtr
-- -----------------------------------------------------------------------------
data TResponse
-- | A function that will be called from C back into Haskell
-- Takes protocol id, input data, input length, pointer to fill with output
-- length, and returns a newly malloc'd set of bytes
type ProcessorCallback = CUShort -> CString -> CSize -> Ptr TResponse -> IO ()
-- Entry point for every request coming into Haskell.
handlerWrapper :: (Processor s)
=> Counter
-> (forall r . s r -> IO r)
-> ProcessorCallback
handlerWrapper counter handler prot_id input_str input_len response_ptr = do
seqNum <- counter
input <- BS.unsafePackCStringLen (input_str, fromIntegral input_len)
(res, exc) <- withProxy (fromIntegral prot_id) $ \proxy ->
process proxy seqNum handler input
(output_str, output_len) <- newByteStringAsCStringLen res
#{poke apache::thrift::TResponse, data} response_ptr output_str
#{poke apache::thrift::TResponse, len} response_ptr
(fromIntegral output_len :: CSize)
case exc of
Just (SomeException ex, blame) -> do
(ex_name, ex_name_len) <- newByteStringAsCStringLen
$ UTF8.fromString
$ show
$ typeOf ex
(ex_text, ex_text_len) <- newByteStringAsCStringLen
$ UTF8.fromString
$ take 1024
$ show ex
#{poke apache::thrift::TResponse, ex_name} response_ptr ex_name
#{poke apache::thrift::TResponse, ex_name_len} response_ptr
(fromIntegral ex_name_len :: CSize)
#{poke apache::thrift::TResponse, ex_text} response_ptr ex_text
#{poke apache::thrift::TResponse, ex_text_len} response_ptr
(fromIntegral ex_text_len :: CSize)
#{poke apache::thrift::TResponse, client_error} response_ptr
(blame == ClientError)
Nothing -> do
#{poke apache::thrift::TResponse, ex_name} response_ptr nullPtr
#{poke apache::thrift::TResponse, ex_name_len} response_ptr (0 :: CSize)
#{poke apache::thrift::TResponse, ex_text} response_ptr nullPtr
#{poke apache::thrift::TResponse, ex_text_len} response_ptr (0 :: CSize)
where
-- Allocates a new buffer to give away ownership of memory
newByteStringAsCStringLen :: ByteString -> IO CStringLen
newByteStringAsCStringLen bs =
BS.unsafeUseAsCStringLen bs $ \(src, len) -> do
buf <- mallocBytes len
copyBytes buf src len
return (buf, len)
foreign import ccall "wrapper"
mkProcessorCallback :: ProcessorCallback -> IO (FunPtr ProcessorCallback)