diff --git a/ArcaneLibs b/ArcaneLibs
-Subproject 3fb65c126b591ca05e76394d4c40f74cbb70de4
+Subproject 26b02bc5459f33d3b9b6bd2e4dda558cb8ac2e9
diff --git a/LibMatrix/Extensions/MatrixHttpClient.Single.cs b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
index c9cd260..bab3e92 100644
--- a/LibMatrix/Extensions/MatrixHttpClient.Single.cs
+++ b/LibMatrix/Extensions/MatrixHttpClient.Single.cs
@@ -1,5 +1,5 @@
#define SINGLE_HTTPCLIENT // Use a single HttpClient instance for all MatrixHttpClient instances
-// #define SYNC_HTTPCLIENT // Only allow one request as a time, for debugging
+// #define SYNC_HTTPCLIENT // Only allow one request as a time, for debugging
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Net.Http.Headers;
@@ -26,7 +26,8 @@ public class MatrixHttpClient {
EnableMultipleHttp2Connections = true
};
Client = new HttpClient(handler) {
- DefaultRequestVersion = new Version(3, 0)
+ DefaultRequestVersion = new Version(3, 0),
+ Timeout = TimeSpan.FromHours(1)
};
}
catch (PlatformNotSupportedException e) {
@@ -68,26 +69,29 @@ public class MatrixHttpClient {
}
public async Task<HttpResponseMessage> SendUnhandledAsync(HttpRequestMessage request, CancellationToken cancellationToken) {
+ if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null");
+ // if (!request.RequestUri.IsAbsoluteUri)
+ request.RequestUri = request.RequestUri.EnsureAbsolute(BaseAddress!);
+ var swWait = Stopwatch.StartNew();
#if SYNC_HTTPCLIENT
await _rateLimitSemaphore.WaitAsync(cancellationToken);
#endif
-
- Console.WriteLine($"Sending {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)})");
-
- if (request.RequestUri is null) throw new NullReferenceException("RequestUri is null");
- if (!request.RequestUri.IsAbsoluteUri) request.RequestUri = new Uri(BaseAddress, request.RequestUri);
+ swWait.Stop();
+ var swExec = Stopwatch.StartNew();
+
foreach (var (key, value) in AdditionalQueryParameters) request.RequestUri = request.RequestUri.AddQuery(key, value);
foreach (var (key, value) in DefaultRequestHeaders) request.Headers.Add(key, value);
-
request.Options.Set(new HttpRequestOptionsKey<bool>("WebAssemblyEnableStreamingResponse"), true);
+ Console.WriteLine("Sending " + request.Summarise(includeHeaders:true, includeQuery: true, includeContentIfText: true));
+
HttpResponseMessage? responseMessage;
try {
responseMessage = await Client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken);
}
catch (Exception e) {
Console.WriteLine(
- $"Failed to send request {request.Method} {BaseAddress}{request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}):\n{e}");
+ $"Failed to send request {request.Method} {request.RequestUri} ({Util.BytesToString(request.GetContentLength())}):\n{e}");
throw;
}
#if SYNC_HTTPCLIENT
@@ -96,8 +100,20 @@ public class MatrixHttpClient {
}
#endif
- Console.WriteLine(
- $"Sending {request.Method} {request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}) -> {(int)responseMessage.StatusCode} {responseMessage.StatusCode} ({Util.BytesToString(responseMessage.Content.Headers.ContentLength ?? 0)})");
+ // Console.WriteLine($"Sending {request.Method} {request.RequestUri} ({Util.BytesToString(request.Content?.Headers.ContentLength ?? 0)}) -> {(int)responseMessage.StatusCode} {responseMessage.StatusCode} ({Util.BytesToString(responseMessage.GetContentLength())}, WAIT={swWait.ElapsedMilliseconds}ms, EXEC={swExec.ElapsedMilliseconds}ms)");
+ Console.WriteLine("Received " + responseMessage.Summarise(includeHeaders: true, includeContentIfText: false, hideHeaders: [
+ "Server",
+ "Date",
+ "Transfer-Encoding",
+ "Connection",
+ "Vary",
+ "Content-Length",
+ "Access-Control-Allow-Origin",
+ "Access-Control-Allow-Methods",
+ "Access-Control-Allow-Headers",
+ "Access-Control-Expose-Headers",
+ "Cache-Control"
+ ]));
return responseMessage;
}
diff --git a/LibMatrix/Helpers/SyncStateResolver.cs b/LibMatrix/Helpers/SyncStateResolver.cs
index 72d600d..0529e50 100644
--- a/LibMatrix/Helpers/SyncStateResolver.cs
+++ b/LibMatrix/Helpers/SyncStateResolver.cs
@@ -37,7 +37,7 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge
oldState.NextBatch = newState.NextBatch ?? oldState.NextBatch;
oldState.AccountData ??= new EventList();
- oldState.AccountData.Events ??= new List<StateEventResponse>();
+ oldState.AccountData.Events ??= [];
if (newState.AccountData?.Events is not null)
oldState.AccountData.Events.MergeStateEventLists(newState.AccountData?.Events ?? new List<StateEventResponse>());
@@ -55,16 +55,18 @@ public class SyncStateResolver(AuthenticatedHomeserverGeneric homeserver, ILogge
oldState.Rooms = MergeRoomsDataStructure(oldState.Rooms, newState.Rooms);
oldState.ToDevice ??= new EventList();
- oldState.ToDevice.Events ??= new List<StateEventResponse>();
+ oldState.ToDevice.Events ??= [];
if (newState.ToDevice?.Events is not null)
oldState.ToDevice.Events.MergeStateEventLists(newState.ToDevice?.Events ?? new List<StateEventResponse>());
oldState.DeviceLists ??= new SyncResponse.DeviceListsDataStructure();
+ oldState.DeviceLists.Changed ??= [];
+ oldState.DeviceLists.Left ??= [];
if (newState.DeviceLists?.Changed is not null)
- foreach (var s in oldState.DeviceLists.Changed!)
+ foreach (var s in newState.DeviceLists.Changed!)
oldState.DeviceLists.Changed.Add(s);
if (newState.DeviceLists?.Left is not null)
- foreach (var s in oldState.DeviceLists.Left!)
+ foreach (var s in newState.DeviceLists.Left!)
oldState.DeviceLists.Left.Add(s);
return oldState;
diff --git a/LibMatrix/MatrixException.cs b/LibMatrix/MatrixException.cs
index eb207da..afdeefe 100644
--- a/LibMatrix/MatrixException.cs
+++ b/LibMatrix/MatrixException.cs
@@ -24,43 +24,47 @@ public class MatrixException : Exception {
public string GetAsJson() => GetAsObject().ToJson(ignoreNull: true);
public override string Message =>
- $"{ErrorCode}: {ErrorCode switch {
- // common
- "M_FORBIDDEN" => $"You do not have permission to perform this action: {Error}",
- "M_UNKNOWN_TOKEN" => $"The access token specified was not recognised: {Error}{(SoftLogout == true ? " (soft logout)" : "")}",
- "M_MISSING_TOKEN" => $"No access token was specified: {Error}",
- "M_BAD_JSON" => $"Request contained valid JSON, but it was malformed in some way: {Error}",
- "M_NOT_JSON" => $"Request did not contain valid JSON: {Error}",
- "M_NOT_FOUND" => $"The requested resource was not found: {Error}",
- "M_LIMIT_EXCEEDED" => $"Too many requests have been sent in a short period of time. Wait a while then try again: {Error}",
- "M_UNRECOGNISED" => $"The server did not recognise the request: {Error}",
- "M_UNKOWN" => $"The server encountered an unexpected error: {Error}",
- // endpoint specific
- "M_UNAUTHORIZED" => $"The request did not contain valid authentication information for the target of the request: {Error}",
- "M_USER_DEACTIVATED" => $"The user ID associated with the request has been deactivated: {Error}",
- "M_USER_IN_USE" => $"The user ID associated with the request is already in use: {Error}",
- "M_INVALID_USERNAME" => $"The requested user ID is not valid: {Error}",
- "M_ROOM_IN_USE" => $"The room alias requested is already taken: {Error}",
- "M_INVALID_ROOM_STATE" => $"The room associated with the request is not in a valid state to perform the request: {Error}",
- "M_THREEPID_IN_USE" => $"The threepid requested is already associated with a user ID on this server: {Error}",
- "M_THREEPID_NOT_FOUND" => $"The threepid requested is not associated with any user ID: {Error}",
- "M_THREEPID_AUTH_FAILED" => $"The provided threepid and/or token was invalid: {Error}",
- "M_THREEPID_DENIED" => $"The homeserver does not permit the third party identifier in question: {Error}",
- "M_SERVER_NOT_TRUSTED" => $"The homeserver does not trust the identity server: {Error}",
- "M_UNSUPPORTED_ROOM_VERSION" => $"The room version is not supported: {Error}",
- "M_INCOMPATIBLE_ROOM_VERSION" => $"The room version is incompatible: {Error}",
- "M_BAD_STATE" => $"The request was invalid because the state was invalid: {Error}",
- "M_GUEST_ACCESS_FORBIDDEN" => $"Guest access is forbidden: {Error}",
- "M_CAPTCHA_NEEDED" => $"Captcha needed: {Error}",
- "M_CAPTCHA_INVALID" => $"Captcha invalid: {Error}",
- "M_MISSING_PARAM" => $"Missing parameter: {Error}",
- "M_INVALID_PARAM" => $"Invalid parameter: {Error}",
- "M_TOO_LARGE" => $"The request or entity was too large: {Error}",
- "M_EXCLUSIVE" => $"The resource being requested is reserved by an application service, or the application service making the request has not created the resource: {Error}",
- "M_RESOURCE_LIMIT_EXCEEDED" => $"Exceeded resource limit: {Error}",
- "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => $"Cannot leave server notice room: {Error}",
- _ => $"Unknown error: {new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)}"
- }}";
+ $"{ErrorCode}: " +
+ (!string.IsNullOrWhiteSpace(Error)
+ ? Error
+ : ErrorCode switch {
+ // common
+ "M_FORBIDDEN" => $"You do not have permission to perform this action: {Error}",
+ "M_UNKNOWN_TOKEN" => $"The access token specified was not recognised: {Error}{(SoftLogout == true ? " (soft logout)" : "")}",
+ "M_MISSING_TOKEN" => $"No access token was specified: {Error}",
+ "M_BAD_JSON" => $"Request contained valid JSON, but it was malformed in some way: {Error}",
+ "M_NOT_JSON" => $"Request did not contain valid JSON: {Error}",
+ "M_NOT_FOUND" => $"The requested resource was not found: {Error}",
+ "M_LIMIT_EXCEEDED" => $"Too many requests have been sent in a short period of time. Wait a while then try again: {Error}",
+ "M_UNRECOGNISED" => $"The server did not recognise the request: {Error}",
+ "M_UNKOWN" => $"The server encountered an unexpected error: {Error}",
+ // endpoint specific
+ "M_UNAUTHORIZED" => $"The request did not contain valid authentication information for the target of the request: {Error}",
+ "M_USER_DEACTIVATED" => $"The user ID associated with the request has been deactivated: {Error}",
+ "M_USER_IN_USE" => $"The user ID associated with the request is already in use: {Error}",
+ "M_INVALID_USERNAME" => $"The requested user ID is not valid: {Error}",
+ "M_ROOM_IN_USE" => $"The room alias requested is already taken: {Error}",
+ "M_INVALID_ROOM_STATE" => $"The room associated with the request is not in a valid state to perform the request: {Error}",
+ "M_THREEPID_IN_USE" => $"The threepid requested is already associated with a user ID on this server: {Error}",
+ "M_THREEPID_NOT_FOUND" => $"The threepid requested is not associated with any user ID: {Error}",
+ "M_THREEPID_AUTH_FAILED" => $"The provided threepid and/or token was invalid: {Error}",
+ "M_THREEPID_DENIED" => $"The homeserver does not permit the third party identifier in question: {Error}",
+ "M_SERVER_NOT_TRUSTED" => $"The homeserver does not trust the identity server: {Error}",
+ "M_UNSUPPORTED_ROOM_VERSION" => $"The room version is not supported: {Error}",
+ "M_INCOMPATIBLE_ROOM_VERSION" => $"The room version is incompatible: {Error}",
+ "M_BAD_STATE" => $"The request was invalid because the state was invalid: {Error}",
+ "M_GUEST_ACCESS_FORBIDDEN" => $"Guest access is forbidden: {Error}",
+ "M_CAPTCHA_NEEDED" => $"Captcha needed: {Error}",
+ "M_CAPTCHA_INVALID" => $"Captcha invalid: {Error}",
+ "M_MISSING_PARAM" => $"Missing parameter: {Error}",
+ "M_INVALID_PARAM" => $"Invalid parameter: {Error}",
+ "M_TOO_LARGE" => $"The request or entity was too large: {Error}",
+ "M_EXCLUSIVE" =>
+ $"The resource being requested is reserved by an application service, or the application service making the request has not created the resource: {Error}",
+ "M_RESOURCE_LIMIT_EXCEEDED" => $"Exceeded resource limit: {Error}",
+ "M_CANNOT_LEAVE_SERVER_NOTICE_ROOM" => $"Cannot leave server notice room: {Error}",
+ _ => $"Unknown error: {new { ErrorCode, Error, SoftLogout, RetryAfterMs }.ToJson(ignoreNull: true)}"
+ });
public static class ErrorCodes {
public const string M_FORBIDDEN = "M_FORBIDDEN";
diff --git a/LibMatrix/Services/HomeserverProviderService.cs b/LibMatrix/Services/HomeserverProviderService.cs
index 3e42c21..a674549 100644
--- a/LibMatrix/Services/HomeserverProviderService.cs
+++ b/LibMatrix/Services/HomeserverProviderService.cs
@@ -23,9 +23,11 @@ public class HomeserverProviderService(ILogger<HomeserverProviderService> logger
AuthenticatedHomeserverGeneric? hs = null;
if (!useGeneric) {
+ var clientVersionsTask = rhs.GetClientVersionsAsync();
+ var serverVersionTask = rhs.FederationClient?.GetServerVersionAsync() ?? Task.FromResult<ServerVersionResponse?>(null)!;
ClientVersionsResponse? clientVersions = new();
try {
- clientVersions = await rhs.GetClientVersionsAsync();
+ clientVersions = await clientVersionsTask;
}
catch (Exception e) {
logger.LogError(e, "Failed to get client versions for {homeserver}", homeserver);
@@ -33,7 +35,7 @@ public class HomeserverProviderService(ILogger<HomeserverProviderService> logger
ServerVersionResponse? serverVersion;
try {
- serverVersion = await (rhs.FederationClient?.GetServerVersionAsync() ?? Task.FromResult<ServerVersionResponse?>(null)!);
+ serverVersion = await serverVersionTask;
}
catch (Exception e) {
logger.LogWarning(e, "Failed to get server version for {homeserver}", homeserver);
diff --git a/LibMatrix/StateEvent.cs b/LibMatrix/StateEvent.cs
index 81ee3fe..58ae7d2 100644
--- a/LibMatrix/StateEvent.cs
+++ b/LibMatrix/StateEvent.cs
@@ -156,13 +156,13 @@ public class StateEventResponse : StateEvent {
public string? Sender { get; set; }
[JsonPropertyName("unsigned")]
- public UnsignedData? Unsigned { get; set; }
+ public JsonObject? Unsigned { get; set; }
[JsonPropertyName("event_id")]
public string? EventId { get; set; }
public class UnsignedData {
- [JsonPropertyName("age")]
+ [JsonPropertyName("age"), JsonNumberHandling(JsonNumberHandling.AllowReadingFromString)]
public ulong? Age { get; set; }
[JsonPropertyName("redacted_because")]
|