diff options
author | Sam Truzjan <pxqr.sta@gmail.com> | 2013-12-19 17:20:16 +0400 |
---|---|---|
committer | Sam Truzjan <pxqr.sta@gmail.com> | 2013-12-19 17:20:16 +0400 |
commit | 7a13eea1ad815411ee7bce4dcaa8a49bdd979356 (patch) | |
tree | fc9f73186891684d23d49e503bfdf4f291d6d405 /src/Network/KRPC/Message.hs | |
parent | 621c73c849332a9446c6e5b9bcd557b30884b318 (diff) |
Rename Protocol module to Message
Diffstat (limited to 'src/Network/KRPC/Message.hs')
-rw-r--r-- | src/Network/KRPC/Message.hs | 159 |
1 files changed, 159 insertions, 0 deletions
diff --git a/src/Network/KRPC/Message.hs b/src/Network/KRPC/Message.hs new file mode 100644 index 00000000..854b733c --- /dev/null +++ b/src/Network/KRPC/Message.hs | |||
@@ -0,0 +1,159 @@ | |||
1 | -- | | ||
2 | -- Copyright : (c) Sam Truzjan 2013 | ||
3 | -- License : BSD3 | ||
4 | -- Maintainer : pxqr.sta@gmail.com | ||
5 | -- Stability : experimental | ||
6 | -- Portability : portable | ||
7 | -- | ||
8 | -- This module provides straightforward implementation of KRPC | ||
9 | -- protocol. In many situations 'Network.KRPC' should be prefered | ||
10 | -- since it gives more safe, convenient and high level api. | ||
11 | -- | ||
12 | -- See <http://www.bittorrent.org/beps/bep_0005.html#krpc-protocol> | ||
13 | -- | ||
14 | {-# LANGUAGE OverloadedStrings #-} | ||
15 | {-# LANGUAGE FlexibleContexts #-} | ||
16 | {-# LANGUAGE TypeSynonymInstances #-} | ||
17 | {-# LANGUAGE MultiParamTypeClasses #-} | ||
18 | {-# LANGUAGE FunctionalDependencies #-} | ||
19 | {-# LANGUAGE DefaultSignatures #-} | ||
20 | {-# LANGUAGE DeriveDataTypeable #-} | ||
21 | module Network.KRPC.Message | ||
22 | ( -- * Error | ||
23 | KError(..) | ||
24 | , serverError | ||
25 | |||
26 | -- * Query | ||
27 | , KQuery(..) | ||
28 | , MethodName | ||
29 | |||
30 | -- * Response | ||
31 | , KResponse(..) | ||
32 | ) where | ||
33 | |||
34 | import Control.Exception.Lifted as Lifted | ||
35 | import Data.BEncode as BE | ||
36 | import Data.BEncode.BDict as BE | ||
37 | import Data.ByteString as B | ||
38 | import Data.ByteString.Char8 as BC | ||
39 | import Data.Typeable | ||
40 | |||
41 | -- | Errors used to signal that some error occurred while processing a | ||
42 | -- procedure call. Error may be send only from server to client but | ||
43 | -- not in the opposite direction. | ||
44 | -- | ||
45 | -- Errors are encoded as bencoded dictionary: | ||
46 | -- | ||
47 | -- > { "y" : "e", "e" : [<error_code>, <human_readable_error_reason>] } | ||
48 | -- | ||
49 | data KError | ||
50 | -- | Some error doesn't fit in any other category. | ||
51 | = GenericError { errorMessage :: !ByteString } | ||
52 | |||
53 | -- | Occur when server fail to process procedure call. | ||
54 | | ServerError { errorMessage :: !ByteString } | ||
55 | |||
56 | -- | Malformed packet, invalid arguments or bad token. | ||
57 | | ProtocolError { errorMessage :: !ByteString } | ||
58 | |||
59 | -- | Occur when client trying to call method server don't know. | ||
60 | | MethodUnknown { errorMessage :: !ByteString } | ||
61 | deriving (Show, Read, Eq, Ord, Typeable) | ||
62 | |||
63 | instance BEncode KError where | ||
64 | {-# SPECIALIZE instance BEncode KError #-} | ||
65 | {-# INLINE toBEncode #-} | ||
66 | toBEncode e = toDict $ | ||
67 | "e" .=! (errorCode e, errorMessage e) | ||
68 | .: "y" .=! ("e" :: ByteString) | ||
69 | .: endDict | ||
70 | |||
71 | {-# INLINE fromBEncode #-} | ||
72 | fromBEncode be @ (BDict d) | ||
73 | | BE.lookup "y" d == Just (BString "e") | ||
74 | = (`fromDict` be) $ do | ||
75 | uncurry mkKError <$>! "e" | ||
76 | |||
77 | fromBEncode _ = decodingError "KError" | ||
78 | |||
79 | instance Exception KError | ||
80 | |||
81 | type ErrorCode = Int | ||
82 | |||
83 | errorCode :: KError -> ErrorCode | ||
84 | errorCode (GenericError _) = 201 | ||
85 | errorCode (ServerError _) = 202 | ||
86 | errorCode (ProtocolError _) = 203 | ||
87 | errorCode (MethodUnknown _) = 204 | ||
88 | {-# INLINE errorCode #-} | ||
89 | |||
90 | mkKError :: ErrorCode -> ByteString -> KError | ||
91 | mkKError 201 = GenericError | ||
92 | mkKError 202 = ServerError | ||
93 | mkKError 203 = ProtocolError | ||
94 | mkKError 204 = MethodUnknown | ||
95 | mkKError _ = GenericError | ||
96 | {-# INLINE mkKError #-} | ||
97 | |||
98 | serverError :: SomeException -> KError | ||
99 | serverError = ServerError . BC.pack . show | ||
100 | |||
101 | type MethodName = ByteString | ||
102 | |||
103 | -- | Query used to signal that caller want to make procedure call to | ||
104 | -- callee and pass arguments in. Therefore query may be only sent from | ||
105 | -- client to server but not in the opposite direction. | ||
106 | -- | ||
107 | -- Queries are encoded as bencoded dictionary: | ||
108 | -- | ||
109 | -- > { "y" : "q", "q" : "<method_name>", "a" : [<arg1>, <arg2>, ...] } | ||
110 | -- | ||
111 | data KQuery = KQuery | ||
112 | { queryMethod :: !MethodName | ||
113 | , queryArgs :: !BValue | ||
114 | } deriving (Show, Read, Eq, Ord, Typeable) | ||
115 | |||
116 | instance BEncode KQuery where | ||
117 | {-# SPECIALIZE instance BEncode KQuery #-} | ||
118 | {-# INLINE toBEncode #-} | ||
119 | toBEncode (KQuery m args) = toDict $ | ||
120 | "a" .=! args | ||
121 | .: "q" .=! m | ||
122 | .: "y" .=! ("q" :: ByteString) | ||
123 | .: endDict | ||
124 | |||
125 | {-# INLINE fromBEncode #-} | ||
126 | fromBEncode bv @ (BDict d) | ||
127 | | BE.lookup "y" d == Just (BString "q") = (`fromDict` bv) $ do | ||
128 | a <- field (req "a") | ||
129 | q <- field (req "q") | ||
130 | return $! KQuery q a | ||
131 | |||
132 | fromBEncode _ = decodingError "KQuery" | ||
133 | |||
134 | -- | KResponse used to signal that callee successufully process a | ||
135 | -- procedure call and to return values from procedure. KResponse should | ||
136 | -- not be sent if error occurred during RPC. Thus KResponse may be only | ||
137 | -- sent from server to client. | ||
138 | -- | ||
139 | -- Responses are encoded as bencoded dictionary: | ||
140 | -- | ||
141 | -- > { "y" : "r", "r" : [<val1>, <val2>, ...] } | ||
142 | -- | ||
143 | newtype KResponse = KResponse | ||
144 | { respVals :: BValue | ||
145 | } deriving (Show, Read, Eq, Ord, Typeable) | ||
146 | |||
147 | instance BEncode KResponse where | ||
148 | {-# INLINE toBEncode #-} | ||
149 | toBEncode (KResponse vals) = toDict $ | ||
150 | "r" .=! vals | ||
151 | .: "y" .=! ("r" :: ByteString) | ||
152 | .: endDict | ||
153 | |||
154 | {-# INLINE fromBEncode #-} | ||
155 | fromBEncode bv @ (BDict d) | ||
156 | | BE.lookup "y" d == Just (BString "r") = (`fromDict` bv) $ do | ||
157 | KResponse <$>! "r" | ||
158 | |||
159 | fromBEncode _ = decodingError "KDict" | ||