packages feed

natskell-1.1.0.0: internal/Plumbing/Pipeline/Streaming/Parser.hs

module Pipeline.Streaming.Parser where
import           Conduit
import           Data.ByteString
import qualified Data.ByteString  as BS
import qualified Data.List        as List
import           Lib.Logger.Types (LogLevel (..), MonadLogger (..))
import           Parser.API
    ( ParseStep (DropPrefix, Emit, NeedMore, Reject)
    , ParserAPI
    , parse
    )
import           Prelude          hiding (drop, length, take)

parser :: (MonadLogger m , MonadIO m)
  => Int
  -> ParserAPI result
  -> ConduitT ByteString result m ()
parser bufferLimit parserApi = loop empty
  where
    loop acc = do
      bs <- await
      case bs of
        Nothing    -> return ()
        Just chunk -> handleChunk $ append acc chunk
    handleChunk bs
      | BS.null bs = parser bufferLimit parserApi
      | otherwise = do
          lift . logMessage Debug $ "parsing chunk"
          case parse parserApi bs of
            NeedMore -> do
              let bsLen = length bs
              if bsLen > bufferLimit
                then do
                  lift . logMessage Error $
                    "closing connection: incomplete protocol frame buffered "
                      ++ show bsLen
                      ++ " bytes exceeds limit "
                      ++ show bufferLimit
                  return ()
                else do
                  lift . logMessage Debug $ "message spans frame, waiting for more data"
                  loop bs
            DropPrefix n reason -> do
              lift . logMessage Error $
                "dropping invalid protocol bytes: category="
                  ++ reasonCategory reason
                  ++ " dropped_bytes="
                  ++ show n
                  ++ " buffered_bytes="
                  ++ show (length bs)
              handleChunk (drop n bs)
            Reject reason ->
              lift . logMessage Error $
                "parser rejected inbound data: category="
                  ++ reasonCategory reason
                  ++ " buffered_bytes="
                  ++ show (length bs)
            Emit message rest -> do
              lift . logMessage Debug $ "parsed message"
              yield message
              handleChunk rest

    reasonCategory reason
      | "unknown protocol prefix" `List.isPrefixOf` reason = "unknown-prefix"
      | "invalid INFO" `List.isPrefixOf` reason = "invalid-info"
      | "invalid MSG" `List.isPrefixOf` reason = "invalid-msg"
      | "invalid HMSG" `List.isPrefixOf` reason = "invalid-hmsg"
      | otherwise = "malformed-frame"