websockets-0.4.0.0: src/Network/WebSockets/Demultiplex.hs
-- | Demultiplexing of frames into messages
module Network.WebSockets.Demultiplex
( DemultiplexState
, emptyDemultiplexState
, demultiplex
) where
import Blaze.ByteString.Builder (Builder)
import Data.Monoid (mappend)
import qualified Blaze.ByteString.Builder as B
import Network.WebSockets.Types
-- | Internal state used by the demultiplexer
newtype DemultiplexState = DemultiplexState
{ unDemultiplexState :: Maybe (FrameType, Builder)
}
emptyDemultiplexState :: DemultiplexState
emptyDemultiplexState = DemultiplexState Nothing
demultiplex :: DemultiplexState
-> Frame
-> (Maybe (Message p), DemultiplexState)
demultiplex state (Frame fin tp pl) = case tp of
-- Return control messages immediately, they have no influence on the state
CloseFrame -> (Just (ControlMessage (Close pl)), state)
PingFrame -> (Just (ControlMessage (Ping pl)), state)
PongFrame -> (Just (ControlMessage (Pong pl)), state)
-- If we're dealing with a continuation...
ContinuationFrame -> case unDemultiplexState state of
-- We received a continuation but we don't have any state. Let's ignore
-- this fragment...
Nothing -> (Nothing, DemultiplexState Nothing)
-- Append the payload to the state
-- TODO: protect against overflows
Just (amt, b)
| not fin -> (Nothing, DemultiplexState (Just (amt, b')))
| otherwise -> case amt of
TextFrame -> (Just (DataMessage (Text m)), e)
BinaryFrame -> (Just (DataMessage (Binary m)), e)
_ -> error "Demultiplex.demultiplex: Internal error"
where
b' = b `mappend` plb
m = B.toLazyByteString b'
TextFrame
| fin -> (Just (DataMessage (Text pl)), e)
| otherwise -> (Nothing, DemultiplexState (Just (TextFrame, plb)))
BinaryFrame
| fin -> (Just (DataMessage (Binary pl)), e)
| otherwise -> (Nothing, DemultiplexState (Just (BinaryFrame, plb)))
where
e = emptyDemultiplexState
plb = B.fromLazyByteString pl