1 using Newtonsoft.Json;
2 using Newtonsoft.Json.Linq;
3 using Newtonsoft.Json.Serialization;
6 using System.Collections.Generic;
9 using System.Threading;
10 using System.Threading.Tasks;
14 namespace GumartinM.JsonRPC4NET
16 public class JsonRpcHttpAsyncClient
26 private static readonly Logger _logger = LogManager.GetCurrentClassLogger();
29 /// The _json settings.
31 private readonly JsonSerializerSettings _jsonSettings =
32 new JsonSerializerSettings
34 Error = delegate(object sender, Newtonsoft.Json.Serialization.ErrorEventArgs args)
36 _logger.Error(args.ErrorContext.Error.Message);
37 args.ErrorContext.Handled = true;
42 /// The _exception resolver.
44 private readonly ExceptionResolver _exceptionResolver = new ExceptionResolver();
47 /// The JSON RPC version.
49 private readonly string _JSON_RPC_VERSION = "2.0";
54 /// Posts the with parameters remote service async.
56 /// <returns>The with parameters remote service async.</returns>
57 /// <param name="uri">URI.</param>
58 /// <param name="method">Method.</param>
59 /// <param name="arguments">Arguments.</param>
60 /// <typeparam name="TResult">The 1st type parameter.</typeparam>
61 async public Task<TResult> PostRemoteServiceAsync<TResult>(string uri, string method, params object[] arguments)
65 if (arguments != null)
67 var inputParameters = new List<object>(arguments);
68 var postData = new POSTWithParameters();
69 postData.id = Interlocked.Increment(ref _nextId).ToString();
70 postData.jsonrpc = _JSON_RPC_VERSION;
71 postData.method = method;
72 postData.@params = inputParameters;
73 jsonData = JsonConvert.SerializeObject(postData, _jsonSettings);
76 var postData = new POST();
77 postData.id = Interlocked.Increment(ref _nextId).ToString();
78 postData.jsonrpc = _JSON_RPC_VERSION;
79 postData.method = method;
80 jsonData = JsonConvert.SerializeObject(postData, _jsonSettings);
83 POSTResult<TResult> postResult = await this.PostAsync<TResult>(uri, method, jsonData, CancellationToken.None).ConfigureAwait(false);
85 return postResult.result;
89 /// Posts the with parameters remote service async.
91 /// <returns>The with parameters remote service async.</returns>
92 /// <param name="uri">URI.</param>
93 /// <param name="method">Method.</param>
94 /// <param name="parameters">Parameters.</param>
95 async public Task PostRemoteServiceAsync(string uri, string method, params object[] parameters)
97 await this.PostRemoteServiceAsync<object>(uri, method, parameters).ConfigureAwait(false);
103 /// <returns>The async.</returns>
104 /// <param name="uri">URI.</param>
105 /// <param name="method">Method.</param>
106 /// <param name="cancellation">Cancellation.</param>
107 /// <typeparam name="TResult">The 1st type parameter.</typeparam>
108 async private Task<POSTResult<TResult>> PostAsync<TResult>(string uri, string method, string jsonData, CancellationToken cancellation)
110 // see: http://stackoverflow.com/questions/1329739/nested-using-statements-in-c-sharp
111 // see: http://stackoverflow.com/questions/5895879/when-do-we-need-to-call-dispose-in-dot-net-c
112 using (HttpClient client = new HttpClient { Timeout = TimeSpan.FromSeconds(5) })
113 using (HttpContent contentPOST = new StringContent(jsonData, System.Text.Encoding.UTF8, "application/json-rpc"))
114 // TODO: HttpCompletionOption, without it, by default, I am buffering the received data.
115 // ConfigureAwait(false): This API will always return data in a different context (different thread) to the calling one.
116 // In this way upper layers may decide what context to use for returning data (the calling context or a diferent one)
117 using (HttpResponseMessage response = await client.PostAsync(uri, contentPOST, cancellation).ConfigureAwait(false))
119 //TODO: What if response is null? :(
120 response.EnsureSuccessStatusCode();
122 using (HttpContent contentRESULT = response.Content)
124 //TODO: What if contentRESULT is null? :(
125 return await this.ReadResponseAsync<TResult>(contentRESULT).ConfigureAwait(false);
130 async private Task<POSTResult<TResult>> ReadResponseAsync<TResult>(HttpContent content)
133 * Taken from HttpContent.cs ReadAsStringAsync() Mono implementation.
136 if (content.Headers != null && content.Headers.ContentType != null && content.Headers.ContentType.CharSet != null)
138 encoding = Encoding.GetEncoding(content.Headers.ContentType.CharSet);
142 encoding = Encoding.UTF8;
145 // Option a) with bytes
146 //byte[] jsonBytes = await contentRESULT.ReadAsByteArrayAsync();
147 //return this.ReadResponse<TResult>(jsonBytes);
149 // Option b) with stream
150 using (Stream stream = await content.ReadAsStreamAsync().ConfigureAwait(false))
151 using (StreamReader streamReader = new StreamReader(stream, encoding))
153 // This line makes this method useless (IMHO it is the same as the one working with bytes)
154 // How could I work with JSON saving memory?
155 string json = await streamReader.ReadToEndAsync().ConfigureAwait(false);
157 return this.ReadResponse<TResult>(json);
162 /// Reads the response.
164 /// <returns>The response.</returns>
165 /// <param name="jsonBytes">Json bytes.</param>
166 /// <typeparam name="TResult">The 1st type parameter.</typeparam>
167 private POSTResult<TResult> ReadResponse<TResult>(byte[] jsonBytes)
169 string json = System.Text.Encoding.UTF8.GetString(jsonBytes, 0, jsonBytes.Length);
171 return this.ReadResponse<TResult>(json);
174 private POSTResult<TResult> ReadResponse<TResult>(string json)
176 JObject jsonObjects = JObject.Parse(json);
177 IDictionary<string, JToken> jsonTokens = jsonObjects;
180 if (jsonTokens.ContainsKey("error"))
182 throw _exceptionResolver.ResolveException(jsonObjects["error"]);
185 if (jsonTokens.ContainsKey("result"))
187 return JsonConvert.DeserializeObject<POSTResult<TResult>>(json, _jsonSettings);
190 throw new JsonRpcClientException(0, "There is not error nor result in JSON response data.", jsonObjects);
195 public string id { get; set; }
196 public string jsonrpc { get; set; }
197 public string method { get; set; }
200 private class POSTWithParameters
202 public string id { get; set; }
203 public string jsonrpc { get; set; }
204 public string method { get; set; }
205 public List<object> @params { get; set; }
208 private class POSTResult<TResult>
210 public string id { get; set; }
211 public string jsonrpc { get; set; }
212 public TResult result { get; set; }