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 string[] Args { get; }
public SockChatContext Chat { get; } public SockChatContext Chat { get; }
public UserInfo User { get; } public UserInfo User { get; }
public SockChatConnectionInfo Connection { get; } public ConnectionInfo Connection { get; }
public ChannelInfo Channel { get; } public ChannelInfo Channel { get; }
public SockChatClientCommandContext( public SockChatClientCommandContext(
string text, string text,
SockChatContext chat, SockChatContext chat,
UserInfo user, UserInfo user,
SockChatConnectionInfo connection, ConnectionInfo connection,
ChannelInfo channel ChannelInfo channel
) { ) {
Chat = chat; Chat = chat;

View file

@ -1,45 +1,33 @@
using Fleck; using Fleck;
using SharpChat.SockChat.PacketsS2C; using SharpChat.SockChat.PacketsS2C;
using System;
using System.Net; using System.Net;
namespace SharpChat.SockChat { namespace SharpChat.SockChat {
public class SockChatConnectionInfo { public class SockChatConnectionInfo : ConnectionInfo {
public IWebSocketConnection Socket { get; } public IWebSocketConnection Socket { get; }
public DateTimeOffset LastPing { get; private set; }
public long UserId { get; private set; } = 0; public SockChatConnectionInfo(
IWebSocketConnection socket,
public string RemoteAddress { get; } IPAddress remoteAddr,
public ushort RemotePort { get; } ushort remotePort
public string RemoteEndPoint { get; } ) : base(remoteAddr, remotePort) {
Socket = socket;
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
);
} }
// please call this through ConnectionsContext public static SockChatConnectionInfo Create(IWebSocketConnection socket) {
public void SetUserId(long userId) { IPAddress remoteAddr = IPAddress.Parse(socket.ConnectionInfo.ClientIpAddress);
if(UserId > 0)
throw new InvalidOperationException("This connection is already associated with a user.");
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) { public void Send(SockChatS2CPacket packet) {
@ -48,15 +36,6 @@ namespace SharpChat.SockChat {
Send(data); 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) { public void Close(int code) {
Socket.Close(code); Socket.Close(code);
} }

View file

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

View file

@ -124,7 +124,7 @@ namespace SharpChat.SockChat {
return; return;
} }
SockChatConnectionInfo conn = new(sock); SockChatConnectionInfo conn = SockChatConnectionInfo.Create(sock);
Context.Connections.Add(conn); Context.Connections.Add(conn);
sock.OnOpen = () => OnOpen(conn); sock.OnOpen = () => OnOpen(conn);
@ -136,17 +136,17 @@ namespace SharpChat.SockChat {
Logger.Write("Listening..."); Logger.Write("Listening...");
} }
private void OnOpen(SockChatConnectionInfo conn) { private void OnOpen(ConnectionInfo conn) {
Logger.Write($"Connection opened from {conn.RemoteEndPoint}"); Logger.Write($"Connection opened from {conn.RemoteEndPoint}");
Context.SafeUpdate(); Context.SafeUpdate();
} }
private void OnError(SockChatConnectionInfo conn, Exception ex) { private void OnError(ConnectionInfo conn, Exception ex) {
Logger.Write($"<{conn.RemoteEndPoint}> {ex}"); Logger.Write($"<{conn.RemoteEndPoint}> {ex}");
Context.SafeUpdate(); Context.SafeUpdate();
} }
private void OnClose(SockChatConnectionInfo conn) { private void OnClose(ConnectionInfo conn) {
Logger.Write($"Connection closed from {conn.RemoteEndPoint}"); Logger.Write($"Connection closed from {conn.RemoteEndPoint}");
Context.ContextAccess.Wait(); Context.ContextAccess.Wait();
@ -243,7 +243,10 @@ namespace SharpChat.SockChat {
IsDisposed = true; IsDisposed = true;
IsShuttingDown = 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(); Server?.Dispose();
HttpClient?.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; using System.Linq;
namespace SharpChat.SockChat { namespace SharpChat.SockChat {
public class SockChatConnectionsContext { public class ConnectionsContext {
public static readonly TimeSpan TimeOut = TimeSpan.FromMinutes(5); public static readonly TimeSpan TimeOut = TimeSpan.FromMinutes(5);
private readonly HashSet<SockChatConnectionInfo> Connections = new(); private readonly HashSet<ConnectionInfo> Connections = new();
private readonly HashSet<SockChatConnectionInfo> AuthedConnections = new(); private readonly HashSet<ConnectionInfo> AuthedConnections = new();
private readonly Dictionary<long, HashSet<SockChatConnectionInfo>> UserConnections = new(); private readonly Dictionary<long, HashSet<ConnectionInfo>> UserConnections = new();
public SockChatConnectionInfo[] All => Connections.ToArray(); public ConnectionInfo[] All => Connections.ToArray();
public SockChatConnectionInfo[] Authed => AuthedConnections.ToArray(); public ConnectionInfo[] Authed => AuthedConnections.ToArray();
public void WithAll(Action<SockChatConnectionInfo> body) { public void WithAll(Action<ConnectionInfo> body) {
foreach(SockChatConnectionInfo conn in Connections) foreach(ConnectionInfo conn in Connections)
body(conn); body(conn);
} }
public void WithAuthed(Action<SockChatConnectionInfo> body) { public void WithAuthed(Action<ConnectionInfo> body) {
foreach(SockChatConnectionInfo conn in AuthedConnections) foreach(ConnectionInfo conn in AuthedConnections)
body(conn); body(conn);
} }
public SockChatConnectionInfo[] GetTimedOut() { public ConnectionInfo[] GetTimedOut() {
List<SockChatConnectionInfo> conns = new(); List<ConnectionInfo> conns = new();
foreach(SockChatConnectionInfo conn in Connections) foreach(ConnectionInfo conn in Connections)
if(DateTimeOffset.UtcNow - conn.LastPing > TimeOut) if(DateTimeOffset.UtcNow - conn.LastPing > TimeOut)
conns.Add(conn); conns.Add(conn);
@ -49,33 +49,33 @@ namespace SharpChat.SockChat {
return HasUser(userInfo.UserId); return HasUser(userInfo.UserId);
} }
public SockChatConnectionInfo[] GetUser(long userId) { public ConnectionInfo[] GetUser(long userId) {
if(!UserConnections.ContainsKey(userId)) if(!UserConnections.ContainsKey(userId))
return Array.Empty<SockChatConnectionInfo>(); return Array.Empty<ConnectionInfo>();
return UserConnections[userId].ToArray(); return UserConnections[userId].ToArray();
} }
public SockChatConnectionInfo[] GetUser(UserInfo userInfo) { public ConnectionInfo[] GetUser(UserInfo userInfo) {
return GetUser(userInfo.UserId); return GetUser(userInfo.UserId);
} }
public void WithUser(long userId, Action<SockChatConnectionInfo> body) { public void WithUser(long userId, Action<ConnectionInfo> body) {
if(!UserConnections.ContainsKey(userId)) if(!UserConnections.ContainsKey(userId))
return; return;
foreach(SockChatConnectionInfo conn in UserConnections[userId]) foreach(ConnectionInfo conn in UserConnections[userId])
body(conn); body(conn);
} }
public void WithUser(UserInfo userInfo, Action<SockChatConnectionInfo> body) { public void WithUser(UserInfo userInfo, Action<ConnectionInfo> body) {
WithUser(userInfo.UserId, body); WithUser(userInfo.UserId, body);
} }
public string[] GetAllRemoteAddresses() { public string[] GetAllRemoteAddresses() {
HashSet<string> addrs = new(); HashSet<string> addrs = new();
foreach(SockChatConnectionInfo conn in Connections) foreach(ConnectionInfo conn in Connections)
addrs.Add(conn.RemoteAddress); addrs.Add(conn.RemoteAddress);
return addrs.ToArray(); return addrs.ToArray();
@ -84,7 +84,7 @@ namespace SharpChat.SockChat {
public string[] GetAuthedRemoteAddresses() { public string[] GetAuthedRemoteAddresses() {
HashSet<string> addrs = new(); HashSet<string> addrs = new();
foreach(SockChatConnectionInfo conn in AuthedConnections) foreach(ConnectionInfo conn in AuthedConnections)
addrs.Add(conn.RemoteAddress); addrs.Add(conn.RemoteAddress);
return addrs.ToArray(); return addrs.ToArray();
@ -96,7 +96,7 @@ namespace SharpChat.SockChat {
HashSet<string> addrs = new(); HashSet<string> addrs = new();
foreach(SockChatConnectionInfo conn in UserConnections[userId]) foreach(ConnectionInfo conn in UserConnections[userId])
addrs.Add(conn.RemoteAddress); addrs.Add(conn.RemoteAddress);
return addrs.ToArray(); return addrs.ToArray();
@ -106,7 +106,7 @@ namespace SharpChat.SockChat {
return GetUserRemoteAddresses(userInfo.UserId); return GetUserRemoteAddresses(userInfo.UserId);
} }
public void Add(SockChatConnectionInfo conn) { public void Add(ConnectionInfo conn) {
if(Connections.Contains(conn)) if(Connections.Contains(conn))
return; return;
@ -122,7 +122,7 @@ namespace SharpChat.SockChat {
} }
} }
public void Remove(SockChatConnectionInfo conn) { public void Remove(ConnectionInfo conn) {
if(Connections.Contains(conn)) if(Connections.Contains(conn))
Connections.Remove(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)) if(!Connections.Contains(conn))
return; 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); SetUser(conn, userInfo.UserId);
} }
} }