packages feed

http2 0.8.0 → 0.9.0

raw patch · 7 files changed

+226/−134 lines, 7 filesdep +Glob

Dependencies added: Glob

Files

Network/HTTP2.hs view
@@ -6,8 +6,10 @@     Frame(..)   , FrameHeader(..)   , FramePayload(..)+  , isPaddingDefined   -- * Encoding   , encodeFrame+  , encodeFrameChunks   , encodeFrameHeader   , encodeFramePayload   , EncodeInfo(..)
Network/HTTP2/Decode.hs view
@@ -42,19 +42,16 @@ decodeFrame :: Settings    -- ^ HTTP/2 settings             -> ByteString  -- ^ Input byte-stream             -> Either HTTP2Error Frame -- ^ Decoded frame-decodeFrame settings bs = decode typ+decodeFrame settings bs = checkFrameHeader settings (decodeFrameHeader bs0)+                      >>= \(typ,header) -> decodeFramePayload typ header bs1+                      >>= \payload -> return $ Frame header payload   where     (bs0,bs1) = BS.splitAt 9 bs-    (typ, header) = decodeFrameHeader bs0-    decode (FrameUnknown v) = Right $ Frame header $ decodeUnknownFrame v bs1-    decode ftyp = case checkFrameHeader settings ftyp header of-        Just h2err -> Left h2err-        Nothing    -> Right $ Frame header $ decodeFramePayload typ header bs1 - ---------------------------------------------------------------- --- | Must supply 9 bytes.+-- | Decoding an HTTP/2 frame header.+--   Must supply 9 bytes. decodeFrameHeader :: ByteString -> (FrameTypeId, FrameHeader) decodeFrameHeader (PS fptr off _) = inlinePerformIO $ withForeignPtr fptr $ \ptr -> do     let p = ptr +. off@@ -76,31 +73,41 @@ -- | Checking a frame header and reporting an error if any. -- -- >>> let stid = toStreamIdentifier 0--- >>> checkFrameHeader defaultSettings FrameData (FrameHeader 100 0 stid)--- Just (ConnectionError ProtocolError "cannot used in control stream")-checkFrameHeader :: Settings -> FrameTypeId -> FrameHeader -> Maybe HTTP2Error-checkFrameHeader Settings {..} typ FrameHeader {..}+-- >>> checkFrameHeader defaultSettings (FrameData,(FrameHeader 100 0 stid))+-- Left (ConnectionError ProtocolError "cannot used in control stream")+checkFrameHeader :: Settings+                 -> (FrameTypeId, FrameHeader)+                 -> Either HTTP2Error (FrameTypeId, FrameHeader)+checkFrameHeader Settings {..} typfrm@(typ,FrameHeader {..})   | payloadLength > maxFrameSize =-      Just $ ConnectionError FrameSizeError "exceeds maximum frame size"+      Left $ ConnectionError FrameSizeError "exceeds maximum frame size"   | typ `elem` nonZeroFrameTypes && isControl streamId =-      Just $ ConnectionError ProtocolError "cannot used in control stream"+      Left $ ConnectionError ProtocolError "cannot used in control stream"   | typ `elem` zeroFrameTypes && not (isControl streamId) =-      Just $ ConnectionError ProtocolError "cannot used in non-zero stream"+      Left $ ConnectionError ProtocolError "cannot used in non-zero stream"   | otherwise = checkType typ   where     checkType FramePriority | payloadLength /= 5 =-        Just $ StreamError FrameSizeError streamId+        Left $ StreamError FrameSizeError streamId     checkType FrameRSTStream | payloadLength /= 4 =-        Just $ ConnectionError FrameSizeError "payload length is not 4 in rst stream frame"-    checkType FrameSettings | payloadLength `mod` 6 /= 0 =-        Just $ ConnectionError FrameSizeError "payload length is not multiple of 6 in settings frame"-    checkType FramePushPromise | not enablePush =-        Just $ ConnectionError ProtocolError "push not enabled" -- checkme+        Left $ ConnectionError FrameSizeError "payload length is not 4 in rst stream frame"+    checkType FrameSettings+      | payloadLength `mod` 6 /= 0 =+        Left $ ConnectionError FrameSizeError "payload length is not multiple of 6 in settings frame"+      | testAck flags && payloadLength /= 0 =+        Left $ ConnectionError FrameSizeError "payload length must be 0 if ack flag is set"+    checkType FramePushPromise+      | not enablePush =+        Left $ ConnectionError ProtocolError "push not enabled" -- checkme+      | not (isResponse streamId) =+        Left $ ConnectionError ProtocolError "push promise must be used with even stream identifier"     checkType FramePing | payloadLength /= 8 =-        Just $ ConnectionError FrameSizeError "payload length is 8 in ping frame"+        Left $ ConnectionError FrameSizeError "payload length is 8 in ping frame"+    checkType FrameGoAway | payloadLength < 8 =+        Left $ ConnectionError FrameSizeError "goaway body must be 8 bytes or larger"     checkType FrameWindowUpdate | payloadLength /= 4 =-        Just $ ConnectionError FrameSizeError "payload length is 4 in window update frame"-    checkType _ = Nothing+        Left $ ConnectionError FrameSizeError "payload length is 4 in window update frame"+    checkType _ = Right typfrm  zeroFrameTypes :: [FrameTypeId] zeroFrameTypes = [@@ -121,7 +128,8 @@  ---------------------------------------------------------------- -type FramePayloadDecoder = FrameHeader -> ByteString -> FramePayload+type FramePayloadDecoder = FrameHeader -> ByteString+                        -> Either HTTP2Error FramePayload  payloadDecoders :: Array Word8 FramePayloadDecoder payloadDecoders = listArray (minFrameType, maxFrameType)@@ -137,8 +145,12 @@     , decodeContinuationFrame     ] +-- | Decoding an HTTP/2 frame payload. decodeFramePayload :: FrameTypeId -> FramePayloadDecoder-decodeFramePayload ftyp = payloadDecoders ! fromFrameTypeId ftyp+decodeFramePayload (FrameUnknown typ) = checkFrameSize $ decodeUnknownFrame typ+decodeFramePayload ftyp               = checkFrameSize decoder+  where+    decoder = payloadDecoders ! fromFrameTypeId ftyp  ---------------------------------------------------------------- @@ -157,13 +169,13 @@     hasPriority = testPriority $ flags header  decodePriorityFrame :: FramePayloadDecoder-decodePriorityFrame _ bs = PriorityFrame $ priority bs+decodePriorityFrame _ bs = Right $ PriorityFrame $ priority bs  decoderstStreamFrame :: FramePayloadDecoder-decoderstStreamFrame _ bs = RSTStreamFrame $ toErrorCodeId (word32 bs)+decoderstStreamFrame _ bs = Right $ RSTStreamFrame $ toErrorCodeId (word32 bs)  decodeSettingsFrame :: FramePayloadDecoder-decodeSettingsFrame FrameHeader{..} (PS fptr off _) = SettingsFrame alist+decodeSettingsFrame FrameHeader{..} (PS fptr off _) = Right $ SettingsFrame alist   where     num = payloadLength `div` 6     alist = inlinePerformIO $ withForeignPtr fptr $ \ptr -> do@@ -188,10 +200,10 @@     in PushPromiseFrame sid bs1  decodePingFrame :: FramePayloadDecoder-decodePingFrame _ bs = PingFrame bs+decodePingFrame _ bs = Right $ PingFrame bs  decodeGoAwayFrame :: FramePayloadDecoder-decodeGoAwayFrame _ bs = GoAwayFrame sid ecid bs2+decodeGoAwayFrame _ bs = Right $ GoAwayFrame sid ecid bs2   where     (bs0,bs1') = BS.splitAt 4 bs     (bs1,bs2)  = BS.splitAt 4 bs1'@@ -199,29 +211,40 @@     ecid = toErrorCodeId (word32 bs1)  decodeWindowUpdateFrame :: FramePayloadDecoder-decodeWindowUpdateFrame _ bs = WindowUpdateFrame wsi+decodeWindowUpdateFrame _ bs+  | wsi == 0  = Left $ ConnectionError ProtocolError "window update must not be 0"+  | otherwise = Right $ WindowUpdateFrame wsi   where     !wsi = word32 bs `clearBit` 31  decodeContinuationFrame :: FramePayloadDecoder-decodeContinuationFrame _ bs = ContinuationFrame bs+decodeContinuationFrame _ bs = Right $ ContinuationFrame bs -decodeUnknownFrame :: FrameType -> ByteString -> FramePayload-decodeUnknownFrame typ bs = UnknownFrame typ bs+decodeUnknownFrame :: FrameType -> FramePayloadDecoder+decodeUnknownFrame typ _ bs = Right $ UnknownFrame typ bs  ---------------------------------------------------------------- +checkFrameSize :: FramePayloadDecoder -> FramePayloadDecoder+checkFrameSize func header@FrameHeader{..} body+  | payloadLength > BS.length body =+      Left $ ConnectionError FrameSizeError "payload is too short"+  | otherwise = func header body+ -- | Helper function to pull off the padding if its there, and will -- eat up the trailing padding automatically. Calls the decoder func -- passed in with the length of the unpadded portion between the -- padding octet and the actual padding-decodeWithPadding :: FrameHeader -> ByteString -> (ByteString -> FramePayload) -> FramePayload+decodeWithPadding :: FrameHeader -> ByteString -> (ByteString -> FramePayload) -> Either HTTP2Error FramePayload decodeWithPadding FrameHeader{..} bs body   | padded = let Just (w8,rest) = BS.uncons bs                  padlen = intFromWord8 w8-                 rest' = BS.take (payloadLength - padlen - 1) rest-             in body rest'-  | otherwise = body bs+                 bodylen = payloadLength - padlen - 1+             in if bodylen < 0 then+                    Left $ ConnectionError ProtocolError "padding is not enough"+                  else+                    Right . body $ BS.take bodylen rest+  | otherwise = Right $ body bs   where     padded = testPadded flags 
Network/HTTP2/Encode.hs view
@@ -2,34 +2,33 @@  module Network.HTTP2.Encode (     encodeFrame+  , encodeFrameChunks   , encodeFrameHeader   , encodeFramePayload-  , buildFrame-  , buildFrameHeader-  , buildFramePayload   , EncodeInfo(..)   , encodeInfo   ) where -import Blaze.ByteString.Builder (Builder)-import qualified Blaze.ByteString.Builder as BB-import Data.Bits-import Data.ByteString (ByteString)-import qualified Data.ByteString as B-import qualified Data.ByteString.Lazy as BL-import Data.Monoid ((<>), mempty)+import Data.Bits (shiftR, (.&.))+import qualified Data.ByteString as BS+import Data.ByteString.Internal (ByteString, unsafeCreate)+import Data.Word (Word8, Word16, Word32)+import Foreign.Ptr (Ptr, plusPtr)+import Foreign.Storable (poke)  import Network.HTTP2.Types  ---------------------------------------------------------------- +type Builder = [ByteString] -> [ByteString]+ -- | Auxiliary information for frame encoding. data EncodeInfo = EncodeInfo {     -- | Flags to be set in a frame header       encodeFlags    :: FrameFlags     -- | Stream id to be set in a frame header     , encodeStreamId :: StreamIdentifier-    -- | Padding if any+    -- | Padding if any. In the case where this value is set but the priority flag is not set, this value gets preference over the priority flag. So, if this value is set, the priority flag is also set.     , encodePadding  :: Maybe Padding     } deriving (Show,Read) @@ -53,40 +52,35 @@ -- >>> encodeFrame (encodeInfo id 1) (DataFrame "body") -- "\NUL\NUL\EOT\NUL\NUL\NUL\NUL\NUL\SOHbody" encodeFrame :: EncodeInfo -> FramePayload -> ByteString-encodeFrame einfo payload = run $ buildFrame einfo payload--encodeFrameHeader :: FrameTypeId -> FrameHeader -> ByteString-encodeFrameHeader ftid header = run $ buildFrameHeader ftid header+encodeFrame einfo payload = BS.concat $ encodeFrameChunks einfo payload -encodeFramePayload :: EncodeInfo -> FramePayload -> ByteString-encodeFramePayload einfo payload = run payloadBuilder+-- | Encoding an HTTP/2 frame to ['ByteString'].+--   This is suitable for sendMany.+encodeFrameChunks :: EncodeInfo -> FramePayload -> [ByteString]+encodeFrameChunks einfo payload = bs : bss   where-    (_, payloadBuilder) = buildFramePayload einfo payload--run :: Builder -> ByteString-run = BL.toStrict . BB.toLazyByteString------------------------------------------------------------------+    ftid = framePayloadToFrameTypeId payload+    bs = encodeFrameHeader ftid header+    (header, bss) = encodeFramePayload einfo payload -buildFrame :: EncodeInfo -> FramePayload -> Builder-buildFrame einfo payload = headerBuilder <> payloadBuilder+-- | Encoding an HTTP/2 frame header.+--   The frame header must be completed.+encodeFrameHeader :: FrameTypeId -> FrameHeader -> ByteString+encodeFrameHeader ftid FrameHeader{..} = unsafeCreate frameHeaderLength $ \ptr -> do+    poke24 ptr payloadLength+    poke8 ptr 3 typ+    poke8 ptr 4 flags+    poke32 (ptr `plusPtr` 5) sid   where-    (header, payloadBuilder) = buildFramePayload einfo payload-    ftyp = framePayloadToFrameTypeId payload-    headerBuilder = buildFrameHeader ftyp header------------------------------------------------------------------+    typ = fromFrameTypeId ftid+    sid = fromIntegral $ fromStreamIdentifier streamId -buildFrameHeader :: FrameTypeId -> FrameHeader -> Builder-buildFrameHeader ftyp FrameHeader{..} = len <> typ <> flg <> sid+-- | Encoding an HTTP/2 frame payload.+--   This returns a complete frame header and chunks of payload.+encodeFramePayload :: EncodeInfo -> FramePayload -> (FrameHeader, [ByteString])+encodeFramePayload einfo payload = (header, builder [])   where-    -- fixme: 2^14 check-    len1 = BB.fromWord16be (fromIntegral (payloadLength `shiftR` 8))-    len2 = BB.fromWord8 (fromIntegral (payloadLength .&. 0xff))-    len = len1 <> len2-    typ = BB.fromWord8 $ fromFrameTypeId ftyp-    flg = BB.fromWord8 flags-    sid = BB.fromWord32be . fromIntegral $ fromStreamIdentifier streamId+    (header, builder) = buildFramePayload einfo payload  ---------------------------------------------------------------- @@ -128,51 +122,44 @@     (header, builder)   where     header = FrameHeader len newflags encodeStreamId-    builder = bpadlen <> btarget <> bpadding-    bpadlen = BB.fromWord8 $ fromIntegral paddingLength-    bpadding = BB.fromByteString padding-    paddingLength = B.length padding+    builder = (b1 :) . btarget . (padding :)+    b1 = BS.singleton $ fromIntegral paddingLength+    paddingLength = BS.length padding     len = targetLength + paddingLength + 1     newflags = setPadded encodeFlags  buildPriority :: Priority -> Builder buildPriority Priority{..} = builder   where-    builder = bstream <> bweight+    builder = (priority :)     stream = fromStreamIdentifier streamDependency     estream       | exclusive = setExclusive stream       | otherwise = stream-    bstream = BB.fromWord32be $ fromIntegral estream-    bweight = BB.fromWord8 $ fromIntegral $ weight - 1---- fixme: clear 31th bit?-buildStream :: StreamIdentifier -> Builder-buildStream = BB.fromWord32be . fromIntegral . fromStreamIdentifier--buildErrorCodeId :: ErrorCodeId -> Builder-buildErrorCodeId = BB.fromWord32be . fromErrorCodeId+    priority = unsafeCreate 5 $ \ptr -> do+        poke32 ptr $ fromIntegral estream+        poke8 ptr 4 $ fromIntegral $ weight - 1  ----------------------------------------------------------------  buildFramePayloadData :: EncodeInfo -> ByteString -> (FrameHeader, Builder) buildFramePayloadData einfo body = buildPadding einfo builder len   where-    builder = BB.fromByteString body-    len = B.length body+    builder = (body :)+    len = BS.length body  buildFramePayloadHeaders :: EncodeInfo -> Maybe Priority -> HeaderBlockFragment                          -> (FrameHeader, Builder) buildFramePayloadHeaders einfo Nothing hdr =     buildPadding einfo builder len   where-    builder = BB.fromByteString hdr-    len = B.length hdr+    builder = (hdr :)+    len = BS.length hdr buildFramePayloadHeaders einfo (Just pri) hdr =     buildPadding einfo' builder len   where-    builder = buildPriority pri <> BB.fromByteString hdr-    len = B.length hdr + 5+    builder = buildPriority pri . (hdr :)+    len = BS.length hdr + 5     einfo' = einfo { encodeFlags = setPriority (encodeFlags einfo) }  buildFramePayloadPriority :: EncodeInfo -> Priority -> (FrameHeader, Builder)@@ -184,50 +171,99 @@ buildFramePayloadRSTStream :: EncodeInfo -> ErrorCodeId -> (FrameHeader, Builder) buildFramePayloadRSTStream EncodeInfo{..} e = (header, builder)   where-    builder = buildErrorCodeId e+    builder = (b4 :)+    b4 = bytestring4 $ fromErrorCodeId e     header = FrameHeader 4 encodeFlags encodeStreamId  buildFramePayloadSettings :: EncodeInfo -> SettingsList -> (FrameHeader, Builder) buildFramePayloadSettings EncodeInfo{..} alist = (header, builder)   where-    builder = foldr op mempty alist-    (key, val) `op` x = BB.fromWord16be (fromSettingsKeyId key)-                        <> BB.fromWord32be (fromIntegral val) <> x+    builder = (settings :)+    settings = unsafeCreate len $ \ptr -> go ptr alist+    go _ []          = return ()+    go p ((k,v):kvs) = do+        poke16 p $ fromSettingsKeyId k+        poke32 (p `plusPtr` 2) $ fromIntegral v+        go (p `plusPtr` 6) kvs     len = length alist * 6     header = FrameHeader len encodeFlags encodeStreamId  buildFramePayloadPushPromise :: EncodeInfo -> StreamIdentifier -> HeaderBlockFragment -> (FrameHeader, Builder)-buildFramePayloadPushPromise einfo sid hdr = buildPadding einfo builder len+buildFramePayloadPushPromise einfo stid hdr = buildPadding einfo builder len   where-    builder = buildStream sid <> BB.fromByteString hdr-    len = 4 + B.length hdr+    builder = (b4 :) . (hdr :)+    b4 = bytestring4 $ fromIntegral $ fromStreamIdentifier stid+    len = 4 + BS.length hdr  buildFramePayloadPing :: EncodeInfo -> ByteString -> (FrameHeader, Builder) buildFramePayloadPing EncodeInfo{..} odata = (header, builder)   where-    builder = BB.fromByteString odata+    builder = (odata :)     header = FrameHeader 8 encodeFlags encodeStreamId  buildFramePayloadGoAway :: EncodeInfo -> LastStreamId -> ErrorCodeId -> ByteString -> (FrameHeader, Builder) buildFramePayloadGoAway EncodeInfo{..} sid e debug = (header, builder)   where-    builder = buildStream sid <> buildErrorCodeId e <> BB.fromByteString debug-    len = 4 + 4 + B.length debug+    builder = (b8 :) . (debug :)+    len0 = 8+    b8 = unsafeCreate len0 $ \ptr -> do+        poke32 ptr $ fromIntegral $ fromStreamIdentifier sid+        poke32 (ptr `plusPtr` 4) $ fromErrorCodeId e+    len = len0 + BS.length debug     header = FrameHeader len encodeFlags encodeStreamId  buildFramePayloadWindowUpdate :: EncodeInfo -> WindowSizeIncrement -> (FrameHeader, Builder) buildFramePayloadWindowUpdate EncodeInfo{..} size = (header, builder)   where     -- fixme: reserve bit-    builder = BB.fromWord32be size+    builder = (b4 :)+    b4 = bytestring4 size     header = FrameHeader 4 encodeFlags encodeStreamId  buildFramePayloadContinuation :: EncodeInfo -> HeaderBlockFragment -> (FrameHeader, Builder) buildFramePayloadContinuation EncodeInfo{..} hdr = (header, builder)   where-    builder = BB.fromByteString hdr-    len = B.length hdr+    builder = (hdr :)+    len = BS.length hdr     header = FrameHeader len encodeFlags encodeStreamId  buildFramePayloadUnknown :: EncodeInfo -> ByteString -> (FrameHeader, Builder) buildFramePayloadUnknown = buildFramePayloadData++----------------------------------------------------------------++poke8 :: Ptr Word8 -> Int -> Word8 -> IO ()+poke8 ptr n w = poke (ptr `plusPtr` n) w++poke16 :: Ptr Word8 -> Word16 -> IO ()+poke16 ptr i = do+    poke ptr w0+    poke8 ptr 1 w1+  where+    w0 = fromIntegral ((i `shiftR`  8) .&. 0xff)+    w1 = fromIntegral  (i              .&. 0xff)++poke24 :: Ptr Word8 -> Int -> IO ()+poke24 ptr i = do+    poke ptr w0+    poke8 ptr 1 w1+    poke8 ptr 2 w2+  where+    w0 = fromIntegral ((i `shiftR` 16) .&. 0xff)+    w1 = fromIntegral ((i `shiftR`  8) .&. 0xff)+    w2 = fromIntegral  (i              .&. 0xff)++poke32 :: Ptr Word8 -> Word32 -> IO ()+poke32 ptr i = do+    poke ptr w0+    poke8 ptr 1 w1+    poke8 ptr 2 w2+    poke8 ptr 3 w3+  where+    w0 = fromIntegral ((i `shiftR` 24) .&. 0xff)+    w1 = fromIntegral ((i `shiftR` 16) .&. 0xff)+    w2 = fromIntegral ((i `shiftR`  8) .&. 0xff)+    w3 = fromIntegral  (i              .&. 0xff)++bytestring4 :: Word32 -> ByteString+bytestring4 i = unsafeCreate 4 $ \ptr -> poke32 ptr i
Network/HTTP2/Types.hs view
@@ -36,6 +36,7 @@   , FrameHeader(..)   , FramePayload(..)   , framePayloadToFrameTypeId+  , isPaddingDefined   -- * Stream identifier   , StreamIdentifier(..)   , fromStreamIdentifier@@ -535,3 +536,19 @@ framePayloadToFrameTypeId (WindowUpdateFrame _)  = FrameWindowUpdate framePayloadToFrameTypeId (ContinuationFrame _)  = FrameContinuation framePayloadToFrameTypeId (UnknownFrame w8 _)    = FrameUnknown w8++----------------------------------------------------------------++-- | Checking if padding is defined in this frame type.+isPaddingDefined :: FramePayload -> Bool+isPaddingDefined (DataFrame _)          = True+isPaddingDefined (HeadersFrame _ _)     = True+isPaddingDefined (PriorityFrame _)      = False+isPaddingDefined (RSTStreamFrame _)     = False+isPaddingDefined (SettingsFrame _)      = False+isPaddingDefined (PushPromiseFrame _ _) = True+isPaddingDefined (PingFrame _)          = False+isPaddingDefined (GoAwayFrame _ _ _)    = False+isPaddingDefined (WindowUpdateFrame _)  = False+isPaddingDefined (ContinuationFrame _)  = False+isPaddingDefined (UnknownFrame _ _)     = False
http2.cabal view
@@ -1,5 +1,5 @@ Name:                   http2-Version:                0.8.0+Version:                0.9.0 Author:                 Kazu Yamamoto <kazu@iij.ad.jp> Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp> License:                BSD3@@ -133,6 +133,7 @@   Other-Modules:        Case                         JSON   Build-Depends:        base >= 4 && < 5+                      , Glob                       , aeson                       , aeson-pretty                       , bytestring
test-frame/Case.hs view
@@ -44,8 +44,8 @@   where     -- fromJust is unsafe     frm = case decodeFrame defaultSettings $ fromJust $ unhex wire_hex of-        Left  e     -> error $ show e-        Right (r,_) -> r+        Left  e -> error $ show e+        Right r -> r wireToCase CaseWire { wire_error = Just e, ..} = Case {     description = wire_description   , wire = wire_hex
test-frame/JSON.hs view
@@ -13,7 +13,7 @@ import qualified Data.ByteString as BS import qualified Data.ByteString.Char8 as B8 import Data.HashMap.Strict (union)-import Data.Maybe (fromJust)+import Data.Maybe (fromJust, fromMaybe) import Data.Text (Text) import qualified Data.Text as T @@ -63,19 +63,6 @@ instance FromJSON StreamIdentifier where     parseJSON x = StreamIdentifier <$> parseJSON x -instance ToJSON Priority where-    toJSON (Priority e s w) = object [-        "exclusive" .= e-      , "stream_dependency" .= s-      , "weight" .= w-      ]--instance FromJSON Priority where-    parseJSON (Object o) = Priority <$> o .: "exclusive"-                                    <*> o .: "stream_dependency"-                                    <*> o .: "weight"-    parseJSON _          = mzero- instance ToJSON ErrorCodeId where     toJSON e = toJSON $ fromErrorCodeId e @@ -101,11 +88,15 @@         "data" .= body       ]     toJSON (HeadersFrame mpri hdr) = object [-        "priority" .= mpri+        "exclusive" .= fromMaybe Null (toJSON . exclusive <$> mpri)+      , "stream_dependency" .= fromMaybe Null (toJSON . streamDependency <$> mpri)+      , "weight" .= fromMaybe Null (toJSON . weight <$> mpri)       , "header_block_fragment" .= hdr       ]     toJSON (PriorityFrame pri) = object [-        "priority" .= pri+        "exclusive" .= exclusive pri+      , "stream_dependency" .= streamDependency pri+      , "weight" .= weight pri       ]     toJSON (RSTStreamFrame e) = object [         "error_code" .= e@@ -147,7 +138,9 @@       ]       where         padObj = case toJSON fpPad of-            Null -> emptyPad+            Null+              | isPaddingDefined framePayload -> emptyPad+              | otherwise                     -> noPad             x    -> x  instance FromJSON FramePad where@@ -175,11 +168,27 @@   where     ftid = toFrameTypeId ftyp +priority :: Object -> Parser Priority+priority o = Priority <$> o .: "exclusive"+                       <*> o .: "stream_dependency"+                       <*> o .: "weight"++mpriority :: Object -> Parser (Maybe Priority)+mpriority o = do+    me <- o .:? "exclusive"+    ms <- o .:? "stream_dependency"+    mw <- o .:? "weight"+    return $ case me of+        Nothing -> Nothing+        Just ex -> Just $ Priority ex (fromJust ms) (fromJust mw)+ parsePayload :: FrameTypeId -> Object -> Parser FramePayload parsePayload FrameData o = DataFrame <$> o .: "data"-parsePayload FrameHeaders o = HeadersFrame <$> o .: "priority"-                                           <*> o .: "header_block_fragment"-parsePayload FramePriority o = PriorityFrame <$> o .: "priority"+parsePayload FrameHeaders o = do+    mpri <- mpriority o+    hdr <- o .: "header_block_fragment"+    return $ HeadersFrame mpri hdr+parsePayload FramePriority o = PriorityFrame <$> priority o parsePayload FrameRSTStream o = RSTStreamFrame <$> o .: "error_code" parsePayload FrameSettings o = SettingsFrame <$> o .: "settings" parsePayload FramePushPromise o = PushPromiseFrame <$> o .: "promised_stream_id"@@ -190,6 +199,7 @@                                          <*> o .: "additional_debug_data" parsePayload FrameWindowUpdate o = WindowUpdateFrame <$> o .: "window_size_increment" parsePayload FrameContinuation o = ContinuationFrame <$> o .: "header_block_fragment"+parsePayload (FrameUnknown typ) o = UnknownFrame typ <$> o .: "dummy"  instance ToJSON Pad where     toJSON (Pad padding) = object [@@ -202,6 +212,9 @@     "padding_length" .= Null   , "padding" .= Null   ]++noPad :: Value+noPad = object []  ----------------------------------------------------------------