Safe Haskell | None |
---|---|
Language | Haskell2010 |
- type JsonRpcT = ReaderT Session
- runJsonRpcT :: (FromRequest q, ToJSON r) => Ver -> Respond q IO r -> Sink ByteString IO () -> Source IO ByteString -> JsonRpcT IO a -> IO a
- sendRequest :: (ToJSON q, ToRequest q, FromResponse r, MonadIO m) => q -> JsonRpcT m (STM (Either ErrorObj (Maybe r)))
- sendNotif :: (ToJSON no, ToNotif no, Monad m) => no -> JsonRpcT m (STM ())
- receiveNotif :: (Monad m, FromNotif n) => JsonRpcT m (STM (Maybe (Either ErrorObj (Maybe n))))
- jsonRpcTcpClient :: (FromRequest q, ToJSON r) => Ver -> ClientSettings -> Respond q IO r -> JsonRpcT IO a -> IO a
- dummyRespond :: Monad m => Respond () m ()
- jsonRpcTcpServer :: (FromRequest q, ToJSON r) => Ver -> ServerSettings -> Respond q IO r -> JsonRpcT IO () -> IO ()
- dummySrv :: MonadIO m => JsonRpcT m ()
- data Request = Request {
- getReqVer :: !Ver
- getReqMethod :: !Method
- getReqParams :: !Value
- getReqId :: !Id
- class FromRequest q where
- parseParams :: Method -> Maybe (Value -> Parser q)
- fromRequest :: FromRequest q => Request -> Maybe q
- class ToRequest q where
- requestMethod :: q -> Method
- buildRequest :: (ToJSON q, ToRequest q) => Ver -> q -> Id -> Request
- data Response = Response {}
- class FromResponse r where
- parseResult :: Method -> Maybe (Value -> Parser r)
- fromResponse :: FromResponse r => Method -> Response -> Maybe r
- type Respond q m r = q -> m (Either ErrorObj r)
- buildResponse :: (Monad m, FromRequest q, ToJSON r) => Respond q m r -> Request -> m (Either RpcError Response)
- data Notif = Notif {
- getNotifVer :: !Ver
- getNotifMethod :: !Method
- getNotifParams :: !Value
- class FromNotif n where
- parseNotif :: Method -> Maybe (Value -> Parser n)
- fromNotif :: FromNotif n => Notif -> Maybe n
- class ToNotif n where
- notifMethod :: n -> Method
- buildNotif :: (ToJSON n, ToNotif n) => Ver -> n -> Notif
- data RpcError = RpcError {}
- data ErrorObj
- = ErrorObj {
- getErrMsg :: !String
- getErrCode :: !Int
- getErrData :: !Value
- | ErrorVal {
- getErrData :: !Value
- = ErrorObj {
- fromError :: ErrorObj -> String
- errorParse :: Value -> ErrorObj
- errorInvalid :: Value -> ErrorObj
- errorParams :: Value -> ErrorObj
- errorMethod :: Method -> ErrorObj
- errorId :: Id -> ErrorObj
- data Message
- = MsgRequest {
- getMsgRequest :: !Request
- | MsgResponse { }
- | MsgNotif {
- getMsgNotif :: !Notif
- | MsgError {
- getMsgError :: !RpcError
- = MsgRequest {
- type Method = Text
- data Id
- data Ver
Introduction
This JSON-RPC library is fully-compatible with JSON-RPC 2.0 and 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.
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.
Server Example
This JSON-RPC server returns the current time.
{-# LANGUAGE OverloadedStrings #-} import Control.Applicative import Data.Aeson.Types hiding (Error) import Data.Conduit.Network import Data.Time.Clock import Data.Time.Format import Network.JsonRpc import System.Locale data TimeReq = TimeReq data TimeRes = TimeRes { timeRes :: UTCTime } instance FromRequest TimeReq where parseParams "time" = Just $ const $ return TimeReq parseParams _ = Nothing instance ToJSON TimeRes where toJSON (TimeRes t) = toJSON $ formatTime defaultTimeLocale "%c" t respond :: Respond TimeReq IO TimeRes respond TimeReq = Right . TimeRes <$> getCurrentTime main :: IO () main = jsonRpcTcpServer V2 (serverSettings 31337 "::1") respond dummySrv
Client Example
Corresponding TCP client to get time from server.
{-# LANGUAGE OverloadedStrings #-} import Control.Concurrent import Control.Concurrent.STM import Control.Monad import Control.Monad.Trans import Data.Aeson import Data.Aeson.Types hiding (Error) 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 { timeRes :: UTCTime } instance ToRequest TimeReq where requestMethod TimeReq = "time" instance ToJSON TimeReq where toJSON TimeReq = emptyArray instance FromResponse TimeRes where parseResult "time" = Just $ withText "time" $ \t -> case f t of Just t' -> return $ TimeRes t' Nothing -> mzero where f t = parseTime defaultTimeLocale "%c" (T.unpack t) parseResult _ = Nothing req :: JsonRpcT IO UTCTime req = sendRequest TimeReq >>= liftIO . atomically >>= \ts -> case ts of Left e -> error $ fromError e Right (Just (TimeRes r)) -> return r _ -> error "Could not parse response" main :: IO () main = jsonRpcTcpClient V2 (clientSettings 31337 "::1") dummyRespond . replicateM_ 4 $ req >>= liftIO . print >> liftIO (threadDelay 1000000)
Establish JSON-RPC context
:: (FromRequest q, ToJSON r) | |
=> Ver | JSON-RPC version |
-> Respond q IO r | Respond to incoming requests |
-> Sink ByteString IO () | Sink to send messages |
-> Source IO ByteString | Source of incoming messages |
-> JsonRpcT IO a | JSON-RPC action |
-> IO a | Output of action |
Create JSON-RPC session around ByteString conduits from transport layer. When context exits, session stops existing.
Communicate with remote party
sendRequest :: (ToJSON q, ToRequest q, FromResponse r, MonadIO m) => q -> JsonRpcT m (STM (Either ErrorObj (Maybe r))) Source
Returns Right Nothing if could not parse response. Run output in STM monad. STM will block until response arrives.
sendNotif :: (ToJSON no, ToNotif no, Monad m) => no -> JsonRpcT m (STM ()) Source
Send notification. Run output in STM monad. Will not block.
receiveNotif :: (Monad m, FromNotif n) => JsonRpcT m (STM (Maybe (Either ErrorObj (Maybe n)))) Source
Receive notifications from peer. Returns Nothing if incoming channel is closed and empty. Result is Right Nothing if it failed to parse notification. Run output in STM monad. Will not block.
Transports
Client
:: (FromRequest q, ToJSON r) | |
=> Ver | JSON-RPC version |
-> ClientSettings | Connection settings |
-> Respond q IO r | Respond to incoming requests |
-> JsonRpcT IO a | JSON-RPC action |
-> IO a | Output of action |
TCP client transport for JSON-RPC.
dummyRespond :: Monad m => Respond () m () Source
Respond function for systems that do not reply to requests, as usual in clients.
Server
:: (FromRequest q, ToJSON r) | |
=> Ver | JSON-RPC version |
-> ServerSettings | Connection settings |
-> Respond q IO r | Respond to incoming requests |
-> JsonRpcT IO () | Action to perform on connecting client thread |
-> IO () |
TCP server transport for JSON-RPC.
dummySrv :: MonadIO m => JsonRpcT m () Source
Dummy server for servers not expecting client to send notifications, that is most cases.
Requests
Request | |
|
Parsing
class FromRequest q where Source
fromRequest :: FromRequest q => Request -> Maybe q Source
Encoding
class ToRequest q where Source
requestMethod :: q -> Method Source
Method associated with request data to build a request object.
Responses
Parsing
class FromResponse r where Source
fromResponse :: FromResponse r => Method -> Response -> Maybe r Source
Encoding
buildResponse :: (Monad m, FromRequest q, ToJSON r) => Respond q m r -> Request -> m (Either RpcError Response) Source
Notifications
Notif | |
|
Parsing
Encoding
Errors
ErrorObj | |
| |
ErrorVal | |
|
Error Messages
errorParse :: Value -> ErrorObj Source
Parse error.
errorInvalid :: Value -> ErrorObj Source
Invalid request.
errorParams :: Value -> ErrorObj Source
Invalid params.
errorMethod :: Method -> ErrorObj Source
Method not found.