packages feed

simple-sendfile-0.0.0: Network/Sendfile/MacOS.hsc

{-# LANGUAGE ForeignFunctionInterface #-}

module Network.Sendfile.MacOS (sendfile) where

import Control.Concurrent
import Control.Exception
import Control.Monad
import Data.Int (Int64)
import Foreign.C.Error (eAGAIN, eINTR, getErrno, throwErrno)
import Foreign.C.Types (CInt)
import Foreign.Marshal (alloca)
import Foreign.Ptr (Ptr, nullPtr)
import Foreign.Storable (peek, poke)
import Network.Sendfile.Types
import Network.Socket
import System.Posix.IO
import System.Posix.Types (Fd(..))

{-|
   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 -> do
                poke lenp 0
                sendEntire dst fd 0 lenp
            PartOfFile off len -> do
                let off' = fromInteger off
                poke lenp (fromInteger len)
                sendPart dst fd off' lenp

sendEntire :: Fd -> Fd -> (#type off_t) -> Ptr (#type off_t) -> IO ()
sendEntire dst src off lenp = do
    do rc <- c_sendfile src dst off lenp
       when (rc /= 0) $ do
           errno <- getErrno
           if errno == eAGAIN || errno == eINTR
              then do
                  sent <- peek lenp
                  poke lenp 0
                  threadWaitWrite dst
                  sendEntire dst src (off + sent) lenp
              else throwErrno "Network.SendFile.MacOS.sendEntire"

sendPart :: Fd -> Fd -> (#type off_t) -> Ptr (#type off_t) -> IO ()
sendPart dst src off lenp = do
    do len <- peek lenp
       rc <- c_sendfile src dst off lenp
       when (rc /= 0) $ do
           errno <- getErrno
           if errno == eAGAIN || errno == eINTR
              then do
                  sent <- peek lenp
                  poke lenp (len - sent)
                  threadWaitWrite dst
                  sendPart dst src (off + sent) lenp
              else throwErrno "Network.SendFile.MacOS.sendPart"

c_sendfile :: Fd -> Fd -> (#type off_t) -> Ptr (#type off_t) -> IO CInt
c_sendfile fd s offset lenp = c_sendfile' fd s offset lenp nullPtr 0

foreign import ccall unsafe "sys/uio.h sendfile" c_sendfile'
    :: Fd -> Fd -> (#type off_t) -> Ptr (#type off_t) -> Ptr () -> CInt -> IO CInt