packages feed

simple-sendfile-0.1.0: Network/Sendfile/Fallback.hs

module Network.Sendfile.Fallback (sendfile) where

import Control.Monad
import Control.Monad.IO.Class
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Enumerator
import Data.Enumerator.Binary as EB
import Data.Enumerator.List as EL
import Network.Sendfile.Types
import Network.Socket
import qualified Network.Socket.ByteString as SB

{-|
   Sendfile emulation using enumerator.

   - Used system calls: open(), stat(), read(), send() and close().
-}
sendfile :: Socket -> FilePath -> FileRange -> IO () -> IO ()
sendfile s fp EntireFile hook =
    run_ $ enumFile fp $$ sendIter s hook
sendfile s fp (PartOfFile off len) hook =
    run_ $ EB.enumFileRange fp (Just off) (Just len) $$ sendIter s hook

sendIter :: Socket -> IO () -> Iteratee ByteString IO ()
sendIter s hook = do
    mb <- EL.head
    case mb of
        Nothing -> return ()
        Just bs -> do
            liftIO $ sendLoop s bs (BS.length bs)
            liftIO hook -- FIXME: Is this a right place to call the hook?
            sendIter s hook

sendLoop :: Socket -> ByteString -> Int -> IO ()
sendLoop s bs len = do
    bytes <- SB.send s bs
    when (bytes /= len) $ sendLoop s (BS.drop bytes bs) (len - bytes)