using CANFDApiProxy.Messages; using Newtonsoft.Json; using System; using System.CodeDom; using System.ComponentModel; using System.IO; using System.Net.Http; using System.Reflection; using System.Text; using System.Threading; using System.Threading.Tasks; namespace CANFDApiProxy { internal static class RESTWrapper { /// /// Get the description part of the enum /// /// /// /// private static string DescriptionAttr(this T source) { FieldInfo fi = source.GetType().GetField(source.ToString()); DescriptionAttribute[] attributes = (DescriptionAttribute[])fi.GetCustomAttributes( typeof(DescriptionAttribute), false); if (attributes != null && attributes.Length > 0) return attributes[0].Description; else return source.ToString(); } public static int Port { get; set; } = 5000; private static readonly TimeSpan TIMEOUT_DEFAULT = new TimeSpan(0, 0, 10); public static TimeSpan Timeout { get; set; } = TIMEOUT_DEFAULT; public static Protocol Protocol { get; set; } = Protocol.http; /// /// /// /// device ip address or host name /// /// /// /// public static async Task GetResourceAsync(string host, CommandName command, CancellationToken cancellationToken) { VerifyHost(host); try { var httpClient = HttpClientFactory.CreateHttpClient(); httpClient.Timeout = Timeout; var commandString = command.DescriptionAttr(); string requestUri = $"{Protocol.ToString()}://{host}:{Port}/{commandString}"; var response = await httpClient.GetAsync(requestUri, cancellationToken); if (!response.IsSuccessStatusCode) { if (response.StatusCode == System.Net.HttpStatusCode.InternalServerError) { var errorJson = await response.Content.ReadAsStringAsync(); var res = JsonConvert.DeserializeObject(errorJson); throw new CanApiException(res.Error, 500); } response.EnsureSuccessStatusCode(); } return await response.Content.ReadAsStringAsync(); } catch (TaskCanceledException ex) { throw new CanApiException("An API call was cancelled or timedout", ex); } } public static async Task PostResourceAsync(string host, CommandName command, T request, CancellationToken cancellationToken) { VerifyHost(host); try { HttpResponseMessage response = await PostAsJsonAsync(host, command, request, Timeout, cancellationToken); return await response.Content.ReadAsStringAsync(); } catch (TaskCanceledException ex) { throw new CanApiException("An API call was cancelled or timedout", ex); } } public static async Task SendResourceReadAsStreamAsync(string host, CommandName command, T request, TimeSpan timeOut, CancellationToken cancellationToken) { VerifyHost(host); return await SendResourceReadAsStreamProcess(host, command, request, timeOut, cancellationToken); } private static async Task SendResourceReadAsStreamProcess(string host, CommandName command, T request, TimeSpan timeOut, CancellationToken cancellationToken) { try { HttpResponseMessage response = await SendAsync(host, command, request, timeOut, cancellationToken); return await response.Content.ReadAsStreamAsync(); } catch (TaskCanceledException ex) { throw new CanApiException("An API call was cancelled or timedout", ex); } } private static void VerifyHost(string host) { if (string.IsNullOrEmpty(host)) { throw new ArgumentNullException("host", "host cannot be empty"); } } public static async Task PostResourceReadAsStringAsync(string host, CommandName command, MultipartFormDataContent multipart, TimeSpan timeOut, CancellationToken cancellationToken) { VerifyHost(host); try { var response = await PostWithMultipartFormAsync(host, command, multipart, timeOut, cancellationToken); return await response.Content.ReadAsStringAsync(); } catch (TaskCanceledException ex) { throw new CanApiException("An API call was cancelled or timedout", ex); } } private static async Task PostAsJsonAsync(string host, CommandName command, T request, TimeSpan timeOut, CancellationToken cancellationToken) { var httpClient = HttpClientFactory.CreateHttpClient(); httpClient.Timeout = timeOut; var commandString = command.DescriptionAttr(); string requestUri = $"{Protocol.ToString()}://{host}:{Port}/{commandString}"; var response = await httpClient.PostAsJsonAsync(requestUri, request, cancellationToken); await ValidateResponse(response); return response; } private static async Task PostWithMultipartFormAsync(string host, CommandName command, MultipartFormDataContent multipart, TimeSpan timeOut, CancellationToken cancellationToken) { var httpClient = HttpClientFactory.CreateHttpClient(); httpClient.Timeout = timeOut; var commandString = command.DescriptionAttr(); string requestUri = $"{Protocol.ToString()}://{host}:{Port}/{commandString}"; var response = await httpClient.PostAsync(requestUri, multipart, cancellationToken); await ValidateResponse(response); return response; } private static async Task SendAsync(string host, CommandName command, T request, TimeSpan timeOut, CancellationToken cancellationToken) { var httpClient = HttpClientFactory.CreateHttpClient(); httpClient.Timeout = timeOut; var commandString = command.DescriptionAttr(); string requestUri = $"{Protocol.ToString()}://{host}:{Port}/{commandString}"; HttpRequestMessage httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, requestUri); var json = JsonConvert.SerializeObject(request); var content = new StringContent(json, Encoding.UTF8, "application/json"); httpRequestMessage.Content = content; var response = await httpClient.SendAsync(httpRequestMessage, HttpCompletionOption.ResponseHeadersRead, cancellationToken); await ValidateResponse(response); return response; } private static async Task BuildErrorMessage(HttpResponseMessage response) { var errorJson = await response.Content.ReadAsStringAsync(); var res = JsonConvert.DeserializeObject(errorJson); return res; } private static async Task ValidateResponse(HttpResponseMessage response) { if (!response.IsSuccessStatusCode) { switch (response.StatusCode) { case System.Net.HttpStatusCode.BadRequest: throw new CanApiException((await BuildErrorMessage(response)).Error, 400); case System.Net.HttpStatusCode.Forbidden: throw new CanApiException((await BuildErrorMessage(response)).Error, 403); case System.Net.HttpStatusCode.NotFound: throw new CanApiException((await BuildErrorMessage(response)).Error, 404); case System.Net.HttpStatusCode.InternalServerError: throw new CanApiException((await BuildErrorMessage(response)).Error, 500); case System.Net.HttpStatusCode.ServiceUnavailable: throw new CanApiException((await BuildErrorMessage(response)).Error, 503); } response.EnsureSuccessStatusCode(); } } public static async Task WriteStreamToFileAsync(Stream inputStream, string filePath) { if (inputStream.CanSeek) { inputStream.Position = 0; } using (FileStream fileStream = new FileStream(filePath, FileMode.Create, FileAccess.Write)) { await inputStream.CopyToAsync(fileStream); } } } }