HsOpenSSL-0.4: OpenSSL/SocketBIO.hsc
-- | This module wraps a Haskell Socket in a BIO. It's different from the
-- @BIO@ module in that it works the 'other way' - gather than being the
-- client of a BIO, we are the implementation.
module OpenSSL.SocketBIO (socketToBIO) where
import Foreign
import Foreign.C
import Network.Socket (Socket(..))
import GHC.Conc (threadWaitRead, threadWaitWrite)
import OpenSSL.BIO (BIO, BIO_, wrapBioPtr)
foreign import ccall "socket_BIO_wrapper" _wrapper :: CInt -> IO (Ptr BIO_)
-- | Convert a Socket into a BIO object. Warning: the file descriptor
-- underlying this Socket is saved in the BIO, but it doesn't carry a
-- reference to the Socket itself. Thus, if you don't keep your own reference
-- then the GC could close the socket from under the BIO.
socketToBIO :: Socket -> IO BIO
socketToBIO (MkSocket s _ _ _ _) = _wrapper s >>= wrapBioPtr
foreign import ccall unsafe "send"
c_send :: CInt -> Ptr a -> CSize -> CInt -> IO CInt
foreign import ccall unsafe "recv"
c_recv :: CInt -> Ptr CChar -> CSize -> CInt -> IO CInt
--------------------------------------------------------------------------------
-- Taken from network-bytestring
{-# SPECIALISE
throwErrnoIfMinus1Retry_mayBlock
:: String -> IO CInt -> IO CInt -> IO CInt #-}
throwErrnoIfMinus1Retry_mayBlock :: Num a => String -> IO a -> IO a -> IO a
throwErrnoIfMinus1Retry_mayBlock name on_block act = do
res <- act
if res == -1
then do
err <- getErrno
if err == eINTR
then throwErrnoIfMinus1Retry_mayBlock name on_block act
else if err == eWOULDBLOCK || err == eAGAIN
then on_block
else return (-1)
else return res
throwErrnoIfMinus1Retry_repeatOnBlock :: Num a => String -> IO b -> IO a -> IO a
throwErrnoIfMinus1Retry_repeatOnBlock name on_block act = do
throwErrnoIfMinus1Retry_mayBlock name (on_block >> repeat) act
where repeat = throwErrnoIfMinus1Retry_repeatOnBlock name on_block act
--------------------------------------------------------------------------------
-- The following functions are callback Haskell functions which are exported to
-- the C code
bioRead :: CInt -> Ptr Word8 -> CInt -> IO CInt
bioRead fd ptr nbytes = do
len <- throwErrnoIfMinus1Retry_repeatOnBlock "bioRead"
(threadWaitRead (fromIntegral fd)) $
c_recv fd (castPtr ptr) (fromIntegral nbytes) 0
if fromIntegral len == (-1 :: Int)
then do errno <- getErrno
if errno == eINTR
then bioRead fd ptr nbytes
else return (-1)
else return len
bioWrite :: CInt -> Ptr Word8 -> CInt -> IO CInt
bioWrite fd ptr nbytes = do
len <- throwErrnoIfMinus1Retry_repeatOnBlock "bioWrite"
(threadWaitWrite (fromIntegral fd)) $
c_send fd (castPtr ptr) (fromIntegral nbytes) 0
return len
foreign export ccall "bioRead" bioRead :: CInt -> Ptr Word8 -> CInt -> IO CInt
foreign export ccall "bioWrite" bioWrite :: CInt -> Ptr Word8 -> CInt -> IO CInt