+{-# LANGUAGE
+ DoAndIfThenElse
+ , RecordWildCards
+ , ScopedTypeVariables
+ , UnicodeSyntax
+ #-}
module Network.HTTP.Lucu.RequestReader
( requestReader
)
where
-
-import Control.Concurrent.STM
-import Control.Exception
-import Control.Monad
-import qualified Data.ByteString.Lazy.Char8 as B
-import Data.ByteString.Lazy.Char8 (ByteString)
-import Data.Maybe
+import Control.Applicative
+import Control.Concurrent.STM
+import Control.Exception hiding (block)
+import Control.Monad
+import qualified Data.Attoparsec.Lazy as LP
+import qualified Data.ByteString as Strict
+import qualified Data.ByteString.Lazy as Lazy
+import Data.Maybe
+import Data.Monoid.Unicode
import qualified Data.Sequence as S
-import Data.Sequence ((<|))
-import GHC.Conc (unsafeIOToSTM)
-import Network.Socket
-import Network.HTTP.Lucu.Config
-import Network.HTTP.Lucu.Chunk
-import Network.HTTP.Lucu.DefaultPage
-import Network.HTTP.Lucu.HandleLike
-import Network.HTTP.Lucu.Interaction
-import Network.HTTP.Lucu.Parser
-import Network.HTTP.Lucu.Postprocess
-import Network.HTTP.Lucu.Preprocess
-import Network.HTTP.Lucu.Request
-import Network.HTTP.Lucu.Response
-import Network.HTTP.Lucu.Resource.Tree
-import Prelude hiding (catch)
-import System.IO (stderr)
+import Data.Sequence.Unicode hiding ((∅))
+import Network.HTTP.Lucu.Config
+import Network.HTTP.Lucu.Chunk
+import Network.HTTP.Lucu.HandleLike
+import Network.HTTP.Lucu.Interaction
+import Network.HTTP.Lucu.Postprocess
+import Network.HTTP.Lucu.Preprocess
+import Network.HTTP.Lucu.Request
+import Network.HTTP.Lucu.Response
+import Network.HTTP.Lucu.Resource.Internal
+import Network.HTTP.Lucu.Resource.Tree
+import Network.Socket
+import Network.URI
+import Prelude.Unicode
+import System.IO (hPutStrLn, stderr)
+
+data Context h
+ = Context {
+ cConfig ∷ !Config
+ , cResTree ∷ !ResTree
+ , cFallbacks ∷ ![FallbackHandler]
+ , cHandle ∷ !h
+ , cPort ∷ !PortNumber
+ , cAddr ∷ !SockAddr
+ , cQueue ∷ !InteractionQueue
+ }
+data ChunkReceivingState
+ = Initial
+ | InChunk !Int -- ^Number of remaining octets in the current
+ -- chunk. It's always positive.
-requestReader :: HandleLike h => Config -> ResTree -> [FallbackHandler] -> h -> PortNumber -> SockAddr -> InteractionQueue -> IO ()
-requestReader !cnf !tree !fbs !h !port !addr !tQueue
- = do input <- hGetLBS h
- acceptRequest input
+requestReader ∷ HandleLike h
+ ⇒ Config
+ → ResTree
+ → [FallbackHandler]
+ → h
+ → PortNumber
+ → SockAddr
+ → InteractionQueue
+ → IO ()
+requestReader cnf tree fbs h port addr tQueue
+ = do input ← hGetLBS h
+ acceptRequest (Context cnf tree fbs h port addr tQueue) input
`catches`
- [ Handler (( \ _ -> return () ) :: IOException -> IO ())
- , Handler ( \ ThreadKilled -> return () )
- , Handler ( \ BlockedIndefinitelyOnSTM -> hPutStrLn stderr "requestReader: blocked indefinitely" )
- , Handler (( \ e -> hPutStrLn stderr (show e) ) :: SomeException -> IO ())
+ [ Handler handleAsyncE
+ , Handler handleBIOS
+ , Handler handleOthers
]
where
- acceptRequest :: ByteString -> IO ()
- acceptRequest input
- -- キューに最大パイプライン深度以上のリクエストが溜まってゐる
- -- 時は、それが限度以下になるまで待つ。
- = {-# SCC "acceptRequest" #-}
- do atomically $ do queue <- readTVar tQueue
- when (S.length queue >= cnfMaxPipelineDepth cnf)
- retry
+ handleAsyncE ∷ AsyncException → IO ()
+ handleAsyncE ThreadKilled = return ()
+ handleAsyncE e = dump e
+
+ handleBIOS ∷ BlockedIndefinitelyOnSTM → IO ()
+ handleBIOS = dump
- -- リクエストを讀む。パースできない場合は直ちに 400 Bad
- -- Request 應答を設定し、それを出力してから切斷するやう
- -- に ResponseWriter に通知する。
- case parse requestP input of
- (# Success req , input' #) -> acceptParsableRequest req input'
- (# IllegalInput, _ #) -> acceptNonparsableRequest BadRequest
- (# ReachedEOF , _ #) -> acceptNonparsableRequest BadRequest
+ handleOthers ∷ SomeException → IO ()
+ handleOthers = dump
- acceptNonparsableRequest :: StatusCode -> IO ()
- acceptNonparsableRequest status
- = {-# SCC "acceptNonparsableRequest" #-}
- do itr <- newInteraction cnf port addr Nothing Nothing
- atomically $ do updateItr itr itrResponse
- $ \ res -> res {
- resStatus = status
- }
- writeItr itr itrWillClose True
- writeItr itr itrState Done
- writeDefaultPage itr
- postprocess itr
- enqueue itr
+ dump ∷ Exception e ⇒ e → IO ()
+ dump e
+ = do hPutStrLn stderr "requestReader caught an exception:"
+ hPutStrLn stderr (show $ toException e)
- acceptParsableRequest :: Request -> ByteString -> IO ()
- acceptParsableRequest req input
- = {-# SCC "acceptParsableRequest" #-}
- do cert <- hGetPeerCert h
- itr <- newInteraction cnf port addr cert (Just req)
- action
- <- atomically $
- do preprocess itr
- isErr <- readItr itr itrResponse (isError . resStatus)
- if isErr then
- acceptSemanticallyInvalidRequest itr input
- else
- do rsrcM <- unsafeIOToSTM $ findResource tree fbs $ reqURI req
- case rsrcM of
- Nothing -- Resource が無かった
- -> acceptRequestForNonexistentResource itr input
+acceptRequest ∷ HandleLike h ⇒ Context h → Lazy.ByteString → IO ()
+acceptRequest ctx@(Context {..}) input
+ -- キューに最大パイプライン深度以上のリクエストが溜まってゐる時は、
+ -- それが限度以下になるまで待つ。
+ = do atomically $
+ do queue ← readTVar cQueue
+ when (S.length queue ≥ cnfMaxPipelineDepth cConfig) $
+ retry
+ -- リクエストを讀む。パースできない場合は直ちに 400 Bad
+ -- Request 應答を設定し、それを出力してから切斷するやうに
+ -- ResponseWriter に通知する。
+ case LP.parse requestP input of
+ LP.Done input' req → acceptParsableRequest ctx req input'
+ LP.Fail _ _ _ → acceptNonparsableRequest ctx BadRequest
- Just (rsrcPath, rsrcDef) -- あった
- -> acceptRequestForExistentResource itr input rsrcPath rsrcDef
- action
+acceptNonparsableRequest ∷ HandleLike h ⇒ Context h → StatusCode → IO ()
+acceptNonparsableRequest ctx@(Context {..}) sc
+ = do itr ← newInteraction cConfig cPort cAddr Nothing (Left sc)
+ atomically $
+ do writeTVar (itrState itr) Done
+ postprocess itr
+ enqueue ctx itr
- acceptSemanticallyInvalidRequest :: Interaction -> ByteString -> STM (IO ())
- acceptSemanticallyInvalidRequest itr input
- = {-# SCC "acceptSemanticallyInvalidRequest" #-}
- do writeItr itr itrState Done
- writeDefaultPage itr
- postprocess itr
- enqueue itr
- return $ acceptRequest input
+acceptParsableRequest ∷ HandleLike h
+ ⇒ Context h
+ → Request
+ → Lazy.ByteString
+ → IO ()
+acceptParsableRequest ctx@(Context {..}) req input
+ = do cert ← hGetPeerCert cHandle
+ itr ← newInteraction cConfig cPort cAddr cert (Right req)
+ join $ atomically
+ $ do isErr ← (isError ∘ resStatus) <$> readTVar (itrResponse itr)
+ if isErr then
+ acceptSemanticallyInvalidRequest ctx itr input
+ else
+ return $ acceptSemanticallyValidRequest ctx itr (reqURI req) input
- acceptRequestForNonexistentResource :: Interaction -> ByteString -> STM (IO ())
- acceptRequestForNonexistentResource itr input
- = {-# SCC "acceptRequestForNonexistentResource" #-}
- do updateItr itr itrResponse
- $ \res -> res {
- resStatus = NotFound
- }
- writeItr itr itrState Done
- writeDefaultPage itr
- postprocess itr
- enqueue itr
- return $ acceptRequest input
+acceptSemanticallyInvalidRequest ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → STM (IO ())
+acceptSemanticallyInvalidRequest ctx itr input
+ = do writeTVar (itrState itr) Done
+ postprocess itr
+ enqueue ctx itr
+ return $ acceptRequest ctx input
- acceptRequestForExistentResource :: Interaction -> ByteString -> [String] -> ResourceDef -> STM (IO ())
- acceptRequestForExistentResource oldItr input rsrcPath rsrcDef
- = {-# SCC "acceptRequestForExistentResource" #-}
- do let itr = oldItr { itrResourcePath = Just rsrcPath }
- requestHasBody <- readItr itr itrRequestHasBody id
- enqueue itr
- return $ do _ <- runResource rsrcDef itr
- if requestHasBody then
- observeRequest itr input
- else
- acceptRequest input
+acceptSemanticallyValidRequest ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → URI
+ → Lazy.ByteString
+ → IO ()
+acceptSemanticallyValidRequest ctx@(Context {..}) itr uri input
+ = do rsrcM ← findResource cResTree cFallbacks uri
+ case rsrcM of
+ Nothing
+ → acceptRequestForNonexistentResource ctx itr input
+ Just (rsrcPath, rsrcDef)
+ → acceptRequestForExistentResource ctx itr input rsrcPath rsrcDef
- observeRequest :: Interaction -> ByteString -> IO ()
- observeRequest itr input
- = {-# SCC "observeRequest" #-}
- do isChunked <- atomically $ readItr itr itrRequestIsChunked id
- if isChunked then
- observeChunkedRequest itr input
- else
- observeNonChunkedRequest itr input
+acceptRequestForNonexistentResource ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → IO ()
+acceptRequestForNonexistentResource ctx itr input
+ = do atomically $
+ do setResponseStatus itr NotFound
+ writeTVar (itrState itr) Done
+ postprocess itr
+ enqueue ctx itr
+ acceptRequest ctx input
- observeChunkedRequest :: Interaction -> ByteString -> IO ()
- observeChunkedRequest itr input
- = {-# SCC "observeChunkedRequest" #-}
- do action
- <- atomically $
- do isOver <- readItr itr itrReqChunkIsOver id
- if isOver then
- return $ acceptRequest input
- else
- do wantedM <- readItr itr itrReqBodyWanted id
- if wantedM == Nothing then
- do wasteAll <- readItr itr itrReqBodyWasteAll id
- if wasteAll then
- -- 破棄要求が來た
- do remainingM <- readItr itr itrReqChunkRemaining id
- if fmap (> 0) remainingM == Just True then
- -- 現在のチャンクをまだ
- -- 讀み終へてゐない
- do let (_, input') = B.splitAt (fromIntegral
- $ fromJust remainingM) input
- (# footerR, input'' #) = parse chunkFooterP input'
+acceptRequestForExistentResource ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → [Strict.ByteString]
+ → ResourceDef
+ → IO ()
+acceptRequestForExistentResource ctx oldItr input rsrcPath rsrcDef
+ = do let itr = oldItr { itrResourcePath = Just rsrcPath }
+ atomically $ enqueue ctx itr
+ do _ ← spawnResource rsrcDef itr
+ if reqMustHaveBody $ fromJust $ itrRequest itr then
+ waitForReceiveBodyReq ctx itr input
+ else
+ acceptRequest ctx input
- if footerR == Success () then
- -- チャンクフッタを正常に讀めた
- do writeItr itr itrReqChunkRemaining $ Just 0
-
- return $ observeChunkedRequest itr input''
- else
- return $ chunkWasMalformed itr
- else
- -- 次のチャンクを讀み始める
- seekNextChunk itr input
- else
- -- 要求がまだ來ない
- retry
- else
- -- 受信要求が來た
- do remainingM <- readItr itr itrReqChunkRemaining id
- if fmap (> 0) remainingM == Just True then
- -- 現在のチャンクをまだ讀み
- -- 終へてゐない
- do let wanted = fromJust wantedM
- remaining = fromJust remainingM
- bytesToRead = fromIntegral $ min wanted remaining
- (chunk, input') = B.splitAt bytesToRead input
- actualReadBytes = fromIntegral $ B.length chunk
- newWanted = case wanted - actualReadBytes of
- 0 -> Nothing
- n -> Just n
- newRemaining = Just $ remaining - actualReadBytes
- updateStates
- = do writeItr itr itrReqChunkRemaining newRemaining
- writeItr itr itrReqBodyWanted newWanted
- updateItr itr itrReceivedBody $ flip B.append chunk
+waitForReceiveBodyReq ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → IO ()
+waitForReceiveBodyReq ctx itr input
+ = case fromJust $ itrReqBodyLength itr of
+ Chunked
+ → waitForReceiveChunkedBodyReqForTheFirstTime ctx itr input
+ Fixed len
+ → waitForReceiveNonChunkedBodyReqForTheFirstTime ctx itr input len
- if newRemaining == Just 0 then
- -- チャンクフッタを讀む
- case parse chunkFooterP input' of
- (# Success _, input'' #)
- -> do updateStates
- return $ observeChunkedRequest itr input''
- (# _, _ #)
- -> return $ chunkWasMalformed itr
- else
- -- まだチャンクの終はりに達してゐない
- do updateStates
- return $ observeChunkedRequest itr input'
- else
- -- 次のチャンクを讀み始める
- seekNextChunk itr input
- action
+-- Toooooo long name for a function...
+waitForReceiveChunkedBodyReqForTheFirstTime ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → IO ()
+waitForReceiveChunkedBodyReqForTheFirstTime ctx itr@(Interaction {..}) input
+ = join $
+ atomically $
+ do req ← takeTMVar itrReceiveBodyReq
+ case req of
+ ReceiveBody wanted
+ → do putTMVar itrSendContinue $ fromJust itrExpectedContinue
+ return $ readCurrentChunk ctx itr input Initial wanted
+ WasteAll
+ → do putTMVar itrSendContinue False
+ return $ wasteAllChunks ctx itr input Initial
- seekNextChunk :: Interaction -> ByteString -> STM (IO ())
- seekNextChunk itr input
- = {-# SCC "seekNextChunk" #-}
- case parse chunkHeaderP input of
- -- 最終チャンク (中身が空)
- (# Success 0, input' #)
- -> case parse chunkTrailerP input' of
- (# Success _, input'' #)
- -> do writeItr itr itrReqChunkLength $ Nothing
- writeItr itr itrReqChunkRemaining $ Nothing
- writeItr itr itrReqChunkIsOver True
-
- return $ acceptRequest input''
- (# _, _ #)
- -> return $ chunkWasMalformed itr
- -- 最終でないチャンク
- (# Success len, input' #)
- -> do writeItr itr itrReqChunkLength $ Just len
- writeItr itr itrReqChunkRemaining $ Just len
-
- return $ observeChunkedRequest itr input'
- -- チャンクヘッダがをかしい
- (# _, _ #)
- -> return $ chunkWasMalformed itr
+waitForReceiveChunkedBodyReq ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → ChunkReceivingState
+ → IO ()
+waitForReceiveChunkedBodyReq ctx itr@(Interaction {..}) input st
+ = do req ← atomically $ takeTMVar itrReceiveBodyReq
+ case req of
+ ReceiveBody wanted
+ → readCurrentChunk ctx itr input st wanted
+ WasteAll
+ → wasteAllChunks ctx itr input st
- chunkWasMalformed :: Interaction -> IO ()
- chunkWasMalformed itr
- = {-# SCC "chunkWasMalformed" #-}
- atomically $ do updateItr itr itrResponse
- $ \ res -> res {
- resStatus = BadRequest
- }
- writeItr itr itrWillClose True
- writeItr itr itrState Done
- writeDefaultPage itr
- postprocess itr
+wasteAllChunks ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → ChunkReceivingState
+ → IO ()
+wasteAllChunks ctx itr = go
+ where
+ go ∷ Lazy.ByteString → ChunkReceivingState → IO ()
+ go input Initial
+ = case LP.parse chunkHeaderP input of
+ LP.Done input' chunkLen
+ | chunkLen ≡ 0 → gotFinalChunk input'
+ | otherwise → gotChunk input' chunkLen
+ LP.Fail _ _ _
+ → chunkWasMalformed itr
+ go input (InChunk chunkLen)
+ = gotChunk input chunkLen
+
+ gotChunk ∷ Lazy.ByteString → Int → IO ()
+ gotChunk input chunkLen
+ = let input' = Lazy.drop (fromIntegral chunkLen) input
+ in
+ case LP.parse chunkFooterP input' of
+ LP.Done input'' _
+ → go input'' Initial
+ LP.Fail _ _ _
+ → chunkWasMalformed itr
- observeNonChunkedRequest :: Interaction -> ByteString -> IO ()
- observeNonChunkedRequest itr input
- = {-# SCC "observeNonChunkedRequest" #-}
- do action
- <- atomically $
- do wantedM <- readItr itr itrReqBodyWanted id
- if wantedM == Nothing then
- do wasteAll <- readItr itr itrReqBodyWasteAll id
- if wasteAll then
- -- 破棄要求が來た
- do remainingM <- readItr itr itrReqChunkRemaining id
-
- let (_, input') = if remainingM == Nothing then
- (B.takeWhile (\ _ -> True) input, B.empty)
- else
- B.splitAt (fromIntegral $ fromJust remainingM) input
+ gotFinalChunk ∷ Lazy.ByteString → IO ()
+ gotFinalChunk input
+ = case LP.parse chunkFooterP input of
+ LP.Done input' _
+ → case LP.parse chunkTrailerP input' of
+ LP.Done input'' _
+ → acceptRequest ctx input''
+ LP.Fail _ _ _
+ → chunkWasMalformed itr
+ LP.Fail _ _ _
+ → chunkWasMalformed itr
- writeItr itr itrReqChunkRemaining $ Just 0
- writeItr itr itrReqChunkIsOver True
+readCurrentChunk ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → ChunkReceivingState
+ → Int
+ → IO ()
+readCurrentChunk ctx itr@(Interaction {..}) input0 st0 wanted
+ = go input0 st0
+ where
+ go ∷ Lazy.ByteString → ChunkReceivingState → IO ()
+ go input Initial
+ = case LP.parse chunkHeaderP input of
+ LP.Done input' chunkLen
+ | chunkLen ≡ 0
+ → gotFinalChunk input'
+ | otherwise
+ → gotChunk input' chunkLen
+ LP.Fail _ _ _
+ → chunkWasMalformed itr
+ go input (InChunk chunkLen)
+ = gotChunk input chunkLen
- return $ acceptRequest input'
- else
- -- 要求がまだ来ない
- retry
- else
- -- 受信要求が來た
- do remainingM <- readItr itr itrReqChunkRemaining id
+ gotChunk ∷ Lazy.ByteString → Int → IO ()
+ gotChunk input chunkLen
+ = do let bytesToRead = min wanted chunkLen
+ (block, input') = Lazy.splitAt (fromIntegral bytesToRead) input
+ block' = Strict.concat $ Lazy.toChunks block
+ actualReadBytes = Strict.length block'
+ chunkLen' = chunkLen - actualReadBytes
+ atomically $ putTMVar itrReceivedBody block'
+ if chunkLen' ≡ 0 then
+ case LP.parse chunkFooterP input' of
+ LP.Done input'' _
+ → waitForReceiveChunkedBodyReq ctx itr input'' Initial
+ LP.Fail _ _ _
+ → chunkWasMalformed itr
+ else
+ waitForReceiveChunkedBodyReq ctx itr input' $ InChunk chunkLen'
- let wanted = fromJust wantedM
- bytesToRead = fromIntegral $ maybe wanted (min wanted) remainingM
- (chunk, input') = B.splitAt bytesToRead input
- newRemaining = fmap
- (\ x -> x - (fromIntegral $ B.length chunk))
- remainingM
- isOver = B.length chunk < bytesToRead || newRemaining == Just 0
+ gotFinalChunk ∷ Lazy.ByteString → IO ()
+ gotFinalChunk input
+ = do atomically $ putTMVar itrReceivedBody (∅)
+ case LP.parse chunkFooterP input of
+ LP.Done input' _
+ → case LP.parse chunkTrailerP input' of
+ LP.Done input'' _
+ → acceptRequest ctx input''
+ LP.Fail _ _ _
+ → chunkWasMalformed itr
+ LP.Fail _ _ _
+ → chunkWasMalformed itr
- writeItr itr itrReqChunkRemaining newRemaining
- writeItr itr itrReqChunkIsOver isOver
- writeItr itr itrReqBodyWanted Nothing
- writeItr itr itrReceivedBody chunk
+chunkWasMalformed ∷ Interaction → IO ()
+chunkWasMalformed itr
+ -- FIXME: This is a totally wrong way to abort!
+ = atomically $
+ do setResponseStatus itr BadRequest
+ writeTVar (itrWillClose itr) True
+ writeTVar (itrState itr) Done
+ postprocess itr
+
+waitForReceiveNonChunkedBodyReqForTheFirstTime ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → Int
+ → IO ()
+waitForReceiveNonChunkedBodyReqForTheFirstTime ctx itr@(Interaction {..}) input bodyLen
+ = join $
+ atomically $
+ do req ← takeTMVar itrReceiveBodyReq
+ case req of
+ ReceiveBody wanted
+ → do putTMVar itrSendContinue $ fromJust itrExpectedContinue
+ return $ readNonChunkedRequestBody ctx itr input bodyLen wanted
+ WasteAll
+ → do putTMVar itrSendContinue False
+ return $ wasteNonChunkedRequestBody ctx input bodyLen
+
+waitForReceiveNonChunkedBodyReq ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → Int
+ → IO ()
+waitForReceiveNonChunkedBodyReq ctx itr@(Interaction {..}) input bodyLen
+ = do req ← atomically $ takeTMVar itrReceiveBodyReq
+ case req of
+ ReceiveBody wanted
+ → readNonChunkedRequestBody ctx itr input bodyLen wanted
+ WasteAll
+ → wasteNonChunkedRequestBody ctx input bodyLen
+
+wasteNonChunkedRequestBody ∷ HandleLike h
+ ⇒ Context h
+ → Lazy.ByteString
+ → Int
+ → IO ()
+wasteNonChunkedRequestBody ctx input bodyLen
+ = do let input' = Lazy.drop (fromIntegral bodyLen) input
+ acceptRequest ctx input'
+
+readNonChunkedRequestBody ∷ HandleLike h
+ ⇒ Context h
+ → Interaction
+ → Lazy.ByteString
+ → Int
+ → Int
+ → IO ()
+readNonChunkedRequestBody ctx itr@(Interaction {..}) input bodyLen wanted
+ | bodyLen ≡ 0 = gotEndOfRequest
+ | otherwise = gotBody
+ where
+ gotBody ∷ IO ()
+ gotBody
+ = do let bytesToRead = min wanted bodyLen
+ (block, input') = Lazy.splitAt (fromIntegral bytesToRead) input
+ block' = Strict.concat $ Lazy.toChunks block
+ actualReadBytes = Strict.length block'
+ bodyLen' = bodyLen - actualReadBytes
+ atomically $ putTMVar itrReceivedBody block'
+ waitForReceiveNonChunkedBodyReq ctx itr input' bodyLen'
- if isOver then
- return $ acceptRequest input'
- else
- return $ observeNonChunkedRequest itr input'
- action
+ gotEndOfRequest ∷ IO ()
+ gotEndOfRequest
+ = do atomically $ putTMVar itrReceivedBody (∅)
+ acceptRequest ctx input
- enqueue :: Interaction -> STM ()
- enqueue itr = {-# SCC "enqueue" #-}
- do queue <- readTVar tQueue
- writeTVar tQueue (itr <| queue)
\ No newline at end of file
+enqueue ∷ HandleLike h ⇒ Context h → Interaction → STM ()
+enqueue (Context {..}) itr
+ = do queue ← readTVar cQueue
+ writeTVar cQueue (itr ⊲ queue)