sockscape/server/Libraries/Kneesocks/Connection.cs

362 lines
12 KiB
C#
Raw Normal View History

2017-04-21 21:04:03 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
2017-04-23 04:55:29 +00:00
using System.Net.Sockets;
2017-04-21 21:04:03 +00:00
using System.Text;
using System.Threading.Tasks;
2017-05-09 12:22:04 +00:00
using Square;
2017-05-11 21:03:28 +00:00
using System.IO;
2017-04-21 21:04:03 +00:00
namespace Kneesocks {
2017-05-04 12:21:27 +00:00
public class Connection {
2017-05-12 21:05:18 +00:00
private bool Initialized = false;
private UInt64? _Id = null;
2017-05-04 12:21:27 +00:00
public UInt64 Id {
get {
if(_Id == null)
throw new ArgumentNullException();
else
return (UInt64)_Id;
}
set {
if(_Id == null)
_Id = value;
}
}
2017-05-12 21:05:18 +00:00
internal bool IsIdNull {
get {
return _Id == null;
}
}
2017-05-04 12:21:27 +00:00
2017-05-12 21:05:18 +00:00
private TcpClient Socket = null;
private NetworkStream Stream = null;
2017-05-05 21:05:52 +00:00
ReadBuffer Buffer;
2017-05-09 21:08:39 +00:00
private byte[] FirstTwoBytes = null;
private int ExtraHeaderSize = 0;
2017-05-09 21:08:39 +00:00
private byte[] FrameHeader = null;
2017-05-11 21:03:28 +00:00
private List<Frame> ReceiveFrameBuffer = new List<Frame>();
private List<Frame> SendFrameBuffer = new List<Frame>();
2017-05-12 21:05:18 +00:00
private const int MaximumSendFrameSize = 0xFFFFF;
2017-05-11 21:03:28 +00:00
2017-05-12 21:05:18 +00:00
protected const int PingInterval = 30;
protected const int TimeoutInterval = 120;
private byte[] PingData = Encoding.ASCII.GetBytes("woomy!");
2017-05-11 21:03:28 +00:00
private DateTime LastPing;
2017-05-12 21:05:18 +00:00
private bool AwaitingPingResponse = false;
private TimeSpan TimeSinceLastPing {
get {
return DateTime.UtcNow - LastPing;
}
}
2017-05-12 21:05:18 +00:00
internal bool OutsidePool = false;
public bool Disconnected { get; private set; } = false;
public string DisconnectReason { get; private set; } = null;
public bool Handshaked { get; private set; } = false;
2017-05-08 21:06:17 +00:00
public Handshake ClientHandshake { get; private set; } = null;
2017-04-23 04:55:29 +00:00
2017-05-12 21:05:18 +00:00
public void Initialize(TcpClient sock) {
if(Initialized)
return;
2017-04-23 04:55:29 +00:00
Socket = sock;
Socket.ReceiveTimeout = 1;
Stream = sock.GetStream();
2017-05-05 21:05:52 +00:00
Buffer = new ReadBuffer(Stream);
2017-05-12 21:05:18 +00:00
Initialized = true;
2017-04-23 04:55:29 +00:00
}
2017-05-12 21:05:18 +00:00
public void Initialize(UInt64 id, TcpClient sock) {
if(Initialized)
return;
Initialize(sock);
2017-05-04 12:21:27 +00:00
Id = id;
2017-05-12 21:05:18 +00:00
Initialized = true;
2017-05-04 12:21:27 +00:00
}
2017-05-12 21:05:18 +00:00
public void Initialize(Connection conn, bool preserveId = false) {
if(Initialized)
return;
2017-05-05 21:05:52 +00:00
if(preserveId)
_Id = conn._Id;
Socket = conn.Socket;
2017-05-05 21:05:52 +00:00
Stream = conn.Stream;
Buffer = conn.Buffer;
2017-05-09 21:08:39 +00:00
FirstTwoBytes = conn.FirstTwoBytes;
ExtraHeaderSize = conn.ExtraHeaderSize;
FrameHeader = conn.FrameHeader;
2017-05-11 21:03:28 +00:00
ReceiveFrameBuffer = conn.ReceiveFrameBuffer;
2017-05-12 21:05:18 +00:00
LastPing = conn.LastPing;
Disconnected = conn.Disconnected;
DisconnectReason = conn.DisconnectReason;
Handshaked = conn.Handshaked;
2017-05-08 21:06:17 +00:00
ClientHandshake = conn.ClientHandshake;
2017-05-12 21:05:18 +00:00
Initialized = true;
2017-05-05 21:05:52 +00:00
}
2017-05-11 21:03:28 +00:00
private void _Send(byte[] message, bool isFinal = true, bool singleFrame = false, bool first = false) {
2017-05-12 21:05:18 +00:00
int frameCount = singleFrame ? 0 : (message.Length / MaximumSendFrameSize);
2017-05-11 21:03:28 +00:00
for(var i = 0; i <= frameCount; ++i) {
SendFrameBuffer.Add(new Frame {
IsFinal = (i == frameCount && isFinal) ? true : false,
IsMasked = false,
Opcode = (i == 0 || (singleFrame && first)) ? Frame.kOpcode.BinaryFrame : Frame.kOpcode.Continuation,
2017-05-12 21:05:18 +00:00
Content = message.Subset(i * (MaximumSendFrameSize + 1), MaximumSendFrameSize)
2017-05-11 21:03:28 +00:00
});
}
}
public void Send(byte[] message) {
lock(SendFrameBuffer) {
_Send(message);
}
}
private void _Send(Stream stream, bool startingFrame = true) {
if(!Socket.Connected)
return;
bool firstRead = true;
2017-05-12 21:05:18 +00:00
byte[] byteBuffer = new byte[MaximumSendFrameSize];
2017-05-11 21:03:28 +00:00
while(true) {
2017-05-12 21:05:18 +00:00
var bytesRead = stream.Read(byteBuffer, 0, MaximumSendFrameSize);
2017-05-11 21:03:28 +00:00
if(stream.Position == stream.Length) {
2017-05-12 21:05:18 +00:00
_Send(bytesRead == MaximumSendFrameSize ? byteBuffer : byteBuffer.Take(bytesRead).ToArray(), true, true, firstRead);
2017-05-11 21:03:28 +00:00
return;
} else {
2017-05-12 21:05:18 +00:00
_Send(bytesRead == MaximumSendFrameSize ? byteBuffer : byteBuffer.Take(bytesRead).ToArray(), false, true, firstRead);
2017-05-11 21:03:28 +00:00
}
firstRead = false;
}
}
public void Send(Stream stream) {
lock(SendFrameBuffer) {
_Send(stream);
}
}
public void Send(byte[] preamble, Stream stream) {
lock(SendFrameBuffer) {
_Send(preamble, false);
_Send(stream, false);
}
}
2017-05-09 21:08:39 +00:00
2017-05-11 21:03:28 +00:00
private void ReadIfNotNull(ref byte[] buffer, int length) {
buffer = buffer == null ? Buffer.AttemptRead(length)
: buffer;
}
2017-05-11 21:03:28 +00:00
private void ReadIfNotNull(ref byte[] buffer, string terminator) {
buffer = buffer == null ? Buffer.AttemptRead(terminator)
: buffer;
}
2017-05-12 21:05:18 +00:00
internal void Parse() {
2017-05-11 21:03:28 +00:00
if(Handshaked) {
2017-05-12 21:05:18 +00:00
if(!Buffer.IsReading) {
if(TimeSinceLastPing.Seconds > TimeoutInterval) {
Disconnect(Frame.kClosingReason.Normal, "Ping response timed out.");
} else if(TimeSinceLastPing.Seconds > PingInterval && !AwaitingPingResponse) {
var frameBytes = new Frame {
IsFinal = true,
IsMasked = false,
Opcode = Frame.kOpcode.Ping,
Content = PingData
}.GetBytes();
Stream.Write(frameBytes, 0, frameBytes.Length);
AwaitingPingResponse = true;
}
}
2017-05-11 21:03:28 +00:00
lock(SendFrameBuffer) {
if(SendFrameBuffer.Count > 0) {
foreach(var frame in SendFrameBuffer) {
var frameBytes = frame.GetBytes();
Stream.Write(frameBytes, 0, frameBytes.Length);
}
SendFrameBuffer = new List<Frame>();
}
}
}
2017-05-11 21:03:28 +00:00
OnParse();
2017-05-05 21:05:52 +00:00
byte[] readBuffer = null;
if(Buffer.IsReading) {
readBuffer = Buffer.AttemptRead();
2017-05-08 21:06:17 +00:00
if(readBuffer == null) {
2017-05-11 21:03:28 +00:00
if((!Handshaked || (Handshaked && FirstTwoBytes != null)) && Buffer.ElapsedReadTime.Seconds > (Handshaked ? 300 : 30))
2017-05-08 21:06:17 +00:00
Disconnect(Frame.kClosingReason.ProtocolError, "Timed out waiting for a full response");
return;
2017-05-08 21:06:17 +00:00
}
2017-05-05 21:05:52 +00:00
}
2017-05-04 21:09:38 +00:00
if(!Handshaked) {
ReadIfNotNull(ref readBuffer, "\r\n\r\n");
if(readBuffer == null)
return;
2017-05-08 21:06:17 +00:00
try {
Handshake request = new Handshake(Encoding.ASCII.GetString(readBuffer));
var response = Handshake.AcceptRequest(request).ToBytes();
Stream.Write(response, 0, response.Length);
ClientHandshake = request;
Handshaked = true;
2017-05-12 21:05:18 +00:00
2017-05-11 21:03:28 +00:00
LastPing = DateTime.UtcNow;
2017-05-08 21:06:17 +00:00
} catch(Exception e) {
Disconnect(Frame.kClosingReason.ProtocolError, e.Message);
return;
2017-05-08 21:06:17 +00:00
}
2017-05-04 21:09:38 +00:00
2017-05-08 21:06:17 +00:00
OnOpen();
return;
2017-05-04 21:09:38 +00:00
}
2017-05-08 21:06:17 +00:00
2017-05-09 21:08:39 +00:00
if(FirstTwoBytes == null) {
ReadIfNotNull(ref readBuffer, 2);
if(readBuffer == null)
return;
2017-05-09 21:08:39 +00:00
FirstTwoBytes = readBuffer;
ExtraHeaderSize = Frame.HeaderLengthFromBytes(FirstTwoBytes) - 2;
2017-05-09 21:08:39 +00:00
readBuffer = null;
}
if(FrameHeader == null) {
if(ExtraHeaderSize == 0)
FrameHeader = FirstTwoBytes;
else {
ReadIfNotNull(ref readBuffer, ExtraHeaderSize);
if(readBuffer == null)
return;
2017-05-09 21:08:39 +00:00
FrameHeader = FirstTwoBytes.Concat(readBuffer).ToArray();
}
readBuffer = null;
}
if(FrameHeader != null) {
Frame tempFrame;
2017-05-09 21:08:39 +00:00
if(readBuffer == null) {
try {
tempFrame = Frame.HeaderFromBytes(FrameHeader);
} catch(Exception e) {
Disconnect(Frame.kClosingReason.ProtocolError, e.Message);
return;
}
2017-05-09 21:08:39 +00:00
readBuffer = Buffer.AttemptRead(tempFrame.BodyLength);
if(readBuffer == null)
return;
}
try {
tempFrame = Frame.FromBytes(FrameHeader.Concat(readBuffer).ToArray());
} catch(Exception e) {
Disconnect(Frame.kClosingReason.ProtocolError, e.Message);
return;
}
2017-05-11 21:03:28 +00:00
ReceiveFrameBuffer.Add(tempFrame);
FirstTwoBytes = null;
ExtraHeaderSize = 0;
FrameHeader = null;
if(tempFrame.IsFinal) {
2017-05-11 21:03:28 +00:00
switch(tempFrame.Opcode) {
case Frame.kOpcode.Ping:
2017-05-12 21:05:18 +00:00
LastPing = DateTime.UtcNow;
AwaitingPingResponse = false;
2017-05-11 21:03:28 +00:00
tempFrame.Opcode = Frame.kOpcode.Pong;
var pingBuffer = tempFrame.GetBytes();
Stream.Write(pingBuffer, 0, pingBuffer.Length);
break;
case Frame.kOpcode.Pong:
2017-05-12 21:05:18 +00:00
LastPing = DateTime.UtcNow;
AwaitingPingResponse = false;
2017-05-11 21:03:28 +00:00
break;
case Frame.kOpcode.Close:
Disconnect(Frame.kClosingReason.Normal, "Connection closed.");
break;
2017-05-12 21:05:18 +00:00
case Frame.kOpcode.BinaryFrame:
case Frame.kOpcode.TextFrame:
case Frame.kOpcode.Continuation:
byte[] byteBuffer = new byte[0];
foreach(var frame in ReceiveFrameBuffer)
byteBuffer = byteBuffer.Concat(frame.Content).ToArray();
2017-05-12 21:05:18 +00:00
ReceiveFrameBuffer = new List<Frame>();
OnReceive(byteBuffer);
break;
}
}
}
2017-05-04 21:09:38 +00:00
}
2017-05-12 21:05:18 +00:00
public void RemoveFromPool() {
OutsidePool = true;
}
public void Disconnect(string reason = null) {
Disconnect(Frame.kClosingReason.Normal, reason);
}
public void Disconnect(Frame.kClosingReason status, string reason = null) {
Disconnected = true;
DisconnectReason = reason;
if(Socket.Connected) {
Socket.SendTimeout = 1000;
var raw = Handshaked ? Frame.Closing(status, reason).GetBytes()
2017-05-11 21:03:28 +00:00
: Handshake.DenyRequest(message: reason).ToString().GetBytes();
Stream.Write(raw, 0, raw.Length);
Socket.Close();
}
OnClose();
}
// called after the client successfully handshakes
2017-05-05 21:05:52 +00:00
protected virtual void OnOpen() { }
2017-05-04 12:21:27 +00:00
// called when the thread manager iterates through
// the thread list and stops on this thread
2017-05-05 21:05:52 +00:00
protected virtual void OnParse() { }
// called when data has been received
2017-05-05 21:05:52 +00:00
protected virtual void OnReceive(byte[] data) { }
2017-04-23 04:55:29 +00:00
// called when the connection is disconnected
2017-05-05 21:05:52 +00:00
protected virtual void OnClose() { }
2017-04-21 21:04:03 +00:00
}
}