about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--LibMatrix/Helpers/SyncHelper.cs2
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs36
-rw-r--r--LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs2
-rw-r--r--LibMatrix/Homeservers/RemoteHomeServer.cs48
-rw-r--r--LibMatrix/Responses/LoginResponse.cs4
-rw-r--r--LibMatrix/RoomTypes/GenericRoom.cs35
-rw-r--r--LibMatrix/Services/HomeserverProviderService.cs31
-rw-r--r--LibMatrix/Services/HomeserverResolverService.cs63
-rw-r--r--Tests/LibMatrix.Tests/Abstractions/HomeserverAbstraction.cs4
-rw-r--r--Tests/LibMatrix.Tests/Tests/ResolverTest.cs2
10 files changed, 130 insertions, 97 deletions
diff --git a/LibMatrix/Helpers/SyncHelper.cs b/LibMatrix/Helpers/SyncHelper.cs
index c6c5378..fb7fad2 100644
--- a/LibMatrix/Helpers/SyncHelper.cs
+++ b/LibMatrix/Helpers/SyncHelper.cs
@@ -27,7 +27,7 @@ public class SyncHelper(AuthenticatedHomeserverGeneric homeserver, ILogger? logg
         // Console.WriteLine("Calling: " + url);
         logger?.LogInformation("SyncHelper: Calling: {}", url);
         try {
-            return await homeserver?._httpClient?.GetFromJsonAsync<SyncResponse>(url, cancellationToken: cancellationToken ?? CancellationToken.None)!;
+            return await homeserver?.ClientHttpClient?.GetFromJsonAsync<SyncResponse>(url, cancellationToken: cancellationToken ?? CancellationToken.None)!;
         }
         catch (TaskCanceledException) {
             Console.WriteLine("Sync cancelled!");
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
index e5e4274..c3684a1 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverGeneric.cs
@@ -13,19 +13,29 @@ using LibMatrix.Services;
 
 namespace LibMatrix.Homeservers;
 
-public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken) : RemoteHomeServer(baseUrl) {
+public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken) : RemoteHomeserver(baseUrl) {
     public static async Task<T> Create<T>(string baseUrl, string accessToken) where T : AuthenticatedHomeserverGeneric {
         var instance = Activator.CreateInstance(typeof(T), baseUrl, accessToken) as T
                        ?? throw new InvalidOperationException($"Failed to create instance of {typeof(T).Name}");
-        instance._httpClient = new() {
-            BaseAddress = new Uri(await new HomeserverResolverService().ResolveHomeserverFromWellKnown(baseUrl)
+        var urls = await new HomeserverResolverService().ResolveHomeserverFromWellKnown(baseUrl);
+        
+        instance.ClientHttpClient = new() {
+            BaseAddress = new Uri(urls.client
                                   ?? throw new InvalidOperationException("Failed to resolve homeserver")),
             Timeout = TimeSpan.FromMinutes(15),
             DefaultRequestHeaders = {
                 Authorization = new AuthenticationHeaderValue("Bearer", accessToken)
             }
         };
-        instance.WhoAmI = await instance._httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami");
+        instance.ServerHttpClient = new() {
+            BaseAddress = new Uri(urls.server
+                                  ?? throw new InvalidOperationException("Failed to resolve homeserver")),
+            Timeout = TimeSpan.FromMinutes(15),
+            DefaultRequestHeaders = {
+                Authorization = new AuthenticationHeaderValue("Bearer", accessToken)
+            }
+        };
+        instance.WhoAmI = await instance.ClientHttpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami");
         return instance;
     }
 
@@ -59,7 +69,7 @@ public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken)
     }
 
     public virtual async Task<List<GenericRoom>> GetJoinedRooms() {
-        var roomQuery = await _httpClient.GetAsync("/_matrix/client/v3/joined_rooms");
+        var roomQuery = await ClientHttpClient.GetAsync("/_matrix/client/v3/joined_rooms");
 
         var roomsJson = await roomQuery.Content.ReadFromJsonAsync<JsonElement>();
         var rooms = roomsJson.GetProperty("joined_rooms").EnumerateArray().Select(room => GetRoom(room.GetString()!)).ToList();
@@ -70,7 +80,7 @@ public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken)
     }
 
     public virtual async Task<string> UploadFile(string fileName, Stream fileStream, string contentType = "application/octet-stream") {
-        var res = await _httpClient.PostAsync($"/_matrix/media/v3/upload?filename={fileName}", new StreamContent(fileStream));
+        var res = await ClientHttpClient.PostAsync($"/_matrix/media/v3/upload?filename={fileName}", new StreamContent(fileStream));
         if (!res.IsSuccessStatusCode) {
             Console.WriteLine($"Failed to upload file: {await res.Content.ReadAsStringAsync()}");
             throw new InvalidDataException($"Failed to upload file: {await res.Content.ReadAsStringAsync()}");
@@ -99,7 +109,7 @@ public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken)
         }
 
         creationEvent.CreationContent["creator"] = WhoAmI.UserId;
-        var res = await _httpClient.PostAsJsonAsync("/_matrix/client/v3/createRoom", creationEvent, new JsonSerializerOptions {
+        var res = await ClientHttpClient.PostAsJsonAsync("/_matrix/client/v3/createRoom", creationEvent, new JsonSerializerOptions {
             DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull
         });
         if (!res.IsSuccessStatusCode) {
@@ -116,7 +126,7 @@ public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken)
     }
 
     public virtual async Task Logout() {
-        var res = await _httpClient.PostAsync("/_matrix/client/v3/logout", null);
+        var res = await ClientHttpClient.PostAsync("/_matrix/client/v3/logout", null);
         if (!res.IsSuccessStatusCode) {
             Console.WriteLine($"Failed to logout: {await res.Content.ReadAsStringAsync()}");
             throw new InvalidDataException($"Failed to logout: {await res.Content.ReadAsStringAsync()}");
@@ -153,11 +163,11 @@ public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken)
         // }
         //
         // return await res.Content.ReadFromJsonAsync<T>();
-        return await _httpClient.GetFromJsonAsync<T>($"/_matrix/client/v3/user/{WhoAmI.UserId}/account_data/{key}");
+        return await ClientHttpClient.GetFromJsonAsync<T>($"/_matrix/client/v3/user/{WhoAmI.UserId}/account_data/{key}");
     }
 
     public virtual async Task SetAccountDataAsync(string key, object data) {
-        var res = await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/user/{WhoAmI.UserId}/account_data/{key}", data);
+        var res = await ClientHttpClient.PutAsJsonAsync($"/_matrix/client/v3/user/{WhoAmI.UserId}/account_data/{key}", data);
         if (!res.IsSuccessStatusCode) {
             Console.WriteLine($"Failed to set account data: {await res.Content.ReadAsStringAsync()}");
             throw new InvalidDataException($"Failed to set account data: {await res.Content.ReadAsStringAsync()}");
@@ -169,7 +179,7 @@ public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken)
     public string? ResolveMediaUri(string? mxcUri) {
         if (mxcUri is null) return null;
         if (mxcUri.StartsWith("https://")) return mxcUri;
-        return $"{_httpClient.BaseAddress}/_matrix/media/v3/download/{mxcUri.Replace("mxc://", "")}".Replace("//_matrix", "/_matrix");
+        return $"{ClientHttpClient.BaseAddress}/_matrix/media/v3/download/{mxcUri.Replace("mxc://", "")}".Replace("//_matrix", "/_matrix");
     }
 
     public async Task UpdateProfileAsync(UserProfileResponse? newProfile, bool preserveCustomRoomProfile = true) {
@@ -217,14 +227,14 @@ public class AuthenticatedHomeserverGeneric(string baseUrl, string accessToken)
         }
 
         if (oldProfile.DisplayName != newProfile.DisplayName) {
-            await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/profile/{WhoAmI.UserId}/displayname", new { displayname = newProfile.DisplayName });
+            await ClientHttpClient.PutAsJsonAsync($"/_matrix/client/v3/profile/{WhoAmI.UserId}/displayname", new { displayname = newProfile.DisplayName });
         }
         else {
             Console.WriteLine($"Not updating display name because {oldProfile.DisplayName} == {newProfile.DisplayName}");
         }
 
         if (oldProfile.AvatarUrl != newProfile.AvatarUrl) {
-            await _httpClient.PutAsJsonAsync($"/_matrix/client/v3/profile/{WhoAmI.UserId}/avatar_url", new { avatar_url = newProfile.AvatarUrl });
+            await ClientHttpClient.PutAsJsonAsync($"/_matrix/client/v3/profile/{WhoAmI.UserId}/avatar_url", new { avatar_url = newProfile.AvatarUrl });
         }
         else {
             Console.WriteLine($"Not updating avatar URL because {newProfile.AvatarUrl} == {newProfile.AvatarUrl}");
diff --git a/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs b/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
index 6d60dd7..0910cbe 100644
--- a/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
+++ b/LibMatrix/Homeservers/AuthenticatedHomeserverSynapse.cs
@@ -23,7 +23,7 @@ public class AuthenticatedHomeserverSynapse : AuthenticatedHomeserverGeneric {
 
                 Console.WriteLine($"--- ADMIN Querying Room List with URL: {url} - Already have {i} items... ---");
 
-                res = await _authenticatedHomeserver._httpClient.GetFromJsonAsync<AdminRoomListingResult>(url);
+                res = await _authenticatedHomeserver.ClientHttpClient.GetFromJsonAsync<AdminRoomListingResult>(url);
                 totalRooms ??= res?.TotalRooms;
                 Console.WriteLine(res.ToJson(false));
                 foreach (var room in res.Rooms) {
diff --git a/LibMatrix/Homeservers/RemoteHomeServer.cs b/LibMatrix/Homeservers/RemoteHomeServer.cs
index a8d0326..0757f6e 100644
--- a/LibMatrix/Homeservers/RemoteHomeServer.cs
+++ b/LibMatrix/Homeservers/RemoteHomeServer.cs
@@ -9,19 +9,25 @@ using LibMatrix.Services;
 
 namespace LibMatrix.Homeservers;
 
-public class RemoteHomeServer(string baseUrl) {
-    public static async Task<RemoteHomeServer> Create(string baseUrl) =>
-        new(baseUrl) {
-            _httpClient = new() {
-                BaseAddress = new Uri(await new HomeserverResolverService().ResolveHomeserverFromWellKnown(baseUrl)
-                                      ?? throw new InvalidOperationException("Failed to resolve homeserver")),
+public class RemoteHomeserver(string baseUrl) {
+    public static async Task<RemoteHomeserver> Create(string baseUrl) {
+        var urls = await new HomeserverResolverService().ResolveHomeserverFromWellKnown(baseUrl);
+        return new RemoteHomeserver(baseUrl) {
+            ClientHttpClient = new() {
+                BaseAddress = new Uri(urls.client ?? throw new InvalidOperationException("Failed to resolve homeserver")),
+                Timeout = TimeSpan.FromSeconds(120)
+            },
+            ServerHttpClient = new() {
+                BaseAddress = new Uri(urls.server ?? throw new InvalidOperationException("Failed to resolve homeserver")),
                 Timeout = TimeSpan.FromSeconds(120)
             }
         };
+    }
 
     private Dictionary<string, object> _profileCache { get; set; } = new();
     public string BaseUrl { get; } = baseUrl;
-    public MatrixHttpClient _httpClient { get; set; }
+    public MatrixHttpClient ClientHttpClient { get; set; }
+    public MatrixHttpClient ServerHttpClient { get; set; }
 
     public async Task<UserProfileResponse> GetProfileAsync(string mxid) {
         if (mxid is null) throw new ArgumentNullException(nameof(mxid));
@@ -32,7 +38,7 @@ public class RemoteHomeServer(string baseUrl) {
 
         _profileCache[mxid] = new SemaphoreSlim(1);
 
-        var resp = await _httpClient.GetAsync($"/_matrix/client/v3/profile/{mxid}");
+        var resp = await ClientHttpClient.GetAsync($"/_matrix/client/v3/profile/{mxid}");
         var data = await resp.Content.ReadFromJsonAsync<UserProfileResponse>();
         if (!resp.IsSuccessStatusCode) Console.WriteLine("Profile: " + data);
         _profileCache[mxid] = data;
@@ -41,14 +47,14 @@ public class RemoteHomeServer(string baseUrl) {
     }
 
     public async Task<ClientVersionsResponse> GetClientVersionsAsync() {
-        var resp = await _httpClient.GetAsync($"/_matrix/client/versions");
+        var resp = await ClientHttpClient.GetAsync($"/_matrix/client/versions");
         var data = await resp.Content.ReadFromJsonAsync<ClientVersionsResponse>();
         if (!resp.IsSuccessStatusCode) Console.WriteLine("ClientVersions: " + data);
         return data;
     }
 
     public async Task<AliasResult> ResolveRoomAliasAsync(string alias) {
-        var resp = await _httpClient.GetAsync($"/_matrix/client/v3/directory/room/{alias.Replace("#", "%23")}");
+        var resp = await ClientHttpClient.GetAsync($"/_matrix/client/v3/directory/room/{alias.Replace("#", "%23")}");
         var data = await resp.Content.ReadFromJsonAsync<AliasResult>();
         var text = await resp.Content.ReadAsStringAsync();
         if (!resp.IsSuccessStatusCode) Console.WriteLine("ResolveAlias: " + data.ToJson());
@@ -58,7 +64,7 @@ public class RemoteHomeServer(string baseUrl) {
 #region Authentication
 
     public async Task<LoginResponse> LoginAsync(string username, string password, string? deviceName = null) {
-        var resp = await _httpClient.PostAsJsonAsync("/_matrix/client/r0/login", new {
+        var resp = await ClientHttpClient.PostAsJsonAsync("/_matrix/client/r0/login", new {
             type = "m.login.password",
             identifier = new {
                 type = "m.id.user",
@@ -73,7 +79,7 @@ public class RemoteHomeServer(string baseUrl) {
     }
 
     public async Task<LoginResponse> RegisterAsync(string username, string password, string? deviceName = null) {
-        var resp = await _httpClient.PostAsJsonAsync("/_matrix/client/r0/register", new {
+        var resp = await ClientHttpClient.PostAsJsonAsync("/_matrix/client/r0/register", new {
             kind = "user",
             auth = new {
                 type = "m.login.dummy"
@@ -90,6 +96,24 @@ public class RemoteHomeServer(string baseUrl) {
     }
 
 #endregion
+
+    public async Task<ServerVersionResponse> GetServerVersionAsync() {
+        return await ServerHttpClient.GetFromJsonAsync<ServerVersionResponse>("/_matrix/federation/v1/version");
+    }
+}
+
+public class ServerVersionResponse {
+
+    [JsonPropertyName("server")]
+    public ServerInfo Server { get; set; }
+    
+    public class ServerInfo {
+        [JsonPropertyName("name")]
+        public string Name { get; set; }
+        
+        [JsonPropertyName("version")]
+        public string Version { get; set; }
+    }
 }
 
 public class AliasResult {
diff --git a/LibMatrix/Responses/LoginResponse.cs b/LibMatrix/Responses/LoginResponse.cs
index 07b1601..a9ef3be 100644
--- a/LibMatrix/Responses/LoginResponse.cs
+++ b/LibMatrix/Responses/LoginResponse.cs
@@ -1,3 +1,4 @@
+using System.ComponentModel.DataAnnotations;
 using System.Text.Json.Serialization;
 using LibMatrix.Homeservers;
 using LibMatrix.Services;
@@ -23,7 +24,8 @@ public class LoginResponse {
     public string UserId { get; set; } = null!;
 
     public async Task<AuthenticatedHomeserverGeneric> GetAuthenticatedHomeserver(string? proxy = null) {
-        return await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverGeneric>(proxy ?? await new HomeserverResolverService().ResolveHomeserverFromWellKnown(Homeserver), AccessToken);
+        var urls = await new HomeserverResolverService().ResolveHomeserverFromWellKnown(Homeserver);
+        return await AuthenticatedHomeserverGeneric.Create<AuthenticatedHomeserverGeneric>(proxy ?? urls.client, AccessToken);
     }
 }
 public class LoginRequest {
diff --git a/LibMatrix/RoomTypes/GenericRoom.cs b/LibMatrix/RoomTypes/GenericRoom.cs
index 1398f14..96bcefd 100644
--- a/LibMatrix/RoomTypes/GenericRoom.cs
+++ b/LibMatrix/RoomTypes/GenericRoom.cs
@@ -18,7 +18,7 @@ public class GenericRoom {
         if (string.IsNullOrWhiteSpace(roomId))
             throw new ArgumentException("Room ID cannot be null or whitespace", nameof(roomId));
         Homeserver = homeserver;
-        _httpClient = homeserver._httpClient;
+        _httpClient = homeserver.ClientHttpClient;
         RoomId = roomId;
         if (GetType() != typeof(SpaceRoom))
             AsSpace = new SpaceRoom(homeserver, RoomId);
@@ -83,11 +83,7 @@ public class GenericRoom {
         return res ?? new MessagesResponse();
     }
 
-    // TODO: should we even error handle here?
-    public async Task<string?> GetNameAsync() {
-        var res = await GetStateAsync<RoomNameEventContent>("m.room.name");
-        return res?.Name;
-    }
+    public async Task<string?> GetNameAsync() => (await GetStateAsync<RoomNameEventContent>("m.room.name"))?.Name;
 
     public async Task<RoomIdResponse> JoinAsync(string[]? homeservers = null, string? reason = null) {
         var join_url = $"/_matrix/client/v3/join/{HttpUtility.UrlEncode(RoomId)}";
@@ -100,7 +96,7 @@ public class GenericRoom {
         return await res.Content.ReadFromJsonAsync<RoomIdResponse>() ?? throw new Exception("Failed to join room?");
     }
 
-    // TODO: rewrite (members endpoint?)
+    
     public async IAsyncEnumerable<StateEventResponse> GetMembersAsync(bool joinedOnly = true) {
         // var res = GetFullStateAsync();
         // await foreach (var member in res) {
@@ -108,7 +104,7 @@ public class GenericRoom {
         //     if (joinedOnly && (member.TypedContent as RoomMemberEventContent)?.Membership is not "join") continue;
         //     yield return member;
         // }
-        var res = await _httpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/members?limit=2");
+        var res = await _httpClient.GetAsync($"/_matrix/client/v3/rooms/{RoomId}/members");
         var resText = await res.Content.ReadAsStringAsync();
         var result = await JsonSerializer.DeserializeAsync<ChunkedStateEventResponse>(await res.Content.ReadAsStreamAsync());
         foreach (var resp in result.Chunk) {
@@ -157,6 +153,29 @@ public class GenericRoom {
     public async Task<RoomPowerLevelEventContent?> GetPowerLevelsAsync() =>
         await GetStateAsync<RoomPowerLevelEventContent>("m.room.power_levels");
 
+    public async Task<string> GetNameOrFallbackAsync() {
+        try {
+            return await GetNameAsync();
+        }
+        catch {
+            try {
+                var members = GetMembersAsync();
+                var memberList = new List<string>();
+                int memberCount = 0;
+                await foreach (var member in members)
+                    memberList.Add((member.TypedContent is RoomMemberEventContent memberEvent ? memberEvent.DisplayName : "") ?? "");
+                memberCount = memberList.Count;
+                memberList.RemoveAll(string.IsNullOrWhiteSpace);
+                if (memberList.Count >= 3)
+                    return string.Join(", ", memberList.Take(2)) + " and " + (memberCount - 2) + " others.";
+                return string.Join(", ", memberList);
+            }
+            catch {
+                return RoomId;
+            }
+        }
+    }
+
 #endregion
 
     public async Task ForgetAsync() =>
diff --git a/LibMatrix/Services/HomeserverProviderService.cs b/LibMatrix/Services/HomeserverProviderService.cs
index c7fa5c3..a43f518 100644
--- a/LibMatrix/Services/HomeserverProviderService.cs
+++ b/LibMatrix/Services/HomeserverProviderService.cs
@@ -16,23 +16,26 @@ public class HomeserverProviderService {
     }
 
     private static Dictionary<string, SemaphoreSlim> _authenticatedHomeserverSemaphore = new();
-    private static Dictionary<string, AuthenticatedHomeserverGeneric> _authenticatedHomeServerCache = new();
+    private static Dictionary<string, AuthenticatedHomeserverGeneric> _authenticatedHomeserverCache = new();
 
     private static Dictionary<string, SemaphoreSlim> _remoteHomeserverSemaphore = new();
-    private static Dictionary<string, RemoteHomeServer> _remoteHomeServerCache = new();
+    private static Dictionary<string, RemoteHomeserver> _remoteHomeserverCache = new();
 
-    public async Task<AuthenticatedHomeserverGeneric> GetAuthenticatedWithToken(string homeserver, string accessToken,
-        string? proxy = null) {
+    public async Task<AuthenticatedHomeserverGeneric> GetAuthenticatedWithToken(string homeserver, string accessToken, string? proxy = null) {
         var sem = _authenticatedHomeserverSemaphore.GetOrCreate(homeserver + accessToken, _ => new SemaphoreSlim(1, 1));
         await sem.WaitAsync();
-        lock (_authenticatedHomeServerCache) {
-            if (_authenticatedHomeServerCache.ContainsKey(homeserver + accessToken)) {
+        lock (_authenticatedHomeserverCache) {
+            if (_authenticatedHomeserverCache.ContainsKey(homeserver + accessToken)) {
                 sem.Release();
-                return _authenticatedHomeServerCache[homeserver + accessToken];
+                return _authenticatedHomeserverCache[homeserver + accessToken];
             }
         }
 
-        var domain = proxy ?? await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver);
+        // var domain = proxy ?? (await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver)).client;
+
+        var rhs = await RemoteHomeserver.Create(homeserver);
+        var serverVersion = await rhs.GetServerVersionAsync();
+        
 
         AuthenticatedHomeserverGeneric hs;
         if (true) {
@@ -44,15 +47,15 @@ public class HomeserverProviderService {
 
         // (() => hs.WhoAmI) = (await hs._httpClient.GetFromJsonAsync<WhoAmIResponse>("/_matrix/client/v3/account/whoami"))!;
 
-        lock(_authenticatedHomeServerCache)
-            _authenticatedHomeServerCache[homeserver + accessToken] = hs;
+        lock (_authenticatedHomeserverCache)
+            _authenticatedHomeserverCache[homeserver + accessToken] = hs;
         sem.Release();
 
         return hs;
     }
 
-    public async Task<RemoteHomeServer> GetRemoteHomeserver(string homeserver, string? proxy = null) {
-        var hs = await RemoteHomeServer.Create(proxy ?? await _homeserverResolverService.ResolveHomeserverFromWellKnown(homeserver));
+    public async Task<RemoteHomeserver> GetRemoteHomeserver(string homeserver, string? proxy = null) {
+        var hs = await RemoteHomeserver.Create(proxy ?? homeserver);
         // hs._httpClient.Dispose();
         // hs._httpClient = new MatrixHttpClient { BaseAddress = new Uri(hs.ServerName) };
         // hs._httpClient.Timeout = TimeSpan.FromSeconds(120);
@@ -65,8 +68,8 @@ public class HomeserverProviderService {
             Identifier = new LoginRequest.LoginIdentifier { User = user },
             Password = password
         };
-        var resp = await hs._httpClient.PostAsJsonAsync("/_matrix/client/v3/login", payload);
+        var resp = await hs.ClientHttpClient.PostAsJsonAsync("/_matrix/client/v3/login", payload);
         var data = await resp.Content.ReadFromJsonAsync<LoginResponse>();
         return data!;
     }
-}
+}
\ No newline at end of file
diff --git a/LibMatrix/Services/HomeserverResolverService.cs b/LibMatrix/Services/HomeserverResolverService.cs
index 75545db..06771b0 100644
--- a/LibMatrix/Services/HomeserverResolverService.cs
+++ b/LibMatrix/Services/HomeserverResolverService.cs
@@ -8,42 +8,28 @@ namespace LibMatrix.Services;
 public class HomeserverResolverService(ILogger<HomeserverResolverService>? logger = null) {
     private readonly MatrixHttpClient _httpClient = new();
 
-    private static readonly Dictionary<string, string> _wellKnownCache = new();
+    private static readonly Dictionary<string, (string, string)> _wellKnownCache = new();
     private static readonly Dictionary<string, SemaphoreSlim> _wellKnownSemaphores = new();
 
-    public async Task<string> ResolveHomeserverFromWellKnown(string homeserver) {
+    public async Task<(string client, string server)> ResolveHomeserverFromWellKnown(string homeserver) {
         if (homeserver is null) throw new ArgumentNullException(nameof(homeserver));
-        if(_wellKnownCache.TryGetValue(homeserver, out var known)) return known;
-        logger?.LogInformation("Resolving homeserver: {}", homeserver);
-        var res = await _resolveHomeserverFromWellKnown(homeserver);
-        if (!res.StartsWith("http")) res = "https://" + res;
-        if (res.EndsWith(":443")) res = res[..^4];
-        return res;
-    }
-
-    private async Task<string> _resolveHomeserverFromWellKnown(string homeserver) {
-        if (homeserver is null) throw new ArgumentNullException(nameof(homeserver));
-        var sem = _wellKnownSemaphores.GetOrCreate(homeserver, _ => new SemaphoreSlim(1, 1));
-        if(_wellKnownCache.TryGetValue(homeserver, out var wellKnown)) return wellKnown;
-        await sem.WaitAsync();
+        // if(!_wellKnownSemaphores.ContainsKey(homeserver))
+            // _wellKnownSemaphores[homeserver] = new(1, 1);
+        _wellKnownSemaphores.TryAdd(homeserver, new(1, 1));
+        await _wellKnownSemaphores[homeserver].WaitAsync();
         if (_wellKnownCache.TryGetValue(homeserver, out var known)) {
-            sem.Release();
+            _wellKnownSemaphores[homeserver].Release();
             return known;
         }
-
-        string? result = null;
-        logger?.LogInformation("Attempting to resolve homeserver: {}", homeserver);
-        result ??= await _tryResolveFromClientWellknown(homeserver);
-        result ??= await _tryResolveFromServerWellknown(homeserver);
-        result ??= await _tryCheckIfDomainHasHomeserver(homeserver);
-
-        if (result is null) throw new InvalidDataException($"Failed to resolve homeserver for {homeserver}! Is it online and configured correctly?");
-
-        //success!
-        logger?.LogInformation("Resolved homeserver: {} -> {}", homeserver, result);
-        _wellKnownCache[homeserver] = result;
-        sem.Release();
-        return result;
+        
+        logger?.LogInformation("Resolving homeserver: {}", homeserver);
+        var res = (
+            await _tryResolveFromClientWellknown(homeserver),
+            await _tryResolveFromServerWellknown(homeserver)
+        );
+        _wellKnownCache.Add(homeserver, res!);
+        _wellKnownSemaphores[homeserver].Release();
+        return res;
     }
 
     private async Task<string?> _tryResolveFromClientWellknown(string homeserver) {
@@ -63,6 +49,8 @@ public class HomeserverResolverService(ILogger<HomeserverResolverService>? logge
         if (await _httpClient.CheckSuccessStatus($"{homeserver}/.well-known/matrix/server")) {
             var resp = await _httpClient.GetFromJsonAsync<JsonElement>($"{homeserver}/.well-known/matrix/server");
             var hs = resp.GetProperty("m.server").GetString();
+            if (!hs.StartsWithAnyOf("http://", "https://"))
+                hs = $"https://{hs}";
             return hs;
         }
 
@@ -70,24 +58,11 @@ public class HomeserverResolverService(ILogger<HomeserverResolverService>? logge
         return null;
     }
 
-    private async Task<string?> _tryCheckIfDomainHasHomeserver(string homeserver) {
-        logger?.LogInformation("Checking if {} hosts a homeserver...", homeserver);
-        if (await _httpClient.CheckSuccessStatus($"{homeserver}/_matrix/client/versions"))
-            return homeserver;
-        logger?.LogInformation("No homeserver on shortname...");
-        return null;
-    }
-
-    private async Task<string?> _tryCheckIfSubDomainHasHomeserver(string homeserver, string subdomain) {
-        homeserver = homeserver.Replace("https://", $"https://{subdomain}.");
-        return await _tryCheckIfDomainHasHomeserver(homeserver);
-    }
-
     public async Task<string?> ResolveMediaUri(string homeserver, string mxc) {
         if (homeserver is null) throw new ArgumentNullException(nameof(homeserver));
         if (mxc is null) throw new ArgumentNullException(nameof(mxc));
         if (!mxc.StartsWith("mxc://")) throw new InvalidDataException("mxc must start with mxc://");
-        homeserver = await ResolveHomeserverFromWellKnown(homeserver);
+        homeserver = (await ResolveHomeserverFromWellKnown(homeserver)).client;
         return mxc.Replace("mxc://", $"{homeserver}/_matrix/media/v3/download/");
     }
 }
diff --git a/Tests/LibMatrix.Tests/Abstractions/HomeserverAbstraction.cs b/Tests/LibMatrix.Tests/Abstractions/HomeserverAbstraction.cs
index e23d4f4..8a976a7 100644
--- a/Tests/LibMatrix.Tests/Abstractions/HomeserverAbstraction.cs
+++ b/Tests/LibMatrix.Tests/Abstractions/HomeserverAbstraction.cs
@@ -6,7 +6,7 @@ namespace LibMatrix.Tests.Abstractions;
 
 public static class HomeserverAbstraction {
     public static async Task<AuthenticatedHomeserverGeneric> GetHomeserver() {
-        var rhs = await RemoteHomeServer.Create("https://matrixunittests.rory.gay");
+        var rhs = await RemoteHomeserver.Create("https://matrixunittests.rory.gay");
         // string username = Guid.NewGuid().ToString();
         // string password = Guid.NewGuid().ToString();
         string username = "@f1a2d2d6-1924-421b-91d0-893b347b2a49:matrixunittests.rory.gay";
@@ -45,7 +45,7 @@ public static class HomeserverAbstraction {
     }
 
     public static async Task<AuthenticatedHomeserverGeneric> GetRandomHomeserver() {
-        var rhs = await RemoteHomeServer.Create("https://matrixunittests.rory.gay");
+        var rhs = await RemoteHomeserver.Create("https://matrixunittests.rory.gay");
         LoginResponse reg = await rhs.RegisterAsync(Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), "Unit tests!");
         var hs = await reg.GetAuthenticatedHomeserver("https://matrixunittests.rory.gay");
 
diff --git a/Tests/LibMatrix.Tests/Tests/ResolverTest.cs b/Tests/LibMatrix.Tests/Tests/ResolverTest.cs
index 345508a..cece41b 100644
--- a/Tests/LibMatrix.Tests/Tests/ResolverTest.cs
+++ b/Tests/LibMatrix.Tests/Tests/ResolverTest.cs
@@ -21,7 +21,7 @@ public class ResolverTest : TestBed<TestFixture> {
     public async Task ResolveServer() {
         foreach (var (domain, expected) in _config.ExpectedHomeserverMappings) {
             var server = await _resolver.ResolveHomeserverFromWellKnown(domain);
-            Assert.Equal(expected, server);
+            Assert.Equal(expected, server.client);
         }
     }