]> gitweb @ CieloNegro.org - Lucu.git/blobdiff - Network/HTTP/Lucu/RequestReader.hs
The library now compiles, and I'm now working on ImplantFile.hs
[Lucu.git] / Network / HTTP / Lucu / RequestReader.hs
index cfc991a1ef5a70f415daf08ac40f6f4a10d17134..49317a99ea8343270f222b7061c8bdd8c00cb322 100644 (file)
+{-# 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
+import Control.Monad
+import qualified Data.Attoparsec.Lazy as LP
+import qualified Data.ByteString.Lazy as Lazy
+import Data.Maybe
 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
+import Data.Text (Text)
+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.Postprocess
+import Network.HTTP.Lucu.Preprocess
+import Network.HTTP.Lucu.Request
+import Network.HTTP.Lucu.Response
+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
+      }
 
-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 $ \ (_ ∷ IOException)        → return ()
+      , Handler $ \ e → case e of
+                           ThreadKilled      → return ()
+                           _                 → hPutStrLn stderr (show e)
+      , Handler $ \ BlockedIndefinitelyOnSTM → hPutStrLn stderr "requestReader: blocked indefinitely"
+      , Handler $ \ (e ∷ SomeException)      → hPutStrLn stderr (show e)
       ]
-    where
-      acceptRequest :: ByteString -> IO ()
-      acceptRequest input
-          -- キューに最大パイプライン深度以上のリクエストが溜まってゐる
-          -- 時は、それが限度以下になるまで待つ。
-          = {-# SCC "acceptRequest" #-}
-            do atomically $ do queue    <- readTVar tQueue
-                               when (S.length queue >= cnfMaxPipelineDepth cnf)
-                                    retry
-
-               -- リクエストを讀む。パースできない場合は直ちに 400 Bad
-               -- Request 應答を設定し、それを出力してから切斷するやう
-               -- に ResponseWriter に通知する。
-               case parse requestP input of
-                 (# Success req , input' #) -> acceptParsableRequest req input'
-                 (# IllegalInput, _      #) -> acceptNonparsableRequest BadRequest
-                 (# ReachedEOF  , _      #) -> acceptNonparsableRequest BadRequest
-
-      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
-
-      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
 
-                                  Just (rsrcPath, rsrcDef) -- あった
-                                      -> acceptRequestForExistentResource itr input rsrcPath rsrcDef
-               action
+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
 
-      acceptSemanticallyInvalidRequest :: Interaction -> ByteString -> STM (IO ())
-      acceptSemanticallyInvalidRequest itr input
-          = {-# SCC "acceptSemanticallyInvalidRequest" #-}
-            do writeItr itr itrState Done
-               writeDefaultPage itr
-               postprocess itr
-               enqueue itr
-               return $ acceptRequest input
+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
+                writeDefaultPage itr
+                postprocess itr
+                enqueue ctx itr
 
-      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
+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
 
-      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
+acceptSemanticallyInvalidRequest ∷ HandleLike h
+                                 ⇒ Context h
+                                 → Interaction
+                                 → Lazy.ByteString
+                                 → STM (IO ())
+acceptSemanticallyInvalidRequest ctx itr input
+    = do writeTVar (itrState itr) Done
+         writeDefaultPage itr
+         postprocess itr
+         enqueue ctx itr
+         return $ acceptRequest ctx input
 
-      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
+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
 
-      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'
+acceptRequestForNonexistentResource ∷ HandleLike h
+                                    ⇒ Context h
+                                    → Interaction
+                                    → Lazy.ByteString
+                                    → IO ()
+acceptRequestForNonexistentResource ctx itr input
+    = do atomically $
+             do setResponseStatus itr NotFound
+                writeTVar (itrState itr) Done
+                writeDefaultPage itr
+                postprocess itr
+                enqueue ctx itr
+         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
+acceptRequestForExistentResource ∷ HandleLike h
+                                 ⇒ Context h
+                                 → Interaction
+                                 → Lazy.ByteString
+                                 → [Text]
+                                 → ResourceDef
+                                 → IO ()
+acceptRequestForExistentResource ctx oldItr input rsrcPath rsrcDef
+    = do let itr = oldItr { itrResourcePath = Just rsrcPath }
+         atomically $ enqueue ctx itr
+         do _ ← runResource rsrcDef itr
+            if reqHasBody $ fromJust $ itrRequest itr then
+                observeRequest ctx itr input
+            else
+                acceptRequest ctx input
 
-                                              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
+observeRequest ∷ HandleLike h
+               ⇒ Context h
+               → Interaction
+               → Lazy.ByteString
+               → IO ()
+observeRequest ctx itr input
+    = case fromJust $ itrReqBodyLength itr of
+        Chunked
+            → observeChunkedRequest ctx itr input 0
+        Fixed len
+            → observeNonChunkedRequest ctx itr input len
 
-      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
+observeChunkedRequest ∷ HandleLike h
+                      ⇒ Context h
+                      → Interaction
+                      → Lazy.ByteString
+                      → Int
+                      → IO ()
+observeChunkedRequest ctx itr input remaining
+    = join $
+      atomically $
+      do isOver ← readTVar $ itrReqChunkIsOver itr
+         if isOver then
+             return $ acceptRequest ctx input
+         else
+             do wanted ← readTVar $ itrReqBodyWanted itr
+                case wanted of
+                  0 → do wasteAll ← readTVar $ itrReqBodyWasteAll itr
+                         if wasteAll then
+                             return $ wasteCurrentChunk ctx itr input remaining
+                         else
+                             retry
+                  _ → return $ readCurrentChunk ctx itr input wanted remaining
 
-      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
+wasteCurrentChunk ∷ HandleLike h
+                  ⇒ Context h
+                  → Interaction
+                  → Lazy.ByteString
+                  → Int
+                  → IO ()
+wasteCurrentChunk ctx itr input len
+    | len > 0
+        = let input' = Lazy.drop (fromIntegral len) input
+          in
+            case LP.parse chunkFooterP input' of
+              LP.Done input'' _
+                  → observeChunkedRequest ctx itr input'' 0
+              LP.Fail _ _ _
+                  → chunkWasMalformed itr
+    | otherwise
+        = seekNextChunk ctx itr input
 
-      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
+readCurrentChunk ∷ HandleLike h
+                 ⇒ Context h
+                 → Interaction
+                 → Lazy.ByteString
+                 → Int
+                 → Int
+                 → IO ()
+readCurrentChunk ctx itr input wanted remaining
+    | remaining > 0
+        = do let bytesToRead     = fromIntegral $ min wanted remaining
+                 (chunk, input') = Lazy.splitAt bytesToRead input
+                 actualReadBytes = fromIntegral $ Lazy.length chunk
+                 newWanted       = wanted - actualReadBytes
+                 newRemaining    = remaining - actualReadBytes
+                 chunk'          = S.fromList $ Lazy.toChunks chunk
+                 updateStates    = atomically $
+                                   do writeTVar (itrReqBodyWanted itr) newWanted
+                                      oldBody    ← readTVar $ itrReceivedBody    itr
+                                      oldBodyLen ← readTVar $ itrReceivedBodyLen itr
+                                      writeTVar (itrReceivedBody    itr) $ oldBody ⋈ chunk'
+                                      writeTVar (itrReceivedBodyLen itr) $ oldBodyLen + actualReadBytes
+             if newRemaining ≡ 0 then
+                 case LP.parse chunkFooterP input' of
+                   LP.Done input'' _
+                       → do updateStates
+                            observeChunkedRequest ctx itr input'' 0
+                   LP.Fail _ _ _
+                       → chunkWasMalformed itr
+             else
+                 do updateStates
+                    observeChunkedRequest ctx itr input' newRemaining
+    | otherwise
+        = seekNextChunk ctx itr input
 
-                                       writeItr itr itrReqChunkRemaining $ Just 0
-                                       writeItr itr itrReqChunkIsOver True
+seekNextChunk ∷ HandleLike h
+              ⇒ Context h
+              → Interaction
+              → Lazy.ByteString
+              → IO ()
+seekNextChunk ctx itr input
+    = case LP.parse chunkHeaderP input of
+        LP.Done input' len
+            | len ≡ 0 -- Final chunk
+                → case LP.parse chunkTrailerP input' of
+                     LP.Done input'' _
+                         → do atomically $
+                                  writeTVar (itrReqChunkIsOver itr) True
+                              acceptRequest ctx input''
+                     LP.Fail _ _ _
+                         → chunkWasMalformed itr
+            | otherwise -- Non-final chunk
+                → observeChunkedRequest ctx itr input' len
+        LP.Fail _ _ _
+                → chunkWasMalformed itr
 
-                                       return $ acceptRequest input'
-                                  else
-                                    -- 要求がまだ来ない
-                                    retry
-                           else
-                               -- 受信要求が來た
-                               do remainingM <- readItr itr itrReqChunkRemaining id
+chunkWasMalformed ∷ Interaction → IO ()
+chunkWasMalformed itr
+    = atomically $
+      do setResponseStatus itr BadRequest
+         writeTVar (itrWillClose itr) True
+         writeTVar (itrState     itr) Done
+         writeDefaultPage itr
+         postprocess itr
 
-                                  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
+observeNonChunkedRequest ∷ HandleLike h
+                         ⇒ Context h
+                         → Interaction
+                         → Lazy.ByteString
+                         → Int
+                         → IO ()
+observeNonChunkedRequest ctx itr input remaining
+    = join $
+      atomically $
+      do wanted ← readTVar $ itrReqBodyWanted itr
+         case wanted of
+           0 → do wasteAll ← readTVar $ itrReqBodyWasteAll itr
+                  if wasteAll then
+                      return $ wasteNonChunkedRequestBody ctx itr input remaining
+                  else
+                      retry
+           _ → return $ readNonChunkedRequestBody ctx itr input wanted remaining
 
-                                  writeItr itr itrReqChunkRemaining newRemaining
-                                  writeItr itr itrReqChunkIsOver isOver
-                                  writeItr itr itrReqBodyWanted Nothing
-                                  writeItr itr itrReceivedBody chunk
+wasteNonChunkedRequestBody ∷ HandleLike h
+                           ⇒ Context h
+                           → Interaction
+                           → Lazy.ByteString
+                           → Int
+                           → IO ()
+wasteNonChunkedRequestBody ctx itr input remaining
+    = do let input' = Lazy.drop (fromIntegral remaining) input
+         atomically $ writeTVar (itrReqChunkIsOver itr) True
+         acceptRequest ctx input'
 
-                                  if isOver then
-                                      return $ acceptRequest input'
-                                    else
-                                      return $ observeNonChunkedRequest itr input'
-               action
+readNonChunkedRequestBody ∷ HandleLike h
+                          ⇒ Context h
+                          → Interaction
+                          → Lazy.ByteString
+                          → Int
+                          → Int
+                          → IO ()
+readNonChunkedRequestBody ctx itr input wanted remaining
+    = do let bytesToRead     = min wanted remaining
+             (chunk, input') = Lazy.splitAt (fromIntegral bytesToRead) input
+             actualReadBytes = fromIntegral $ Lazy.length chunk
+             newWanted       = wanted - actualReadBytes
+             newRemaining    = remaining - actualReadBytes
+             isOver          = actualReadBytes < bytesToRead ∨ newRemaining ≡ 0
+             chunk'          = S.fromList $ Lazy.toChunks chunk
+         atomically $
+             do writeTVar (itrReqChunkIsOver  itr) isOver
+                writeTVar (itrReqBodyWanted   itr) newWanted
+                writeTVar (itrReceivedBody    itr) chunk'
+                writeTVar (itrReceivedBodyLen itr) actualReadBytes
+         if isOver then
+             acceptRequest ctx input'
+         else
+             observeNonChunkedRequest ctx itr input' newRemaining
 
-      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)