{-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE OverloadedStrings #-} {-# LANGUAGE FunctionalDependencies, FlexibleInstances #-} {-# OPTIONS_HADDOCK hide #-} module SecondTransfer.MainLoop.Tokens( packHeaderTuples ,unpackHeaderTuples ,getHeader ,actionIsForAssociatedStream ,UnpackedNameValueList (..) ,StreamInputToken (..) ,StreamOutputAction (..) ,StreamWorker ,StreamWorkerClass (..) ,LocalStreamId ,GlobalStreamId ) where import Control.Monad (forM_, replicateM) import Data.Binary (Binary, get, put) import Data.Binary.Get (getByteString, getWord32be) import Data.Binary.Put (putWord32be, putByteString) import qualified Data.ByteString as B import qualified Data.ByteString as BS import Data.Conduit (Conduit) import Data.List (sortBy, find) import Data.Word -- Not to use raw.... newtype UnpackedNameValueList = UnpackedNameValueList [(B.ByteString, B.ByteString)] deriving Show data StreamInputToken = Headers_STk UnpackedNameValueList | Data_Stk B.ByteString | Finish_Stk deriving Show type LocalStreamId = Int type GlobalStreamId = Int data StreamOutputAction = SendHeaders_SOA UnpackedNameValueList | SendAssociatedHeaders_SOA LocalStreamId UnpackedNameValueList | SendData_SOA B.ByteString | SendAssociatedData_SOA LocalStreamId B.ByteString | SendAssociatedFinish_SOA LocalStreamId | Finish_SOA deriving Show actionIsForAssociatedStream :: StreamOutputAction -> Maybe (LocalStreamId, StreamOutputAction) actionIsForAssociatedStream (SendAssociatedData_SOA stream_id x ) = Just (stream_id, SendData_SOA x) actionIsForAssociatedStream (SendAssociatedHeaders_SOA stream_id x ) = Just (stream_id, SendHeaders_SOA x) actionIsForAssociatedStream (SendAssociatedFinish_SOA stream_id ) = Just (stream_id, Finish_SOA ) actionIsForAssociatedStream _ = Nothing -- | A StreamWorker: a conduit that takes input tokens and answers with output -- tokens. It can perform I/O. type StreamWorker = Conduit StreamInputToken IO StreamOutputAction -- | Sequence of steps to get a StreamWorker. This class is independent of things -- like the finer details concerning the frames and the streams. -- -- Todo: although this shows a common pattern, I'm not sure how having a class -- here helps.... class StreamWorkerClass serviceParams servicePocket sessionPocket | serviceParams -> sessionPocket servicePocket, servicePocket -> sessionPocket serviceParams, sessionPocket -> servicePocket where initService :: serviceParams -> IO servicePocket initSession :: servicePocket -> IO sessionPocket initStream :: servicePocket -> sessionPocket -> IO StreamWorker instance Binary UnpackedNameValueList where put unvl = do putWord32be length32 forM_ packed $ \ (h,v) -> do putWord32be $ fromIntegral (BS.length h) putByteString h putWord32be $ fromIntegral (BS.length v) putByteString v where length32 = (fromIntegral $ length packed)::Word32 packed = packHeaderTuples unvl get = do entry_count <- getWord32be packed_entries <- replicateM (fromIntegral entry_count) $ do { name_length <- getWord32be ; name <- getByteString (fromIntegral name_length) ; value_length <- getWord32be ; value <- getByteString (fromIntegral value_length) ; return (name, value) } return $ unpackHeaderTuples packed_entries -- Just puts them together, as per the spec packHeaderTuples :: UnpackedNameValueList -> [(BS.ByteString, BS.ByteString)] packHeaderTuples (UnpackedNameValueList uvl) = let sortFun (h1, _) (h2, _) = compare h1 h2 sorted_uvl = sortBy sortFun uvl sameName [] = [] sameName ((h, v):rest) = let (cousins,nocousins) = span (\ (hh, _) -> hh == h ) rest cousings_value = BS.intercalate "\0" $ v:(map snd cousins) in (h, cousings_value):(sameName nocousins) in sameName sorted_uvl -- And unputs them together unpackHeaderTuples :: [(BS.ByteString, BS.ByteString)] -> UnpackedNameValueList unpackHeaderTuples [] = UnpackedNameValueList [] unpackHeaderTuples vl = UnpackedNameValueList $ step vl where valueSplit v = BS.split 0 v step [] = [] step ((h,v):rest) = [ (h,vv) | vv <- valueSplit v ] ++ (step rest) getHeader :: UnpackedNameValueList -> BS.ByteString -> Maybe BS.ByteString getHeader (UnpackedNameValueList unvl) bs = case find (\ (x,_) -> x==bs ) unvl of Just (_, found_value) -> Just found_value Nothing -> Nothing