@@ -7,71 +7,55 @@ module Network.Wai.Handler.Warp.HTTP2.Sender (frameSender) where
7#if __GLASGOW_HASKELL__ < 709
7#if __GLASGOW_HASKELL__ < 709
8import Control.Applicative
8import Control.Applicative
10−import Control.Concurrent.MVar (putMVar)
11import Control.Concurrent.STM
10import Control.Concurrent.STM
12import qualified Control.Exception as E
11import qualified Control.Exception as E
13−import Control.Monad (void, when)
12+import Control.Monad (unless, void, when)
14import qualified Data.ByteString as BS
13import qualified Data.ByteString as BS
15−import qualified Data.ByteString.Builder as B (int32BE)
16import qualified Data.ByteString.Builder.Extra as B
14import qualified Data.ByteString.Builder.Extra as B
17−import Data.Monoid ((<>))
18import Foreign.Ptr
15import Foreign.Ptr
19−import qualified Network.HTTP.Types as H
20−import Network.HPACK (setLimitForEncoding)
21import Network.HTTP2
16import Network.HTTP2
22import Network.HTTP2.Priority
17import Network.HTTP2.Priority
23−import Network.Wai (FilePart(..))
18+import Network.Wai
24−import Network.Wai.HTTP2 (Trailers, promiseHeaders)
25import Network.Wai.Handler.Warp.Buffer
19import Network.Wai.Handler.Warp.Buffer
26import Network.Wai.Handler.Warp.HTTP2.EncodeFrame
20import Network.Wai.Handler.Warp.HTTP2.EncodeFrame
27import Network.Wai.Handler.Warp.HTTP2.HPACK
21import Network.Wai.Handler.Warp.HTTP2.HPACK
28import Network.Wai.Handler.Warp.HTTP2.Types
22import Network.Wai.Handler.Warp.HTTP2.Types
29import Network.Wai.Handler.Warp.IORef
23import Network.Wai.Handler.Warp.IORef
30import qualified Network.Wai.Handler.Warp.Settings as S
24import qualified Network.Wai.Handler.Warp.Settings as S
25+import qualified Network.Wai.Handler.Warp.Timeout as T
31import Network.Wai.Handler.Warp.Types
26import Network.Wai.Handler.Warp.Types
27+import Network.Wai.Internal (Response(..))
28+import qualified System.PosixCompat.Files as P
33#ifdef WINDOWS
30#ifdef WINDOWS
34import qualified System.IO as IO
31import qualified System.IO as IO
36−import Network.Wai.Handler.Warp.FdCache (getFd)
33+import Network.Wai.Handler.Warp.FdCache
37import Network.Wai.Handler.Warp.SendFile (positionRead)
34import Network.Wai.Handler.Warp.SendFile (positionRead)
38−import qualified Network.Wai.Handler.Warp.Timeout as T
39import System.Posix.IO (openFd, OpenFileFlags(..), defaultFileFlags, OpenMode(ReadOnly), closeFd)
35import System.Posix.IO (openFd, OpenFileFlags(..), defaultFileFlags, OpenMode(ReadOnly), closeFd)
40−import System.Posix.Types (Fd)
36+import System.Posix.Types
43----------------------------------------------------------------
39----------------------------------------------------------------
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.
49−type OpenFile = IO.Handle
54data Leftover = LZero
41data Leftover = LZero
55 | LOne B.BufferWriter
42 | LOne B.BufferWriter
56 | LTwo BS.ByteString B.BufferWriter
43 | LTwo BS.ByteString B.BufferWriter
57− | LFile OpenFile Integer Integer (IO ())
59----------------------------------------------------------------
45----------------------------------------------------------------
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
47+unlessClosed :: Context -> Connection -> Stream -> IO () -> IO ()
64−unlessClosed Connection{connSendAll}
49+ Connection{connSendAll}
65 strm@Stream{streamState,streamNumber}
50 strm@Stream{streamState,streamNumber}
66 body = E.handle resetStream $ do
51 body = E.handle resetStream $ do
67 state <- readIORef streamState
52 state <- readIORef streamState
68− if (isClosed state) then return False else body >> return True
53+ unless (isClosed state) body
70 resetStream e = do
55 resetStream e = do
71− closed strm (ResetByMe e)
56+ closed ctx strm (ResetByMe e)
72 let rst = resetFrame InternalError streamNumber
57 let rst = resetFrame InternalError streamNumber
73 connSendAll rst
58 connSendAll rst
76getWindowSize :: TVar WindowSize -> TVar WindowSize -> IO WindowSize
60getWindowSize :: TVar WindowSize -> TVar WindowSize -> IO WindowSize
77getWindowSize connWindow strmWindow = do
61getWindowSize connWindow strmWindow = do
@@ -85,7 +69,7 @@ getWindowSize connWindow strmWindow = do
85 return $ min cw sw
69 return $ min cw sw
87frameSender :: Context -> Connection -> InternalInfo -> S.Settings -> IO ()
71frameSender :: Context -> Connection -> InternalInfo -> S.Settings -> IO ()
88−frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
72+frameSender ctx@Context{outputQ,connectionWindow}
89 conn@Connection{connWriteBuffer,connBufferSize,connSendAll}
73 conn@Connection{connWriteBuffer,connBufferSize,connSendAll}
90 ii settings = go `E.catch` ignore
74 ii settings = go `E.catch` ignore
@@ -106,112 +90,69 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
107 switch OFinish _ = return ()
91 switch OFinish _ = return ()
108 switch (OGoaway frame) _ = connSendAll frame
92 switch (OGoaway frame) _ = connSendAll frame
109− switch (OSettings frame alist) _ = do
111− case lookup SettingsHeaderTableSize alist of
112− Nothing -> return ()
114− dyntbl <- readIORef encodeDynamicTable
115− setLimitForEncoding siz dyntbl
117 switch (OFrame frame) _ = do
93 switch (OFrame frame) _ = do
118 connSendAll frame
94 connSendAll frame
120− switch (OResponse strm s h aux) pre = do
96+ switch (OResponse strm rsp aux) pre = do
121− writeIORef (streamPrecedence strm) pre -- fixme
97+ writeIORef (streamPrecedence strm) pre
122− _ <- unlessClosed conn strm $
98+ unlessClosed ctx conn strm $ do
123− getWindowSize connectionWindow (streamWindow strm) >>=
99+ lim <- getWindowSize connectionWindow (streamWindow strm)
124− sendResponse strm s h aux
100+ -- Header frame and Continuation frame
101+ let sid = streamNumber strm
102+ endOfStream = case aux of
104+ Oneshot hb -> not hb
105+ len <- headerContinue sid rsp endOfStream
106+ let total = len + frameHeaderLength
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
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
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
132+ when needSend $ flushN off
133+ maybeEnqueueNext strm mnext
126 switch (ONext strm curr) pre = do
135 switch (ONext strm curr) pre = do
127 writeIORef (streamPrecedence strm) pre
136 writeIORef (streamPrecedence strm) pre
128− _ <- unlessClosed conn strm $ do
137+ unlessClosed ctx conn strm $ do
129 lim <- getWindowSize connectionWindow (streamWindow strm)
138 lim <- getWindowSize connectionWindow (streamWindow strm)
130 -- Data frame payload
139 -- Data frame payload
131 Next datPayloadLen mnext <- curr lim
140 Next datPayloadLen mnext <- curr lim
132− fillDataHeaderSend strm 0 datPayloadLen
141+ fillDataHeaderSend strm 0 datPayloadLen mnext
133− dispatchNext strm mnext
142+ maybeEnqueueNext strm mnext
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
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
167− when needSend $ flushN off
168− dispatchNext strm mnext
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
180− (setEndStream defaultFlags)
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
189− closed strm Finished
192 -- Flush the connection buffer to the socket, where the first 'n' bytes of
145 -- Flush the connection buffer to the socket, where the first 'n' bytes of
193 -- the buffer are filled.
146 -- the buffer are filled.
194 flushN :: Int -> IO ()
147 flushN :: Int -> IO ()
195 flushN n = bufferIO connWriteBuffer n connSendAll
148 flushN n = bufferIO connWriteBuffer n connSendAll
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
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
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
213 (len, signal) <- B.runBuilder builder bufHeaderPayload headerPayloadLim
152 (len, signal) <- B.runBuilder builder bufHeaderPayload headerPayloadLim
214− let flag0 = maybeEndHeaders signal
153+ let flag0 = case signal of
154+ B.Done -> setEndHeader defaultFlags
215 flag = if endOfStream then setEndStream flag0 else flag0
156 flag = if endOfStream then setEndStream flag0 else flag0
216 fillFrameHeader FrameHeaders len sid flag connWriteBuffer
157 fillFrameHeader FrameHeaders len sid flag connWriteBuffer
217 continue sid len signal
158 continue sid len signal
@@ -220,7 +161,9 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
220 continue sid len (B.More _ writer) = do
161 continue sid len (B.More _ writer) = do
221 flushN $ len + frameHeaderLength
162 flushN $ len + frameHeaderLength
222 (len', signal') <- writer bufHeaderPayload headerPayloadLim
163 (len', signal') <- writer bufHeaderPayload headerPayloadLim
223− let flag = maybeEndHeaders signal'
164+ let flag = case signal' of
165+ B.Done -> setEndHeader defaultFlags
224 fillFrameHeader FrameContinuation len' sid flag connWriteBuffer
167 fillFrameHeader FrameContinuation len' sid flag connWriteBuffer
225 continue sid len' signal'
168 continue sid len' signal'
226 continue sid len (B.Chunk bs writer) = do
169 continue sid len (B.Chunk bs writer) = do
@@ -237,22 +180,16 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
237 -- True if the connection buffer has room for a 1-byte data frame.
180 -- True if the connection buffer has room for a 1-byte data frame.
238 canFitDataFrame total = total + frameHeaderLength < connBufferSize
181 canFitDataFrame total = total + frameHeaderLength < connBufferSize
240− -- Take the appropriate action based on the given 'Control':
183+ -- Re-enqueue the stream in the output queue if more output is immediately
241− -- - If more output is immediately available, re-enqueue the stream in the
243− -- - If the output is over and trailers are available, send them now and
245− -- - If we've drained the queue and handed the stream back to its waiter,
246− -- do nothing.
184+ -- available; do nothing otherwise.
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 ()
185+ maybeEnqueueNext :: Stream -> Control DynaNext -> IO ()
251− dispatchNext _ CNone = return ()
252− dispatchNext strm (CFinish trailers) = sendTrailers strm trailers
253− dispatchNext strm (CNext next) = do
186+ maybeEnqueueNext strm (CNext next) = do
254 let out = ONext strm next
187 let out = ONext strm next
255 enqueueOrSpawnTemporaryWaiter strm outputQ out
188 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 ()
258 -- Send headers if there is not room for a 1-byte data frame, and return
195 -- 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}
264 flushN total
201 flushN total
265 return (0, False)
202 return (0, False)
267− fillDataHeaderSend strm otherLen datPayloadLen = do
204+ fillDataHeaderSend strm otherLen datPayloadLen mnext = do
268 -- Data frame header
205 -- Data frame header
269 let sid = streamNumber strm
206 let sid = streamNumber strm
270 buf = connWriteBuffer `plusPtr` otherLen
207 buf = connWriteBuffer `plusPtr` otherLen
271 total = otherLen + frameHeaderLength + datPayloadLen
208 total = otherLen + frameHeaderLength + datPayloadLen
209+ flag = case mnext of
210+ CFinish -> setEndStream defaultFlags
272− fillFrameHeader FrameData datPayloadLen sid defaultFlags buf
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.
217+ CFinish -> closed ctx strm Finished
273 flushN total
219 flushN total
274 atomically $ do
220 atomically $ do
275 modifyTVar' connectionWindow (subtract datPayloadLen)
221 modifyTVar' connectionWindow (subtract datPayloadLen)
@@ -281,108 +227,130 @@ frameSender ctx@Context{outputQ,connectionWindow,encodeDynamicTable}
282----------------------------------------------------------------
228----------------------------------------------------------------
284−fillStreamBodyGetNext :: InternalInfo -> Connection -> Int -> WindowSize
230+{-
285− -> TBQueue Sequence -> TVar Sync -> Stream -> IO Next
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
237+fillResponseBodyGetNext :: Connection -> InternalInfo -> Int -> WindowSize -> Response -> IO Next
286−fillStreamBodyGetNext ii Connection{connWriteBuffer,connBufferSize}
238+fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
287− off lim sq tvar strm = do
239+ _ off lim (ResponseBuilder _ _ bb) = do
288 let datBuf = connWriteBuffer `plusPtr` off
240 let datBuf = connWriteBuffer `plusPtr` off
289 room = min (connBufferSize - off) lim
241 room = min (connBufferSize - off) lim
290− (leftover, cont, len) <- runStreamBuilder ii datBuf room sq
242+ (len, signal) <- B.runBuilder bb datBuf room
291− nextForStream ii connWriteBuffer connBufferSize sq tvar strm leftover cont len
243+ return $ nextForBuilder connWriteBuffer connBufferSize len signal
293−----------------------------------------------------------------
295−runStreamBuilder :: InternalInfo -> Buffer -> BufSize -> TBQueue Sequence
296− -> IO (Leftover, Maybe Trailers, BytesFilled)
297−runStreamBuilder ii buf0 room0 sq = loop buf0 room0 0
299− loop !buf !room !total = do
300− mbuilder <- atomically $ tryReadTBQueue sq
302− Nothing -> return (LZero, Nothing, total)
303− Just (SBuilder builder) -> do
304− (len, signal) <- B.runBuilder builder buf room
305− let !total' = total + len
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
314− LZero -> loop (buf `plusPtr` len) (room - len) total'
315− _ -> return (leftover, Nothing, total')
317− Just SFlush -> return (LZero, Nothing, total)
318− Just (SFinish trailers) -> return (LZero, Just trailers, total)
320−-- | Open the file and start reading into the send buffer.
321−runStreamFile :: InternalInfo -> Buffer -> BufSize -> FilePath -> FilePart
322− -> IO (Leftover, BytesFilled)
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
330#ifdef WINDOWS
245#ifdef WINDOWS
331−runStreamFile _ buf room path part = do
246+fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
332− let start = filePartOffset part
247+ _ off lim (ResponseFile _ _ path mpart) = do
333− bytes = filePartByteCount part
248+ let datBuf = connWriteBuffer `plusPtr` off
249+ room = min (connBufferSize - off) lim
250+ (start, bytes) <- fileStartEnd path mpart
334 -- fixme: how to close Handle? GC does it at this moment.
251 -- fixme: how to close Handle? GC does it at this moment.
335 h <- IO.openBinaryFile path IO.ReadMode
252 h <- IO.openBinaryFile path IO.ReadMode
336 IO.hSeek h IO.AbsoluteSeek start
253 IO.hSeek h IO.AbsoluteSeek start
337− fillBufFile buf room h start bytes (return ())
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 ())
341−runStreamFile ii buf room path part = do
258+fillResponseBodyGetNext Connection{connWriteBuffer,connBufferSize}
342− let start = filePartOffset part
259+ ii off lim (ResponseFile _ _ path mpart) = do
343− bytes = filePartByteCount part
344 (fd, refresh) <- case fdCacher ii of
260 (fd, refresh) <- case fdCacher ii of
345 Just fdcache -> getFd fdcache path
261 Just fdcache -> getFd fdcache path
346 Nothing -> do
262 Nothing -> do
347 fd' <- openFd path ReadOnly Nothing defaultFileFlags{nonBlock=True}
263 fd' <- openFd path ReadOnly Nothing defaultFileFlags{nonBlock=True}
348 th <- T.register (timeoutManager ii) (closeFd fd')
264 th <- T.register (timeoutManager ii) (closeFd fd')
349 return (fd', T.tickle th)
265 return (fd', T.tickle th)
350− fillBufFile buf room fd start bytes refresh
266+ let datBuf = connWriteBuffer `plusPtr` off
351−
267+ room = min (connBufferSize - off) lim
352−readOpenFile = positionRead
268+ (start, bytes) <- fileStartEnd path mpart
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
269+ len <- positionRead fd datBuf (mini room bytes) start
365 let len' = fromIntegral len
271 let len' = fromIntegral len
366− leftover = if bytes > len' then
367− LFile f (start + len') (bytes - len') refresh
272+ return $ nextForFile len connWriteBuffer connBufferSize fd (start + len') (bytes - len') refresh
370− return (leftover, len)
372−mini :: Int -> Integer -> Int
275+fillResponseBodyGetNext _ _ _ _ _ = error "fillResponseBodyGetNext"
277+fileStartEnd :: FilePath -> Maybe FilePart -> IO (Integer, Integer)
278+fileStartEnd path Nothing = do
374− | fromIntegral i < n = i
279+ end <- fromIntegral . P.fileSize <$> P.getFileStatus path
281+fileStartEnd _ (Just part) =
282+ return (filePartOffset part, filePartByteCount part)
284+----------------------------------------------------------------
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
294+----------------------------------------------------------------
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
301+ LZero -> error "fillBufBuilder: LZero"
303+ (len, signal) <- writer payloadBuf room
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
375− | otherwise = fromIntegral n
311+ | otherwise -> do
312+ let (bs1,bs2) = BS.splitAt room bs
313+ void $ copy payloadBuf bs1
314+ getNext room (B.Chunk bs2 writer)
316+ getNext l s = return $ nextForBuilder buf0 siz0 l s
318+nextForBuilder :: Buffer -> BufSize -> BytesFilled -> B.Next -> Next
319+nextForBuilder _ _ len B.Done
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)))
326+----------------------------------------------------------------
328+runStreamBuilder :: Buffer -> BufSize -> TBQueue Sequence
329+ -> IO (Leftover, Bool, BytesFilled)
330+runStreamBuilder buf0 room0 sq = loop buf0 room0 0
332+ loop !buf !room !total = do
333+ mbuilder <- atomically $ tryReadTBQueue sq
335+ Nothing -> return (LZero, True, total)
336+ Just (SBuilder builder) -> do
337+ (len, signal) <- B.runBuilder builder buf room
338+ let !total' = total + len
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)
377−fillBufStream :: InternalInfo -> Buffer -> BufSize -> Leftover
378− -> TBQueue Sequence -> TVar Sync -> Stream -> DynaNext
346+fillBufStream :: Buffer -> BufSize -> Leftover -> TBQueue Sequence -> TVar Sync -> Stream -> DynaNext
379−fillBufStream ii buf0 siz0 leftover0 sq tvar strm lim0 = do
347+fillBufStream buf0 siz0 leftover0 sq tvar strm lim0 = do
380 let payloadBuf = buf0 `plusPtr` frameHeaderLength
348 let payloadBuf = buf0 `plusPtr` frameHeaderLength
381 room0 = min (siz0 - frameHeaderLength) lim0
349 room0 = min (siz0 - frameHeaderLength) lim0
382 case leftover0 of
350 case leftover0 of
383 LZero -> do
351 LZero -> do
384− (leftover, end, len) <- runStreamBuilder ii payloadBuf room0 sq
352+ (leftover, cont, len) <- runStreamBuilder payloadBuf room0 sq
385− getNext leftover end len
353+ getNext leftover cont len
386 LOne writer -> write writer payloadBuf room0 0
354 LOne writer -> write writer payloadBuf room0 0
387 LTwo bs writer
355 LTwo bs writer
388 | BS.length bs <= room0 -> do
356 | BS.length bs <= room0 -> do
@@ -392,35 +360,71 @@ fillBufStream ii buf0 siz0 leftover0 sq tvar strm lim0 = do
392 | otherwise -> do
360 | otherwise -> do
393 let (bs1,bs2) = BS.splitAt room0 bs
361 let (bs1,bs2) = BS.splitAt room0 bs
394 void $ copy payloadBuf bs1
362 void $ copy payloadBuf bs1
395− getNext (LTwo bs2 writer) Nothing room0
363+ getNext (LTwo bs2 writer) True room0
396− LFile fd start bytes refresh -> do
397− (leftover, len) <- fillBufFile payloadBuf room0 fd start bytes refresh
398− getNext leftover Nothing len
400− getNext = nextForStream ii buf0 siz0 sq tvar strm
365+ getNext = nextForStream buf0 siz0 sq tvar strm
401 write writer1 buf room sofar = do
366 write writer1 buf room sofar = do
402 (len, signal) <- writer1 buf room
367 (len, signal) <- writer1 buf room
403 case signal of
368 case signal of
404 B.Done -> do
369 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
406 let !total = sofar + len + extra
371 let !total = sofar + len + extra
407− getNext leftover end total
372+ getNext leftover cont total
408 B.More _ writer -> do
373 B.More _ writer -> do
409 let !total = sofar + len
374 let !total = sofar + len
410− getNext (LOne writer) Nothing total
375+ getNext (LOne writer) True total
411 B.Chunk bs writer -> do
376 B.Chunk bs writer -> do
412 let !total = sofar + len
377 let !total = sofar + len
413− getNext (LTwo bs writer) Nothing total
378+ getNext (LTwo bs writer) True total
415−nextForStream :: InternalInfo -> Buffer -> BufSize -> TBQueue Sequence
380+nextForStream :: Buffer -> BufSize -> TBQueue Sequence -> TVar Sync -> Stream
416− -> TVar Sync -> Stream -> Leftover -> Maybe Trailers
417− -> BytesFilled -> IO Next
381+ -> Leftover -> Bool -> BytesFilled
418−nextForStream _ _ _ _ tvar _ _ (Just trailers) len = do
383+nextForStream _ _ _ tvar _ _ False len = do
419− atomically $ writeTVar tvar $ SyncFinish
384+ atomically $ writeTVar tvar SyncFinish
420− return $ Next len $ CFinish trailers
385+ return $ Next len CFinish
421−nextForStream ii buf siz sq tvar strm LZero Nothing len = do
386+nextForStream buf siz sq tvar strm LZero True len = do
422− let out = ONext strm (fillBufStream ii buf siz LZero sq tvar strm)
387+ let out = ONext strm (fillBufStream buf siz LZero sq tvar strm)
423 atomically $ writeTVar tvar $ SyncNext out
388 atomically $ writeTVar tvar $ SyncNext out
424 return $ Next len CNone
389 return $ Next len CNone
425−nextForStream ii buf siz sq tvar strm leftover Nothing len =
390+nextForStream buf siz sq tvar strm leftover True len =
426− return $ Next len (CNext (fillBufStream ii buf siz leftover sq tvar strm))
391+ return $ Next len (CNext (fillBufStream buf siz leftover sq tvar strm))
393+----------------------------------------------------------------
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
402+ let bytes' = bytes - fromIntegral len
403+ return $ nextForFile len buf siz h bytes' refresh
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))
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
418+ return $ nextForFile len buf siz fd (start + len') (bytes - len') refresh
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))
427+mini :: Int -> Integer -> Int
429+ | fromIntegral i < n = i
430+ | otherwise = fromIntegral n