module Network.LibP2P.Mux.Yamux.Frame
( FrameType (..)
, Flags (..)
, YamuxHeader (..)
, GoAwayCode (..)
, encodeHeader
, decodeHeader
, defaultFlags
, headerSize
, initialWindowSize
) where
import Data.Bits (shiftL, shiftR, (.&.), (.|.))
import Data.ByteString (ByteString)
import qualified Data.ByteString as BS
import Data.Word (Word16, Word32, Word8)
headerSize :: Int
= Int
12
initialWindowSize :: Word32
initialWindowSize :: Word32
initialWindowSize = Word32
262144
data FrameType
= FrameData
| FrameWindowUpdate
| FramePing
| FrameGoAway
deriving (Int -> FrameType -> ShowS
[FrameType] -> ShowS
FrameType -> String
(Int -> FrameType -> ShowS)
-> (FrameType -> String)
-> ([FrameType] -> ShowS)
-> Show FrameType
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> FrameType -> ShowS
showsPrec :: Int -> FrameType -> ShowS
$cshow :: FrameType -> String
show :: FrameType -> String
$cshowList :: [FrameType] -> ShowS
showList :: [FrameType] -> ShowS
Show, FrameType -> FrameType -> Bool
(FrameType -> FrameType -> Bool)
-> (FrameType -> FrameType -> Bool) -> Eq FrameType
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: FrameType -> FrameType -> Bool
== :: FrameType -> FrameType -> Bool
$c/= :: FrameType -> FrameType -> Bool
/= :: FrameType -> FrameType -> Bool
Eq)
data GoAwayCode
= GoAwayNormal
| GoAwayProtocol
| GoAwayInternal
deriving (Int -> GoAwayCode -> ShowS
[GoAwayCode] -> ShowS
GoAwayCode -> String
(Int -> GoAwayCode -> ShowS)
-> (GoAwayCode -> String)
-> ([GoAwayCode] -> ShowS)
-> Show GoAwayCode
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> GoAwayCode -> ShowS
showsPrec :: Int -> GoAwayCode -> ShowS
$cshow :: GoAwayCode -> String
show :: GoAwayCode -> String
$cshowList :: [GoAwayCode] -> ShowS
showList :: [GoAwayCode] -> ShowS
Show, GoAwayCode -> GoAwayCode -> Bool
(GoAwayCode -> GoAwayCode -> Bool)
-> (GoAwayCode -> GoAwayCode -> Bool) -> Eq GoAwayCode
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: GoAwayCode -> GoAwayCode -> Bool
== :: GoAwayCode -> GoAwayCode -> Bool
$c/= :: GoAwayCode -> GoAwayCode -> Bool
/= :: GoAwayCode -> GoAwayCode -> Bool
Eq)
data Flags = Flags
{ Flags -> Bool
flagSYN :: !Bool
, Flags -> Bool
flagACK :: !Bool
, Flags -> Bool
flagFIN :: !Bool
, Flags -> Bool
flagRST :: !Bool
}
deriving (Int -> Flags -> ShowS
[Flags] -> ShowS
Flags -> String
(Int -> Flags -> ShowS)
-> (Flags -> String) -> ([Flags] -> ShowS) -> Show Flags
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> Flags -> ShowS
showsPrec :: Int -> Flags -> ShowS
$cshow :: Flags -> String
show :: Flags -> String
$cshowList :: [Flags] -> ShowS
showList :: [Flags] -> ShowS
Show, Flags -> Flags -> Bool
(Flags -> Flags -> Bool) -> (Flags -> Flags -> Bool) -> Eq Flags
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: Flags -> Flags -> Bool
== :: Flags -> Flags -> Bool
$c/= :: Flags -> Flags -> Bool
/= :: Flags -> Flags -> Bool
Eq)
defaultFlags :: Flags
defaultFlags :: Flags
defaultFlags = Bool -> Bool -> Bool -> Bool -> Flags
Flags Bool
False Bool
False Bool
False Bool
False
data =
{ YamuxHeader -> Word8
yhVersion :: !Word8
, YamuxHeader -> FrameType
yhType :: !FrameType
, YamuxHeader -> Flags
yhFlags :: !Flags
, YamuxHeader -> Word32
yhStreamId :: !Word32
, YamuxHeader -> Word32
yhLength :: !Word32
}
deriving (Int -> YamuxHeader -> ShowS
[YamuxHeader] -> ShowS
YamuxHeader -> String
(Int -> YamuxHeader -> ShowS)
-> (YamuxHeader -> String)
-> ([YamuxHeader] -> ShowS)
-> Show YamuxHeader
forall a.
(Int -> a -> ShowS) -> (a -> String) -> ([a] -> ShowS) -> Show a
$cshowsPrec :: Int -> YamuxHeader -> ShowS
showsPrec :: Int -> YamuxHeader -> ShowS
$cshow :: YamuxHeader -> String
show :: YamuxHeader -> String
$cshowList :: [YamuxHeader] -> ShowS
showList :: [YamuxHeader] -> ShowS
Show, YamuxHeader -> YamuxHeader -> Bool
(YamuxHeader -> YamuxHeader -> Bool)
-> (YamuxHeader -> YamuxHeader -> Bool) -> Eq YamuxHeader
forall a. (a -> a -> Bool) -> (a -> a -> Bool) -> Eq a
$c== :: YamuxHeader -> YamuxHeader -> Bool
== :: YamuxHeader -> YamuxHeader -> Bool
$c/= :: YamuxHeader -> YamuxHeader -> Bool
/= :: YamuxHeader -> YamuxHeader -> Bool
Eq)
frameTypeToWord8 :: FrameType -> Word8
frameTypeToWord8 :: FrameType -> Word8
frameTypeToWord8 FrameType
FrameData = Word8
0x00
frameTypeToWord8 FrameType
FrameWindowUpdate = Word8
0x01
frameTypeToWord8 FrameType
FramePing = Word8
0x02
frameTypeToWord8 FrameType
FrameGoAway = Word8
0x03
word8ToFrameType :: Word8 -> Either String FrameType
word8ToFrameType :: Word8 -> Either String FrameType
word8ToFrameType Word8
0x00 = FrameType -> Either String FrameType
forall a b. b -> Either a b
Right FrameType
FrameData
word8ToFrameType Word8
0x01 = FrameType -> Either String FrameType
forall a b. b -> Either a b
Right FrameType
FrameWindowUpdate
word8ToFrameType Word8
0x02 = FrameType -> Either String FrameType
forall a b. b -> Either a b
Right FrameType
FramePing
word8ToFrameType Word8
0x03 = FrameType -> Either String FrameType
forall a b. b -> Either a b
Right FrameType
FrameGoAway
word8ToFrameType Word8
n = String -> Either String FrameType
forall a b. a -> Either a b
Left (String -> Either String FrameType)
-> String -> Either String FrameType
forall a b. (a -> b) -> a -> b
$ String
"unknown frame type: " String -> ShowS
forall a. Semigroup a => a -> a -> a
<> Word8 -> String
forall a. Show a => a -> String
show Word8
n
flagsToWord16 :: Flags -> Word16
flagsToWord16 :: Flags -> Word16
flagsToWord16 (Flags Bool
syn Bool
ack Bool
fin Bool
rst) =
(if Bool
syn then Word16
0x0001 else Word16
0)
Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. (if Bool
ack then Word16
0x0002 else Word16
0)
Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. (if Bool
fin then Word16
0x0004 else Word16
0)
Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. (if Bool
rst then Word16
0x0008 else Word16
0)
word16ToFlags :: Word16 -> Flags
word16ToFlags :: Word16 -> Flags
word16ToFlags Word16
w =
Flags
{ flagSYN :: Bool
flagSYN = Word16
w Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.&. Word16
0x0001 Word16 -> Word16 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word16
0
, flagACK :: Bool
flagACK = Word16
w Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.&. Word16
0x0002 Word16 -> Word16 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word16
0
, flagFIN :: Bool
flagFIN = Word16
w Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.&. Word16
0x0004 Word16 -> Word16 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word16
0
, flagRST :: Bool
flagRST = Word16
w Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.&. Word16
0x0008 Word16 -> Word16 -> Bool
forall a. Eq a => a -> a -> Bool
/= Word16
0
}
encodeHeader :: YamuxHeader -> ByteString
(YamuxHeader Word8
ver FrameType
typ Flags
flags Word32
sid Word32
len) =
let f :: Word16
f = Flags -> Word16
flagsToWord16 Flags
flags
in [Word8] -> ByteString
BS.pack
[ Word8
ver
, FrameType -> Word8
frameTypeToWord8 FrameType
typ
, Word16 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word16
f Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftR` Int
8)
, Word16 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word16
f
, Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
sid Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
24)
, Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
sid Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
16)
, Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
sid Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
8)
, Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
sid
, Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
len Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
24)
, Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
len Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
16)
, Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral (Word32
len Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftR` Int
8)
, Word32 -> Word8
forall a b. (Integral a, Num b) => a -> b
fromIntegral Word32
len
]
decodeHeader :: ByteString -> Either String YamuxHeader
ByteString
bs
| ByteString -> Int
BS.length ByteString
bs Int -> Int -> Bool
forall a. Ord a => a -> a -> Bool
< Int
12 = String -> Either String YamuxHeader
forall a b. a -> Either a b
Left String
"decodeHeader: need 12 bytes"
| Bool
otherwise = do
let ver :: Word8
ver = HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
0
typ <- Word8 -> Either String FrameType
word8ToFrameType (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
1)
let flags =
Word16 -> Flags
word16ToFlags
( (Word8 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
2) Word16 -> Int -> Word16
forall a. Bits a => a -> Int -> a
`shiftL` Int
8)
Word16 -> Word16 -> Word16
forall a. Bits a => a -> a -> a
.|. Word8 -> Word16
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
3)
)
sid =
(Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
4) Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
24)
Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. (Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
5) Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
16)
Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. (Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
6) Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
8)
Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
7)
len =
(Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
8) Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
24)
Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. (Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
9) Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
16)
Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. (Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
10) Word32 -> Int -> Word32
forall a. Bits a => a -> Int -> a
`shiftL` Int
8)
Word32 -> Word32 -> Word32
forall a. Bits a => a -> a -> a
.|. Word8 -> Word32
forall a b. (Integral a, Num b) => a -> b
fromIntegral (HasCallStack => ByteString -> Int -> Word8
ByteString -> Int -> Word8
BS.index ByteString
bs Int
11)
Right (YamuxHeader ver typ flags sid len)