| Safe Haskell | None |
|---|---|
| Language | Haskell2010 |
Network.JsonRpc
Contents
- type AppConduits qo no ro qi ni ri m = (Source m (IncomingMsg qo qi ni ri), Sink (Message qo no ro) m ())
- data IncomingMsg qo qi ni ri
- = IncomingMsg {
- incomingMsg :: !(Message qi ni ri)
- matchingReq :: !(Maybe (Request qo))
- | IncomingError { }
- = IncomingMsg {
- runConduits :: (FromRequest qi, FromNotif ni, FromResponse ri, ToJSON qo, ToJSON no, ToJSON ro) => Ver -> Bool -> Sink ByteString IO () -> Source IO ByteString -> (AppConduits qo no ro qi ni ri IO -> IO a) -> IO a
- tcpClient :: (FromRequest qi, FromNotif ni, FromResponse ri, ToJSON qo, ToJSON no, ToJSON ro) => Ver -> Bool -> ClientSettings -> (AppConduits qo no ro qi ni ri IO -> IO a) -> IO a
- tcpServer :: (FromRequest qi, FromNotif ni, FromResponse ri, ToJSON qo, ToJSON no, ToJSON ro) => Ver -> ServerSettings -> (AppConduits qo no ro qi ni ri IO -> IO ()) -> IO ()
- query :: (ToJSON qo, ToRequest qo, FromResponse ri) => Ver -> [qo] -> AppConduits qo () () () () ri IO -> IO [IncomingMsg qo () () ri]
- data Session qo = Session {
- lastId :: TVar Id
- sentRequests :: TVar (SentRequests qo)
- isLast :: TQueue Bool
- type SentRequests qo = HashMap Id (Request qo)
- initSession :: STM (Session qo)
- encodeConduit :: (ToJSON a, Monad m) => Conduit a m ByteString
- msgConduit :: MonadIO m => Bool -> Session qo -> Conduit (Message qo no ro) m (Message qo no ro)
- decodeConduit :: (FromRequest qi, FromNotif ni, FromResponse ri, MonadIO m) => Ver -> Bool -> Session qo -> Conduit ByteString m (IncomingMsg qo qi ni ri)
- data Request q = Request {
- getReqVer :: !Ver
- getReqMethod :: !Method
- getReqParams :: !q
- getReqId :: !Id
- class FromRequest q where
- paramsParser :: Method -> Maybe (Value -> Parser q)
- parseRequest :: FromRequest q => Value -> Parser (Either ErrorObj (Request q))
- class ToRequest q where
- requestMethod :: q -> Method
- buildRequest :: ToRequest q => Ver -> q -> Request q
- data Response r = Response {}
- class FromResponse r where
- parseResult :: Method -> Value -> Parser r
- parseResponse :: FromResponse r => Request q -> Value -> Parser (Either ErrorObj (Response r))
- data Notif n = Notif {
- getNotifVer :: !Ver
- getNotifMethod :: !Method
- getNotifParams :: !n
- class FromNotif n where
- notifParamsParser :: Method -> Maybe (Value -> Parser n)
- parseNotif :: FromNotif n => Value -> Parser (Either ErrorObj (Notif n))
- class ToNotif n where
- notifMethod :: n -> Method
- buildNotif :: ToNotif n => Ver -> n -> Notif n
- data ErrorObj = ErrorObj {
- getErrVer :: !Ver
- getErrMsg :: !String
- getErrCode :: !Int
- getErrData :: !Value
- getErrId :: !Id
- errorParse :: Ver -> Value -> ErrorObj
- errorInvalid :: Ver -> Value -> ErrorObj
- errorParams :: Ver -> Value -> Id -> ErrorObj
- errorMethod :: Ver -> Method -> Id -> ErrorObj
- errorId :: Ver -> Id -> ErrorObj
- data Message q n r
- = MsgRequest {
- getMsgRequest :: !(Request q)
- | MsgNotif {
- getMsgNotif :: !(Notif n)
- | MsgResponse {
- getMsgResponse :: !(Response r)
- | MsgError {
- getMsgError :: !ErrorObj
- = MsgRequest {
- type Method = Text
- data Id
- data Ver
Introduction
This JSON-RPC library is fully-compatible with JSON-RPC 2.0 and partially-compatible with JSON-RPC 1.0. It provides an interface that combines a JSON-RPC client and server. It can set and keep track of request ids to parse responses. There is support for sending and receiving notifications. You may use any underlying transport. Basic TCP client and server provided.
The recommended interface to this library is provided as conduits that
encode outgoing messages, and decode incoming messages. Incoming messages
are delivered as an IncomingMsg data structure, while outgoing messages
are sent in a Message data structure. The former packs responses and
errors with their corresponding request, and has a separate constructor for
decoding errors.
A JSON-RPC application using this interface is considered to be peer-to-peer, as it can send and receive all types of JSON-RPC message independent of whether it originated the connection.
Type classes ToRequest, ToNotif are for data that can be converted into
JSON-RPC requests and notifications respectively. An instance of aeson's
ToJSON class is also required to serialize these data structures. Make
sure that they serialize as a structured JSON value (array or object) that
can go into the params field of the JSON-RPC object. Type classes
FromRequest, FromNotif and FromResult are for deserializing JSON-RPC
messages.
Errors are deserialized to the ErrorObj data type. Only a string is
supported as contents inside a JSON-RPC 1.0 error. JSON-RPC 2.0 errors also
have a code, and possibly additional data as an aeson Value.
Server Example
This server returns the current time.
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson.Types
import Data.Conduit
import qualified Data.Conduit.List as CL
import Data.Conduit.Network
import Data.Time.Clock
import Data.Time.Format
import Network.JsonRpc
import System.Locale
data TimeReq = TimeReq
data TimeRes = TimeRes UTCTime
instance FromRequest TimeReq where
paramsParser "time" = Just $ const $ return TimeReq
paramsParser _ = Nothing
instance ToJSON TimeRes where
toJSON (TimeRes t) = toJSON $ formatTime defaultTimeLocale "%c" t
srv :: AppConduits () () TimeRes TimeReq () () IO -> IO ()
srv (src, snk) = src $= CL.mapM respond $$ snk
respond :: IncomingMsg () TimeReq () ()
-> IO (Message () () TimeRes)
respond (IncomingMsg (MsgRequest (Request ver _ TimeReq i)) Nothing) = do
t <- getCurrentTime
return $ MsgResponse (Response ver (TimeRes t) i)
respond (IncomingError e) = return $ MsgError e
respond (IncomingMsg (MsgError e) _) = return $ MsgError $ e
respond _ = undefined
main :: IO ()
main = tcpServer V2 (serverSettings 31337 "127.0.0.1") srvClient Example
Corresponding TCP client to get time from server.
{-# LANGUAGE OverloadedStrings #-}
import Data.Aeson.Types hiding (Error)
import Data.Conduit
import qualified Data.Conduit.List as CL
import Data.Conduit.Network
import qualified Data.Text as T
import Data.Time.Clock
import Data.Time.Format
import Network.JsonRpc
import System.Locale
data TimeReq = TimeReq
data TimeRes = TimeRes UTCTime
instance ToRequest TimeReq where
requestMethod TimeReq = "time"
instance ToJSON TimeReq where
toJSON TimeReq = emptyArray
instance FromResponse TimeRes where
parseResult "time" = withText "time" $ \t -> case f t of
Nothing -> fail "Could not parse time"
Just t' -> return $ TimeRes t'
where
f t = parseTime defaultTimeLocale "%c" (T.unpack t)
cli :: AppConduits TimeReq () () () () TimeRes IO
-> IO UTCTime
cli (src, snk) = do
CL.sourceList [MsgRequest $ buildRequest V2 TimeReq] $$ snk
ts <- src $$ CL.consume
case ts of
[] -> error "No response received"
[IncomingError (ErrorObj _ m _ _ _)] -> error $ "Unknown: " ++ m
[IncomingMsg (MsgError (ErrorObj _ m _ _ _)) _] -> error m
[IncomingMsg (MsgResponse (Response _ (TimeRes t) _)) _] -> return t
_ -> undefined
main :: IO ()
main = tcpClient V2 True (clientSettings 31337 "127.0.0.1") cli >>= printConduits
High-Level
type AppConduits qo no ro qi ni ri m = (Source m (IncomingMsg qo qi ni ri), Sink (Message qo no ro) m ()) Source
Conduits of sending and receiving JSON-RPC messages.
data IncomingMsg qo qi ni ri Source
Incoming messages. Responses and corresponding requests go together.
IncomingError is for problems decoding incoming messages. These should be
sent to the remote party.
Constructors
| IncomingMsg | |
Fields
| |
| IncomingError | |
Fields | |
Arguments
| :: (FromRequest qi, FromNotif ni, FromResponse ri, ToJSON qo, ToJSON no, ToJSON ro) | |
| => Ver | JSON-RPC version |
| -> Bool | Disconnect on last response |
| -> Sink ByteString IO () | Sink to send messages |
| -> Source IO ByteString | Source of incoming messages |
| -> (AppConduits qo no ro qi ni ri IO -> IO a) | JSON-RPC action |
| -> IO a | Output of action |
Arguments
| :: (FromRequest qi, FromNotif ni, FromResponse ri, ToJSON qo, ToJSON no, ToJSON ro) | |
| => Ver | JSON-RPC version |
| -> Bool | Disconnect on last response |
| -> ClientSettings | Connection settings |
| -> (AppConduits qo no ro qi ni ri IO -> IO a) | JSON-RPC action |
| -> IO a | Output of action |
Arguments
| :: (FromRequest qi, FromNotif ni, FromResponse ri, ToJSON qo, ToJSON no, ToJSON ro) | |
| => Ver | JSON-RPC version |
| -> ServerSettings | Connection settings |
| -> (AppConduits qo no ro qi ni ri IO -> IO ()) | JSON-RPC action to perform on connecting client thread |
| -> IO () |
Arguments
| :: (ToJSON qo, ToRequest qo, FromResponse ri) | |
| => Ver | JSON-RPC version |
| -> [qo] | List of requests |
| -> AppConduits qo () () () () ri IO | Message conduits |
| -> IO [IncomingMsg qo () () ri] | Incoming messages |
Send requests and get responses (or errors).
Example:
tcpClient V2 True (clientSettings 31337 "127.0.0.1") (query V2 [TimeReq])
Low-Level
JSON-RPC session mutable data.
Constructors
| Session | |
Fields
| |
type SentRequests qo = HashMap Id (Request qo) Source
Map of ids to sent requests.
initSession :: STM (Session qo) Source
Initialize JSON-RPC session.
encodeConduit :: (ToJSON a, Monad m) => Conduit a m ByteString Source
Conduit that serializes JSON documents for sending to the network.
Arguments
| :: MonadIO m | |
| => Bool | Set to true if decodeConduit must disconnect on last response |
| -> Session qo | |
| -> Conduit (Message qo no ro) m (Message qo no ro) |
Conduit for outgoing JSON-RPC messages.
Adds an id to requests whose id is IdNull.
Tracks all sent requests to match corresponding responses.
Arguments
| :: (FromRequest qi, FromNotif ni, FromResponse ri, MonadIO m) | |
| => Ver | JSON-RPC version |
| -> Bool | Close on last response |
| -> Session qo | JSON-RPC session |
| -> Conduit ByteString m (IncomingMsg qo qi ni ri) | Decoded incoming messages |
Conduit to decode incoming JSON-RPC messages. An error in the decoding
operation will output an IncomingError, which should be relayed to the
remote party.
Requests
Constructors
| Request | |
Fields
| |
Parsing
parseRequest :: FromRequest q => Value -> Parser (Either ErrorObj (Request q)) Source
Parse JSON-RPC request.
Encoding
class ToRequest q where Source
Class for data that can be sent as JSON-RPC requests. Define a method name for each request.
Methods
requestMethod :: q -> Method Source
Responses
JSON-RPC response data type
Parsing
class FromResponse r where Source
Class for data that can be received inside JSON-RPC responses.
Instances
parseResponse :: FromResponse r => Request q -> Value -> Parser (Either ErrorObj (Response r)) Source
Parse JSON-RPC response.
Notifications
Class for JSON-RPC notifications.
Constructors
| Notif | |
Fields
| |
Parsing
class FromNotif n where Source
Class for data that can be received in JSON-RPC notifications.
Encoding
Methods
notifMethod :: n -> Method Source
Build notifications.
Errors
JSON-RPC errors.
Error Messages
errorParse :: Ver -> Value -> ErrorObj Source
Parse error.
errorInvalid :: Ver -> Value -> ErrorObj Source
Invalid request.
Others
Class for any JSON-RPC message.
Constructors
| MsgRequest | |
Fields
| |
| MsgNotif | |
Fields
| |
| MsgResponse | |
Fields
| |
| MsgError | |
Fields
| |
JSON-RPC message id.