using System; using System.Text; using System.Linq; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Net.Http.Headers; using System.Threading.Tasks; using LC.Newtonsoft.Json; using TapTap.Common; using TapTap.Common.Internal.Json; using TapTap.Common.Internal.Http; using TapTap.AntiAddiction.Model; namespace TapTap.AntiAddiction.Internal.Http { public class AntiAddictionHttpClient { static readonly int INTERNAL_SERVER_ERROR_LIMIT = 3; internal readonly string serverUrl; private readonly HttpClient client; private readonly Dictionary>> runtimeHeaderTasks = new Dictionary>>(); private readonly Dictionary additionalHeaders = new Dictionary(); /// /// 服务器错误计数,到达数量后则执行单机模式 /// private int internalServerErrorCount; public AntiAddictionHttpClient(string serverUrl) { this.serverUrl = serverUrl; internalServerErrorCount = 0; client = new HttpClient(); client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); } public void AddRuntimeHeaderTask(string key, Func> task) { if (string.IsNullOrEmpty(key)) { return; } if (task == null) { return; } runtimeHeaderTasks[key] = task; } public void ChangeAddtionalHeader(string key, string value) { if (string.IsNullOrEmpty(key)) { return; } if (string.IsNullOrEmpty(value)) { if (additionalHeaders.ContainsKey(key)) additionalHeaders.Remove(key); } else { additionalHeaders[key] = value; } } public Task Get(string path, Dictionary headers = null, Dictionary queryParams = null) { return Request(path, HttpMethod.Get, headers, null, queryParams); } public Task Post(string path, Dictionary headers = null, object data = null, Dictionary queryParams = null) { return Request(path, HttpMethod.Post, headers, data, queryParams); } public Task Put(string path, Dictionary headers = null, object data = null, Dictionary queryParams = null) { return Request(path, HttpMethod.Put, headers, data, queryParams); } public Task Delete(string path, Dictionary headers = null, object data = null, Dictionary queryParams = null) { return Request>(path, HttpMethod.Delete, headers, data, queryParams); } async Task Request(string path, HttpMethod method, Dictionary headers = null, object data = null, Dictionary queryParams = null) { bool standaloneEnabled = await TapTapAntiAddictionManager.IsStandaloneEnabled(); if (standaloneEnabled && IsInternalServerErrorLimit) { throw new Exception("Interval server error."); } string url = BuildUrl(path, queryParams); HttpRequestMessage request = new HttpRequestMessage { RequestUri = new Uri(url), Method = method, }; await FillHeaders(request.Headers, headers); string content = null; if (data != null) { content = JsonConvert.SerializeObject(data); StringContent requestContent = new StringContent(content); requestContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); request.Content = requestContent; } TapHttpUtils.PrintRequest(client, request, content); HttpResponseMessage response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); request.Dispose(); string resultString = await response.Content.ReadAsStringAsync(); response.Dispose(); TapHttpUtils.PrintResponse(response, resultString); if (response.IsSuccessStatusCode) { T ret = JsonConvert.DeserializeObject(resultString, TapJsonConverter.Default); return ret; } throw HandleErrorResponse(response.StatusCode, resultString); } Exception HandleErrorResponse(HttpStatusCode statusCode, string responseContent) { if (statusCode == HttpStatusCode.Unauthorized) { AntiAddictionUIKit.Exit(); return new AntiAddictionException((int)statusCode, responseContent); } if (statusCode >= HttpStatusCode.InternalServerError) { internalServerErrorCount++; return new AntiAddictionException((int)statusCode, responseContent); } int code = (int)statusCode; string message = responseContent; string err = null; string desc = null; try { // 尝试获取 LeanCloud 返回错误信息 ErrorResponse error = JsonConvert.DeserializeObject(responseContent, TapJsonConverter.Default); code = error.Result.Code; message = error.Result.Description; err = error.Result.Error; desc = error.Result.Description; } catch (Exception e) { TapLogger.Error(e); } return new AntiAddictionException(code, message) { Error = err, Description = desc }; } string BuildUrl(string path, Dictionary queryParams) { string apiServer = serverUrl; StringBuilder urlSB = new StringBuilder(apiServer.TrimEnd('/')); urlSB.Append($"/{path}"); string url = urlSB.ToString(); if (queryParams != null) { IEnumerable queryPairs = queryParams.Select(kv => $"{kv.Key}={Uri.EscapeDataString(kv.Value.ToString())}"); string queries = string.Join("&", queryPairs); url = $"{url}?{queries}"; } return url; } async Task FillHeaders(HttpRequestHeaders headers, Dictionary reqHeaders = null) { // 额外 headers if (reqHeaders != null) { foreach (KeyValuePair kv in reqHeaders) { headers.Add(kv.Key, kv.Value.ToString()); } } if (additionalHeaders.Count > 0) { foreach (KeyValuePair kv in additionalHeaders) { headers.Add(kv.Key, kv.Value); } } // 服务额外 headers foreach (KeyValuePair>> kv in runtimeHeaderTasks) { if (headers.Contains(kv.Key)) { continue; } string value = await kv.Value.Invoke(); if (string.IsNullOrEmpty(value)) { continue; } headers.Add(kv.Key, value); } } bool IsInternalServerErrorLimit => internalServerErrorCount >= INTERNAL_SERVER_ERROR_LIMIT; } }