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:
Luc ♥
2026-01-17 22:16:31 +01:00
committed by GitHub
parent 2d812d7c05
commit 56bf69f64e
6 changed files with 215 additions and 243 deletions

View File

@@ -51,13 +51,19 @@ jobs:
${{ runner.os }}-nuget-
- name: Build Cef .NET Application
run: dotnet build Dotnet/VRCX-Cef.csproj -p:Configuration=Release -p:WarningLevel=0 -p:Platform=x64 -p:PlatformTarget=x64 -p:RestorePackagesConfig=true -t:"Restore;Clean;Build" -m -a x64 --self-contained
- name: Check if we want to sign
id: check_sign
run: |
echo "sign=${{ secrets.AZURE_CLIENT_ID != '' }}" >> $GITHUB_OUTPUT
- name: Azure login
if: steps.check_sign.outputs.sign == 'true'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_CLIENT_ID }}
tenant-id: ${{ secrets.AZURE_TENANT_ID }}
subscription-id: ${{ secrets.AZURE_SUBSCRIPTION_ID }}
- name: Sign Dotnet executables
if: steps.check_sign.outputs.sign == 'true'
uses: azure/trusted-signing-action@v0
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
@@ -103,6 +109,7 @@ jobs:
mv Installer/VRCX_Setup.exe $file_name
- name: Sign Cef setup
uses: azure/trusted-signing-action@v0
if: steps.check_sign.outputs.sign == 'true'
with:
azure-tenant-id: ${{ secrets.AZURE_TENANT_ID }}
azure-client-id: ${{ secrets.AZURE_CLIENT_ID }}

View File

@@ -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};";
}

View File

@@ -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;
}
}
}
}

View File

@@ -199,9 +199,7 @@
uploadFilePUT: true,
fileData: avatarImage.value.base64File,
fileMIME: 'image/png',
headers: {
'Content-MD5': avatarImage.value.fileMd5
}
fileMD5: avatarImage.value.fileMd5
});
if (json.status !== 200) {
@@ -252,9 +250,7 @@
uploadFilePUT: true,
fileData: avatarImage.value.base64SignatureFile,
fileMIME: 'application/x-rsync-signature',
headers: {
'Content-MD5': avatarImage.value.signatureMd5
}
fileMD5: avatarImage.value.signatureMd5
});
if (json.status !== 200) {

View File

@@ -192,9 +192,7 @@
uploadFilePUT: true,
fileData: worldImage.value.base64File,
fileMIME: 'image/png',
headers: {
'Content-MD5': worldImage.value.fileMd5
}
fileMD5: worldImage.value.fileMd5
});
if (json.status !== 200) {
@@ -245,9 +243,7 @@
uploadFilePUT: true,
fileData: worldImage.value.base64SignatureFile,
fileMIME: 'application/x-rsync-signature',
headers: {
'Content-MD5': worldImage.value.signatureMd5
}
fileMD5: worldImage.value.signatureMd5
});
if (json.status !== 200) {

View File

@@ -408,10 +408,11 @@ declare global {
uploadFilePUT?: boolean;
fileData?: string;
fileMIME?: string;
fileMD5?: string;
headers?: Record<string, string>;
data?: any;
}): Promise<{ status: number; data: string }>;
};
}
export { };
export {};