Archived
1
0
Fork 0
This repository has been archived on 2024-05-21. You can view files and clone it, but cannot push or open issues or pull requests.
maki/Maki/Rest/WebRequest.cs

261 lines
9.2 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
{
internal 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";
internal readonly HttpMethod Method;
internal readonly string Url;
internal string ContentType { get; set; } = GENERIC_CONTENT_TYPE;
// 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;
internal byte[] RawResponse
{
get
{
if (rawResponse == null)
{
rawResponse = new byte[4096];
responseStream.Read(rawResponse, 0, rawResponse.Length);
}
return rawResponse;
}
}
private string responseString = string.Empty;
internal string Response
{
get
{
if (string.IsNullOrEmpty(responseString))
responseString = Encoding.UTF8.GetString(RawResponse).Trim('\0');
return responseString;
}
}
internal T ResponseJson<T>() =>
JsonConvert.DeserializeObject<T>(Response);
internal short Status =>
(short)wResponse?.StatusCode;
static WebRequest()
{
ServicePointManager.Expect100Continue = false;
}
internal WebRequest(HttpMethod method, string url)
{
Method = method;
Url = url;
}
internal void AddRaw(byte[] bytes) =>
rawContent = bytes;
internal void AddRaw(string str) =>
AddRaw(Encoding.UTF8.GetBytes(str));
internal void AddJson(object obj)
{
ContentType = JSON_CONTENT_TYPE;
AddRaw(JsonConvert.SerializeObject(obj));
}
internal void AddParam(string name, string contents) =>
parameters.Add(name, contents);
internal void AddFile(string name, byte[] bytes) =>
files.Add(name, bytes);
internal 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 = USER_AGENT;
wRequest.KeepAlive = true;
//wRequest.AutomaticDecompression = DecompressionMethods.Deflate | DecompressionMethods.GZip;
wRequest.ReadWriteTimeout = Timeout.Infinite;
wRequest.Timeout = Timeout.Infinite;
if (!string.IsNullOrEmpty(Authorisation))
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
}
}