summaryrefslogtreecommitdiff
path: root/src/Network/KRPC/Message.hs
diff options
context:
space:
mode:
Diffstat (limited to 'src/Network/KRPC/Message.hs')
-rw-r--r--src/Network/KRPC/Message.hs159
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 #-}
21module Network.KRPC.Message
22 ( -- * Error
23 KError(..)
24 , serverError
25
26 -- * Query
27 , KQuery(..)
28 , MethodName
29
30 -- * Response
31 , KResponse(..)
32 ) where
33
34import Control.Exception.Lifted as Lifted
35import Data.BEncode as BE
36import Data.BEncode.BDict as BE
37import Data.ByteString as B
38import Data.ByteString.Char8 as BC
39import 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--
49data 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
63instance 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
79instance Exception KError
80
81type ErrorCode = Int
82
83errorCode :: KError -> ErrorCode
84errorCode (GenericError _) = 201
85errorCode (ServerError _) = 202
86errorCode (ProtocolError _) = 203
87errorCode (MethodUnknown _) = 204
88{-# INLINE errorCode #-}
89
90mkKError :: ErrorCode -> ByteString -> KError
91mkKError 201 = GenericError
92mkKError 202 = ServerError
93mkKError 203 = ProtocolError
94mkKError 204 = MethodUnknown
95mkKError _ = GenericError
96{-# INLINE mkKError #-}
97
98serverError :: SomeException -> KError
99serverError = ServerError . BC.pack . show
100
101type 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--
111data KQuery = KQuery
112 { queryMethod :: !MethodName
113 , queryArgs :: !BValue
114 } deriving (Show, Read, Eq, Ord, Typeable)
115
116instance 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--
143newtype KResponse = KResponse
144 { respVals :: BValue
145 } deriving (Show, Read, Eq, Ord, Typeable)
146
147instance 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"