simple-sendfile-0.1.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 BSD.
- Used system calls: open(), sendfile(), and close().
The fourth action argument is called when a file is sent as chunks.
Chucking is inevitable if the socket is non-blocking (this is the
default) and the file is large. The action is called after a chunk
is sent and bofore waiting the socket to be ready for writing.
-}
sendfile :: Socket -> FilePath -> FileRange -> IO () -> IO ()
sendfile sock path range hook = bracket
(openFd path ReadOnly Nothing defaultFileFlags)
closeFd
sendfile'
where
dst = Fd $ fdSocket sock
sendfile' fd = alloca $ \lenp ->
case range of
EntireFile -> sendEntire dst fd 0 lenp hook
PartOfFile off len -> do
let off' = fromInteger off
len' = fromInteger len
sendPart dst fd off' len' lenp hook
sendEntire :: Fd -> Fd -> COff -> Ptr COff -> IO () -> IO ()
sendEntire dst src off lenp hook = do
rc <- c_sendfile src dst off 0 lenp
when (rc /= 0) $ do
errno <- getErrno
if errno `elem` [eAGAIN, eINTR]
then do
sent <- peek lenp
hook
threadWaitWrite dst
sendEntire dst src (off + sent) lenp hook
else throwErrno "Network.SendFile.BSD.sendEntire"
sendPart :: Fd -> Fd -> COff -> CSize -> Ptr COff -> IO () -> IO ()
sendPart dst src off len lenp hook = do
rc <- c_sendfile src dst off len lenp
when (rc /= 0) $ do
errno <- getErrno
if errno `elem` [eAGAIN, eINTR]
then do
sent <- peek lenp
let off' = off + sent
len' = len - fromIntegral sent
hook
threadWaitWrite dst
sendPart dst src off' len' lenp hook
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