import qualified Data.ByteString.Lazy as B
import Data.Char
import Data.Dynamic
+import Data.Int
import qualified Data.Map as M
import Data.Time.Clock.POSIX
import Data.Time.Format
import System.FilePath
import System.IO
import System.IO.Error (isDoesNotExistError, isPermissionError)
+import System.IO.Unsafe
import System.Locale
import System.Posix
import System.Posix.Handle
(checkIfRange mTime >>=) $ maybe (return Nothing) $ \() -> do
case parseRange range size of
Just (a, b) | a <= b -> return $ Just (a, b)
- _ -> throw BadRange
+ Just _ -> throw BadRange
+ Nothing -> return Nothing
outputAll :: Handle -> FileOffset -> CGI CGIResult
outputAll h size = do
setHeader "Content-Length" $ show size
outputFPS =<< liftIO (B.hGetContents h)
+-- | Lazily read a given number of bytes from the handle into a
+-- 'ByteString', then close the handle.
+hGetClose :: Handle -> Int64 -> IO B.ByteString
+hGetClose h len = do
+ contents <- B.hGetContents h
+ end <- unsafeInterleaveIO (hClose h >> return B.empty)
+ return (B.append (B.take len contents) end)
+
outputRange :: Handle -> FileOffset -> Maybe (FileOffset, FileOffset) -> CGI CGIResult
outputRange h size Nothing = outputAll h size
outputRange h size (Just (a, b)) = do
"bytes " ++ show a ++ "-" ++ show b ++ "/" ++ show size
setHeader "Content-Length" $ show len
liftIO $ hSeek h AbsoluteSeek (fromIntegral a)
- outputFPS =<< liftIO (B.hGet h (fromIntegral len))
+ outputFPS =<< liftIO (hGetClose h (fromIntegral len))
serveFile :: FilePath -> CGI CGIResult
serveFile file = (`catch` outputMyError) $ do