simple-sendfile-0.0.0: Network/Sendfile/BSD.hsc
{-# LANGUAGE ForeignFunctionInterface #-}
module Network.Sendfile.BSD (sendfile) where
import Control.Concurrent
import Control.Exception
import Control.Monad
import Foreign.C.Error (eAGAIN, eINTR, getErrno, throwErrno)
import Foreign.C.Types (CInt, CSize)
import Foreign.Marshal (alloca)
import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (peek)
import Network.Sendfile.Types
import Network.Socket
import System.Posix.IO
import System.Posix.Types (Fd(..), COff)
{-|
Simple binding for sendfile() of MacOS.
- Used system calls: open(), sendfile(), and close().
-}
sendfile :: Socket -> FilePath -> FileRange -> IO ()
sendfile sock path range = bracket
(openFd path ReadOnly Nothing defaultFileFlags)
closeFd
sendfile'
where
dst = Fd $ fdSocket sock
sendfile' fd = alloca $ \lenp -> do
case range of
EntireFile -> sendEntire dst fd 0 lenp
PartOfFile off len -> do
let off' = fromInteger off
len' = fromInteger len
sendPart dst fd off' len' lenp
sendEntire :: Fd -> Fd -> COff -> Ptr COff -> IO ()
sendEntire dst src off lenp = do
do rc <- c_sendfile src dst off 0 lenp
when (rc /= 0) $ do
errno <- getErrno
if errno == eAGAIN || errno == eINTR
then do
sent <- peek lenp
threadWaitWrite dst
sendEntire dst src (off + sent) lenp
else throwErrno "Network.SendFile.BSD.sendEntire"
sendPart :: Fd -> Fd -> COff -> CSize -> Ptr COff -> IO ()
sendPart dst src off len lenp = do
do rc <- c_sendfile src dst off len lenp
when (rc /= 0) $ do
errno <- getErrno
if errno == eAGAIN || errno == eINTR
then do
sent <- peek lenp
threadWaitWrite dst
let off' = off + sent
len' = len - fromIntegral sent
sendPart dst src off' len' lenp
else throwErrno "Network.SendFile.BSD.sendPart"
c_sendfile :: Fd -> Fd -> COff -> CSize -> Ptr COff -> IO CInt
c_sendfile fd s offset len lenp = c_sendfile' fd s offset len nullPtr lenp 0
foreign import ccall unsafe "sys/uio.h sendfile" c_sendfile'
:: Fd -> Fd -> COff -> CSize -> Ptr () -> Ptr COff -> CInt -> IO CInt