kademlia-1.0.0.0: src/Network/Kademlia/Protocol.hs
{-|
Module : Network.Kademlia.Protocol
Description : Implementation of the actual protocol
Network.Kademlia.Protocol implements the parsing and serialisation of
ByteStrings into 'Protocol'-Values.
-}
module Network.Kademlia.Protocol
( serialize
, parse
) where
import qualified Data.ByteString as B
import qualified Data.ByteString.Lazy as L
import qualified Data.ByteString.Char8 as C
import Data.ByteString.Builder (toLazyByteString, word16BE)
import Data.Word (Word8)
import Data.List (foldl')
import Network.Kademlia.Types
import Network.Kademlia.Protocol.Parsing
-- | Retrieve the assigned protocolId
commandId :: Command i a -> Word8
commandId PING = 0
commandId PONG = 1
commandId (STORE _ _) = 2
commandId (FIND_NODE _) = 3
commandId (RETURN_NODES _ _) = 4
commandId (FIND_VALUE _) = 5
commandId (RETURN_VALUE _ _) = 6
-- | Turn the command arguments into a ByteString
commandArgs :: (Serialize i, Serialize a) => Command i a -> B.ByteString
commandArgs PING = B.empty
commandArgs PONG = B.empty
commandArgs (STORE k v) = toBS k `B.append` toBS v
commandArgs (FIND_NODE id) = toBS id
commandArgs (FIND_VALUE k) = toBS k
commandArgs (RETURN_VALUE id v) = toBS id `B.append` toBS v
commandArgs (RETURN_NODES id kb) = toBS id `B.append`
foldl' B.append B.empty (fmap nodeToArg kb)
nodeToArg :: (Serialize i) => Node i -> B.ByteString
nodeToArg node = id `B.append` C.pack (host ++ " ") `B.append` port
where id = toBS . nodeId $ node
host = peerHost . peer $ node
port = toBinary . fromIntegral . peerPort . peer $ node
-- Converts a Word16 into a two character ByteString
toBinary = B.concat . L.toChunks . toLazyByteString . word16BE
-- | Turn a command into a sendable ByteString
serialize :: (Serialize i, Serialize a) => i -> Command i a -> B.ByteString
serialize id command = cId `B.cons` toBS id `B.append` args
where cId = commandId command
args = commandArgs command