Split connection classes.

This commit is contained in:
flash 2024-05-21 20:08:23 +00:00
parent 12e7bd2768
commit 968df2b161
6 changed files with 114 additions and 84 deletions

View file

@ -7,14 +7,14 @@ namespace SharpChat.SockChat.Commands {
public string[] Args { get; }
public SockChatContext Chat { get; }
public UserInfo User { get; }
public SockChatConnectionInfo Connection { get; }
public ConnectionInfo Connection { get; }
public ChannelInfo Channel { get; }
public SockChatClientCommandContext(
string text,
SockChatContext chat,
UserInfo user,
SockChatConnectionInfo connection,
ConnectionInfo connection,
ChannelInfo channel
) {
Chat = chat;

View file

@ -1,45 +1,33 @@
using Fleck;
using SharpChat.SockChat.PacketsS2C;
using System;
using System.Net;
namespace SharpChat.SockChat {
public class SockChatConnectionInfo {
public class SockChatConnectionInfo : ConnectionInfo {
public IWebSocketConnection Socket { get; }
public DateTimeOffset LastPing { get; private set; }
public long UserId { get; private set; } = 0;
public string RemoteAddress { get; }
public ushort RemotePort { get; }
public string RemoteEndPoint { get; }
public SockChatConnectionInfo(IWebSocketConnection sock) {
Socket = sock;
BumpPing();
IPAddress remoteAddr = IPAddress.Parse(sock.ConnectionInfo.ClientIpAddress);
if(IPAddress.IsLoopback(remoteAddr)
&& sock.ConnectionInfo.Headers.ContainsKey("X-Real-IP")
&& IPAddress.TryParse(sock.ConnectionInfo.Headers["X-Real-IP"], out IPAddress? realAddr))
remoteAddr = realAddr;
RemoteAddress = remoteAddr.ToString();
RemotePort = (ushort)sock.ConnectionInfo.ClientPort;
RemoteEndPoint = string.Format(
RemoteAddress.Contains(':') ? "[{0}]:{1}" : "{0}:{1}",
RemoteAddress, RemotePort
);
public SockChatConnectionInfo(
IWebSocketConnection socket,
IPAddress remoteAddr,
ushort remotePort
) : base(remoteAddr, remotePort) {
Socket = socket;
}
// please call this through ConnectionsContext
public void SetUserId(long userId) {
if(UserId > 0)
throw new InvalidOperationException("This connection is already associated with a user.");
public static SockChatConnectionInfo Create(IWebSocketConnection socket) {
IPAddress remoteAddr = IPAddress.Parse(socket.ConnectionInfo.ClientIpAddress);
UserId = userId;
if(IPAddress.IsLoopback(remoteAddr)
&& socket.ConnectionInfo.Headers.ContainsKey("X-Real-IP")
&& IPAddress.TryParse(socket.ConnectionInfo.Headers["X-Real-IP"], out IPAddress? realAddr))
remoteAddr = realAddr;
return new SockChatConnectionInfo(socket, remoteAddr, (ushort)socket.ConnectionInfo.ClientPort);
}
public void Send(string packet) {
if(Socket.IsAvailable)
Socket.Send(packet).Wait();
}
public void Send(SockChatS2CPacket packet) {
@ -48,15 +36,6 @@ namespace SharpChat.SockChat {
Send(data);
}
public void Send(string packet) {
if(Socket.IsAvailable)
Socket.Send(packet).Wait();
}
public void BumpPing() {
LastPing = DateTimeOffset.UtcNow;
}
public void Close(int code) {
Socket.Close(code);
}

View file

@ -12,7 +12,7 @@ namespace SharpChat {
public readonly SemaphoreSlim ContextAccess = new(1, 1);
public ChannelsContext Channels { get; } = new();
public SockChatConnectionsContext Connections { get; } = new();
public ConnectionsContext Connections { get; } = new();
public UsersContext Users { get; } = new();
public IEventStorage Events { get; }
public ChannelsUsersContext ChannelsUsers { get; } = new();
@ -79,10 +79,11 @@ namespace SharpChat {
}
public void Update() {
SockChatConnectionInfo[] timedOut = Connections.GetTimedOut();
foreach(SockChatConnectionInfo conn in timedOut) {
ConnectionInfo[] timedOut = Connections.GetTimedOut();
foreach(ConnectionInfo conn in timedOut) {
Connections.Remove(conn);
conn.Close(1002);
if(conn is SockChatConnectionInfo scConn)
scConn.Close(1002);
Logger.Write($"<{conn.RemoteEndPoint}> Nuked timed out connection from user #{conn.UserId}.");
}
@ -190,10 +191,11 @@ namespace SharpChat {
} else
SendTo(user, new ForceDisconnectS2CPacket());
SockChatConnectionInfo[] conns = Connections.GetUser(user);
foreach(SockChatConnectionInfo conn in conns) {
ConnectionInfo[] conns = Connections.GetUser(user);
foreach(ConnectionInfo conn in conns) {
Connections.Remove(conn);
conn.Close(1000);
if(conn is SockChatConnectionInfo scConn)
scConn.Close(1000);
Logger.Write($"<{conn.RemoteEndPoint}> Nuked connection from banned user #{conn.UserId}.");
}
@ -374,12 +376,18 @@ namespace SharpChat {
public void Send(SockChatS2CPacket packet) {
string data = packet.Pack();
Connections.WithAuthed(conn => conn.Send(data));
Connections.WithAuthed(conn => {
if(conn is SockChatConnectionInfo scConn)
scConn.Send(data);
});
}
public void SendTo(UserInfo user, SockChatS2CPacket packet) {
string data = packet.Pack();
Connections.WithUser(user, conn => conn.Send(data));
Connections.WithUser(user, conn => {
if(conn is SockChatConnectionInfo scConn)
scConn.Send(data);
});
}
public void SendTo(ChannelInfo channel, SockChatS2CPacket packet) {
@ -389,7 +397,10 @@ namespace SharpChat {
public void SendTo(ChannelInfo channel, string packet) {
long[] userIds = ChannelsUsers.GetChannelUserIds(channel);
foreach(long userId in userIds)
Connections.WithUser(userId, conn => conn.Send(packet));
Connections.WithUser(userId, conn => {
if(conn is SockChatConnectionInfo scConn)
scConn.Send(packet);
});
}
public void SendToUserChannels(UserInfo user, SockChatS2CPacket packet) {

View file

@ -124,7 +124,7 @@ namespace SharpChat.SockChat {
return;
}
SockChatConnectionInfo conn = new(sock);
SockChatConnectionInfo conn = SockChatConnectionInfo.Create(sock);
Context.Connections.Add(conn);
sock.OnOpen = () => OnOpen(conn);
@ -136,17 +136,17 @@ namespace SharpChat.SockChat {
Logger.Write("Listening...");
}
private void OnOpen(SockChatConnectionInfo conn) {
private void OnOpen(ConnectionInfo conn) {
Logger.Write($"Connection opened from {conn.RemoteEndPoint}");
Context.SafeUpdate();
}
private void OnError(SockChatConnectionInfo conn, Exception ex) {
private void OnError(ConnectionInfo conn, Exception ex) {
Logger.Write($"<{conn.RemoteEndPoint}> {ex}");
Context.SafeUpdate();
}
private void OnClose(SockChatConnectionInfo conn) {
private void OnClose(ConnectionInfo conn) {
Logger.Write($"Connection closed from {conn.RemoteEndPoint}");
Context.ContextAccess.Wait();
@ -243,7 +243,10 @@ namespace SharpChat.SockChat {
IsDisposed = true;
IsShuttingDown = true;
Context.Connections.WithAll(conn => conn.Close(IsRestarting ? 1012 : 1001));
Context.Connections.WithAll(conn => {
if(conn is SockChatConnectionInfo scConn)
scConn.Close(IsRestarting ? 1012 : 1001);
});
Server?.Dispose();
HttpClient?.Dispose();

View file

@ -0,0 +1,37 @@
using System;
using System.Net;
namespace SharpChat.SockChat {
public abstract class ConnectionInfo {
public DateTimeOffset LastPing { get; private set; }
public long UserId { get; private set; } = 0;
public string RemoteAddress { get; }
public ushort RemotePort { get; }
public string RemoteEndPoint { get; }
public ConnectionInfo(IPAddress remoteAddr, ushort remotePort) {
BumpPing();
RemoteAddress = remoteAddr.ToString();
RemotePort = remotePort;
RemoteEndPoint = string.Format(
RemoteAddress.Contains(':') ? "[{0}]:{1}" : "{0}:{1}",
RemoteAddress, RemotePort
);
}
// please call this through ConnectionsContext
public void SetUserId(long userId) {
if(UserId > 0)
throw new InvalidOperationException("This connection is already associated with a user.");
UserId = userId;
}
public void BumpPing() {
LastPing = DateTimeOffset.UtcNow;
}
}
}

View file

@ -3,30 +3,30 @@ using System.Collections.Generic;
using System.Linq;
namespace SharpChat.SockChat {
public class SockChatConnectionsContext {
public class ConnectionsContext {
public static readonly TimeSpan TimeOut = TimeSpan.FromMinutes(5);
private readonly HashSet<SockChatConnectionInfo> Connections = new();
private readonly HashSet<SockChatConnectionInfo> AuthedConnections = new();
private readonly Dictionary<long, HashSet<SockChatConnectionInfo>> UserConnections = new();
private readonly HashSet<ConnectionInfo> Connections = new();
private readonly HashSet<ConnectionInfo> AuthedConnections = new();
private readonly Dictionary<long, HashSet<ConnectionInfo>> UserConnections = new();
public SockChatConnectionInfo[] All => Connections.ToArray();
public SockChatConnectionInfo[] Authed => AuthedConnections.ToArray();
public ConnectionInfo[] All => Connections.ToArray();
public ConnectionInfo[] Authed => AuthedConnections.ToArray();
public void WithAll(Action<SockChatConnectionInfo> body) {
foreach(SockChatConnectionInfo conn in Connections)
public void WithAll(Action<ConnectionInfo> body) {
foreach(ConnectionInfo conn in Connections)
body(conn);
}
public void WithAuthed(Action<SockChatConnectionInfo> body) {
foreach(SockChatConnectionInfo conn in AuthedConnections)
public void WithAuthed(Action<ConnectionInfo> body) {
foreach(ConnectionInfo conn in AuthedConnections)
body(conn);
}
public SockChatConnectionInfo[] GetTimedOut() {
List<SockChatConnectionInfo> conns = new();
public ConnectionInfo[] GetTimedOut() {
List<ConnectionInfo> conns = new();
foreach(SockChatConnectionInfo conn in Connections)
foreach(ConnectionInfo conn in Connections)
if(DateTimeOffset.UtcNow - conn.LastPing > TimeOut)
conns.Add(conn);
@ -49,33 +49,33 @@ namespace SharpChat.SockChat {
return HasUser(userInfo.UserId);
}
public SockChatConnectionInfo[] GetUser(long userId) {
public ConnectionInfo[] GetUser(long userId) {
if(!UserConnections.ContainsKey(userId))
return Array.Empty<SockChatConnectionInfo>();
return Array.Empty<ConnectionInfo>();
return UserConnections[userId].ToArray();
}
public SockChatConnectionInfo[] GetUser(UserInfo userInfo) {
public ConnectionInfo[] GetUser(UserInfo userInfo) {
return GetUser(userInfo.UserId);
}
public void WithUser(long userId, Action<SockChatConnectionInfo> body) {
public void WithUser(long userId, Action<ConnectionInfo> body) {
if(!UserConnections.ContainsKey(userId))
return;
foreach(SockChatConnectionInfo conn in UserConnections[userId])
foreach(ConnectionInfo conn in UserConnections[userId])
body(conn);
}
public void WithUser(UserInfo userInfo, Action<SockChatConnectionInfo> body) {
public void WithUser(UserInfo userInfo, Action<ConnectionInfo> body) {
WithUser(userInfo.UserId, body);
}
public string[] GetAllRemoteAddresses() {
HashSet<string> addrs = new();
foreach(SockChatConnectionInfo conn in Connections)
foreach(ConnectionInfo conn in Connections)
addrs.Add(conn.RemoteAddress);
return addrs.ToArray();
@ -84,7 +84,7 @@ namespace SharpChat.SockChat {
public string[] GetAuthedRemoteAddresses() {
HashSet<string> addrs = new();
foreach(SockChatConnectionInfo conn in AuthedConnections)
foreach(ConnectionInfo conn in AuthedConnections)
addrs.Add(conn.RemoteAddress);
return addrs.ToArray();
@ -96,7 +96,7 @@ namespace SharpChat.SockChat {
HashSet<string> addrs = new();
foreach(SockChatConnectionInfo conn in UserConnections[userId])
foreach(ConnectionInfo conn in UserConnections[userId])
addrs.Add(conn.RemoteAddress);
return addrs.ToArray();
@ -106,7 +106,7 @@ namespace SharpChat.SockChat {
return GetUserRemoteAddresses(userInfo.UserId);
}
public void Add(SockChatConnectionInfo conn) {
public void Add(ConnectionInfo conn) {
if(Connections.Contains(conn))
return;
@ -122,7 +122,7 @@ namespace SharpChat.SockChat {
}
}
public void Remove(SockChatConnectionInfo conn) {
public void Remove(ConnectionInfo conn) {
if(Connections.Contains(conn))
Connections.Remove(conn);
@ -137,7 +137,7 @@ namespace SharpChat.SockChat {
}
}
public void SetUser(SockChatConnectionInfo conn, long userId) {
public void SetUser(ConnectionInfo conn, long userId) {
if(!Connections.Contains(conn))
return;
@ -169,7 +169,7 @@ namespace SharpChat.SockChat {
}
}
public void SetUser(SockChatConnectionInfo conn, UserInfo userInfo) {
public void SetUser(ConnectionInfo conn, UserInfo userInfo) {
SetUser(conn, userInfo.UserId);
}
}