diff options
author | Sam Truzjan <pxqr.sta@gmail.com> | 2013-12-08 06:10:22 +0400 |
---|---|---|
committer | Sam Truzjan <pxqr.sta@gmail.com> | 2013-12-08 06:10:22 +0400 |
commit | 6e5d3aacb36d7b6353a5a2b397fa237193f1e1e7 (patch) | |
tree | 63993ec100b809d854ed11388311d548e1d8256b /src/Network/BitTorrent/Exchange/Message.hs | |
parent | 04104b0615ed13f823a85913f48fece29afb0c84 (diff) |
Serialization for metadata exchange extension
Diffstat (limited to 'src/Network/BitTorrent/Exchange/Message.hs')
-rw-r--r-- | src/Network/BitTorrent/Exchange/Message.hs | 89 |
1 files changed, 66 insertions, 23 deletions
diff --git a/src/Network/BitTorrent/Exchange/Message.hs b/src/Network/BitTorrent/Exchange/Message.hs index 8659eb89..4d1694c6 100644 --- a/src/Network/BitTorrent/Exchange/Message.hs +++ b/src/Network/BitTorrent/Exchange/Message.hs | |||
@@ -78,9 +78,10 @@ module Network.BitTorrent.Exchange.Message | |||
78 | 78 | ||
79 | import Control.Applicative | 79 | import Control.Applicative |
80 | import Control.Arrow ((&&&), (***)) | 80 | import Control.Arrow ((&&&), (***)) |
81 | import Data.Attoparsec.ByteString.Char8 as BS | ||
81 | import Data.BEncode as BE | 82 | import Data.BEncode as BE |
82 | import Data.BEncode.BDict as BE | 83 | import Data.BEncode.BDict as BE |
83 | import Data.BEncode.Internal (ppBEncode) | 84 | import Data.BEncode.Internal as BE (ppBEncode, parser) |
84 | import Data.BEncode.Types (BDict) | 85 | import Data.BEncode.Types (BDict) |
85 | import Data.Bits | 86 | import Data.Bits |
86 | import Data.ByteString as BS | 87 | import Data.ByteString as BS |
@@ -99,11 +100,12 @@ import Data.Typeable | |||
99 | import Data.Word | 100 | import Data.Word |
100 | import Network | 101 | import Network |
101 | import Network.Socket hiding (KeepAlive) | 102 | import Network.Socket hiding (KeepAlive) |
102 | import Text.PrettyPrint as PP | 103 | import Text.PrettyPrint as PP hiding ((<>)) |
103 | import Text.PrettyPrint.Class | 104 | import Text.PrettyPrint.Class |
104 | 105 | ||
105 | import Data.Torrent.Bitfield | 106 | import Data.Torrent.Bitfield |
106 | import Data.Torrent.InfoHash | 107 | import Data.Torrent.InfoHash |
108 | import qualified Data.Torrent.Piece as Data | ||
107 | import Network.BitTorrent.Core | 109 | import Network.BitTorrent.Core |
108 | import Network.BitTorrent.Exchange.Block | 110 | import Network.BitTorrent.Exchange.Block |
109 | 111 | ||
@@ -556,8 +558,8 @@ data ExtendedHandshake = ExtendedHandshake | |||
556 | -- , yourip :: Maybe (Either HostAddress HostAddress6) | 558 | -- , yourip :: Maybe (Either HostAddress HostAddress6) |
557 | } deriving (Show, Eq, Typeable) | 559 | } deriving (Show, Eq, Typeable) |
558 | 560 | ||
559 | extendedHandshakeId :: ExtendedMessageId | 561 | extHandshakeId :: ExtendedMessageId |
560 | extendedHandshakeId = 0 | 562 | extHandshakeId = 0 |
561 | 563 | ||
562 | -- | Default 'Request' queue size. | 564 | -- | Default 'Request' queue size. |
563 | defaultQueueLength :: Int | 565 | defaultQueueLength :: Int |
@@ -626,9 +628,12 @@ data ExtendedMetadata | |||
626 | -- is either a 'MetadataReject' or a 'MetadataData' message. | 628 | -- is either a 'MetadataReject' or a 'MetadataData' message. |
627 | = MetadataRequest PieceIx | 629 | = MetadataRequest PieceIx |
628 | 630 | ||
631 | -- | If sender requested a valid 'PieceIx' and receiver have the | ||
632 | -- corresponding piece then receiver should respond with this | ||
633 | -- message. | ||
629 | | MetadataData | 634 | | MetadataData |
630 | { -- | FIXME add piece data | 635 | { -- | A piece of 'Data.Torrent.InfoDict'. |
631 | piece :: PieceIx | 636 | piece :: Data.Piece BS.ByteString |
632 | 637 | ||
633 | -- | This key has the same semantics as the 'ehsMetadataSize' in | 638 | -- | This key has the same semantics as the 'ehsMetadataSize' in |
634 | -- the 'ExtendedHandshake' — it is size of the torrent info | 639 | -- the 'ExtendedHandshake' — it is size of the torrent info |
@@ -663,7 +668,7 @@ instance BEncode ExtendedMetadata where | |||
663 | msg_type_key .=! (0 :: MetadataId) | 668 | msg_type_key .=! (0 :: MetadataId) |
664 | .: piece_key .=! pix | 669 | .: piece_key .=! pix |
665 | .: endDict | 670 | .: endDict |
666 | toBEncode (MetadataData pix totalSize) = toDict $ | 671 | toBEncode (MetadataData (Data.Piece pix _) totalSize) = toDict $ |
667 | msg_type_key .=! (1 :: MetadataId) | 672 | msg_type_key .=! (1 :: MetadataId) |
668 | .: piece_key .=! pix | 673 | .: piece_key .=! pix |
669 | .: total_size_key .=! totalSize | 674 | .: total_size_key .=! totalSize |
@@ -678,15 +683,17 @@ instance BEncode ExtendedMetadata where | |||
678 | mid <- field $ req msg_type_key | 683 | mid <- field $ req msg_type_key |
679 | case mid :: MetadataId of | 684 | case mid :: MetadataId of |
680 | 0 -> MetadataRequest <$>! piece_key | 685 | 0 -> MetadataRequest <$>! piece_key |
681 | 1 -> MetadataData <$>! piece_key <*>! total_size_key | 686 | 1 -> metadataData <$>! piece_key <*>! total_size_key |
682 | 2 -> MetadataReject <$>! piece_key | 687 | 2 -> MetadataReject <$>! piece_key |
683 | _ -> pure (MetadataUnknown bval) | 688 | _ -> pure (MetadataUnknown bval) |
689 | where | ||
690 | metadataData pix s = MetadataData (Data.Piece pix BS.empty) s | ||
684 | 691 | ||
685 | -- | Piece data bytes are omitted. | 692 | -- | Piece data bytes are omitted. |
686 | instance Pretty ExtendedMetadata where | 693 | instance Pretty ExtendedMetadata where |
687 | pretty (MetadataRequest pix ) = "Request" <+> PP.int pix | 694 | pretty (MetadataRequest pix ) = "Request" <+> PP.int pix |
688 | pretty (MetadataData pix s) = "Data" <+> PP.int pix <+> PP.int s | 695 | pretty (MetadataData pi t) = "Data" <+> pretty pi <+> PP.int t |
689 | pretty (MetadataReject pix ) = "Reject" <+> PP.int pix | 696 | pretty (MetadataReject pix ) = "Reject" <+> PP.int pix |
690 | pretty (MetadataUnknown bval ) = "Unknown" <+> ppBEncode bval | 697 | pretty (MetadataUnknown bval ) = "Unknown" <+> ppBEncode bval |
691 | 698 | ||
692 | instance PeerMessage ExtendedMetadata where | 699 | instance PeerMessage ExtendedMetadata where |
@@ -699,6 +706,43 @@ instance PeerMessage ExtendedMetadata where | |||
699 | metadataPieceSize :: Int | 706 | metadataPieceSize :: Int |
700 | metadataPieceSize = 16 * 1024 | 707 | metadataPieceSize = 16 * 1024 |
701 | 708 | ||
709 | -- TODO we can check if the piece payload bytestring have appropriate | ||
710 | -- length; otherwise serialization MUST fail. | ||
711 | isLastMetadata :: ExtendedMetadata -> Bool | ||
712 | isLastMetadata = undefined -- FIXME | ||
713 | |||
714 | checkPiece :: ExtendedMetadata -> Bool | ||
715 | checkPiece = undefined -- FIXME | ||
716 | |||
717 | setMetadataPayload :: BS.ByteString -> ExtendedMetadata -> ExtendedMetadata | ||
718 | setMetadataPayload bs (MetadataData (Data.Piece pix _) t) = | ||
719 | MetadataData (Data.Piece pix bs) t | ||
720 | setMetadataPayload _ msg = msg | ||
721 | |||
722 | getMetadataPayload :: ExtendedMetadata -> Maybe BS.ByteString | ||
723 | getMetadataPayload (MetadataData (Data.Piece _ bs) _) = Just bs | ||
724 | getMetadataPayload _ = Nothing | ||
725 | |||
726 | -- to make MetadataData constructor fields a little bit prettier we | ||
727 | -- cheat here: first we read empty 'pieceData' from bdict, but then we | ||
728 | -- fill that field with the actual piece data — trailing bytes of | ||
729 | -- the message | ||
730 | getMetadata :: Int -> S.Get ExtendedMetadata | ||
731 | getMetadata len = do | ||
732 | bs <- getByteString len | ||
733 | case BS.parse BE.parser bs of | ||
734 | BS.Fail _ _ _ -> fail "unable to parse metadata bdict: possible corrupted" | ||
735 | BS.Partial c -> fail "unable to parse metadata bdict: not enough bytes" | ||
736 | BS.Done piece bvalueBS -> do | ||
737 | let msg = "metadata dictionary is invalid" | ||
738 | metadata <- either (fail msg) pure $ fromBEncode bvalueBS | ||
739 | pure $ setMetadataPayload piece metadata | ||
740 | |||
741 | putMetadata :: ExtendedMetadata -> BL.ByteString | ||
742 | putMetadata msg | ||
743 | | Just bs <- getMetadataPayload msg = BE.encode msg <> BL.fromStrict bs | ||
744 | | otherwise = BE.encode msg | ||
745 | |||
702 | {----------------------------------------------------------------------- | 746 | {----------------------------------------------------------------------- |
703 | -- Extension protocol messages | 747 | -- Extension protocol messages |
704 | -----------------------------------------------------------------------} | 748 | -----------------------------------------------------------------------} |
@@ -900,23 +944,22 @@ getExtendedMessage messageSize = do | |||
900 | let msgBodySize = messageSize - 1 | 944 | let msgBodySize = messageSize - 1 |
901 | case msgId of | 945 | case msgId of |
902 | 0 -> EHandshake <$> getExtendedHandshake msgBodySize | 946 | 0 -> EHandshake <$> getExtendedHandshake msgBodySize |
903 | 1 -> EMetadata msgId <$> undefined | 947 | 1 -> EMetadata msgId <$> getMetadata msgBodySize |
904 | _ -> EUnknown msgId <$> getByteString msgBodySize | 948 | _ -> EUnknown msgId <$> getByteString msgBodySize |
905 | 949 | ||
906 | extendedMessageId :: MessageId | 950 | extendedMessageId :: MessageId |
907 | extendedMessageId = 20 | 951 | extendedMessageId = 20 |
908 | 952 | ||
953 | putExt :: ExtendedMessageId -> BL.ByteString -> Put | ||
954 | putExt mid lbs = do | ||
955 | putWord32be $ fromIntegral (1 + 1 + BL.length lbs) | ||
956 | putWord8 extendedMessageId | ||
957 | putWord8 mid | ||
958 | putLazyByteString lbs | ||
959 | |||
909 | -- NOTE: in contrast to getExtendedMessage this function put length | 960 | -- NOTE: in contrast to getExtendedMessage this function put length |
910 | -- and message id too! | 961 | -- and message id too! |
911 | putExtendedMessage :: Putter ExtendedMessage | 962 | putExtendedMessage :: Putter ExtendedMessage |
912 | putExtendedMessage (EHandshake hs) = do | 963 | putExtendedMessage (EHandshake hs) = putExt extHandshakeId (BE.encode hs) |
913 | putExtendedMessage $ EUnknown extendedHandshakeId | 964 | putExtendedMessage (EMetadata mid msg) = putExt mid (BE.encode msg) |
914 | $ BL.toStrict $ BE.encode hs | 965 | putExtendedMessage (EUnknown mid bs) = putExt mid (BL.fromStrict bs) |
915 | putExtendedMessage (EMetadata mid msg) = do | ||
916 | putExtendedMessage $ EUnknown mid | ||
917 | $ BL.toStrict $ BE.encode msg | ||
918 | putExtendedMessage (EUnknown mid bs) = do | ||
919 | putWord32be $ fromIntegral (1 + 1 + BS.length bs) | ||
920 | putWord8 extendedMessageId | ||
921 | putWord8 mid | ||
922 | putByteString bs | ||