mirror of
https://github.com/vrcx-team/VRCX.git
synced 2026-04-06 00:32:02 +02:00
Rework WebApi.cs to use HttpClient (#1584)
* Rewrk webapi.cs to use HttpClient * only run code signing when secrets are configured * Workaround to check if secret is present or not * apply it to all azure steps... * dont replace cookie container, reuse managed object * Fix adding MD5 --------- Co-authored-by: Natsumi <cmcooper123@hotmail.com>
This commit is contained in:
@@ -57,10 +57,10 @@ internal static class ImageCache
|
||||
|
||||
var cookieString = string.Empty;
|
||||
if (WebApi.Instance != null &&
|
||||
WebApi.Instance._cookieContainer != null &&
|
||||
WebApi.Instance.CookieContainer != null &&
|
||||
uri.Host == "api.vrchat.cloud")
|
||||
{
|
||||
CookieCollection cookies = WebApi.Instance._cookieContainer.GetCookies(new Uri("https://api.vrchat.cloud"));
|
||||
CookieCollection cookies = WebApi.Instance.CookieContainer.GetCookies(new Uri("https://api.vrchat.cloud"));
|
||||
foreach (Cookie cookie in cookies)
|
||||
cookieString += $"{cookie.Name}={cookie.Value};";
|
||||
}
|
||||
|
||||
428
Dotnet/WebApi.cs
428
Dotnet/WebApi.cs
@@ -4,6 +4,8 @@ using System.Collections.Generic;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
@@ -25,20 +27,21 @@ namespace VRCX
|
||||
{
|
||||
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||||
public static WebApi Instance;
|
||||
|
||||
|
||||
public static bool ProxySet;
|
||||
public static string ProxyUrl = "";
|
||||
public static IWebProxy Proxy = WebRequest.DefaultWebProxy;
|
||||
|
||||
public CookieContainer _cookieContainer;
|
||||
|
||||
public CookieContainer CookieContainer;
|
||||
private bool _cookieDirty;
|
||||
private Timer _timer;
|
||||
|
||||
private HttpClient _httpClient;
|
||||
private SocketsHttpHandler _httpHandler;
|
||||
|
||||
static WebApi()
|
||||
{
|
||||
Instance = new WebApi();
|
||||
ServicePointManager.DefaultConnectionLimit = 10;
|
||||
ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
|
||||
}
|
||||
|
||||
// leave this as public, private makes nodeapi angry
|
||||
@@ -48,7 +51,7 @@ namespace VRCX
|
||||
if (Instance == null)
|
||||
Instance = this;
|
||||
#endif
|
||||
_cookieContainer = new CookieContainer();
|
||||
CookieContainer = new CookieContainer();
|
||||
_timer = new Timer(TimerCallback, null, -1, -1);
|
||||
}
|
||||
|
||||
@@ -67,10 +70,32 @@ namespace VRCX
|
||||
public void Init()
|
||||
{
|
||||
SetProxy();
|
||||
InitializeHttpClient();
|
||||
LoadCookies();
|
||||
_timer.Change(1000, 1000);
|
||||
}
|
||||
|
||||
private void InitializeHttpClient()
|
||||
{
|
||||
_httpHandler = new SocketsHttpHandler
|
||||
{
|
||||
CookieContainer = CookieContainer,
|
||||
UseCookies = true,
|
||||
AutomaticDecompression = DecompressionMethods.All,
|
||||
PooledConnectionLifetime = TimeSpan.FromMinutes(5),
|
||||
MaxConnectionsPerServer = 10
|
||||
};
|
||||
|
||||
if (ProxySet)
|
||||
{
|
||||
_httpHandler.Proxy = Proxy;
|
||||
_httpHandler.UseProxy = true;
|
||||
}
|
||||
|
||||
_httpClient = new HttpClient(_httpHandler);
|
||||
_httpClient.DefaultRequestHeaders.Add("User-Agent", Program.Version);
|
||||
}
|
||||
|
||||
private void SetProxy()
|
||||
{
|
||||
if (!string.IsNullOrEmpty(StartupArgs.LaunchArguments.ProxyUrl))
|
||||
@@ -95,7 +120,8 @@ namespace VRCX
|
||||
{
|
||||
VRCXStorage.Instance.Set("VRCX_ProxyServer", string.Empty);
|
||||
VRCXStorage.Instance.Save();
|
||||
const string message = "The proxy server URI you used is invalid.\nVRCX will close, please correct the proxy URI.";
|
||||
const string message =
|
||||
"The proxy server URI you used is invalid.\nVRCX will close, please correct the proxy URI.";
|
||||
#if !LINUX
|
||||
System.Windows.Forms.MessageBox.Show(message, "Invalid Proxy URI", MessageBoxButtons.OK, MessageBoxIcon.Error);
|
||||
#endif
|
||||
@@ -115,13 +141,15 @@ namespace VRCX
|
||||
#if !LINUX
|
||||
Cef.GetGlobalCookieManager().DeleteCookies();
|
||||
#endif
|
||||
_cookieContainer = new CookieContainer();
|
||||
CookieContainer = new CookieContainer();
|
||||
InitializeHttpClient();
|
||||
SaveCookies();
|
||||
}
|
||||
|
||||
private void LoadCookies()
|
||||
{
|
||||
SQLite.Instance.ExecuteNonQuery("CREATE TABLE IF NOT EXISTS `cookies` (`key` TEXT PRIMARY KEY, `value` TEXT)");
|
||||
SQLite.Instance.ExecuteNonQuery(
|
||||
"CREATE TABLE IF NOT EXISTS `cookies` (`key` TEXT PRIMARY KEY, `value` TEXT)");
|
||||
var values = SQLite.Instance.Execute("SELECT `value` FROM `cookies` WHERE `key` = @key",
|
||||
new Dictionary<string, object>
|
||||
{
|
||||
@@ -132,25 +160,26 @@ namespace VRCX
|
||||
{
|
||||
var item = values[0];
|
||||
using var stream = new MemoryStream(Convert.FromBase64String((string)item[0]));
|
||||
_cookieContainer = new CookieContainer();
|
||||
_cookieContainer.Add(System.Text.Json.JsonSerializer.Deserialize<CookieCollection>(stream));
|
||||
CookieContainer = new CookieContainer();
|
||||
CookieContainer.Add(System.Text.Json.JsonSerializer.Deserialize<CookieCollection>(stream));
|
||||
InitializeHttpClient();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Logger.Error($"Failed to load cookies: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private List<Cookie> GetAllCookies()
|
||||
{
|
||||
var cookieTable = (Hashtable)_cookieContainer.GetType().InvokeMember("m_domainTable",
|
||||
var cookieTable = (Hashtable)CookieContainer.GetType().InvokeMember("m_domainTable",
|
||||
BindingFlags.NonPublic |
|
||||
BindingFlags.GetField |
|
||||
BindingFlags.Instance,
|
||||
null,
|
||||
_cookieContainer,
|
||||
CookieContainer,
|
||||
new object[] { });
|
||||
|
||||
|
||||
var uniqueCookies = new Dictionary<string, Cookie>();
|
||||
foreach (var item in cookieTable.Keys)
|
||||
{
|
||||
@@ -165,7 +194,7 @@ namespace VRCX
|
||||
if (!Uri.TryCreate(address, UriKind.Absolute, out var uri))
|
||||
continue;
|
||||
|
||||
foreach (Cookie cookie in _cookieContainer.GetCookies(uri))
|
||||
foreach (Cookie cookie in CookieContainer.GetCookies(uri))
|
||||
{
|
||||
var key = $"{domain}.{cookie.Name}";
|
||||
if (!uniqueCookies.TryGetValue(key, out var value) ||
|
||||
@@ -184,7 +213,7 @@ namespace VRCX
|
||||
{
|
||||
if (!_cookieDirty)
|
||||
return;
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
var cookies = GetAllCookies();
|
||||
@@ -192,12 +221,13 @@ namespace VRCX
|
||||
System.Text.Json.JsonSerializer.Serialize(memoryStream, cookies);
|
||||
SQLite.Instance.ExecuteNonQuery(
|
||||
"INSERT OR REPLACE INTO `cookies` (`key`, `value`) VALUES (@key, @value)",
|
||||
new Dictionary<string, object>() {
|
||||
{"@key", "default"},
|
||||
{"@value", Convert.ToBase64String(memoryStream.ToArray())}
|
||||
new Dictionary<string, object>()
|
||||
{
|
||||
{ "@key", "default" },
|
||||
{ "@value", Convert.ToBase64String(memoryStream.ToArray()) }
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
_cookieDirty = false;
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -219,130 +249,77 @@ namespace VRCX
|
||||
{
|
||||
using (var stream = new MemoryStream(Convert.FromBase64String(cookies)))
|
||||
{
|
||||
_cookieContainer = new CookieContainer();
|
||||
_cookieContainer.Add(System.Text.Json.JsonSerializer.Deserialize<CookieCollection>(stream));
|
||||
CookieContainer.Add(System.Text.Json.JsonSerializer.Deserialize<CookieCollection>(stream));
|
||||
}
|
||||
|
||||
_cookieDirty = true; // force cookies to be saved for lastUserLoggedIn
|
||||
}
|
||||
|
||||
private static async Task LegacyImageUpload(HttpWebRequest request, IDictionary<string, object> options)
|
||||
private async Task<HttpRequestMessage> BuildLegacyImageUploadRequest(string url, IDictionary<string, object> options)
|
||||
{
|
||||
if (ProxySet)
|
||||
request.Proxy = Proxy;
|
||||
|
||||
request.AutomaticDecompression = DecompressionMethods.All;
|
||||
request.Method = "POST";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
|
||||
request.ContentType = "multipart/form-data; boundary=" + boundary;
|
||||
var requestStream = request.GetRequestStream();
|
||||
var content = new MultipartFormDataContent(boundary);
|
||||
|
||||
if (options.TryGetValue("postData", out var postDataObject))
|
||||
{
|
||||
var postData = new Dictionary<string, string>();
|
||||
postData.Add("data", (string)postDataObject);
|
||||
const string formDataTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n";
|
||||
foreach (var key in postData.Keys)
|
||||
{
|
||||
var item = string.Format(formDataTemplate, boundary, key, postData[key]);
|
||||
var itemBytes = Encoding.UTF8.GetBytes(item);
|
||||
await requestStream.WriteAsync(itemBytes);
|
||||
}
|
||||
content.Add(new StringContent((string)postDataObject), "data");
|
||||
}
|
||||
|
||||
var imageData = options["imageData"] as string;
|
||||
var fileToUpload = Program.AppApiInstance.ResizeImageToFitLimits(Convert.FromBase64String(imageData), false);
|
||||
const string fileFormKey = "image";
|
||||
const string fileName = "image.png";
|
||||
const string fileMimeType = "image/png";
|
||||
const string headerTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n";
|
||||
var header = string.Format(headerTemplate, boundary, fileFormKey, fileName, fileMimeType);
|
||||
var headerBytes = Encoding.UTF8.GetBytes(header);
|
||||
await requestStream.WriteAsync(headerBytes);
|
||||
using (var fileStream = new MemoryStream(fileToUpload))
|
||||
{
|
||||
var buffer = new byte[1024];
|
||||
var bytesRead = 0;
|
||||
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
|
||||
{
|
||||
await requestStream.WriteAsync(buffer.AsMemory(0, bytesRead));
|
||||
}
|
||||
fileStream.Close();
|
||||
}
|
||||
var newlineBytes = Encoding.UTF8.GetBytes("\r\n");
|
||||
await requestStream.WriteAsync(newlineBytes);
|
||||
var endBytes = Encoding.UTF8.GetBytes("--" + boundary + "--");
|
||||
await requestStream.WriteAsync(endBytes);
|
||||
requestStream.Close();
|
||||
var imageContent = new ByteArrayContent(fileToUpload);
|
||||
imageContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||
content.Add(imageContent, "image", "image.png");
|
||||
|
||||
request.Content = content;
|
||||
return request;
|
||||
}
|
||||
|
||||
private static async Task UploadFilePut(HttpWebRequest request, IDictionary<string, object> options)
|
||||
private async Task<HttpRequestMessage> BuildUploadFilePutRequest(string url, IDictionary<string, object> options)
|
||||
{
|
||||
if (ProxySet)
|
||||
request.Proxy = Proxy;
|
||||
|
||||
request.AutomaticDecompression = DecompressionMethods.All;
|
||||
request.Method = "PUT";
|
||||
request.ContentType = options["fileMIME"] as string;
|
||||
var request = new HttpRequestMessage(HttpMethod.Put, url);
|
||||
var fileData = options["fileData"] as string;
|
||||
var sentData = Convert.FromBase64CharArray(fileData.ToCharArray(), 0, fileData.Length);
|
||||
request.ContentLength = sentData.Length;
|
||||
await using var sendStream = request.GetRequestStream();
|
||||
await sendStream.WriteAsync(sentData);
|
||||
sendStream.Close();
|
||||
var content = new ByteArrayContent(sentData);
|
||||
content.Headers.ContentType = new MediaTypeHeaderValue(options["fileMIME"] as string);
|
||||
if (options.TryGetValue("fileMD5", out var fileMd5))
|
||||
content.Headers.ContentMD5 = Convert.FromBase64String(fileMd5 as string);
|
||||
request.Content = content;
|
||||
return request;
|
||||
}
|
||||
|
||||
private static async Task ImageUpload(HttpWebRequest request, IDictionary<string, object> options)
|
||||
{
|
||||
if (ProxySet)
|
||||
request.Proxy = Proxy;
|
||||
|
||||
request.AutomaticDecompression = DecompressionMethods.All;
|
||||
request.Method = "POST";
|
||||
private async Task<HttpRequestMessage> BuildImageUploadRequest(string url, IDictionary<string, object> options)
|
||||
{
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
|
||||
request.ContentType = "multipart/form-data; boundary=" + boundary;
|
||||
var requestStream = request.GetRequestStream();
|
||||
if (options.TryGetValue("postData", out object postDataObject))
|
||||
var content = new MultipartFormDataContent(boundary);
|
||||
|
||||
if (options.TryGetValue("postData", out var postDataObject))
|
||||
{
|
||||
var jsonPostData = (JObject)JsonConvert.DeserializeObject((string)postDataObject);
|
||||
const string formDataTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\n\r\n{2}\r\n";
|
||||
if (jsonPostData != null)
|
||||
{
|
||||
foreach (var data in jsonPostData)
|
||||
{
|
||||
var item = string.Format(formDataTemplate, boundary, data.Key, data.Value);
|
||||
var itemBytes = Encoding.UTF8.GetBytes(item);
|
||||
await requestStream.WriteAsync(itemBytes);
|
||||
content.Add(new StringContent(data.Value?.ToString() ?? string.Empty), data.Key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var imageData = options["imageData"] as string;
|
||||
var matchingDimensions = options["matchingDimensions"] as bool? ?? false;
|
||||
var fileToUpload = Program.AppApiInstance.ResizeImageToFitLimits(Convert.FromBase64String(imageData), matchingDimensions);
|
||||
|
||||
const string fileFormKey = "file";
|
||||
const string fileName = "blob";
|
||||
const string fileMimeType = "image/png";
|
||||
const string headerTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\n\r\n";
|
||||
var header = string.Format(headerTemplate, boundary, fileFormKey, fileName, fileMimeType);
|
||||
var headerBytes = Encoding.UTF8.GetBytes(header);
|
||||
await requestStream.WriteAsync(headerBytes);
|
||||
using (var fileStream = new MemoryStream(fileToUpload))
|
||||
{
|
||||
var buffer = new byte[1024];
|
||||
var bytesRead = 0;
|
||||
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
|
||||
{
|
||||
await requestStream.WriteAsync(buffer.AsMemory(0, bytesRead));
|
||||
}
|
||||
fileStream.Close();
|
||||
}
|
||||
var newlineBytes = Encoding.UTF8.GetBytes("\r\n");
|
||||
await requestStream.WriteAsync(newlineBytes);
|
||||
var endBytes = Encoding.UTF8.GetBytes("--" + boundary + "--");
|
||||
await requestStream.WriteAsync(endBytes);
|
||||
requestStream.Close();
|
||||
var imageContent = new ByteArrayContent(fileToUpload);
|
||||
imageContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||
content.Add(imageContent, "file", "blob");
|
||||
|
||||
request.Content = content;
|
||||
return request;
|
||||
}
|
||||
|
||||
private static async Task PrintImageUpload(HttpWebRequest request, IDictionary<string, object> options)
|
||||
|
||||
private async Task<HttpRequestMessage> BuildPrintImageUploadRequest(string url, IDictionary<string, object> options)
|
||||
{
|
||||
if (options.TryGetValue("cropWhiteBorder", out var cropWhiteBorder) && (bool)cropWhiteBorder)
|
||||
{
|
||||
@@ -356,40 +333,19 @@ namespace VRCX
|
||||
options["imageData"] = Convert.ToBase64String(ms2.ToArray());
|
||||
}
|
||||
}
|
||||
|
||||
if (ProxySet)
|
||||
request.Proxy = Proxy;
|
||||
|
||||
request.AutomaticDecompression = DecompressionMethods.All;
|
||||
request.Method = "POST";
|
||||
var request = new HttpRequestMessage(HttpMethod.Post, url);
|
||||
var boundary = "---------------------------" + DateTime.Now.Ticks.ToString("x");
|
||||
request.ContentType = "multipart/form-data; boundary=" + boundary;
|
||||
var requestStream = request.GetRequestStream();
|
||||
var content = new MultipartFormDataContent(boundary);
|
||||
|
||||
var imageData = options["imageData"] as string;
|
||||
var fileToUpload = Program.AppApiInstance.ResizePrintImage(Convert.FromBase64String(imageData));
|
||||
const string fileFormKey = "image";
|
||||
const string fileName = "image";
|
||||
const string fileMimeType = "image/png";
|
||||
var fileSize = fileToUpload.Length;
|
||||
const string headerTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"; filename=\"{2}\"\r\nContent-Type: {3}\r\nContent-Length: {4}\r\n";
|
||||
var header = string.Format(headerTemplate, boundary, fileFormKey, fileName, fileMimeType, fileSize);
|
||||
var headerBytes = Encoding.UTF8.GetBytes(header);
|
||||
await requestStream.WriteAsync(headerBytes);
|
||||
var newlineBytes = Encoding.UTF8.GetBytes("\r\n");
|
||||
await requestStream.WriteAsync(newlineBytes);
|
||||
using (var fileStream = new MemoryStream(fileToUpload))
|
||||
{
|
||||
var buffer = new byte[1024];
|
||||
int bytesRead;
|
||||
while ((bytesRead = fileStream.Read(buffer, 0, buffer.Length)) != 0)
|
||||
{
|
||||
await requestStream.WriteAsync(buffer.AsMemory(0, bytesRead));
|
||||
}
|
||||
fileStream.Close();
|
||||
}
|
||||
const string textContentType = "text/plain; charset=utf-8";
|
||||
const string formDataTemplate = "--{0}\r\nContent-Disposition: form-data; name=\"{1}\"\r\nContent-Type: {2}\r\nContent-Length: {3}\r\n\r\n{4}\r\n";
|
||||
await requestStream.WriteAsync(newlineBytes);
|
||||
|
||||
var imageContent = new ByteArrayContent(fileToUpload);
|
||||
imageContent.Headers.ContentType = new MediaTypeHeaderValue("image/png");
|
||||
imageContent.Headers.ContentLength = fileToUpload.Length;
|
||||
content.Add(imageContent, "image", "image");
|
||||
|
||||
if (options.TryGetValue("postData", out var postDataObject))
|
||||
{
|
||||
var jsonPostData = JsonConvert.DeserializeObject<Dictionary<string, string>>(postDataObject.ToString());
|
||||
@@ -397,17 +353,16 @@ namespace VRCX
|
||||
{
|
||||
foreach (var (key, value) in jsonPostData)
|
||||
{
|
||||
var section = string.Format(formDataTemplate, boundary, key, textContentType, value.Length, value);
|
||||
var sectionBytes = Encoding.UTF8.GetBytes(section);
|
||||
await requestStream.WriteAsync(sectionBytes);
|
||||
var stringContent = new StringContent(value, Encoding.UTF8, "text/plain");
|
||||
content.Add(stringContent, key);
|
||||
}
|
||||
}
|
||||
}
|
||||
var endBytes = Encoding.UTF8.GetBytes("--" + boundary + "--");
|
||||
await requestStream.WriteAsync(endBytes);
|
||||
requestStream.Close();
|
||||
|
||||
request.Content = content;
|
||||
return request;
|
||||
}
|
||||
|
||||
|
||||
public async Task<string> ExecuteJson(string options)
|
||||
{
|
||||
var data = JsonConvert.DeserializeObject<Dictionary<string, object>>(options);
|
||||
@@ -423,112 +378,112 @@ namespace VRCX
|
||||
{
|
||||
try
|
||||
{
|
||||
// TODO: switch to HttpClient
|
||||
#pragma warning disable SYSLIB0014 // Type or member is obsolete
|
||||
var request = WebRequest.CreateHttp((string)options["url"]);
|
||||
#pragma warning restore SYSLIB0014 // Type or member is obsolete
|
||||
if (ProxySet)
|
||||
request.Proxy = Proxy;
|
||||
var url = (string)options["url"];
|
||||
HttpRequestMessage request;
|
||||
|
||||
request.CookieContainer = _cookieContainer;
|
||||
request.KeepAlive = true;
|
||||
request.UserAgent = Program.Version;
|
||||
request.AutomaticDecompression = DecompressionMethods.All;
|
||||
// Handle special upload types
|
||||
if (options.TryGetValue("uploadImageLegacy", out _))
|
||||
{
|
||||
request = await BuildLegacyImageUploadRequest(url, options);
|
||||
}
|
||||
else if (options.TryGetValue("uploadFilePUT", out _))
|
||||
{
|
||||
request = await BuildUploadFilePutRequest(url, options);
|
||||
}
|
||||
else if (options.TryGetValue("uploadImage", out _))
|
||||
{
|
||||
request = await BuildImageUploadRequest(url, options);
|
||||
}
|
||||
else if (options.TryGetValue("uploadImagePrint", out _))
|
||||
{
|
||||
request = await BuildPrintImageUploadRequest(url, options);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Standard request
|
||||
var httpMethod = HttpMethod.Get;
|
||||
if (options.TryGetValue("method", out var methodObj))
|
||||
{
|
||||
httpMethod = HttpMethod.Parse(methodObj.ToString());
|
||||
}
|
||||
|
||||
request = new HttpRequestMessage(httpMethod, url);
|
||||
|
||||
// Handle body for non-GET requests
|
||||
if (httpMethod != HttpMethod.Get && options.TryGetValue("body", out var body))
|
||||
{
|
||||
var bodyContent = new StringContent((string)body, Encoding.UTF8);
|
||||
|
||||
// Set content type if specified in headers
|
||||
if (options.TryGetValue("headers", out var headersObj))
|
||||
{
|
||||
var headersDict = ParseHeaders(headersObj);
|
||||
if (headersDict.TryGetValue("Content-Type", out var contentType))
|
||||
{
|
||||
bodyContent.Headers.ContentType = MediaTypeHeaderValue.Parse(contentType);
|
||||
}
|
||||
}
|
||||
|
||||
request.Content = bodyContent;
|
||||
}
|
||||
}
|
||||
|
||||
// Apply headers
|
||||
if (options.TryGetValue("headers", out var headers))
|
||||
{
|
||||
Dictionary<string, string> headersDict;
|
||||
if (headers.GetType() == typeof(JObject))
|
||||
{
|
||||
headersDict = ((JObject)headers).ToObject<Dictionary<string, string>>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var headersKvp = (IEnumerable<KeyValuePair<string, object>>)headers;
|
||||
headersDict = new Dictionary<string, string>();
|
||||
foreach (var (key, value) in headersKvp)
|
||||
headersDict.Add(key, value.ToString());
|
||||
}
|
||||
|
||||
var headersDict = ParseHeaders(headers);
|
||||
foreach (var (key, value) in headersDict)
|
||||
{
|
||||
if (string.Compare(key, "Content-Type", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
request.ContentType = value;
|
||||
else if (string.Compare(key, "Referer", StringComparison.OrdinalIgnoreCase) == 0)
|
||||
request.Referer = value;
|
||||
// Skip Content-Type as it's set on content
|
||||
if (string.Equals(key, "Content-Type", StringComparison.OrdinalIgnoreCase))
|
||||
continue;
|
||||
|
||||
if (string.Equals(key, "Referer", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
request.Headers.Referrer = new Uri(value);
|
||||
}
|
||||
else
|
||||
request.Headers.Add(key, value);
|
||||
{
|
||||
request.Headers.TryAddWithoutValidation(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (options.TryGetValue("method", out var method))
|
||||
{
|
||||
request.Method = (string)method;
|
||||
if (string.Compare(request.Method, "GET", StringComparison.OrdinalIgnoreCase) != 0 &&
|
||||
options.TryGetValue("body", out var body))
|
||||
{
|
||||
await using var bodyStream = await request.GetRequestStreamAsync();
|
||||
await using var streamWriter = new StreamWriter(bodyStream);
|
||||
await streamWriter.WriteAsync((string)body);
|
||||
}
|
||||
}
|
||||
using var response = await _httpClient.SendAsync(request);
|
||||
|
||||
if (options.TryGetValue("uploadImage", out _))
|
||||
await ImageUpload(request, options);
|
||||
|
||||
if (options.TryGetValue("uploadFilePUT", out _))
|
||||
await UploadFilePut(request, options);
|
||||
|
||||
if (options.TryGetValue("uploadImageLegacy", out _))
|
||||
await LegacyImageUpload(request, options);
|
||||
|
||||
if (options.TryGetValue("uploadImagePrint", out _))
|
||||
await PrintImageUpload(request, options);
|
||||
|
||||
using var response = await request.GetResponseAsync() as HttpWebResponse;
|
||||
if (response?.Headers["Set-Cookie"] != null)
|
||||
// Check if cookies were modified
|
||||
if (response.Headers.Contains("Set-Cookie"))
|
||||
_cookieDirty = true;
|
||||
|
||||
await using var imageStream = response.GetResponseStream();
|
||||
using var streamReader = new StreamReader(imageStream);
|
||||
if (response.ContentType.Contains("image/") ||
|
||||
response.ContentType.Contains("application/octet-stream"))
|
||||
var contentTypeResponse = response.Content.Headers.ContentType?.MediaType ?? string.Empty;
|
||||
|
||||
if (contentTypeResponse.Contains("image/") || contentTypeResponse.Contains("application/octet-stream"))
|
||||
{
|
||||
// base64 response data for image
|
||||
using var memoryStream = new MemoryStream();
|
||||
await imageStream.CopyToAsync(memoryStream);
|
||||
// Base64 response data for image
|
||||
var imageBytes = await response.Content.ReadAsByteArrayAsync();
|
||||
return new Tuple<int, string>(
|
||||
(int)response.StatusCode,
|
||||
$"data:image/png;base64,{Convert.ToBase64String(memoryStream.ToArray())}"
|
||||
$"data:image/png;base64,{Convert.ToBase64String(imageBytes)}"
|
||||
);
|
||||
}
|
||||
|
||||
var responseBody = await response.Content.ReadAsStringAsync();
|
||||
return new Tuple<int, string>(
|
||||
(int)response.StatusCode,
|
||||
await streamReader.ReadToEndAsync()
|
||||
responseBody
|
||||
);
|
||||
}
|
||||
catch (WebException webException)
|
||||
catch (HttpRequestException httpException)
|
||||
{
|
||||
if (webException.Response is HttpWebResponse response)
|
||||
{
|
||||
if (response.Headers["Set-Cookie"] != null)
|
||||
_cookieDirty = true;
|
||||
if (httpException.InnerException != null)
|
||||
Logger.Error($"{httpException.Message} | {httpException.InnerException}");
|
||||
|
||||
await using var stream = response.GetResponseStream();
|
||||
using var streamReader = new StreamReader(stream);
|
||||
return new Tuple<int, string>(
|
||||
(int)response.StatusCode,
|
||||
await streamReader.ReadToEndAsync()
|
||||
);
|
||||
}
|
||||
|
||||
if (webException.InnerException != null)
|
||||
Logger.Error($"{webException.Message} | {webException.InnerException}");
|
||||
// Try to get status code if available
|
||||
var statusCode = httpException.StatusCode.HasValue ? (int)httpException.StatusCode.Value : -1;
|
||||
|
||||
return new Tuple<int, string>(
|
||||
-1,
|
||||
webException.Message
|
||||
statusCode,
|
||||
httpException.Message
|
||||
);
|
||||
}
|
||||
catch (Exception e)
|
||||
@@ -542,5 +497,22 @@ namespace VRCX
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private static Dictionary<string, string> ParseHeaders(object headers)
|
||||
{
|
||||
Dictionary<string, string> headersDict;
|
||||
if (headers.GetType() == typeof(JObject))
|
||||
{
|
||||
headersDict = ((JObject)headers).ToObject<Dictionary<string, string>>();
|
||||
}
|
||||
else
|
||||
{
|
||||
var headersKvp = (IEnumerable<KeyValuePair<string, object>>)headers;
|
||||
headersDict = new Dictionary<string, string>();
|
||||
foreach (var (key, value) in headersKvp)
|
||||
headersDict.Add(key, value.ToString());
|
||||
}
|
||||
return headersDict;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user