@@ -18,20 +18,34 @@ import Foreign.Ptr
18import qualified Network.HTTP.Types as H
18import qualified Network.HTTP.Types as H
19import Network.HTTP2
19import Network.HTTP2
20import Network.HTTP2.Priority
20import Network.HTTP2.Priority
21+import Network.Wai (FilePart(..))
21import Network.Wai.HTTP2 (Trailers, promiseHeaders)
22import Network.Wai.HTTP2 (Trailers, promiseHeaders)
22import Network.Wai.Handler.Warp.Buffer
23import Network.Wai.Handler.Warp.Buffer
24+import Network.Wai.Handler.Warp.FdCache (getFd)
25+import Network.Wai.Handler.Warp.SendFile (positionRead)
23import Network.Wai.Handler.Warp.HTTP2.EncodeFrame
26import Network.Wai.Handler.Warp.HTTP2.EncodeFrame
24import Network.Wai.Handler.Warp.HTTP2.HPACK
27import Network.Wai.Handler.Warp.HTTP2.HPACK
25import Network.Wai.Handler.Warp.HTTP2.Types
28import Network.Wai.Handler.Warp.HTTP2.Types
26import Network.Wai.Handler.Warp.IORef
29import Network.Wai.Handler.Warp.IORef
27import qualified Network.Wai.Handler.Warp.Settings as S
30import qualified Network.Wai.Handler.Warp.Settings as S
31+import qualified Network.Wai.Handler.Warp.Timeout as T
28import Network.Wai.Handler.Warp.Types
32import Network.Wai.Handler.Warp.Types
33+import System.Posix.IO (openFd, OpenFileFlags(..), defaultFileFlags, OpenMode(ReadOnly), closeFd)
34+import System.Posix.Types (Fd)
35+import qualified System.PosixCompat.Files as P
30----------------------------------------------------------------
37----------------------------------------------------------------
32data Leftover = LZero
39data Leftover = LZero
33 | LOne B.BufferWriter
40 | LOne B.BufferWriter
34 | LTwo BS.ByteString B.BufferWriter
41 | LTwo BS.ByteString B.BufferWriter
42+ | LFile OpenFile Integer Integer (IO ())
45+type OpenFile = IO.Handle
36----------------------------------------------------------------
50----------------------------------------------------------------
@@ -138,7 +152,7 @@ frameSender ctx@Context{outputQ,connectionWindow}
138 (off, needSend) <- sendHeadersIfNecessary total
152 (off, needSend) <- sendHeadersIfNecessary total
139 let payloadOff = off + frameHeaderLength
153 let payloadOff = off + frameHeaderLength
140 Next datPayloadLen mnext <-
154 Next datPayloadLen mnext <-
141− fillStreamBodyGetNext conn payloadOff lim sq tvar strm
155+ fillStreamBodyGetNext ii conn payloadOff lim sq tvar strm
142 -- If no data was immediately available, avoid sending an
156 -- If no data was immediately available, avoid sending an
143 -- empty data frame.
157 -- empty data frame.
144 if datPayloadLen > 0 then
158 if datPayloadLen > 0 then
@@ -240,19 +254,20 @@ frameSender ctx@Context{outputQ,connectionWindow}
241----------------------------------------------------------------
255----------------------------------------------------------------
243−fillStreamBodyGetNext :: Connection -> Int -> WindowSize -> TBQueue Sequence -> TVar Sync -> Stream -> IO Next
257+fillStreamBodyGetNext :: InternalInfo -> Connection -> Int -> WindowSize
258+ -> TBQueue Sequence -> TVar Sync -> Stream -> IO Next
244−fillStreamBodyGetNext Connection{connWriteBuffer,connBufferSize}
259+fillStreamBodyGetNext ii Connection{connWriteBuffer,connBufferSize}
245 off lim sq tvar strm = do
260 off lim sq tvar strm = do
246 let datBuf = connWriteBuffer `plusPtr` off
261 let datBuf = connWriteBuffer `plusPtr` off
247 room = min (connBufferSize - off) lim
262 room = min (connBufferSize - off) lim
248− (leftover, cont, len) <- runStreamBuilder datBuf room sq
263+ (leftover, cont, len) <- runStreamBuilder ii datBuf room sq
249− nextForStream connWriteBuffer connBufferSize sq tvar strm leftover cont len
264+ nextForStream ii connWriteBuffer connBufferSize sq tvar strm leftover cont len
251----------------------------------------------------------------
266----------------------------------------------------------------
253−runStreamBuilder :: Buffer -> BufSize -> TBQueue Sequence
268+runStreamBuilder :: InternalInfo -> Buffer -> BufSize -> TBQueue Sequence
254 -> IO (Leftover, Maybe Trailers, BytesFilled)
269 -> IO (Leftover, Maybe Trailers, BytesFilled)
255−runStreamBuilder buf0 room0 sq = loop buf0 room0 0
270+runStreamBuilder ii buf0 room0 sq = loop buf0 room0 0
257 loop !buf !room !total = do
272 loop !buf !room !total = do
258 mbuilder <- atomically $ tryReadTBQueue sq
273 mbuilder <- atomically $ tryReadTBQueue sq
@@ -265,16 +280,85 @@ runStreamBuilder buf0 room0 sq = loop buf0 room0 0
265 B.Done -> loop (buf `plusPtr` len) (room - len) total'
280 B.Done -> loop (buf `plusPtr` len) (room - len) total'
266 B.More _ writer -> return (LOne writer, Nothing, total')
281 B.More _ writer -> return (LOne writer, Nothing, total')
267 B.Chunk bs writer -> return (LTwo bs writer, Nothing, total')
282 B.Chunk bs writer -> return (LTwo bs writer, Nothing, total')
283+ Just (SFile path mpart) -> do
284+ (leftover, len) <- runStreamFile ii buf room path mpart
285+ let !total' = total + len
286+ return (leftover, Nothing, total')
287+ -- TODO if file part is done, go back to loop
268 Just SFlush -> return (LZero, Nothing, total)
288 Just SFlush -> return (LZero, Nothing, total)
269 Just (SFinish trailers) -> return (LZero, Just trailers, total)
289 Just (SFinish trailers) -> return (LZero, Just trailers, total)
291+-- | Open the file, determine the range we should send, and start reading into
293+runStreamFile :: InternalInfo -> Buffer -> BufSize -> FilePath -> Maybe FilePart
294+ -> IO (Leftover, BytesFilled)
296+-- | Read the given (OS-specific) file representation into the buffer. On
297+-- non-Windows systems this uses pread; on Windows this ignores the position
298+-- because we use the Handle's internal read position instead (because it's not
299+-- potentially shared with other readers).
300+readOpenFile :: OpenFile -> Buffer -> BufSize -> Integer -> IO Int
303+runStreamFile ii buf room path mpart = do
304+ (start, bytes) <- fileStartEnd path mpart
305+ -- fixme: how to close Handle? GC does it at this moment.
306+ h <- IO.openBinaryFile path IO.ReadMode
307+ IO.hSeek h IO.AbsoluteSeek start
308+ fillBufFile buf room h start bytes (return ())
310+readOpenFile h buf room _ = IO.hGetBufSome h buf room
312+runStreamFile ii buf room path mpart = do
313+ (start, bytes) <- fileStartEnd path mpart
314+ (fd, refresh) <- case fdCacher ii of
315+ Just fdcache -> getFd fdcache path
317+ fd' <- openFd path ReadOnly Nothing defaultFileFlags{nonBlock=True}
318+ th <- T.register (timeoutManager ii) (closeFd fd')
319+ return (fd', T.tickle th)
320+ fillBufFile buf room fd start bytes refresh
322+readOpenFile = positionRead
325+-- | Read as much of the file as is currently available into the buffer, then
326+-- return a 'Leftover' to indicate whether this file chunk has more data to
327+-- send. If this read hit the end of the file range, return 'LZero'; otherwise
328+-- return 'LFile' so this stream will continue reading from the file the next
329+-- time it's pulled from the queue.
330+fillBufFile :: Buffer -> BufSize -> OpenFile -> Integer -> Integer -> (IO ())
331+ -> IO (Leftover, BytesFilled)
332+fillBufFile buf room f start bytes refresh = do
333+ len <- readOpenFile f buf (mini room bytes) start
335+ let len' = fromIntegral len
336+ leftover = if bytes > len' then
337+ LFile f (start + len') (bytes - len') refresh
340+ return (leftover, len)
342+fileStartEnd :: FilePath -> Maybe FilePart -> IO (Integer, Integer)
343+fileStartEnd path Nothing = do
344+ end <- fromIntegral . P.fileSize <$> P.getFileStatus path
346+fileStartEnd _ (Just part) =
347+ return (filePartOffset part, filePartByteCount part)
349+mini :: Int -> Integer -> Int
351+ | fromIntegral i < n = i
352+ | otherwise = fromIntegral n
354+fillBufStream :: InternalInfo -> Buffer -> BufSize -> Leftover
271−fillBufStream :: Buffer -> BufSize -> Leftover -> TBQueue Sequence -> TVar Sync -> Stream -> DynaNext
355+ -> TBQueue Sequence -> TVar Sync -> Stream -> DynaNext
272−fillBufStream buf0 siz0 leftover0 sq tvar strm lim0 = do
356+fillBufStream ii buf0 siz0 leftover0 sq tvar strm lim0 = do
273 let payloadBuf = buf0 `plusPtr` frameHeaderLength
357 let payloadBuf = buf0 `plusPtr` frameHeaderLength
274 room0 = min (siz0 - frameHeaderLength) lim0
358 room0 = min (siz0 - frameHeaderLength) lim0
275 case leftover0 of
359 case leftover0 of
276 LZero -> do
360 LZero -> do
277− (leftover, end, len) <- runStreamBuilder payloadBuf room0 sq
361+ (leftover, end, len) <- runStreamBuilder ii payloadBuf room0 sq
278 getNext leftover end len
362 getNext leftover end len
279 LOne writer -> write writer payloadBuf room0 0
363 LOne writer -> write writer payloadBuf room0 0
280 LTwo bs writer
364 LTwo bs writer
@@ -286,13 +370,16 @@ fillBufStream buf0 siz0 leftover0 sq tvar strm lim0 = do
286 let (bs1,bs2) = BS.splitAt room0 bs
370 let (bs1,bs2) = BS.splitAt room0 bs
287 void $ copy payloadBuf bs1
371 void $ copy payloadBuf bs1
288 getNext (LTwo bs2 writer) Nothing room0
372 getNext (LTwo bs2 writer) Nothing room0
373+ LFile fd start bytes refresh -> do
374+ (leftover, len) <- fillBufFile payloadBuf room0 fd start bytes refresh
375+ getNext leftover Nothing len
290− getNext = nextForStream buf0 siz0 sq tvar strm
377+ getNext = nextForStream ii buf0 siz0 sq tvar strm
291 write writer1 buf room sofar = do
378 write writer1 buf room sofar = do
292 (len, signal) <- writer1 buf room
379 (len, signal) <- writer1 buf room
293 case signal of
380 case signal of
294 B.Done -> do
381 B.Done -> do
295− (leftover, end, extra) <- runStreamBuilder (buf `plusPtr` len) (room - len) sq
382+ (leftover, end, extra) <- runStreamBuilder ii (buf `plusPtr` len) (room - len) sq
296 let !total = sofar + len + extra
383 let !total = sofar + len + extra
297 getNext leftover end total
384 getNext leftover end total
298 B.More _ writer -> do
385 B.More _ writer -> do
@@ -302,15 +389,15 @@ fillBufStream buf0 siz0 leftover0 sq tvar strm lim0 = do
302 let !total = sofar + len
389 let !total = sofar + len
303 getNext (LTwo bs writer) Nothing total
390 getNext (LTwo bs writer) Nothing total
305−nextForStream :: Buffer -> BufSize -> TBQueue Sequence -> TVar Sync -> Stream
392+nextForStream :: InternalInfo -> Buffer -> BufSize -> TBQueue Sequence
306− -> Leftover -> Maybe Trailers -> BytesFilled
393+ -> TVar Sync -> Stream -> Leftover -> Maybe Trailers
307− -> IO Next
394+ -> BytesFilled -> IO Next
308−nextForStream _ _ _ tvar _ _ (Just trailers) len = do
395+nextForStream _ _ _ _ tvar _ _ (Just trailers) len = do
309 atomically $ writeTVar tvar $ SyncFinish trailers
396 atomically $ writeTVar tvar $ SyncFinish trailers
310 return $ Next len CNone
397 return $ Next len CNone
311−nextForStream buf siz sq tvar strm LZero Nothing len = do
398+nextForStream ii buf siz sq tvar strm LZero Nothing len = do
312− let out = ONext strm (fillBufStream buf siz LZero sq tvar strm)
399+ let out = ONext strm (fillBufStream ii buf siz LZero sq tvar strm)
313 atomically $ writeTVar tvar $ SyncNext out
400 atomically $ writeTVar tvar $ SyncNext out
314 return $ Next len CNone
401 return $ Next len CNone
315−nextForStream buf siz sq tvar strm leftover Nothing len =
402+nextForStream ii buf siz sq tvar strm leftover Nothing len =
316− return $ Next len (CNext (fillBufStream buf siz leftover sq tvar strm))
403+ return $ Next len (CNext (fillBufStream ii buf siz leftover sq tvar strm))