packages feed

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

module Network.Sendfile.Fallback (
    sendfile
  , sendfileWithHeader
  ) where

import Control.Monad.IO.Class (MonadIO(liftIO))
import Data.ByteString (ByteString)
import Data.Conduit
import qualified Data.Conduit.Binary as EB
import Network.Sendfile.Types
import Network.Socket
import qualified Network.Socket.ByteString as SB
import System.IO (Handle, IOMode(ReadMode), withBinaryFile)

-- |
-- Sendfile emulation using conduit.
--
--  - Used system calls: open(), stat(), read(), send() and close().

sendfile :: Socket -> FilePath -> FileRange -> IO () -> IO ()
sendfile sock path range hook = do
    checkRange range
    withBinaryFile path ReadMode $ \h -> sendHandle sock h range hook

-- |
-- Sendfile emulation using conduit.
--
--  - Used system calls: open(), stat(), read(), writev(), send() and close().
--
-- The file is opened before the header is sent, so nothing is sent
-- if the file cannot be opened.

sendfileWithHeader :: Socket -> FilePath -> FileRange -> IO () -> [ByteString] -> IO ()
sendfileWithHeader sock path range hook hdr = do
    checkRange range
    withBinaryFile path ReadMode $ \h -> do
        SB.sendMany sock hdr
        sendHandle sock h range hook

sendHandle :: Socket -> Handle -> FileRange -> IO () -> IO ()
sendHandle sock h range hook = runConduit $ source .| sinkSocket sock hook
  where
    source = case range of
        EntireFile         -> EB.sourceHandle h
        PartOfFile off len -> EB.sourceHandleRange h (Just off) (Just len)

-- See sinkHandle.
sinkSocket :: MonadIO m => Socket -> IO () -> ConduitT ByteString Void m ()
sinkSocket s hook = awaitForever $ \bs -> liftIO $ SB.sendAll s bs >> hook