/// <summary> /// JSON-RPC2リクエストを送信する。 /// </summary> /// <param name="method">メソッド名。</param> /// <param name="param">引数のJSONオブジェクト(MiniJSONで変換可能な型のみ)。</param> /// <returns>メソッドの戻り値。</returns> public IObservable <string> Call(string method, object param = null) { return(Observable.Defer(() => { // リトライされた場合もIDは変える var id = this.GenerateId(); var json = new JsonRpc2Request(method, param, id).ToJson(); var subject = new Subject <object>(); this.callback[id] = subject; this.Send(json) .Subscribe(_ => { }, (ex) => { this.callback.Remove(id); subject.OnError(ex); }); // タイムアウトチェック用のタイマーもセットする if (this.Timeout > 0) { Observable.Timer(TimeSpan.FromMilliseconds(this.Timeout)).Subscribe(_ => { Subject <object> sub; if (this.callback.TryGetValue(id, out sub)) { this.callback.Remove(id); sub.OnError(new JsonRpc2Exception((int)JsonRpc2Exception.ErrorCode.InternalError, "RPC response timeouted. (" + json + ")")); } }); } return subject.Select((s) => (string)s); })); }
/// <summary> /// JSON-RPC2リクエスト/レスポンスを処理する。 /// </summary> /// <param name="json">リクエスト/レスポンスのJSON文字列。</param> /// <returns>レスポンスのJSON文字列。通知やレスポンス無しの場合はnull。</returns> private async Task <JsonRpc2Response> DoMethodOrCallback(string json) { // TODO: バッチリクエスト/レスポンスには未対応 // レスポンス/リクエストかチェック JsonRpc2Response res; JsonRpc2Request req; if (JsonRpc2Response.TryParse(json, out res)) { // ※ intのIDしか送ってないのでintのIDしか返ってこない想定 var id = (int)(long)res.Id; Subject <object> subject; if (this.callback.TryGetValue(id, out subject)) { if (res.Error != null) { subject.OnError(res.Error); } else { subject.OnNext(Json.Serialize(res.Result)); subject.OnCompleted(); } this.callback.Remove(id); } return(null); } else if (JsonRpc2Request.TryParse(json, out req)) { // リクエストの場合はハンドラーに任せる try { var result = await this.RequestHandler(req.Method, req.Params, req.Id, this); if (req.Id != null) { return(new JsonRpc2Response(result, req.Id)); } return(null); } catch (Exception ex) { return(new JsonRpc2Response(JsonRpc2Exception.Convert(ex), req.Id)); } } throw new JsonRpc2Exception(JsonRpc2Exception.ErrorCode.ParseError); }
/// <summary> /// MiniJSONのオブジェクトをJSON-RPC2リクエストとして解析する。 /// </summary> /// <param name="json">JSONオブジェクト。</param> /// <param name="request">解析したリクエスト。</param> /// <returns>解析できた場合true。</returns> public static bool TryParse(object json, out JsonRpc2Request request) { request = null; var req = json as IDictionary <string, object>; if (req == null) { return(false); } // 以下methodだけ必須。他はあれば取得(jsonrpcのバージョンチェックはしない) object method; if (!req.TryGetValue("method", out method) || method == null) { return(false); } object jsonrpcObj; var jsonrpc = ""; if (req.TryGetValue("jsonrpc", out jsonrpcObj) && jsonrpcObj != null) { jsonrpc = jsonrpcObj.ToString(); } object param; req.TryGetValue("params", out param); object id; req.TryGetValue("id", out id); request = new JsonRpc2Request(method.ToString(), param, id) { Jsonrpc = jsonrpc }; return(true); }
/// <summary> /// JSON文字列をJSON-RPC2リクエストとして解析する。 /// </summary> /// <param name="json">JSON文字列。</param> /// <param name="request">解析したリクエスト。</param> /// <returns>解析できた場合true。</returns> public static bool TryParse(string json, out JsonRpc2Request request) { return(TryParse(Json.Deserialize(json), out request)); }