273 lines
9.9 KiB
C#
273 lines
9.9 KiB
C#
using Newtonsoft.Json;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.IO;
|
|
using System.Linq;
|
|
using System.Net;
|
|
using System.Text;
|
|
using System.Threading;
|
|
|
|
namespace Maki.Rest
|
|
{
|
|
public class WebRequest : IDisposable
|
|
{
|
|
private const string USER_AGENT = @"DiscordBot (https://github.com/flashwave/maki, 1.0.0.0)";
|
|
private const string GENERIC_CONTENT_TYPE = @"application/octet-stream";
|
|
private const string JSON_CONTENT_TYPE = @"application/json";
|
|
private const string FORM_CONTENT_TYPE = @"multipart/form-data";
|
|
|
|
private const long BUFFER_SIZE = 8192000;
|
|
|
|
public readonly HttpMethod Method;
|
|
public readonly string Url;
|
|
|
|
public string UserAgent { get; set; } = USER_AGENT;
|
|
|
|
public string ContentType { get; set; } = GENERIC_CONTENT_TYPE;
|
|
public long ContentLength => HttpWebResponse.ContentLength < 1 ? BUFFER_SIZE : HttpWebResponse.ContentLength;
|
|
|
|
// TODO: make this not static
|
|
internal static string Authorisation { get; set; }
|
|
|
|
private readonly Dictionary<string, string> Headers = new Dictionary<string, string>();
|
|
private readonly Dictionary<string, string> Parameters = new Dictionary<string, string>();
|
|
private readonly Dictionary<string, byte[]> Files = new Dictionary<string, byte[]>();
|
|
|
|
private readonly Dictionary<string, string> MimeTypes = new Dictionary<string, string>()
|
|
{
|
|
{ "png", "image/png" },
|
|
{ "jpg", "image/jpeg" },
|
|
{ "jpeg", "image/jpeg" },
|
|
{ "gif", "image/gif" },
|
|
};
|
|
|
|
private byte[] RawRequestBody = new byte[0];
|
|
private HttpWebRequest HttpWebRequest;
|
|
private Stream RequestStream;
|
|
private HttpWebResponse HttpWebResponse;
|
|
private Stream ResponseStream;
|
|
|
|
private byte[] RawResponseValue;
|
|
public byte[] RawResponse
|
|
{
|
|
get
|
|
{
|
|
if (RawResponseValue == null)
|
|
using (MemoryStream ms = new MemoryStream())
|
|
{
|
|
byte[] bytes = new byte[4096];
|
|
int read = 0;
|
|
|
|
while ((read = ResponseStream.Read(bytes, 0, bytes.Length)) > 0)
|
|
ms.Write(bytes, 0, read);
|
|
|
|
ms.Seek(0, SeekOrigin.Begin);
|
|
RawResponseValue = new byte[ms.Length];
|
|
ms.Read(RawResponseValue, 0, RawResponseValue.Length);
|
|
}
|
|
|
|
return RawResponseValue;
|
|
}
|
|
}
|
|
|
|
private string ResponseString = string.Empty;
|
|
|
|
public string Response
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrEmpty(ResponseString))
|
|
ResponseString = Encoding.UTF8.GetString(RawResponse);
|
|
|
|
return ResponseString;
|
|
}
|
|
}
|
|
|
|
public T ResponseJson<T>() =>
|
|
JsonConvert.DeserializeObject<T>(Response);
|
|
|
|
public short Status =>
|
|
(short)HttpWebResponse?.StatusCode;
|
|
|
|
static WebRequest()
|
|
{
|
|
ServicePointManager.Expect100Continue = false;
|
|
}
|
|
|
|
public WebRequest(HttpMethod method, string url)
|
|
{
|
|
Method = method;
|
|
Url = url;
|
|
}
|
|
|
|
public void AddRaw(byte[] bytes) =>
|
|
RawRequestBody = bytes;
|
|
|
|
public void AddRaw(string str) =>
|
|
AddRaw(Encoding.UTF8.GetBytes(str));
|
|
|
|
public void AddJson(object obj)
|
|
{
|
|
ContentType = JSON_CONTENT_TYPE;
|
|
AddRaw(JsonConvert.SerializeObject(obj));
|
|
}
|
|
|
|
public void AddParam(string name, string contents) =>
|
|
Parameters.Add(name, contents);
|
|
|
|
public void AddFile(string name, byte[] bytes) =>
|
|
Files.Add(name, bytes);
|
|
|
|
public void Perform()
|
|
{
|
|
StringBuilder urlBuilder = new StringBuilder();
|
|
|
|
if (!Url.StartsWith("http://") && !Url.StartsWith("https://"))
|
|
{
|
|
urlBuilder.Append(RestEndpoints.BASE_URL);
|
|
urlBuilder.Append(RestEndpoints.BASE_PATH);
|
|
}
|
|
|
|
urlBuilder.Append(Url);
|
|
|
|
if (Method == HttpMethod.GET
|
|
|| Method == HttpMethod.DELETE)
|
|
if (Parameters.Count > 1)
|
|
{
|
|
if (!Url.Contains('?'))
|
|
urlBuilder.Append(@"?");
|
|
|
|
foreach (KeyValuePair<string, string> param in Parameters)
|
|
urlBuilder.Append($@"{param.Key}={param.Value}&");
|
|
}
|
|
|
|
string url = urlBuilder.ToString().TrimEnd('&');
|
|
|
|
HttpWebRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
|
|
HttpWebRequest.Method = Method.ToString();
|
|
HttpWebRequest.UserAgent = UserAgent;
|
|
HttpWebRequest.KeepAlive = true;
|
|
//wRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
|
|
HttpWebRequest.ReadWriteTimeout = Timeout.Infinite;
|
|
HttpWebRequest.Timeout = Timeout.Infinite;
|
|
|
|
if (!string.IsNullOrEmpty(Authorisation) && url.StartsWith(RestEndpoints.BASE_URL + RestEndpoints.BASE_PATH))
|
|
HttpWebRequest.Headers[HttpRequestHeader.Authorization] = Authorisation;
|
|
|
|
foreach (KeyValuePair<string, string> header in Headers)
|
|
HttpWebRequest.Headers[header.Key] = header.Value;
|
|
|
|
if (Method == HttpMethod.POST
|
|
|| Method == HttpMethod.PUT
|
|
|| Method == HttpMethod.PATCH)
|
|
{
|
|
RequestStream = HttpWebRequest.GetRequestStream();
|
|
|
|
if (Parameters.Count + Files.Count < 1)
|
|
RequestStream.Write(RawRequestBody, 0, RawRequestBody.Length);
|
|
else
|
|
{
|
|
string boundary = $@"-----------------------------{DateTime.Now.Ticks}";
|
|
ContentType = $@"{FORM_CONTENT_TYPE}; boundary={boundary}";
|
|
|
|
if (Parameters.Count >= 1)
|
|
{
|
|
StringBuilder postBodyBuilder = new StringBuilder();
|
|
byte[] postBody = new byte[0];
|
|
|
|
foreach (KeyValuePair<string, string> param in Parameters)
|
|
{
|
|
postBodyBuilder.AppendLine($@"--{boundary}");
|
|
postBodyBuilder.AppendLine($@"Content-Disposition: form-data; name=""{param.Key}""");
|
|
postBodyBuilder.AppendLine();
|
|
postBodyBuilder.AppendLine(param.Value);
|
|
}
|
|
|
|
postBody = Encoding.UTF8.GetBytes(postBodyBuilder.ToString());
|
|
RequestStream.Write(postBody, 0, postBody.Length);
|
|
}
|
|
|
|
if (Files.Count >= 1)
|
|
{
|
|
byte[] boundaryBytes = Encoding.UTF8.GetBytes($@"--{boundary}");
|
|
byte[] newLineBytes = Encoding.UTF8.GetBytes("\r\n");
|
|
|
|
foreach (KeyValuePair<string, byte[]> file in Files)
|
|
{
|
|
string cType = GENERIC_CONTENT_TYPE;
|
|
string fileExt = Path.GetExtension(file.Key).ToLower().TrimStart('.');
|
|
|
|
if (MimeTypes.ContainsKey(fileExt))
|
|
cType = MimeTypes[fileExt];
|
|
|
|
byte[] cDisposBytes = Encoding.UTF8.GetBytes($@"Content-Disposition: form-data; name=""{file.Key}""; filename=""{file.Key}""");
|
|
byte[] cTypeBytes = Encoding.UTF8.GetBytes($@"Content-Type: {cType}");
|
|
|
|
// Boundary + newline
|
|
RequestStream.Write(boundaryBytes, 0, boundaryBytes.Length);
|
|
RequestStream.Write(newLineBytes, 0, newLineBytes.Length);
|
|
|
|
// Disposition header + newline
|
|
RequestStream.Write(cDisposBytes, 0, cDisposBytes.Length);
|
|
RequestStream.Write(newLineBytes, 0, newLineBytes.Length);
|
|
|
|
// Type header + newline
|
|
RequestStream.Write(cTypeBytes, 0, cTypeBytes.Length);
|
|
RequestStream.Write(newLineBytes, 0, newLineBytes.Length);
|
|
|
|
// newline + contents + newline
|
|
RequestStream.Write(newLineBytes, 0, newLineBytes.Length);
|
|
RequestStream.Write(file.Value, 0, file.Value.Length);
|
|
RequestStream.Write(newLineBytes, 0, newLineBytes.Length);
|
|
}
|
|
}
|
|
|
|
byte[] closingBound = Encoding.UTF8.GetBytes($@"--{boundary}--");
|
|
RequestStream.Write(closingBound, 0, closingBound.Length);
|
|
}
|
|
}
|
|
|
|
HttpWebRequest.ContentType = ContentType;
|
|
|
|
try
|
|
{
|
|
HttpWebResponse = HttpWebRequest.GetResponse() as HttpWebResponse;
|
|
} catch (WebException ex)
|
|
{
|
|
HttpWebResponse = ex.Response as HttpWebResponse;
|
|
}
|
|
|
|
ResponseStream = HttpWebResponse.GetResponseStream();
|
|
}
|
|
|
|
#region IDisposable
|
|
|
|
private bool IsDisposed = false;
|
|
|
|
/// <summary>
|
|
/// Disconnects and releases all unmanaged objects
|
|
/// </summary>
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (IsDisposed)
|
|
return;
|
|
|
|
IsDisposed = true;
|
|
RequestStream?.Dispose();
|
|
HttpWebRequest?.Abort();
|
|
ResponseStream?.Dispose();
|
|
HttpWebResponse?.Close();
|
|
|
|
if (disposing)
|
|
GC.SuppressFinalize(this);
|
|
}
|
|
|
|
~WebRequest()
|
|
=> Dispose(false);
|
|
|
|
public void Dispose()
|
|
=> Dispose(true);
|
|
|
|
#endregion
|
|
}
|
|
}
|