1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Linq;
5 using System.Collections.Generic;
7 using System.Threading;
8 using System.Threading.Tasks;
12 namespace GumartinM.JsonRPC4NET
15 /// Json rpc http async client.
17 public class JsonRpcHttpAsyncClient
27 private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
30 /// The _json settings.
32 private readonly JsonSerializerSettings _jsonSettings =
33 new JsonSerializerSettings
35 Error = delegate(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
37 _logger.Error(args.ErrorContext.Error.Message);
38 args.ErrorContext.Handled = true;
43 /// The _exception resolver.
45 private readonly ExceptionResolver _exceptionResolver = new ExceptionResolver();
48 /// The JSON RPC version.
50 private readonly string _JSON_RPC_VERSION = "2.0";
55 /// Posts the with parameters remote service async.
57 /// <returns>The with parameters remote service async.</returns>
58 /// <param name="uri">URI.</param>
59 /// <param name="method">Method.</param>
60 /// <param name="arguments">Arguments.</param>
61 /// <typeparam name="TResult">The 1st type parameter.</typeparam>
62 async public Task<TResult> PostRemoteServiceAsync<TResult>(string uri, string method, params object[] arguments)
66 if (arguments != null)
68 var inputParameters = new List<object>(arguments);
69 var postData = new POSTWithParameters();
70 postData.id = Interlocked.Increment(ref _nextId).ToString();
71 postData.jsonrpc = _JSON_RPC_VERSION;
72 postData.method = method;
73 postData.@params = inputParameters;
74 jsonData = JsonConvert.SerializeObject(postData, _jsonSettings);
77 var postData = new POST();
78 postData.id = Interlocked.Increment(ref _nextId).ToString();
79 postData.jsonrpc = _JSON_RPC_VERSION;
80 postData.method = method;
81 jsonData = JsonConvert.SerializeObject(postData, _jsonSettings);
84 POSTResult<TResult> postResult = await this.PostAsync<TResult>(uri, method, jsonData, CancellationToken.None).ConfigureAwait(false);
86 return postResult.result;
90 /// Posts the with parameters remote service async.
92 /// <returns>The with parameters remote service async.</returns>
93 /// <param name="uri">URI.</param>
94 /// <param name="method">Method.</param>
95 /// <param name="parameters">Parameters.</param>
96 async public Task PostRemoteServiceAsync(string uri, string method, params object[] parameters)
98 await this.PostRemoteServiceAsync<object>(uri, method, parameters).ConfigureAwait(false);
104 /// <returns>The async.</returns>
105 /// <param name="uri">URI.</param>
106 /// <param name="method">Method.</param>
107 /// <param name="jsonData">JsonData.</param>
108 /// <param name="cancellation">Cancellation.</param>
109 /// <typeparam name="TResult">The 1st type parameter.</typeparam>
110 async private Task<POSTResult<TResult>> PostAsync<TResult>(string uri, string method, string jsonData, CancellationToken cancellation)
112 // see: http://stackoverflow.com/questions/1329739/nested-using-statements-in-c-sharp
113 // see: http://stackoverflow.com/questions/5895879/when-do-we-need-to-call-dispose-in-dot-net-c
114 using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) })
115 using (HttpContent contentPOST = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json-rpc"))
116 // TODO: HttpCompletionOption, without it, by default, I am buffering the received data.
117 // ConfigureAwait(false): This API will always return data in a different context (different thread) to the calling one.
118 // In this way upper layers may decide what context to use for returning data (the calling context or a diferent one)
119 using (HttpResponseMessage response = await client.PostAsync(uri, contentPOST, cancellation).ConfigureAwait(false))
121 //TODO: What if response is null? :(
122 response.EnsureSuccessStatusCode();
124 using (HttpContent contentRESULT = response.Content)
126 //TODO: What if contentRESULT is null? :(
127 return await this.ReadResponseAsync<TResult>(contentRESULT).ConfigureAwait(false);
133 /// Reads the response async.
135 /// <returns>The response async.</returns>
136 /// <param name="content">Content.</param>
137 /// <typeparam name="TResult">The 1st type parameter.</typeparam>
138 async private Task<POSTResult<TResult>> ReadResponseAsync<TResult>(HttpContent content)
140 // Taken from HttpContent.cs ReadAsStringAsync() Mono implementation.
142 if (content.Headers != null && content.Headers.ContentType != null && content.Headers.ContentType.CharSet != null)
144 encoding = Encoding.GetEncoding(content.Headers.ContentType.CharSet);
148 encoding = Encoding.UTF8;
151 // Option a) with bytes
152 //byte[] jsonBytes = await contentRESULT.ReadAsByteArrayAsync();
153 //return this.ReadResponse<TResult>(jsonBytes);
155 // Option b) with stream
156 using (Stream stream = await content.ReadAsStreamAsync().ConfigureAwait(false))
157 using (StreamReader streamReader = new StreamReader(stream, encoding))
159 // This line makes this method useless (IMHO it is the same as the one working with bytes)
160 // How could I work with JSON saving memory?
161 string json = await streamReader.ReadToEndAsync().ConfigureAwait(false);
163 return this.ReadResponse<TResult>(json);
168 /// Reads the response.
170 /// <returns>The response.</returns>
171 /// <param name="jsonBytes">Json bytes.</param>
172 /// <typeparam name="TResult">The 1st type parameter.</typeparam>
173 private POSTResult<TResult> ReadResponse<TResult>(byte[] jsonBytes)
175 string json = System.Text.Encoding.UTF8.GetString(jsonBytes, 0, jsonBytes.Length);
177 return this.ReadResponse<TResult>(json);
180 private POSTResult<TResult> ReadResponse<TResult>(string json)
182 JObject jsonObjects = JObject.Parse(json);
183 IDictionary<string, JToken> jsonTokens = jsonObjects;
186 if (jsonTokens.ContainsKey("error"))
188 throw _exceptionResolver.ResolveException(jsonObjects["error"]);
191 if (jsonTokens.ContainsKey("result"))
193 return JsonConvert.DeserializeObject<POSTResult<TResult>>(json, _jsonSettings);
196 throw new JsonRpcClientException(0, "There is not error nor result in JSON response data.", jsonObjects);
201 public string id { get; set; }
202 public string jsonrpc { get; set; }
203 public string method { get; set; }
206 private class POSTWithParameters
208 public string id { get; set; }
209 public string jsonrpc { get; set; }
210 public string method { get; set; }
211 public List<object> @params { get; set; }
214 private class POSTResult<TResult>
216 public string id { get; set; }
217 public string jsonrpc { get; set; }
218 public TResult result { get; set; }