packages feed

happstack-state-6.1.4: src/Happstack/State/NetworkChan.hs

module Happstack.State.NetworkChan
    ( BEncodeable(..)
    , NetworkChan
    , newNetworkChan
    , peek
    , poke
    ) where

import Data.BEncode
import Data.BEncode.Parser

import Control.Monad (when, forever, forM_ )
import System.IO
import qualified Data.ByteString.Lazy as L
import Data.Binary
import Control.Concurrent.STM
import Control.Concurrent (forkIO, ThreadId)

class BEncodeable a where
    bencode :: a -> BEncode
    bdecode :: BParser a


data NetworkChan = NetworkChan { outgoingChan :: TVar [L.ByteString]
                               , incomingHandle :: Handle
                               , writerThread :: ThreadId }

newNetworkChan :: Handle -> Handle -> IO NetworkChan
newNetworkChan outgoing incoming
    = do cache <- newTVarIO []
         tid <- forkIO $ forever $ do ls <- atomically $ do ls <- readTVar cache
                                                            when (null ls) retry
                                                            writeTVar cache []
                                                            return (reverse ls)
                                      forM_ ls $ \entry -> do L.hPut outgoing (encode (L.length entry))
                                                              L.hPut outgoing entry
                                      hFlush outgoing
         return NetworkChan { outgoingChan = cache
                            , incomingHandle = incoming
                            , writerThread = tid }

peek :: BEncodeable a => NetworkChan -> IO a
peek chan
    = do size <- L.hGet (incomingHandle chan) 8
         when (L.length size < 8) $ error "NetworkChan.peek: Connection closed."
         body <- L.hGet (incomingHandle chan) (decode size)
         case bRead body of
           Nothing      -> error "NetworkChan.peek: Malformed bencoded data."
           Just encoded -> case runParser bdecode encoded of
                             Left err  -> error "NetworkChan.peek: Parsing bencoded data failed."
                             Right msg -> return msg

poke :: BEncodeable a => NetworkChan -> a -> IO ()
poke chan msg
    = atomically $
      do ls <- readTVar (outgoingChan chan)
         writeTVar (outgoingChan chan) (bPack (bencode msg):ls)
{-
do L.hPut (inputHandle chan) (encode (L.length body))
          L.hPut (inputHandle chan) body
          hFlush (inputHandle chan)
    where body = bPack (bencode msg)
-}