265 lines
9.4 KiB
C#
265 lines
9.4 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 = 5242880;
|
|
|
|
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 => wResponse.ContentLength < 1 ? BUFFER_SIZE : wResponse.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[] rawContent = new byte[0];
|
|
private HttpWebRequest wRequest;
|
|
private Stream requestStream;
|
|
private HttpWebResponse wResponse;
|
|
private Stream responseStream;
|
|
|
|
private byte[] rawResponse;
|
|
public byte[] RawResponse
|
|
{
|
|
get
|
|
{
|
|
if (rawResponse == null)
|
|
{
|
|
rawResponse = new byte[BUFFER_SIZE];
|
|
responseStream.Read(rawResponse, 0, rawResponse.Length);
|
|
}
|
|
|
|
return rawResponse;
|
|
}
|
|
}
|
|
|
|
private string responseString = string.Empty;
|
|
|
|
public string Response
|
|
{
|
|
get
|
|
{
|
|
if (string.IsNullOrEmpty(responseString))
|
|
responseString = Encoding.UTF8.GetString(RawResponse).Trim('\0');
|
|
|
|
return responseString;
|
|
}
|
|
}
|
|
|
|
public T ResponseJson<T>() =>
|
|
JsonConvert.DeserializeObject<T>(Response);
|
|
|
|
public short Status =>
|
|
(short)wResponse?.StatusCode;
|
|
|
|
static WebRequest()
|
|
{
|
|
ServicePointManager.Expect100Continue = false;
|
|
}
|
|
|
|
public WebRequest(HttpMethod method, string url)
|
|
{
|
|
Method = method;
|
|
Url = url;
|
|
}
|
|
|
|
public void AddRaw(byte[] bytes) =>
|
|
rawContent = 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('&');
|
|
|
|
wRequest = System.Net.WebRequest.Create(url) as HttpWebRequest;
|
|
wRequest.Method = Method.ToString();
|
|
wRequest.UserAgent = UserAgent;
|
|
wRequest.KeepAlive = true;
|
|
//wRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
|
|
wRequest.ReadWriteTimeout = Timeout.Infinite;
|
|
wRequest.Timeout = Timeout.Infinite;
|
|
|
|
if (!string.IsNullOrEmpty(Authorisation) && url.StartsWith(RestEndpoints.BASE_URL + RestEndpoints.BASE_PATH))
|
|
wRequest.Headers[HttpRequestHeader.Authorization] = Authorisation;
|
|
|
|
foreach (KeyValuePair<string, string> header in headers)
|
|
wRequest.Headers[header.Key] = header.Value;
|
|
|
|
if (Method == HttpMethod.POST
|
|
|| Method == HttpMethod.PUT
|
|
|| Method == HttpMethod.PATCH)
|
|
{
|
|
requestStream = wRequest.GetRequestStream();
|
|
|
|
if (parameters.Count + files.Count < 1)
|
|
requestStream.Write(rawContent, 0, rawContent.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);
|
|
}
|
|
}
|
|
|
|
wRequest.ContentType = ContentType;
|
|
|
|
try
|
|
{
|
|
wResponse = wRequest.GetResponse() as HttpWebResponse;
|
|
} catch (WebException ex)
|
|
{
|
|
wResponse = ex.Response as HttpWebResponse;
|
|
}
|
|
|
|
responseStream = wResponse.GetResponseStream();
|
|
}
|
|
|
|
#region IDisposable
|
|
private bool isDisposed = false;
|
|
|
|
private void Dispose(bool disposing)
|
|
{
|
|
if (!isDisposed)
|
|
{
|
|
isDisposed = true;
|
|
requestStream?.Dispose();
|
|
wRequest?.Abort();
|
|
responseStream?.Dispose();
|
|
wResponse?.Close();
|
|
}
|
|
}
|
|
|
|
~WebRequest()
|
|
{
|
|
Dispose(false);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Disconnects and releases all unmanaged objects
|
|
/// </summary>
|
|
public void Dispose()
|
|
{
|
|
Dispose(true);
|
|
GC.SuppressFinalize(true);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|