218 lines
7.8 KiB
C#
218 lines
7.8 KiB
C#
using Newtonsoft.Json;
|
|
using Newtonsoft.Json.Linq;
|
|
using System;
|
|
using System.IO;
|
|
using System.IO.Compression;
|
|
using System.Net;
|
|
using System.Net.Cache;
|
|
using System.Text;
|
|
using System.Threading.Tasks;
|
|
|
|
namespace Discord.Helpers
|
|
{
|
|
internal class HttpOptions
|
|
{
|
|
public string UserAgent, Token;
|
|
public CookieContainer Cookies;
|
|
|
|
public HttpOptions(string userAgent = null)
|
|
{
|
|
UserAgent = userAgent ?? "DiscordAPI";
|
|
Cookies = new CookieContainer(1);
|
|
}
|
|
}
|
|
|
|
internal static class Http
|
|
{
|
|
private static readonly RequestCachePolicy _cachePolicy = new RequestCachePolicy(RequestCacheLevel.NoCacheNoStore);
|
|
#if DEBUG
|
|
private const bool _isDebug = true;
|
|
#else
|
|
private const bool _isDebug = false;
|
|
#endif
|
|
|
|
internal static Task<ResponseT> Get<ResponseT>(string path, object data, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("GET", path, data, options);
|
|
internal static Task<string> Get(string path, object data, HttpOptions options)
|
|
=> Send("GET", path, data, options);
|
|
internal static Task<ResponseT> Get<ResponseT>(string path, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("GET", path, null, options);
|
|
internal static Task<string> Get(string path, HttpOptions options)
|
|
=> Send("GET", path, null, options);
|
|
|
|
internal static Task<ResponseT> Post<ResponseT>(string path, object data, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("POST", path, data, options);
|
|
internal static Task<string> Post(string path, object data, HttpOptions options)
|
|
=> Send("POST", path, data, options);
|
|
internal static Task<ResponseT> Post<ResponseT>(string path, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("POST", path, null, options);
|
|
internal static Task<string> Post(string path, HttpOptions options)
|
|
=> Send("POST", path, null, options);
|
|
|
|
internal static Task<ResponseT> Put<ResponseT>(string path, object data, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("PUT", path, data, options);
|
|
internal static Task<string> Put(string path, object data, HttpOptions options)
|
|
=> Send("PUT", path, data, options);
|
|
internal static Task<ResponseT> Put<ResponseT>(string path, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("PUT", path, null, options);
|
|
internal static Task<string> Put(string path, HttpOptions options)
|
|
=> Send("PUT", path, null, options);
|
|
|
|
internal static Task<ResponseT> Patch<ResponseT>(string path, object data, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("PATCH", path, data, options);
|
|
internal static Task<string> Patch(string path, object data, HttpOptions options)
|
|
=> Send("PATCH", path, data, options);
|
|
internal static Task<ResponseT> Patch<ResponseT>(string path, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("PATCH", path, null, options);
|
|
internal static Task<string> Patch(string path, HttpOptions options)
|
|
=> Send("PATCH", path, null, options);
|
|
|
|
internal static Task<ResponseT> Delete<ResponseT>(string path, object data, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("DELETE", path, data, options);
|
|
internal static Task<string> Delete(string path, object data, HttpOptions options)
|
|
=> Send("DELETE", path, data, options);
|
|
internal static Task<ResponseT> Delete<ResponseT>(string path, HttpOptions options)
|
|
where ResponseT : class
|
|
=> Send<ResponseT>("DELETE", path, null, options);
|
|
internal static Task<string> Delete(string path, HttpOptions options)
|
|
=> Send("DELETE", path, null, options);
|
|
|
|
internal static async Task<ResponseT> Send<ResponseT>(string method, string path, object data, HttpOptions options)
|
|
where ResponseT : class
|
|
{
|
|
string requestJson = JsonConvert.SerializeObject(data);
|
|
string responseJson = await SendRequest("POST", path, requestJson, options, true);
|
|
var response = JsonConvert.DeserializeObject<ResponseT>(responseJson);
|
|
#if DEBUG
|
|
CheckResponse(responseJson, response);
|
|
#endif
|
|
return response;
|
|
}
|
|
internal static async Task<string> Send(string method, string path, object data, HttpOptions options)
|
|
{
|
|
string requestJson = JsonConvert.SerializeObject(data);
|
|
string responseJson = await SendRequest("POST", path, requestJson, options, _isDebug);
|
|
#if DEBUG
|
|
CheckEmptyResponse(responseJson);
|
|
#endif
|
|
return responseJson;
|
|
}
|
|
internal static async Task<ResponseT> Send<ResponseT>(string method, string path, HttpOptions options)
|
|
where ResponseT : class
|
|
{
|
|
string responseJson = await SendRequest("POST", path, null, options, true);
|
|
var response = JsonConvert.DeserializeObject<ResponseT>(responseJson);
|
|
#if DEBUG
|
|
CheckResponse(responseJson, response);
|
|
#endif
|
|
return response;
|
|
}
|
|
internal static async Task<string> Send(string method, string path, HttpOptions options)
|
|
{
|
|
string responseJson = await SendRequest("POST", path, null, options, _isDebug);
|
|
#if DEBUG
|
|
CheckEmptyResponse(responseJson);
|
|
#endif
|
|
return responseJson;
|
|
}
|
|
|
|
private static async Task<string> SendRequest(string method, string path, string data, HttpOptions options, bool hasResponse)
|
|
{
|
|
options = options ?? new HttpOptions();
|
|
|
|
//Create Request
|
|
HttpWebRequest request = WebRequest.CreateHttp(path);
|
|
request.Accept = "*/*";
|
|
request.Headers[HttpRequestHeader.AcceptLanguage] = "en-US;q=0.8";
|
|
request.Headers[HttpRequestHeader.AcceptEncoding] = "gzip, deflate";
|
|
request.CachePolicy = _cachePolicy;
|
|
request.CookieContainer = options.Cookies;
|
|
request.Method = method;
|
|
request.UserAgent = options.UserAgent;
|
|
|
|
//Add Payload
|
|
if (data != null)
|
|
{
|
|
byte[] buffer = Encoding.UTF8.GetBytes(data);
|
|
using (var payload = await request.GetRequestStreamAsync())
|
|
payload.Write(buffer, 0, buffer.Length);
|
|
request.ContentType = "application/json";
|
|
}
|
|
|
|
//Get Response
|
|
using (var response = (HttpWebResponse)(await request.GetResponseAsync()))
|
|
{
|
|
if (hasResponse)
|
|
{
|
|
using (var stream = response.GetResponseStream())
|
|
using (var reader = new BinaryReader(stream))
|
|
using (var largeBuffer = new MemoryStream())
|
|
{
|
|
//Read the response in small chunks and add them to a larger buffer.
|
|
//ContentLength isn't always provided, so this is safer.
|
|
int bytesRead = 0;
|
|
byte[] smallBuffer = new byte[4096];
|
|
while ((bytesRead = reader.Read(smallBuffer, 0, smallBuffer.Length)) > 0)
|
|
largeBuffer.Write(smallBuffer, 0, bytesRead);
|
|
|
|
//Do we need to decompress?
|
|
if (!string.IsNullOrEmpty(response.ContentEncoding))
|
|
{
|
|
largeBuffer.Position = 0;
|
|
using (var decoder = GetDecoder(response.ContentEncoding, largeBuffer))
|
|
using (var decodedStream = new MemoryStream())
|
|
{
|
|
decoder.CopyTo(decodedStream);
|
|
return Encoding.UTF8.GetString(decodedStream.GetBuffer(), 0, (int)decodedStream.Length);
|
|
}
|
|
}
|
|
else
|
|
return Encoding.UTF8.GetString(largeBuffer.GetBuffer(), 0, (int)largeBuffer.Length);
|
|
}
|
|
}
|
|
else
|
|
return null;
|
|
}
|
|
|
|
}
|
|
|
|
private static Stream GetDecoder(string contentEncoding, MemoryStream encodedStream)
|
|
{
|
|
switch (contentEncoding)
|
|
{
|
|
case "gzip":
|
|
return new GZipStream(encodedStream, CompressionMode.Decompress, true);
|
|
case "deflate":
|
|
return new DeflateStream(encodedStream, CompressionMode.Decompress, true);
|
|
default:
|
|
throw new ArgumentOutOfRangeException("Unknown encoding: " + contentEncoding);
|
|
}
|
|
}
|
|
|
|
#if DEBUG
|
|
private static void CheckResponse<T>(string json, T obj)
|
|
{
|
|
/*JToken token = JToken.Parse(json);
|
|
JToken token2 = JToken.FromObject(obj);
|
|
if (!JToken.DeepEquals(token, token2))
|
|
throw new Exception("API check failed: Objects do not match.");*/
|
|
}
|
|
|
|
private static void CheckEmptyResponse(string json)
|
|
{
|
|
if (!string.IsNullOrEmpty(json))
|
|
throw new Exception("API check failed: Response is not empty.");
|
|
}
|
|
#endif
|
|
}
|
|
}
|