You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

DefaultRestClient.cs 6.7 kB

8 years ago
8 years ago
8 years ago
8 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. using Newtonsoft.Json;
  2. using System;
  3. using System.Collections.Generic;
  4. using System.Globalization;
  5. using System.IO;
  6. using System.Linq;
  7. using System.Net;
  8. using System.Net.Http;
  9. using System.Text;
  10. using System.Threading;
  11. using System.Threading.Tasks;
  12. namespace Discord.Net.Rest
  13. {
  14. internal sealed class DefaultRestClient : IRestClient, IDisposable
  15. {
  16. private const int HR_SECURECHANNELFAILED = -2146233079;
  17. private readonly HttpClient _client;
  18. private readonly string _baseUrl;
  19. private readonly JsonSerializer _errorDeserializer;
  20. private CancellationToken _cancelToken;
  21. private bool _isDisposed;
  22. public DefaultRestClient(string baseUrl, bool useProxy = false)
  23. {
  24. _baseUrl = baseUrl;
  25. _client = new HttpClient(new HttpClientHandler
  26. {
  27. AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate,
  28. UseCookies = false,
  29. UseProxy = useProxy,
  30. });
  31. SetHeader("accept-encoding", "gzip, deflate");
  32. _cancelToken = CancellationToken.None;
  33. _errorDeserializer = new JsonSerializer();
  34. }
  35. private void Dispose(bool disposing)
  36. {
  37. if (!_isDisposed)
  38. {
  39. if (disposing)
  40. _client.Dispose();
  41. _isDisposed = true;
  42. }
  43. }
  44. public void Dispose()
  45. {
  46. Dispose(true);
  47. }
  48. public void SetHeader(string key, string value)
  49. {
  50. _client.DefaultRequestHeaders.Remove(key);
  51. if (value != null)
  52. _client.DefaultRequestHeaders.Add(key, value);
  53. }
  54. public void SetCancelToken(CancellationToken cancelToken)
  55. {
  56. _cancelToken = cancelToken;
  57. }
  58. public async Task<RestResponse> SendAsync(string method, string endpoint, CancellationToken cancelToken, bool headerOnly, string reason = null)
  59. {
  60. string uri = Path.Combine(_baseUrl, endpoint);
  61. using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
  62. {
  63. if (reason != null) restRequest.Headers.Add("X-Audit-Log-Reason", Uri.EscapeDataString(reason));
  64. return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false);
  65. }
  66. }
  67. public async Task<RestResponse> SendAsync(string method, string endpoint, string json, CancellationToken cancelToken, bool headerOnly, string reason = null)
  68. {
  69. string uri = Path.Combine(_baseUrl, endpoint);
  70. using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
  71. {
  72. if (reason != null) restRequest.Headers.Add("X-Audit-Log-Reason", Uri.EscapeDataString(reason));
  73. restRequest.Content = new StringContent(json, Encoding.UTF8, "application/json");
  74. return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false);
  75. }
  76. }
  77. public async Task<RestResponse> SendAsync(string method, string endpoint, IReadOnlyDictionary<string, object> multipartParams, CancellationToken cancelToken, bool headerOnly, string reason = null)
  78. {
  79. string uri = Path.Combine(_baseUrl, endpoint);
  80. using (var restRequest = new HttpRequestMessage(GetMethod(method), uri))
  81. {
  82. if (reason != null) restRequest.Headers.Add("X-Audit-Log-Reason", Uri.EscapeDataString(reason));
  83. var content = new MultipartFormDataContent("Upload----" + DateTime.Now.ToString(CultureInfo.InvariantCulture));
  84. if (multipartParams != null)
  85. {
  86. foreach (var p in multipartParams)
  87. {
  88. switch (p.Value)
  89. {
  90. case string stringValue: { content.Add(new StringContent(stringValue), p.Key); continue; }
  91. case byte[] byteArrayValue: { content.Add(new ByteArrayContent(byteArrayValue), p.Key); continue; }
  92. case Stream streamValue: { content.Add(new StreamContent(streamValue), p.Key); continue; }
  93. case MultipartFile fileValue:
  94. {
  95. var stream = fileValue.Stream;
  96. if (!stream.CanSeek)
  97. {
  98. var memoryStream = new MemoryStream();
  99. await stream.CopyToAsync(memoryStream).ConfigureAwait(false);
  100. memoryStream.Position = 0;
  101. stream = memoryStream;
  102. }
  103. content.Add(new StreamContent(stream), p.Key, fileValue.Filename);
  104. continue;
  105. }
  106. default: throw new InvalidOperationException($"Unsupported param type \"{p.Value.GetType().Name}\"");
  107. }
  108. }
  109. }
  110. restRequest.Content = content;
  111. return await SendInternalAsync(restRequest, cancelToken, headerOnly).ConfigureAwait(false);
  112. }
  113. }
  114. private async Task<RestResponse> SendInternalAsync(HttpRequestMessage request, CancellationToken cancelToken, bool headerOnly)
  115. {
  116. cancelToken = CancellationTokenSource.CreateLinkedTokenSource(_cancelToken, cancelToken).Token;
  117. HttpResponseMessage response = await _client.SendAsync(request, cancelToken).ConfigureAwait(false);
  118. var headers = response.Headers.ToDictionary(x => x.Key, x => x.Value.FirstOrDefault(), StringComparer.OrdinalIgnoreCase);
  119. var stream = !headerOnly ? await response.Content.ReadAsStreamAsync().ConfigureAwait(false) : null;
  120. return new RestResponse(response.StatusCode, headers, stream);
  121. }
  122. private static readonly HttpMethod _patch = new HttpMethod("PATCH");
  123. private HttpMethod GetMethod(string method)
  124. {
  125. switch (method)
  126. {
  127. case "DELETE": return HttpMethod.Delete;
  128. case "GET": return HttpMethod.Get;
  129. case "PATCH": return _patch;
  130. case "POST": return HttpMethod.Post;
  131. case "PUT": return HttpMethod.Put;
  132. default: throw new ArgumentOutOfRangeException(nameof(method), $"Unknown HttpMethod: {method}");
  133. }
  134. }
  135. }
  136. }