Split connection classes.
This commit is contained in:
parent
12e7bd2768
commit
968df2b161
6 changed files with 114 additions and 84 deletions
|
@ -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;
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
37
SharpChatCommon/ConnectionInfo.cs
Normal file
37
SharpChatCommon/ConnectionInfo.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Reference in a new issue