mirror of
https://github.com/JogmansonReal/Radicade.git
synced 2026-10-25 15:09:26 -07:00
Radicade Public Code
This commit is contained in:
@@ -0,0 +1,107 @@
|
||||
using System;
|
||||
using System.IO;
|
||||
using System.Text;
|
||||
using System.Reflection;
|
||||
using BestHTTP;
|
||||
using HarmonyLib;
|
||||
|
||||
/// <summary>
|
||||
/// Fabric-level repairs for two things the game cannot survive as-is:
|
||||
/// a chunked response the server truncated, and two responses whose shape the
|
||||
/// client's reader rejects.
|
||||
/// </summary>
|
||||
internal static class HttpTolerancePatches
|
||||
{
|
||||
private static long _healed;
|
||||
private static int _lastHealReport;
|
||||
|
||||
internal static void Apply(Harmony harmony)
|
||||
{
|
||||
try
|
||||
{
|
||||
var target = TargetMethod();
|
||||
if (target == null)
|
||||
{
|
||||
Diag.Warn("[Http] BestHTTP.HTTPResponse.ReadChunkLength(Stream) was not found: " +
|
||||
"a truncated chunked body will keep failing its request.");
|
||||
return;
|
||||
}
|
||||
|
||||
harmony.Patch(target,
|
||||
finalizer: new HarmonyMethod(AccessTools.Method(typeof(HttpTolerancePatches), nameof(Finalizer))));
|
||||
Diag.Info("[Http] truncated chunked responses are completed instead of failing their request.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Diag.Warn("[Http] could not arm the tolerant chunked reader (" + ex.GetType().Name + ": " + ex.Message + ").");
|
||||
}
|
||||
}
|
||||
|
||||
internal static MethodBase TargetMethod() =>
|
||||
AccessTools.Method(typeof(HTTPResponse), "ReadChunkLength", new[] { typeof(Stream) });
|
||||
|
||||
/// <summary>
|
||||
/// "Can't parse '' as a hex number" is BestHTTP reading a chunk-size line from a
|
||||
/// connection the server had already closed - the pooled keep-alive connection
|
||||
/// it picked was dead. Ending the body there (chunk length 0) delivers the
|
||||
/// response that did arrive instead of failing the request, which is what the
|
||||
/// heartbeats need: their ack body is empty anyway.
|
||||
/// </summary>
|
||||
private static Exception Finalizer(Exception __exception, ref int __result)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (__exception == null) return null;
|
||||
if (!ResponseRepair.IsTruncatedChunkFailure(__exception.Message)) return __exception;
|
||||
|
||||
ReportHealed();
|
||||
__result = 0;
|
||||
__exception = null;
|
||||
return null;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return __exception;
|
||||
}
|
||||
}
|
||||
|
||||
private static void ReportHealed()
|
||||
{
|
||||
var count = System.Threading.Interlocked.Increment(ref _healed);
|
||||
var now = Environment.TickCount;
|
||||
if (count != 1 && unchecked(now - _lastHealReport) < 60000) return;
|
||||
_lastHealReport = now;
|
||||
Diag.Warn("[Http] a chunked response ended early (dead pooled connection); the request was completed "
|
||||
+ "with the bytes that arrived instead of failing. Occurrences so far: " + count + ".");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Applies <see cref="ResponseRepair"/> to a live response before the client's own
|
||||
/// callback reads it. Called from the request funnel, always on the transport
|
||||
/// thread that owns the response.
|
||||
/// </summary>
|
||||
internal static void RepairResponse(HTTPResponse response, string path, string query)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (response == null || string.IsNullOrEmpty(path)) return;
|
||||
if (response.StatusCode != 200) return;
|
||||
|
||||
string body;
|
||||
try { body = response.DataAsText; }
|
||||
catch { return; }
|
||||
|
||||
var repaired = ResponseRepair.RepairBody(path, query, body, out var note);
|
||||
if (repaired == null) return;
|
||||
|
||||
response.Data = Encoding.UTF8.GetBytes(repaired);
|
||||
|
||||
if (Diag.FirstTime("repair:" + path))
|
||||
Diag.Warn("[Repair] " + path + ": " + note + ".");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Diag.Warn("[Repair] could not repair a " + path + " response: " + ex.GetType().Name + ": " + ex.Message);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user