@@ -7,71 +7,55 @@ module Network.Wai.Handler.Warp.HTTP2.Sender (frameSender) where
77#if __GLASGOW_HASKELL__ < 709
88import Control.Applicative
99#endif
10−import Control.Concurrent.MVar (putMVar)
1110import Control.Concurrent.STM
1211import qualified Control.Exception as E
13−import Control.Monad (void, when)
12+import Control.Monad (unless, void, when)
1413import qualified Data.ByteString as BS
15−import qualified Data.ByteString.Builder as B (int32BE)
1614import qualified Data.ByteString.Builder.Extra as B
17−import Data.Monoid ((<>))
1815import Foreign.Ptr
19−import qualified Network.HTTP.Types as H
20−import Network.HPACK (setLimitForEncoding)
2116import Network.HTTP2
2217import Network.HTTP2.Priority
23−import Network.Wai (FilePart(..))
24−import Network.Wai.HTTP2 (Trailers, promiseHeaders)
18+import Network.Wai
2519import Network.Wai.Handler.Warp.Buffer
2620import Network.Wai.Handler.Warp.HTTP2.EncodeFrame
2721import Network.Wai.Handler.Warp.HTTP2.HPACK
2822import Network.Wai.Handler.Warp.HTTP2.Types
2923import Network.Wai.Handler.Warp.IORef
3024import qualified Network.Wai.Handler.Warp.Settings as S
25+import qualified Network.Wai.Handler.Warp.Timeout as T
3126import Network.Wai.Handler.Warp.Types
27+import Network.Wai.Internal (Response(..))
28+import qualified System.PosixCompat.Files as P
3229
3330#ifdef WINDOWS
3431import qualified System.IO as IO
3532#else
36−import Network.Wai.Handler.Warp.FdCache (getFd)
33+import Network.Wai.Handler.Warp.FdCache
3734import Network.Wai.Handler.Warp.SendFile (positionRead)
38−import qualified Network.Wai.Handler.Warp.Timeout as T
3935import System.Posix.IO (openFd, OpenFileFlags(..), defaultFileFlags, OpenMode(ReadOnly), closeFd)
40−import System.Posix.Types (Fd)
36+import System.Posix.Types
4137#endif
4238
4339----------------------------------------------------------------
4440
45−-- | The platform-specific type of an open file to stream from. On Windows we
46−-- don't have pread, so this is just a Handle; on Unix platforms with pread,
47−-- this is a file descriptor supplied by the fd cache.
48−#ifdef WINDOWS
49−type OpenFile = IO.Handle
50−#else
51−type OpenFile = Fd
52−#endif
53−
5441data Leftover = LZero
5542 | LOne B.BufferWriter
5643 | LTwo BS.ByteString B.BufferWriter
57− | LFile OpenFile Integer Integer (IO ())
5844
5945----------------------------------------------------------------
6046
61−-- | Run the given action if the stream is not closed; handle any exceptions by
62−-- resetting the stream.
63−unlessClosed :: Connection -> Stream -> IO () -> IO Bool
64−unlessClosed Connection{connSendAll}
47+unlessClosed :: Context -> Connection -> Stream -> IO () -> IO ()
48+unlessClosed ctx
49+ Connection{connSendAll}
6550 strm@Stream{streamState,streamNumber}
6651 body = E.handle resetStream $ do
6752 state <- readIORef streamState
68− if (isClosed state) then return False else body >> return True
53+ unless (isClosed state) body
6954 where
7055 resetStream e = do
71− closed strm (ResetByMe e)
56+ closed ctx strm (ResetByMe e)
7257 let rst = resetFrame InternalError streamNumber
7358 connSendAll rst
74− return False
7559
7660getWindowSize :: TVar WindowSize -> TVar WindowSize -> IO WindowSize
7761getWindowSize connWindow strmWindow = do
@@ -85,7 +69,7 @@ getWindowSize connWindow strmWindow = do
8569 return $ min cw sw
8670
8771frameSender :: Context -> Connection -> InternalInfo -> S.Settings -> IO ()
88−frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
72+frameSender ctx@Context{outputQ,connectionWindow}
8973 conn@Connection{connWriteBuffer,connBufferSize,connSendAll}
9074 ii settings = go `E.catch` ignore
9175 where
@@ -106,112 +90,69 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
10690
10791 switch OFinish _ = return ()
10892 switch (OGoaway frame) _ = connSendAll frame
109− switch (OSettings frame alist) _ = do
110− connSendAll frame
111− case lookup SettingsHeaderTableSize alist of
112− Nothing -> return ()
113− Just siz -> do
114− dyntbl <- readIORef encodeDynamicTable
115− setLimitForEncoding siz dyntbl
116− loop
11793 switch (OFrame frame) _ = do
11894 connSendAll frame
11995 loop
120− switch (OResponse strm s h aux) pre = do
121− writeIORef (streamPrecedence strm) pre -- fixme
122− _ <- unlessClosed conn strm $
123− getWindowSize connectionWindow (streamWindow strm) >>=
124− sendResponse strm s h aux
96+ switch (OResponse strm rsp aux) pre = do
97+ writeIORef (streamPrecedence strm) pre
98+ unlessClosed ctx conn strm $ do
99+ lim <- getWindowSize connectionWindow (streamWindow strm)
100+ -- Header frame and Continuation frame
101+ let sid = streamNumber strm
102+ endOfStream = case aux of
103+ Persist{} -> False
104+ Oneshot hb -> not hb
105+ len <- headerContinue sid rsp endOfStream
106+ let total = len + frameHeaderLength
107+ case aux of
108+ Oneshot True -> do -- hasBody
109+ -- Data frame payload
110+ (off, _) <- sendHeadersIfNecessary total
111+ let payloadOff = off + frameHeaderLength
112+ Next datPayloadLen mnext <-
113+ fillResponseBodyGetNext conn ii payloadOff lim rsp
114+ fillDataHeaderSend strm total datPayloadLen mnext
115+ maybeEnqueueNext strm mnext
116+ Oneshot False -> do
117+ -- "closed" must be before "connSendAll". If not,
118+ -- the context would be switched to the receiver,
119+ -- resulting the inconsistency of concurrency.
120+ closed ctx strm Finished
121+ flushN total
122+ Persist sq tvar -> do
123+ (off, needSend) <- sendHeadersIfNecessary total
124+ let payloadOff = off + frameHeaderLength
125+ Next datPayloadLen mnext <-
126+ fillStreamBodyGetNext conn payloadOff lim sq tvar strm
127+ -- If no data was immediately available, avoid sending an
128+ -- empty data frame.
129+ if datPayloadLen > 0 then
130+ fillDataHeaderSend strm total datPayloadLen mnext
131+ else
132+ when needSend $ flushN off
133+ maybeEnqueueNext strm mnext
125134 loop
126135 switch (ONext strm curr) pre = do
127136 writeIORef (streamPrecedence strm) pre
128− _ <- unlessClosed conn strm $ do
137+ unlessClosed ctx conn strm $ do
129138 lim <- getWindowSize connectionWindow (streamWindow strm)
130139 -- Data frame payload
131140 Next datPayloadLen mnext <- curr lim
132− fillDataHeaderSend strm 0 datPayloadLen
133− dispatchNext strm mnext
134− loop
135− switch (OPush oldStrm push mvar strm s h aux) pre = do
136− writeIORef (streamPrecedence strm) pre -- fixme
137− pushed <- unlessClosed conn oldStrm $ do
138− lim <- getWindowSize connectionWindow (streamWindow strm)
139− -- Write and send the promise.
140− builder <- hpackEncodeCIHeaders ctx $ promiseHeaders push
141− off <- pushContinue (streamNumber oldStrm) (streamNumber strm) builder
142− flushN $ off + frameHeaderLength
143− -- TODO(awpr): refactor sendResponse to be able to handle non-zero
144− -- initial offsets and use that to potentially avoid the extra syscall.
145− sendResponse strm s h aux lim
146− putMVar mvar pushed
141+ fillDataHeaderSend strm 0 datPayloadLen mnext
142+ maybeEnqueueNext strm mnext
147143 loop
148144
149− -- Send the response headers and as much of the response as is immediately
150− -- available; shared by normal responses and pushed streams.
151− sendResponse :: Stream -> H.Status -> H.ResponseHeaders -> Aux -> WindowSize -> IO ()
152− sendResponse strm s h (Persist sq tvar) lim = do
153− -- Header frame and Continuation frame
154− let sid = streamNumber strm
155− builder <- hpackEncodeHeader ctx ii settings s h
156− len <- headerContinue sid builder False
157− let total = len + frameHeaderLength
158− (off, needSend) <- sendHeadersIfNecessary total
159− let payloadOff = off + frameHeaderLength
160− Next datPayloadLen mnext <-
161− fillStreamBodyGetNext ii conn payloadOff lim sq tvar strm
162− -- If no data was immediately available, avoid sending an
163− -- empty data frame.
164− if datPayloadLen > 0 then
165− fillDataHeaderSend strm total datPayloadLen
166− else
167− when needSend $ flushN off
168− dispatchNext strm mnext
169−
170− -- Send the stream's trailers and close the stream.
171− sendTrailers :: Stream -> Trailers -> IO ()
172− sendTrailers strm trailers = do
173− -- Trailers always indicate the end of a stream; send them in
174− -- consecutive header+continuation frames and end the stream. Some
175− -- clients dislike empty headers frames, so end the stream with an
176− -- empty data frame instead, as recommended by the spec.
177− toFlush <- case trailers of
178− [] -> frameHeaderLength <$ fillFrameHeader FrameData 0
179− (streamNumber strm)
180− (setEndStream defaultFlags)
181− connWriteBuffer
182− _ -> do
183− builder <- hpackEncodeCIHeaders ctx trailers
184− off <- headerContinue (streamNumber strm) builder True
185− return (off + frameHeaderLength)
186− -- 'closed' must be before 'flushN'. If not, the context would be
187− -- switched to the receiver, resulting in the inconsistency of
188− -- concurrency.
189− closed strm Finished
190− flushN toFlush
191−
192145 -- Flush the connection buffer to the socket, where the first 'n' bytes of
193146 -- the buffer are filled.
194147 flushN :: Int -> IO ()
195148 flushN n = bufferIO connWriteBuffer n connSendAll
196149
197− -- A flags value with the end-header flag set iff the argument is B.Done.
198− maybeEndHeaders B.Done = setEndHeader defaultFlags
199− maybeEndHeaders _ = defaultFlags
200−
201− -- Write PUSH_PROMISE and possibly CONTINUATION frames into the connection
202− -- buffer, using the given builder as their contents; flush them to the
203− -- socket as necessary.
204− pushContinue sid newSid builder = do
205− let builder' = B.int32BE (fromIntegral newSid) <> builder
206− (len, signal) <- B.runBuilder builder' bufHeaderPayload headerPayloadLim
207− let flag = maybeEndHeaders signal
208− fillFrameHeader FramePushPromise len sid flag connWriteBuffer
209− continue sid len signal
210−
211− -- Write HEADER and possibly CONTINUATION frames.
212− headerContinue sid builder endOfStream = do
150+ headerContinue sid rsp endOfStream = do
151+ builder <- hpackEncodeHeader ctx ii settings rsp
213152 (len, signal) <- B.runBuilder builder bufHeaderPayload headerPayloadLim
214− let flag0 = maybeEndHeaders signal
153+ let flag0 = case signal of
154+ B.Done -> setEndHeader defaultFlags
155+ _ -> defaultFlags
215156 flag = if endOfStream then setEndStream flag0 else flag0
216157 fillFrameHeader FrameHeaders len sid flag connWriteBuffer
217158 continue sid len signal
@@ -220,7 +161,9 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
220161 continue sid len (B.More _ writer) = do
221162 flushN $ len + frameHeaderLength
222163 (len', signal') <- writer bufHeaderPayload headerPayloadLim
223− let flag = maybeEndHeaders signal'
164+ let flag = case signal' of
165+ B.Done -> setEndHeader defaultFlags
166+ _ -> defaultFlags
224167 fillFrameHeader FrameContinuation len' sid flag connWriteBuffer
225168 continue sid len' signal'
226169 continue sid len (B.Chunk bs writer) = do
@@ -237,22 +180,16 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
237180 -- True if the connection buffer has room for a 1-byte data frame.
238181 canFitDataFrame total = total + frameHeaderLength < connBufferSize
239182
240− -- Take the appropriate action based on the given 'Control':
241− -- - If more output is immediately available, re-enqueue the stream in the
242− -- output queue.
243− -- - If the output is over and trailers are available, send them now and
244− -- end the stream.
245− -- - If we've drained the queue and handed the stream back to its waiter,
246− -- do nothing.
247− --
248− -- This is done after sending any part of the stream body, so it's shared
249− -- by 'sendResponse' and @switch (ONext ...)@.
250− dispatchNext :: Stream -> Control DynaNext -> IO ()
251− dispatchNext _ CNone = return ()
252− dispatchNext strm (CFinish trailers) = sendTrailers strm trailers
253− dispatchNext strm (CNext next) = do
183+ -- Re-enqueue the stream in the output queue if more output is immediately
184+ -- available; do nothing otherwise.
185+ maybeEnqueueNext :: Stream -> Control DynaNext -> IO ()
186+ maybeEnqueueNext strm (CNext next) = do
254187 let out = ONext strm next
255188 enqueueOrSpawnTemporaryWaiter strm outputQ out
189+ -- If the streaming is not finished, it must already have been
190+ -- written to the 'TVar' owned by 'waiter', which will
191+ -- put it back into the queue when more output becomes available.
192+ maybeEnqueueNext _ _ = return ()
256193
257194
258195 -- Send headers if there is not room for a 1-byte data frame, and return
@@ -264,12 +201,21 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
264201 flushN total
265202 return (0, False)
266203
267− fillDataHeaderSend strm otherLen datPayloadLen = do
204+ fillDataHeaderSend strm otherLen datPayloadLen mnext = do
268205 -- Data frame header
269206 let sid = streamNumber strm
270207 buf = connWriteBuffer `plusPtr` otherLen
271208 total = otherLen + frameHeaderLength + datPayloadLen
272− fillFrameHeader FrameData datPayloadLen sid defaultFlags buf
209+ flag = case mnext of
210+ CFinish -> setEndStream defaultFlags
211+ _ -> defaultFlags
212+ fillFrameHeader FrameData datPayloadLen sid flag buf
213+ -- "closed" must be before "connSendAll". If not,
214+ -- the context would be switched to the receiver,
215+ -- resulting the inconsistency of concurrency.
216+ case mnext of
217+ CFinish -> closed ctx strm Finished
218+ _ -> return ()
273219 flushN total
274220 atomically $ do
275221 modifyTVar' connectionWindow (subtract datPayloadLen)
@@ -281,108 +227,130 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
281227
282228----------------------------------------------------------------
283229
284−fillStreamBodyGetNext :: InternalInfo -> Connection -> Int -> WindowSize
285− -> TBQueue Sequence -> TVar Sync -> Stream -> IO Next
286−fillStreamBodyGetNext ii Connection{connWriteBuffer,connBufferSize}
287− off lim sq tvar strm = do
230+{-
231+ResponseFile Status ResponseHeaders FilePath (Maybe FilePart)
232+ResponseBuilder Status ResponseHeaders Builder
233+ResponseStream Status ResponseHeaders StreamingBody
234+ResponseRaw (IO ByteString -> (ByteString -> IO ()) -> IO ()) Response
235+-}
236+
237+fillResponseBodyGetNext :: Connection -> InternalInfo -> Int -> WindowSize -> Response -> IO Next
238+fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
239+ _ off lim (ResponseBuilder _ _ bb) = do
288240 let datBuf = connWriteBuffer `plusPtr` off
289241 room = min (connBufferSize - off) lim
290− (leftover, cont, len) <- runStreamBuilder ii datBuf room sq
291− nextForStream ii connWriteBuffer connBufferSize sq tvar strm leftover cont len
292−
293−----------------------------------------------------------------
294−
295−runStreamBuilder :: InternalInfo -> Buffer -> BufSize -> TBQueue Sequence
296− -> IO (Leftover, Maybe Trailers, BytesFilled)
297−runStreamBuilder ii buf0 room0 sq = loop buf0 room0 0
298− where
299− loop !buf !room !total = do
300− mbuilder <- atomically $ tryReadTBQueue sq
301− case mbuilder of
302− Nothing -> return (LZero, Nothing, total)
303− Just (SBuilder builder) -> do
304− (len, signal) <- B.runBuilder builder buf room
305− let !total' = total + len
306− case signal of
307− B.Done -> loop (buf `plusPtr` len) (room - len) total'
308− B.More _ writer -> return (LOne writer, Nothing, total')
309− B.Chunk bs writer -> return (LTwo bs writer, Nothing, total')
310− Just (SFile path part) -> do
311− (leftover, len) <- runStreamFile ii buf room path part
312− let !total' = total + len
313− case leftover of
314− LZero -> loop (buf `plusPtr` len) (room - len) total'
315− _ -> return (leftover, Nothing, total')
316−
317− Just SFlush -> return (LZero, Nothing, total)
318− Just (SFinish trailers) -> return (LZero, Just trailers, total)
319−
320−-- | Open the file and start reading into the send buffer.
321−runStreamFile :: InternalInfo -> Buffer -> BufSize -> FilePath -> FilePart
322− -> IO (Leftover, BytesFilled)
323−
324−-- | Read the given (OS-specific) file representation into the buffer. On
325−-- non-Windows systems this uses pread; on Windows this ignores the position
326−-- because we use the Handle's internal read position instead (because it's not
327−-- potentially shared with other readers).
328−readOpenFile :: OpenFile -> Buffer -> BufSize -> Integer -> IO Int
242+ (len, signal) <- B.runBuilder bb datBuf room
243+ return $ nextForBuilder connWriteBuffer connBufferSize len signal
329244
330245#ifdef WINDOWS
331−runStreamFile _ buf room path part = do
332− let start = filePartOffset part
333− bytes = filePartByteCount part
246+fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
247+ _ off lim (ResponseFile _ _ path mpart) = do
248+ let datBuf = connWriteBuffer `plusPtr` off
249+ room = min (connBufferSize - off) lim
250+ (start, bytes) <- fileStartEnd path mpart
334251 -- fixme: how to close Handle? GC does it at this moment.
335252 h <- IO.openBinaryFile path IO.ReadMode
336253 IO.hSeek h IO.AbsoluteSeek start
337− fillBufFile buf room h start bytes (return ())
338−
339−readOpenFile h buf room _ = IO.hGetBufSome h buf room
254+ len <- IO.hGetBufSome h datBuf (mini room bytes)
255+ let bytes' = bytes - fromIntegral len
256+ return $ nextForFile len connWriteBuffer connBufferSize h bytes' (return ())
340257#else
341−runStreamFile ii buf room path part = do
342− let start = filePartOffset part
343− bytes = filePartByteCount part
258+fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
259+ ii off lim (ResponseFile _ _ path mpart) = do
344260 (fd, refresh) <- case fdCacher ii of
345261 Just fdcache -> getFd fdcache path
346262 Nothing -> do
347263 fd' <- openFd path ReadOnly Nothing defaultFileFlags{nonBlock=True}
348264 th <- T.register (timeoutManager ii) (closeFd fd')
349265 return (fd', T.tickle th)
350− fillBufFile buf room fd start bytes refresh
351−
352−readOpenFile = positionRead
353−#endif
354−
355−-- | Read as much of the file as is currently available into the buffer, then
356−-- return a 'Leftover' to indicate whether this file chunk has more data to
357−-- send. If this read hit the end of the file range, return 'LZero'; otherwise
358−-- return 'LFile' so this stream will continue reading from the file the next
359−-- time it's pulled from the queue.
360−fillBufFile :: Buffer -> BufSize -> OpenFile -> Integer -> Integer -> (IO ())
361− -> IO (Leftover, BytesFilled)
362−fillBufFile buf room f start bytes refresh = do
363− len <- readOpenFile f buf (mini room bytes) start
266+ let datBuf = connWriteBuffer `plusPtr` off
267+ room = min (connBufferSize - off) lim
268+ (start, bytes) <- fileStartEnd path mpart
269+ len <- positionRead fd datBuf (mini room bytes) start
364270 refresh
365271 let len' = fromIntegral len
366− leftover = if bytes > len' then
367− LFile f (start + len') (bytes - len') refresh
368− else
369− LZero
370− return (leftover, len)
272+ return $ nextForFile len connWriteBuffer connBufferSize fd (start + len') (bytes - len') refresh
273+#endif
371274
372−mini :: Int -> Integer -> Int
373−mini i n
374− | fromIntegral i < n = i
375− | otherwise = fromIntegral n
275+fillResponseBodyGetNext _ _ _ _ _ = error "fillResponseBodyGetNext"
276+
277+fileStartEnd :: FilePath -> Maybe FilePart -> IO (Integer, Integer)
278+fileStartEnd path Nothing = do
279+ end <- fromIntegral . P.fileSize <$> P.getFileStatus path
280+ return (0, end)
281+fileStartEnd _ (Just part) =
282+ return (filePartOffset part, filePartByteCount part)
283+
284+----------------------------------------------------------------
285+
286+fillStreamBodyGetNext :: Connection -> Int -> WindowSize -> TBQueue Sequence -> TVar Sync -> Stream -> IO Next
287+fillStreamBodyGetNext Connection{connWriteBuffer,connBufferSize}
288+ off lim sq tvar strm = do
289+ let datBuf = connWriteBuffer `plusPtr` off
290+ room = min (connBufferSize - off) lim
291+ (leftover, cont, len) <- runStreamBuilder datBuf room sq
292+ nextForStream connWriteBuffer connBufferSize sq tvar strm leftover cont len
293+
294+----------------------------------------------------------------
295+
296+fillBufBuilder :: Buffer -> BufSize -> Leftover -> DynaNext
297+fillBufBuilder buf0 siz0 leftover lim = do
298+ let payloadBuf = buf0 `plusPtr` frameHeaderLength
299+ room = min (siz0 - frameHeaderLength) lim
300+ case leftover of
301+ LZero -> error "fillBufBuilder: LZero"
302+ LOne writer -> do
303+ (len, signal) <- writer payloadBuf room
304+ getNext len signal
305+ LTwo bs writer
306+ | BS.length bs <= room -> do
307+ buf1 <- copy payloadBuf bs
308+ let len1 = BS.length bs
309+ (len2, signal) <- writer buf1 (room - len1)
310+ getNext (len1 + len2) signal
311+ | otherwise -> do
312+ let (bs1,bs2) = BS.splitAt room bs
313+ void $ copy payloadBuf bs1
314+ getNext room (B.Chunk bs2 writer)
315+ where
316+ getNext l s = return $ nextForBuilder buf0 siz0 l s
317+
318+nextForBuilder :: Buffer -> BufSize -> BytesFilled -> B.Next -> Next
319+nextForBuilder _ _ len B.Done
320+ = Next len CFinish
321+nextForBuilder buf siz len (B.More _ writer)
322+ = Next len (CNext (fillBufBuilder buf siz (LOne writer)))
323+nextForBuilder buf siz len (B.Chunk bs writer)
324+ = Next len (CNext (fillBufBuilder buf siz (LTwo bs writer)))
325+
326+----------------------------------------------------------------
327+
328+runStreamBuilder :: Buffer -> BufSize -> TBQueue Sequence
329+ -> IO (Leftover, Bool, BytesFilled)
330+runStreamBuilder buf0 room0 sq = loop buf0 room0 0
331+ where
332+ loop !buf !room !total = do
333+ mbuilder <- atomically $ tryReadTBQueue sq
334+ case mbuilder of
335+ Nothing -> return (LZero, True, total)
336+ Just (SBuilder builder) -> do
337+ (len, signal) <- B.runBuilder builder buf room
338+ let !total' = total + len
339+ case signal of
340+ B.Done -> loop (buf `plusPtr` len) (room - len) total'
341+ B.More _ writer -> return (LOne writer, True, total')
342+ B.Chunk bs writer -> return (LTwo bs writer, True, total')
343+ Just SFlush -> return (LZero, True, total)
344+ Just SFinish -> return (LZero, False, total)
376345
377−fillBufStream :: InternalInfo -> Buffer -> BufSize -> Leftover
378− -> TBQueue Sequence -> TVar Sync -> Stream -> DynaNext
379−fillBufStream ii buf0 siz0 leftover0 sq tvar strm lim0 = do
346+fillBufStream :: Buffer -> BufSize -> Leftover -> TBQueue Sequence -> TVar Sync -> Stream -> DynaNext
347+fillBufStream buf0 siz0 leftover0 sq tvar strm lim0 = do
380348 let payloadBuf = buf0 `plusPtr` frameHeaderLength
381349 room0 = min (siz0 - frameHeaderLength) lim0
382350 case leftover0 of
383351 LZero -> do
384− (leftover, end, len) <- runStreamBuilder ii payloadBuf room0 sq
385− getNext leftover end len
352+ (leftover, cont, len) <- runStreamBuilder payloadBuf room0 sq
353+ getNext leftover cont len
386354 LOne writer -> write writer payloadBuf room0 0
387355 LTwo bs writer
388356 | BS.length bs <= room0 -> do
@@ -392,35 +360,71 @@ fillBufStream ii buf0 siz0 leftover0 sq tvar strm lim0 = do
392360 | otherwise -> do
393361 let (bs1,bs2) = BS.splitAt room0 bs
394362 void $ copy payloadBuf bs1
395− getNext (LTwo bs2 writer) Nothing room0
396− LFile fd start bytes refresh -> do
397− (leftover, len) <- fillBufFile payloadBuf room0 fd start bytes refresh
398− getNext leftover Nothing len
363+ getNext (LTwo bs2 writer) True room0
399364 where
400− getNext = nextForStream ii buf0 siz0 sq tvar strm
365+ getNext = nextForStream buf0 siz0 sq tvar strm
401366 write writer1 buf room sofar = do
402367 (len, signal) <- writer1 buf room
403368 case signal of
404369 B.Done -> do
405− (leftover, end, extra) <- runStreamBuilder ii (buf `plusPtr` len) (room - len) sq
370+ (leftover, cont, extra) <- runStreamBuilder (buf `plusPtr` len) (room - len) sq
406371 let !total = sofar + len + extra
407− getNext leftover end total
372+ getNext leftover cont total
408373 B.More _ writer -> do
409374 let !total = sofar + len
410− getNext (LOne writer) Nothing total
375+ getNext (LOne writer) True total
411376 B.Chunk bs writer -> do
412377 let !total = sofar + len
413− getNext (LTwo bs writer) Nothing total
414−
415−nextForStream :: InternalInfo -> Buffer -> BufSize -> TBQueue Sequence
416− -> TVar Sync -> Stream -> Leftover -> Maybe Trailers
417− -> BytesFilled -> IO Next
418−nextForStream _ _ _ _ tvar _ _ (Just trailers) len = do
419− atomically $ writeTVar tvar $ SyncFinish
420− return $ Next len $ CFinish trailers
421−nextForStream ii buf siz sq tvar strm LZero Nothing len = do
422− let out = ONext strm (fillBufStream ii buf siz LZero sq tvar strm)
378+ getNext (LTwo bs writer) True total
379+
380+nextForStream :: Buffer -> BufSize -> TBQueue Sequence -> TVar Sync -> Stream
381+ -> Leftover -> Bool -> BytesFilled
382+ -> IO Next
383+nextForStream _ _ _ tvar _ _ False len = do
384+ atomically $ writeTVar tvar SyncFinish
385+ return $ Next len CFinish
386+nextForStream buf siz sq tvar strm LZero True len = do
387+ let out = ONext strm (fillBufStream buf siz LZero sq tvar strm)
423388 atomically $ writeTVar tvar $ SyncNext out
424389 return $ Next len CNone
425−nextForStream ii buf siz sq tvar strm leftover Nothing len =
426− return $ Next len (CNext (fillBufStream ii buf siz leftover sq tvar strm))
390+nextForStream buf siz sq tvar strm leftover True len =
391+ return $ Next len (CNext (fillBufStream buf siz leftover sq tvar strm))
392+
393+----------------------------------------------------------------
394+
395+#ifdef WINDOWS
396+fillBufFile :: Buffer -> BufSize -> IO.Handle -> Integer -> IO () -> DynaNext
397+fillBufFile buf siz h bytes refresh lim = do
398+ let payloadBuf = buf `plusPtr` frameHeaderLength
399+ room = min (siz - frameHeaderLength) lim
400+ len <- IO.hGetBufSome h payloadBuf room
401+ refresh
402+ let bytes' = bytes - fromIntegral len
403+ return $ nextForFile len buf siz h bytes' refresh
404+
405+nextForFile :: BytesFilled -> Buffer -> BufSize -> IO.Handle -> Integer -> IO () -> Next
406+nextForFile 0 _ _ _ _ _ = Next 0 CFinish
407+nextForFile len _ _ _ 0 _ = Next len CFinish
408+nextForFile len buf siz h bytes refresh =
409+ Next len (CNext (fillBufFile buf siz h bytes refresh))
410+#else
411+fillBufFile :: Buffer -> BufSize -> Fd -> Integer -> Integer -> IO () -> DynaNext
412+fillBufFile buf siz fd start bytes refresh lim = do
413+ let payloadBuf = buf `plusPtr` frameHeaderLength
414+ room = min (siz - frameHeaderLength) lim
415+ len <- positionRead fd payloadBuf (mini room bytes) start
416+ let len' = fromIntegral len
417+ refresh
418+ return $ nextForFile len buf siz fd (start + len') (bytes - len') refresh
419+
420+nextForFile :: BytesFilled -> Buffer -> BufSize -> Fd -> Integer -> Integer -> IO () -> Next
421+nextForFile 0 _ _ _ _ _ _ = Next 0 CFinish
422+nextForFile len _ _ _ _ 0 _ = Next len CFinish
423+nextForFile len buf siz fd start bytes refresh =
424+ Next len (CNext (fillBufFile buf siz fd start bytes refresh))
425+#endif
426+
427+mini :: Int -> Integer -> Int
428+mini i n
429+ | fromIntegral i < n = i
430+ | otherwise = fromIntegral n