First commit.
This commit is contained in:
336
src/IO/Socket/BaseTCP.cpp
Normal file
336
src/IO/Socket/BaseTCP.cpp
Normal file
@@ -0,0 +1,336 @@
|
||||
#include "../../../include/IO/Socket/BaseTCP.h"
|
||||
#include "../../../include/Log.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
BaseTCP::BaseTCP()
|
||||
: addrType(AddrType::IPV6), localPort(0), remotePort(0), connection(false), bound(false), listening(false),
|
||||
connected(false)
|
||||
{
|
||||
}
|
||||
|
||||
BaseTCP::BaseTCP(const AddrType addrType)
|
||||
: addrType(addrType), localPort(0), remotePort(0), connection(false), bound(false), listening(false),
|
||||
connected(false)
|
||||
{
|
||||
}
|
||||
|
||||
BaseTCP::BaseTCP(BaseTCP&& tcp) noexcept
|
||||
: addrType(tcp.addrType), localAddr(std::move(tcp.localAddr)), localPort(tcp.localPort),
|
||||
remoteHostName(std::move(tcp.remoteHostName)), remoteAddr(std::move(tcp.remoteAddr)), remotePort(tcp.remotePort),
|
||||
connection(tcp.connection), bound(tcp.bound), listening(tcp.listening), connected(tcp.connected)
|
||||
{
|
||||
}
|
||||
|
||||
BaseTCP::BaseTCP(const BaseTCP& tcp)
|
||||
: addrType(tcp.addrType), localPort(0), remotePort(0), connection(false), bound(false), listening(false),
|
||||
connected(false)
|
||||
{
|
||||
}
|
||||
|
||||
BaseTCP& BaseTCP::operator=(BaseTCP&& tcp) noexcept
|
||||
{
|
||||
if (this == &tcp)
|
||||
return *this;
|
||||
|
||||
addrType = tcp.addrType;
|
||||
localAddr = std::move(tcp.localAddr);
|
||||
localPort = tcp.localPort;
|
||||
remoteHostName = std::move(tcp.remoteHostName);
|
||||
remoteAddr = std::move(tcp.remoteAddr);
|
||||
remotePort = tcp.remotePort;
|
||||
connection = tcp.connection;
|
||||
bound = tcp.bound;
|
||||
listening = tcp.listening;
|
||||
connected = tcp.connected;
|
||||
|
||||
tcp.addrType = AddrType::IPV6;
|
||||
tcp.localPort = 0;
|
||||
tcp.remotePort = 0;
|
||||
tcp.connection = false;
|
||||
tcp.bound = false;
|
||||
tcp.listening = false;
|
||||
tcp.connected = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseTCP& BaseTCP::operator=(const BaseTCP& tcp)
|
||||
{
|
||||
if (this == &tcp)
|
||||
return *this;
|
||||
|
||||
addrType = tcp.addrType;
|
||||
localAddr = Str_8();
|
||||
localPort = 0;
|
||||
remoteHostName = Str_8();
|
||||
remoteAddr = Str_8();
|
||||
remotePort = 0;
|
||||
connection = false;
|
||||
bound = false;
|
||||
listening = false;
|
||||
connected = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void BaseTCP::SendStr(const Str_8& str)
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
UInt_64 offset = 0;
|
||||
UInt_64 size = str.Size(true);
|
||||
|
||||
while (offset < size)
|
||||
{
|
||||
UInt_64 sent = Send((Byte*)&str[offset], size - offset);
|
||||
if (!sent)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to send data.");
|
||||
return;
|
||||
}
|
||||
|
||||
offset += sent;
|
||||
}
|
||||
}
|
||||
|
||||
void BaseTCP::SendRes(const Response& res)
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
SendStr(res.FormResult());
|
||||
}
|
||||
|
||||
void BaseTCP::SendReq(Request& req)
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
req.AddToHeader("Host", remoteHostName);
|
||||
|
||||
SendStr(req.FormResult());
|
||||
}
|
||||
|
||||
Response BaseTCP::RecvRes()
|
||||
{
|
||||
if (!IsValid())
|
||||
return {};
|
||||
|
||||
Str_8 header = RecvHeader();
|
||||
if (!header.Size())
|
||||
return {};
|
||||
|
||||
Response response(header);
|
||||
|
||||
Str_8 encoding = response.GetHeader("Transfer-Encoding");
|
||||
if (!encoding.Size())
|
||||
{
|
||||
int bodySize = response.GetHeader("content-length").ToDecimal<int>();
|
||||
if (!bodySize)
|
||||
return response;
|
||||
|
||||
response.SetBody(RecvBody(bodySize));
|
||||
}
|
||||
else if (encoding == "chunked")
|
||||
{
|
||||
Str_8 body;
|
||||
UInt_64 chunkSize = RecvChunkSize();
|
||||
while (chunkSize)
|
||||
{
|
||||
body += RecvChunk(chunkSize);
|
||||
chunkSize = RecvChunkSize();
|
||||
}
|
||||
|
||||
response.SetBody(body);
|
||||
}
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
Request BaseTCP::RecvReq()
|
||||
{
|
||||
if (!IsValid())
|
||||
return {};
|
||||
|
||||
Str_8 header = RecvHeader();
|
||||
if (!header.Size())
|
||||
return {};
|
||||
|
||||
Request request(header);
|
||||
|
||||
if (request.GetVerb() == Verb::GET)
|
||||
return request;
|
||||
|
||||
Str_8 encoding = request.GetHeader("Transfer-Encoding");
|
||||
if (!encoding.Size())
|
||||
{
|
||||
int bodySize = request.GetHeader("Content-Length").ToDecimal<int>();
|
||||
if (!bodySize)
|
||||
return request;
|
||||
|
||||
request.SetBody(RecvBody(bodySize));
|
||||
}
|
||||
else if (encoding == "chunked")
|
||||
{
|
||||
Str_8 body;
|
||||
UInt_64 chunkSize = RecvChunkSize();
|
||||
while (chunkSize)
|
||||
{
|
||||
body += RecvChunk(chunkSize);
|
||||
chunkSize = RecvChunkSize();
|
||||
}
|
||||
|
||||
request.SetBody(body);
|
||||
}
|
||||
|
||||
return request;
|
||||
}
|
||||
|
||||
AddrType BaseTCP::GetAddressType() const
|
||||
{
|
||||
return addrType;
|
||||
}
|
||||
|
||||
Str_8 BaseTCP::GetLocalAddress() const
|
||||
{
|
||||
return localAddr;
|
||||
}
|
||||
|
||||
unsigned short BaseTCP::GetLocalPort() const
|
||||
{
|
||||
return localPort;
|
||||
}
|
||||
|
||||
Str_8 BaseTCP::GetRemoteAddress() const
|
||||
{
|
||||
return remoteAddr;
|
||||
}
|
||||
|
||||
unsigned short BaseTCP::GetRemotePort() const
|
||||
{
|
||||
return remotePort;
|
||||
}
|
||||
|
||||
bool BaseTCP::IsConnection() const
|
||||
{
|
||||
return connection;
|
||||
}
|
||||
|
||||
bool BaseTCP::IsBound() const
|
||||
{
|
||||
return bound;
|
||||
}
|
||||
|
||||
bool BaseTCP::IsListening() const
|
||||
{
|
||||
return listening;
|
||||
}
|
||||
|
||||
bool BaseTCP::IsConnected() const
|
||||
{
|
||||
return connected;
|
||||
}
|
||||
|
||||
Str_8 BaseTCP::RecvHeader()
|
||||
{
|
||||
Byte buffer[MaxHeaderSize];
|
||||
|
||||
UInt_64 offset = 0;
|
||||
|
||||
while (true)
|
||||
{
|
||||
UInt_64 received = Receive(&buffer[offset], 1);
|
||||
if (!received)
|
||||
{
|
||||
return {};
|
||||
}
|
||||
else if (buffer[offset] == '\n' && offset - 3 && buffer[offset - 1] == '\r' && buffer[offset - 2] == '\n' && buffer[offset - 3] == '\r')
|
||||
{
|
||||
offset -= 3;
|
||||
break;
|
||||
}
|
||||
|
||||
offset += received;
|
||||
}
|
||||
|
||||
return {(Char_8*)buffer, (UInt_64)offset};
|
||||
}
|
||||
|
||||
Str_8 BaseTCP::RecvBody(const UInt_64 contentLength)
|
||||
{
|
||||
Str_8 buffer(contentLength);
|
||||
|
||||
UInt_64 offset = 0;
|
||||
|
||||
while (offset < contentLength)
|
||||
{
|
||||
UInt_64 received = Receive((Byte*)&buffer[offset], contentLength - offset);
|
||||
if (!received)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to receive data.");
|
||||
return {};
|
||||
}
|
||||
|
||||
offset += received;
|
||||
}
|
||||
|
||||
return buffer;
|
||||
}
|
||||
|
||||
UInt_64 BaseTCP::RecvChunkSize()
|
||||
{
|
||||
Str_8 hexSize(10);
|
||||
|
||||
UInt_64 offset = 0;
|
||||
|
||||
bool cr = false;
|
||||
|
||||
while (true)
|
||||
{
|
||||
UInt_64 received = Receive((Byte*)&hexSize[offset], 1);
|
||||
if (!received)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to receive data.");
|
||||
return 0;
|
||||
}
|
||||
else if (hexSize[offset] == '\r')
|
||||
cr = true;
|
||||
else if (cr && hexSize[offset] == '\n')
|
||||
break;
|
||||
|
||||
++offset;
|
||||
}
|
||||
|
||||
if (hexSize[0] == '0')
|
||||
Receive((Byte*)&hexSize[offset + 1], 2);
|
||||
|
||||
hexSize.Resize(offset - 1);
|
||||
|
||||
return hexSize.HexToNum<UInt_64>();
|
||||
}
|
||||
|
||||
Str_8 BaseTCP::RecvChunk(const UInt_64 chunkSize)
|
||||
{
|
||||
Str_8 buffer(chunkSize + 2);
|
||||
|
||||
UInt_64 offset = 0;
|
||||
|
||||
while (offset < chunkSize + 2)
|
||||
{
|
||||
UInt_64 received = Receive((Byte*)&buffer[offset], chunkSize + 2 - offset);
|
||||
if (!received)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to receive data.");
|
||||
return {};
|
||||
}
|
||||
|
||||
offset += received;
|
||||
}
|
||||
|
||||
buffer.Resize(offset - 2);
|
||||
|
||||
return buffer;
|
||||
}
|
||||
}
|
77
src/IO/Socket/BaseUDP.cpp
Normal file
77
src/IO/Socket/BaseUDP.cpp
Normal file
@@ -0,0 +1,77 @@
|
||||
#include "../../../include/IO/Socket/BaseUDP.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
BaseUDP::BaseUDP()
|
||||
: addrType(AddrType::IPV6), port(0), bound(false)
|
||||
{
|
||||
}
|
||||
|
||||
BaseUDP::BaseUDP(const AddrType addrType)
|
||||
: addrType(addrType), port(0), bound(false)
|
||||
{
|
||||
}
|
||||
|
||||
BaseUDP::BaseUDP(BaseUDP&& udp) noexcept
|
||||
: addrType(udp.addrType), address(std::move(udp.address)), port(udp.port), bound(true)
|
||||
{
|
||||
udp.addrType = AddrType::IPV6;
|
||||
udp.port = 0;
|
||||
udp.bound = false;
|
||||
}
|
||||
|
||||
BaseUDP::BaseUDP(const BaseUDP& udp)
|
||||
: addrType(udp.addrType), address(udp.address), port(udp.port), bound(false)
|
||||
{
|
||||
}
|
||||
|
||||
BaseUDP& BaseUDP::operator=(BaseUDP&& udp) noexcept
|
||||
{
|
||||
if (this == &udp)
|
||||
return *this;
|
||||
|
||||
addrType = udp.addrType;
|
||||
address = std::move(udp.address);
|
||||
port = udp.port;
|
||||
bound = udp.bound;
|
||||
|
||||
udp.addrType = AddrType::IPV6;
|
||||
udp.port = 0;
|
||||
udp.bound = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
BaseUDP& BaseUDP::operator=(const BaseUDP& udp)
|
||||
{
|
||||
if (this == &udp)
|
||||
return *this;
|
||||
|
||||
addrType = udp.addrType;
|
||||
address = udp.address;
|
||||
port = udp.port;
|
||||
bound = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool BaseUDP::IsBound() const
|
||||
{
|
||||
return bound;
|
||||
}
|
||||
|
||||
AddrType BaseUDP::GetAddressType() const
|
||||
{
|
||||
return addrType;
|
||||
}
|
||||
|
||||
Str_8 BaseUDP::GetLocalAddress() const
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
UInt_16 BaseUDP::GetLocalPort() const
|
||||
{
|
||||
return port;
|
||||
}
|
||||
}
|
1316
src/IO/Socket/Comms.cpp
Normal file
1316
src/IO/Socket/Comms.cpp
Normal file
File diff suppressed because it is too large
Load Diff
84
src/IO/Socket/CommsSystem.cpp
Normal file
84
src/IO/Socket/CommsSystem.cpp
Normal file
@@ -0,0 +1,84 @@
|
||||
#include "../../../include/IO/Socket/CommsSystem.h"
|
||||
#include "../../../include/IO/Socket/Comms.h"
|
||||
#include "../../../include/IO/Socket/Endpoint.h"
|
||||
#include "../../../include/IO/Socket/Operation.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
CommsSystem::~CommsSystem()
|
||||
{
|
||||
for (UInt_64 i = 0; i < ops.Size(); ++i)
|
||||
delete ops[i];
|
||||
ops.Clear();
|
||||
}
|
||||
|
||||
CommsSystem::CommsSystem()
|
||||
: hashId(0)
|
||||
{
|
||||
}
|
||||
|
||||
CommsSystem::CommsSystem(const Str_8& id)
|
||||
: id(id), hashId(id.Hash_64())
|
||||
{
|
||||
}
|
||||
|
||||
CommsSystem::CommsSystem(const CommsSystem& sys)
|
||||
: id(sys.id), hashId(sys.hashId)
|
||||
{
|
||||
}
|
||||
|
||||
CommsSystem& CommsSystem::operator=(const CommsSystem& sys)
|
||||
{
|
||||
if (this == &sys)
|
||||
return *this;
|
||||
|
||||
id = sys.id;
|
||||
hashId = sys.hashId;
|
||||
ops = Array<Operation*>();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Str_8 CommsSystem::GetId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
UInt_64 CommsSystem::GetHashId() const
|
||||
{
|
||||
return hashId;
|
||||
}
|
||||
|
||||
bool CommsSystem::HasOperation(const UInt_64 hashId)
|
||||
{
|
||||
for (UInt_64 i = 0; i < ops.Size(); ++i)
|
||||
if (ops[i]->GetHashId() == hashId)
|
||||
return true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool CommsSystem::AddOperation(Operation* op)
|
||||
{
|
||||
if (HasOperation(op->GetHashId()))
|
||||
return false;
|
||||
|
||||
ops.Push(op);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void CommsSystem::Execute(Comms* comms, Endpoint* endpoint, const UInt_64 hashId, Serializer<>& payload)
|
||||
{
|
||||
for (UInt_64 i = 0; i < ops.Size(); ++i)
|
||||
{
|
||||
if (ops[i]->GetHashId() == hashId)
|
||||
{
|
||||
ops[i]->Process(comms, endpoint, this, payload);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
LWE_LOG_INT("Info", 0, "System not found.");
|
||||
}
|
||||
}
|
75
src/IO/Socket/DNS.cpp
Normal file
75
src/IO/Socket/DNS.cpp
Normal file
@@ -0,0 +1,75 @@
|
||||
#include "../../../include/IO/Socket/DNS.h"
|
||||
#include "../../../include/Log.h"
|
||||
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#elif defined(LWE_OS_LINUX)
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#endif
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
Str_8 DNS::Resolve(const AddrType addrType, const Str_8& hostName)
|
||||
{
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
WSADATA data = {};
|
||||
|
||||
Int_32 wsaCode = WSAStartup(MAKEWORD(2, 2), &data);
|
||||
if (wsaCode)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to start WSA with error #" + Str_8::FromNum(wsaCode) + ".");
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
addrinfo hints = {};
|
||||
if (addrType == AddrType::IPV6)
|
||||
hints.ai_family = AF_INET6;
|
||||
else if (addrType == AddrType::IPV4)
|
||||
hints.ai_family = AF_INET;
|
||||
|
||||
addrinfo* result = nullptr;
|
||||
|
||||
Int_32 code = getaddrinfo(hostName, nullptr, &hints, &result);
|
||||
if (code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Failed to resolve host with error #" + Str_8::FromNum(code) + ".");
|
||||
return {};
|
||||
}
|
||||
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
if (WSACleanup() == SOCKET_ERROR)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
return {};
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
{
|
||||
Char_8 ipResult[INET6_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(result->ai_family, &((sockaddr_in6*)result->ai_addr)->sin6_addr, ipResult, INET6_ADDRSTRLEN);
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
return ipResult;
|
||||
}
|
||||
else if (addrType == AddrType::IPV4)
|
||||
{
|
||||
Char_8 ipResult[INET_ADDRSTRLEN];
|
||||
|
||||
inet_ntop(result->ai_family, &((sockaddr_in*)result->ai_addr)->sin_addr, ipResult, INET_ADDRSTRLEN);
|
||||
|
||||
freeaddrinfo(result);
|
||||
|
||||
return ipResult;
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
}
|
650
src/IO/Socket/Endpoint.cpp
Normal file
650
src/IO/Socket/Endpoint.cpp
Normal file
@@ -0,0 +1,650 @@
|
||||
#include "../../../include/IO/Socket/Endpoint.h"
|
||||
#include "../../../include/IO/Socket/Comms.h"
|
||||
#include "../../../include/Encryption.h"
|
||||
#include "../../../include/System/CPU.h"
|
||||
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
#elif defined(LWE_OS_LINUX)
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cerrno>
|
||||
#endif
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
Endpoint::Endpoint()
|
||||
: hdl(LWE_INVALID_SOCKET), disposition(EndDisp::UNKNOWN), status(Status::PENDING), arch(Architecture::UNKNOWN),
|
||||
hashId(0), nextSendId(0), nextRecvId(0), port(0), deltaDuration(0.0f), deltaRate(1.0f / 60.0f),
|
||||
timeout(0.0f), lastPing(0.0f), oldLatency(0.0f), latency(0.0f), queueSlot(0)
|
||||
{
|
||||
AddType("Endpoint");
|
||||
}
|
||||
|
||||
Endpoint::Endpoint(Socket hdl, const EndDisp disposition, const Architecture arch, const Str_8& id,
|
||||
const AddrType& type, const Str_8& address, const UInt_16 port)
|
||||
: hdl(hdl), disposition(disposition), status(Status::ACTIVE), arch(arch), id(id), hashId(id.Hash_32()), nextSendId(0),
|
||||
nextRecvId(0), address(address), port(port), deltaDuration(0.0f), deltaRate(1.0f / 60.0f), timeout(0.0f),
|
||||
lastPing(0.0f), oldLatency(0.0f), latency(0.0f), queueSlot(0)
|
||||
{
|
||||
AddType("Endpoint");
|
||||
}
|
||||
|
||||
Endpoint::Endpoint(Socket hdl, const AddrType& type, const Str_8& address, const UInt_16 port)
|
||||
: hdl(hdl), disposition(EndDisp::UNKNOWN), status(Status::PENDING), arch(Architecture::UNKNOWN), hashId(0),
|
||||
nextSendId(0), nextRecvId(0), address(address), port(port), deltaDuration(0.0f), deltaRate(1.0f / 60.0f),
|
||||
timeout(0.0f), lastPing(0.0f), oldLatency(0.0f), latency(0.0f), queueSlot(0)
|
||||
{
|
||||
AddType("Endpoint");
|
||||
}
|
||||
|
||||
Endpoint::Endpoint(const Endpoint& end)
|
||||
: BaseObj(end), hdl(LWE_INVALID_SOCKET), disposition(end.disposition), status(Status::PENDING), arch(end.arch),
|
||||
id(end.id), hashId(end.hashId), nextSendId(0), nextRecvId(0), address(end.address), port(end.port),
|
||||
deltaDuration(0.0f), deltaRate(1.0f / 60.0f), timeout(0.0f), lastPing(0.0f),
|
||||
oldLatency(0.0f), latency(0.0f), queueSlot(0)
|
||||
{
|
||||
}
|
||||
|
||||
Endpoint& Endpoint::operator=(const Endpoint& end)
|
||||
{
|
||||
if (this == &end)
|
||||
return *this;
|
||||
|
||||
BaseObj::operator=(end);
|
||||
|
||||
if (status == Status::PENDING)
|
||||
{
|
||||
disposition = end.disposition;
|
||||
status = end.status;
|
||||
arch = end.arch;
|
||||
id = end.id;
|
||||
hashId = end.hashId;
|
||||
nextSendId = end.nextSendId;
|
||||
sent = end.sent;
|
||||
nextRecvId = end.nextRecvId;
|
||||
received = end.received;
|
||||
address = end.address;
|
||||
port = end.port;
|
||||
deltaDuration = end.deltaDuration;
|
||||
deltaRate = end.deltaRate;
|
||||
timeout = end.timeout;
|
||||
lastPing = end.lastPing;
|
||||
oldLatency = end.oldLatency;
|
||||
latency = end.latency;
|
||||
queueSlot = end.queueSlot;
|
||||
}
|
||||
else
|
||||
{
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
disposition = end.disposition;
|
||||
status = Status::PENDING;
|
||||
arch = end.arch;
|
||||
id = end.id;
|
||||
hashId = end.hashId;
|
||||
nextSendId = 0;
|
||||
sent = Vector<Insurance>();
|
||||
nextRecvId = 0;
|
||||
received = Vector<Fragments>();
|
||||
address = end.address;
|
||||
port = end.port;
|
||||
deltaDuration = 0.0f;
|
||||
deltaRate = 1.0f / 60.0f;
|
||||
timeout = 0.0f;
|
||||
lastPing = 0.0f;
|
||||
oldLatency = 0.0f;
|
||||
latency = 0.0f;
|
||||
queueSlot = 0;
|
||||
}
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Endpoint::Poll(const float delta)
|
||||
{
|
||||
Comms* comms = (Comms*)GetParent();
|
||||
if (!comms)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Endpoint must be a child of a Socket object.");
|
||||
return;
|
||||
}
|
||||
|
||||
SortReceived();
|
||||
|
||||
if (deltaDuration >= deltaRate)
|
||||
deltaDuration = Math::Mod(deltaDuration, deltaRate);
|
||||
|
||||
deltaDuration += delta;
|
||||
timeout += delta;
|
||||
latency += delta;
|
||||
|
||||
if (sent.Size())
|
||||
{
|
||||
for (UInt_64 i = 0; i < sent.Size(); ++i)
|
||||
{
|
||||
sent[i].lastResend += delta;
|
||||
if (sent[i].lastResend >= comms->GetResendRate())
|
||||
{
|
||||
Serializer result(Endianness::LE);
|
||||
result.Write(sent[i].header);
|
||||
result.WriteSer(sent[i].payload);
|
||||
|
||||
if (sent[i].header.encrypted)
|
||||
Encryption::Encrypt_64(result.Size() - sizeof(bool), &result[sizeof(bool)]);
|
||||
|
||||
if (comms->GetAddressType() == AddrType::IPV6)
|
||||
Send_v6(result);
|
||||
else if (comms->GetAddressType() == AddrType::IPV4)
|
||||
Send_v4(result);
|
||||
|
||||
sent[i].lastResend = Math::Mod(sent[i].lastResend, comms->GetResendRate());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (comms->GetDisposition() == EndDisp::SERVICE)
|
||||
{
|
||||
lastPing += delta;
|
||||
if (lastPing >= 1.0f)
|
||||
Ping(delta);
|
||||
}
|
||||
}
|
||||
|
||||
EndDisp Endpoint::GetDisposition() const
|
||||
{
|
||||
return disposition;
|
||||
}
|
||||
|
||||
void Endpoint::SetStatus(const Status newStatus)
|
||||
{
|
||||
status = newStatus;
|
||||
}
|
||||
|
||||
Status Endpoint::GetStatus() const
|
||||
{
|
||||
return status;
|
||||
}
|
||||
|
||||
Architecture Endpoint::GetArchitecture() const
|
||||
{
|
||||
return arch;
|
||||
}
|
||||
|
||||
Str_8 Endpoint::GetId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
UInt_64 Endpoint::GetHashId() const
|
||||
{
|
||||
return hashId;
|
||||
}
|
||||
|
||||
UInt_64 Endpoint::GetNextSendId() const
|
||||
{
|
||||
return nextSendId;
|
||||
}
|
||||
|
||||
void Endpoint::Send(const bool deltaLocked, const bool encrypted, const bool ensure, const UInt_64 sys,
|
||||
const UInt_64 op, const Serializer<>& payload)
|
||||
{
|
||||
Comms* comms = (Comms*)GetParent();
|
||||
if (!comms)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Endpoint must be a child of a Socket object.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (deltaLocked && deltaDuration < deltaRate)
|
||||
return;
|
||||
|
||||
Header header = {
|
||||
encrypted,
|
||||
nextSendId++,
|
||||
1,
|
||||
0,
|
||||
ensure,
|
||||
comms->GetDisposition(),
|
||||
comms->GetHashId(),
|
||||
sys,
|
||||
op
|
||||
};
|
||||
|
||||
if ((comms->GetAddressType() == AddrType::IPV6 && payload.Size() > COMMS_IPV6_PAYLOAD) || (comms->GetAddressType() == AddrType::IPV4 && payload.Size() > COMMS_IPV4_PAYLOAD))
|
||||
{
|
||||
Fragments frags = FragmentData(header, payload);
|
||||
for (UInt_64 i = 0; i < frags.Size(); ++i)
|
||||
{
|
||||
Header newHeader = frags.GetHeader();
|
||||
newHeader.fragment = i;
|
||||
|
||||
Send(newHeader, frags[i]);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Send(header, payload);
|
||||
}
|
||||
}
|
||||
|
||||
void Endpoint::Send(const bool deltaLocked, const bool encrypted, const bool ensure, const Str_8& sys,
|
||||
const Str_8& op, const Serializer<>& payload)
|
||||
{
|
||||
Send(deltaLocked, encrypted, ensure, sys.Hash_64(), op.Hash_64(), payload);
|
||||
}
|
||||
|
||||
void Endpoint::RemoveInsurance(const UInt_64 msgId, const UInt_64 fragment)
|
||||
{
|
||||
for (UInt_64 i = 0; i < sent.Size(); ++i)
|
||||
{
|
||||
if (sent[i].header.id == msgId && sent[i].header.fragment == fragment)
|
||||
{
|
||||
sent.Remove(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
timeout = 0.0f;
|
||||
}
|
||||
|
||||
UInt_64 Endpoint::GetNextRecvId() const
|
||||
{
|
||||
return nextRecvId;
|
||||
}
|
||||
|
||||
void Endpoint::AddReceived(const Header& header, const Serializer<>& payload)
|
||||
{
|
||||
Fragments* frags = nullptr;
|
||||
|
||||
for (UInt_64 i = 0; i < received.Size(); ++i)
|
||||
{
|
||||
if (received[i].GetHeader().id == header.id)
|
||||
{
|
||||
if (received[i][header.fragment].Size())
|
||||
return;
|
||||
|
||||
frags = &received[i];
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (header.id > nextRecvId)
|
||||
nextRecvId = header.id + 1;
|
||||
|
||||
if (frags)
|
||||
(*frags)[header.fragment] = payload;
|
||||
else
|
||||
received.Push({header, payload});
|
||||
|
||||
timeout = 0.0f;
|
||||
}
|
||||
|
||||
Vector<Fragments> Endpoint::GetReceived() const
|
||||
{
|
||||
return received;
|
||||
}
|
||||
|
||||
Vector<Fragments>* Endpoint::GetReceived()
|
||||
{
|
||||
return &received;
|
||||
}
|
||||
|
||||
Str_8 Endpoint::GetAddress() const
|
||||
{
|
||||
return address;
|
||||
}
|
||||
|
||||
UInt_16 Endpoint::GetPort() const
|
||||
{
|
||||
return port;
|
||||
}
|
||||
|
||||
void Endpoint::SetDeltaRate(const float newDeltaRate)
|
||||
{
|
||||
deltaRate = newDeltaRate;
|
||||
}
|
||||
|
||||
float Endpoint::GetDeltaRate() const
|
||||
{
|
||||
return deltaRate;
|
||||
}
|
||||
|
||||
float Endpoint::GetTimeout() const
|
||||
{
|
||||
return timeout;
|
||||
}
|
||||
|
||||
float Endpoint::GetLastPing() const
|
||||
{
|
||||
return lastPing;
|
||||
}
|
||||
|
||||
void Endpoint::Ping(const float delta)
|
||||
{
|
||||
Serializer payload(Endianness::LE);
|
||||
payload.Write(delta);
|
||||
|
||||
Send(false, true, false, "Internal", "Ping", payload);
|
||||
lastPing = 0.0f;
|
||||
latency = 0.0f;
|
||||
}
|
||||
|
||||
void Endpoint::Pong(const float delta)
|
||||
{
|
||||
Serializer payload(Endianness::LE);
|
||||
payload.Write(delta);
|
||||
|
||||
Send(false, true, false, "Internal", "Pong", payload);
|
||||
|
||||
timeout = 0.0f;
|
||||
}
|
||||
|
||||
void Endpoint::SendLatency()
|
||||
{
|
||||
oldLatency = latency * 1000;
|
||||
|
||||
Serializer sPayload(Endianness::LE);
|
||||
sPayload.Write(oldLatency);
|
||||
|
||||
Send(false, true, false, "Internal", "Latency", sPayload);
|
||||
|
||||
latency = 0.0f;
|
||||
timeout = 0.0f;
|
||||
}
|
||||
|
||||
void Endpoint::SetLatency(const float newLatency)
|
||||
{
|
||||
oldLatency = newLatency;
|
||||
}
|
||||
|
||||
float Endpoint::GetLatency() const
|
||||
{
|
||||
return oldLatency;
|
||||
}
|
||||
|
||||
void Endpoint::SetQueueSlot(const UInt_64 slot)
|
||||
{
|
||||
queueSlot = slot;
|
||||
}
|
||||
|
||||
UInt_64 Endpoint::GetQueueSlot() const
|
||||
{
|
||||
return queueSlot;
|
||||
}
|
||||
|
||||
Fragments Endpoint::FragmentData(const Header& header, const Serializer<>& data)
|
||||
{
|
||||
Comms* comms = (Comms*)GetParent();
|
||||
if (!comms)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Endpoint must be a child of a Socket object.");
|
||||
return {};
|
||||
}
|
||||
|
||||
Fragments result;
|
||||
|
||||
if (comms->GetAddressType() == AddrType::IPV6)
|
||||
{
|
||||
UInt_64 frags = data.Size() / COMMS_IPV6_PAYLOAD;
|
||||
if (data.Size() % COMMS_IPV6_PAYLOAD)
|
||||
++frags;
|
||||
|
||||
result = Fragments(header, frags);
|
||||
|
||||
UInt_64 size = COMMS_IPV6_PAYLOAD;
|
||||
|
||||
for (UInt_64 i = 0; i < result.Size(); ++i)
|
||||
{
|
||||
size = COMMS_IPV6_PAYLOAD;
|
||||
if (i == result.Size() - 1)
|
||||
size = data.Size() % COMMS_IPV6_PAYLOAD;
|
||||
|
||||
result[i] = {data.GetEndianness(), &data[i * COMMS_IPV6_PAYLOAD], size};
|
||||
}
|
||||
}
|
||||
else if (comms->GetAddressType() == AddrType::IPV4)
|
||||
{
|
||||
UInt_64 frags = data.Size() / COMMS_IPV4_PAYLOAD;
|
||||
if (data.Size() % COMMS_IPV4_PAYLOAD)
|
||||
++frags;
|
||||
|
||||
result = Fragments(header, frags);
|
||||
|
||||
UInt_64 size = COMMS_IPV4_PAYLOAD;
|
||||
|
||||
for (UInt_64 i = 0; i < result.Size(); ++i)
|
||||
{
|
||||
size = COMMS_IPV4_PAYLOAD;
|
||||
if (i == result.Size() - 1)
|
||||
size = data.Size() % COMMS_IPV4_PAYLOAD;
|
||||
|
||||
result[i] = {data.GetEndianness(), &data[i * COMMS_IPV4_PAYLOAD], size};
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void Endpoint::Send(const Header& header, const Serializer<>& payload)
|
||||
{
|
||||
Comms* comms = (Comms*)GetParent();
|
||||
if (!comms)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Endpoint must be a child of a Socket object.");
|
||||
return;
|
||||
}
|
||||
|
||||
Serializer result(Endianness::LE);
|
||||
result.Write(header);
|
||||
result.WriteSer(payload);
|
||||
|
||||
if (header.encrypted)
|
||||
Encryption::Encrypt_64(result.Size() - sizeof(bool), &result[sizeof(bool)]);
|
||||
|
||||
if (header.ensure)
|
||||
sent.Push({header, payload});
|
||||
|
||||
if (comms->GetAddressType() == AddrType::IPV6)
|
||||
Send_v6(result);
|
||||
else if (comms->GetAddressType() == AddrType::IPV4)
|
||||
Send_v4(result);
|
||||
}
|
||||
|
||||
bool Endpoint::SortingNeeded() const
|
||||
{
|
||||
UInt_64 lastPacket = 0;
|
||||
|
||||
for (UInt_64 i = 0; i < received.Size(); ++i)
|
||||
{
|
||||
if (received[i].GetHeader().id < lastPacket)
|
||||
return true;
|
||||
else
|
||||
lastPacket = received[i].GetHeader().id;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Endpoint::SortReceived()
|
||||
{
|
||||
if (!SortingNeeded())
|
||||
return;
|
||||
|
||||
Vector<Fragments> sorted(0, received.Stride());
|
||||
|
||||
for (UInt_64 a = 0; a < received.Size(); ++a)
|
||||
{
|
||||
if (sorted.Size())
|
||||
{
|
||||
for (UInt_64 b = sorted.Size(); b; --b)
|
||||
{
|
||||
if (received[a].GetHeader().id > sorted[b - 1].GetHeader().id)
|
||||
{
|
||||
if (b == sorted.Size())
|
||||
sorted.Push(received[a]);
|
||||
else
|
||||
sorted.Insert(b, received[a]);
|
||||
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
sorted.Insert(b - 1, received[a]);
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
sorted.Push(received[a]);
|
||||
}
|
||||
}
|
||||
|
||||
received = sorted;
|
||||
}
|
||||
|
||||
UInt_16 Endpoint::Send_v6(const Serializer<>& payload)
|
||||
{
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
{
|
||||
LWE_LOG_INT("Info", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (payload.Size() > LWE_IPV6_UDP_PAYLOAD)
|
||||
{
|
||||
LWE_LOG_INT("Info", 1, "Attempted to send a packet with the size, \"" + Str_8::FromNum(payload.Size())
|
||||
+ "\", that exceeds the max payload of, \"" + Str_8::FromNum(LWE_IPV6_UDP_PAYLOAD) + "\".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "The given address, \"" + address + "\" is not valid.");
|
||||
return 0;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = 0;
|
||||
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
dCode = WSAGetLastError();
|
||||
#elif defined(LWE_OS_LINUX)
|
||||
dCode = errno;
|
||||
#endif
|
||||
|
||||
LWE_LOG_INT("Error", 3, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int_32 sent = sendto(hdl, (char*)&payload[0], (int)payload.Size(), 0, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
if (sent == SOCKET_ERROR)
|
||||
#elif defined(LWE_OS_LINUX)
|
||||
if (sent == -1)
|
||||
#endif
|
||||
{
|
||||
Int_32 dCode = 0;
|
||||
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
code = WSAGetLastError();
|
||||
#elif defined(LWE_OS_LINUX)
|
||||
code = errno;
|
||||
#endif
|
||||
|
||||
LWE_LOG_INT("Error", 4, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
||||
|
||||
((Comms*)GetParent())->UnInitialize();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt_16)sent;
|
||||
}
|
||||
|
||||
UInt_16 Endpoint::Send_v4(const Serializer<>& payload)
|
||||
{
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
{
|
||||
LWE_LOG_INT("Info", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (payload.Size() > LWE_IPV4_UDP_PAYLOAD)
|
||||
{
|
||||
LWE_LOG_INT("Info", 1, "Attempted to send a packet with the size, \"" + Str_8::FromNum(payload.Size())
|
||||
+ "\", that exceeds the max payload of, \"" + Str_8::FromNum(LWE_IPV4_UDP_PAYLOAD) + "\".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "The given address, \"" + address + "\" is not valid.");
|
||||
return 0;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
#elif defined(LWE_OS_LINUX)
|
||||
Int_32 dCode = errno;
|
||||
#else
|
||||
Int_32 dCode = 0;
|
||||
#endif
|
||||
|
||||
LWE_LOG_INT("Error", 2, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SInt_64 sent = sendto(hdl, (char*)&payload[0], (int)payload.Size(), 0, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
if (sent == SOCKET_ERROR)
|
||||
#elif defined(LWE_OS_LINUX)
|
||||
if (sent == -1)
|
||||
#endif
|
||||
{
|
||||
#if defined(LWE_OS_WINDOWS)
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
if (dCode != WSAEWOULDBLOCK)
|
||||
{
|
||||
LWE_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
||||
|
||||
((Comms*)GetParent())->UnInitialize();
|
||||
}
|
||||
#elif defined(LWE_OS_LINUX)
|
||||
Int_32 dCode = errno;
|
||||
|
||||
if (dCode != EWOULDBLOCK)
|
||||
{
|
||||
LWE_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
||||
|
||||
((Comms*)GetParent())->UnInitialize();
|
||||
}
|
||||
#else
|
||||
Int_32 dCode = 0;
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt_16)sent;
|
||||
}
|
||||
}
|
100
src/IO/Socket/Fragments.cpp
Normal file
100
src/IO/Socket/Fragments.cpp
Normal file
@@ -0,0 +1,100 @@
|
||||
#include "../../../include/IO/Socket/Fragments.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
Fragments::~Fragments()
|
||||
{
|
||||
delete[] data;
|
||||
}
|
||||
|
||||
Fragments::Fragments()
|
||||
: header{}, data(nullptr), size(0)
|
||||
{
|
||||
}
|
||||
|
||||
Fragments::Fragments(const Header& header, const Serializer<>& payload)
|
||||
: header(header), data(new Serializer<>[header.fragments]), size(header.fragments)
|
||||
{
|
||||
this->header.fragment = 0;
|
||||
data[header.fragment] = payload;
|
||||
}
|
||||
|
||||
Fragments::Fragments(const Header& header, const UInt_64 size)
|
||||
: header(header), data(new Serializer<>[size]), size(size)
|
||||
{
|
||||
this->header.fragments = size;
|
||||
this->header.fragment = 0;
|
||||
}
|
||||
|
||||
Fragments::Fragments(const Fragments& frags)
|
||||
: header(frags.header), data(new Serializer<>[frags.size]), size(frags.size)
|
||||
{
|
||||
for (UInt_64 i = 0; i < size; ++i)
|
||||
data[i] = frags.data[i];
|
||||
}
|
||||
|
||||
Fragments& Fragments::operator=(const Fragments& frags)
|
||||
{
|
||||
if (this == &frags)
|
||||
return *this;
|
||||
|
||||
header = frags.header;
|
||||
delete[] data;
|
||||
data = new Serializer<>[frags.size];
|
||||
for (UInt_64 i = 0; i < frags.size; ++i)
|
||||
data[i] = frags.data[i];
|
||||
size = frags.size;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
Fragments::operator const Serializer<>* () const
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
Fragments::operator Serializer<>* ()
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
Header Fragments::GetHeader() const
|
||||
{
|
||||
return header;
|
||||
}
|
||||
|
||||
UInt_64 Fragments::Size() const
|
||||
{
|
||||
return size;
|
||||
}
|
||||
|
||||
bool Fragments::IsComplete() const
|
||||
{
|
||||
for (UInt_64 i = 0; i < size; ++i)
|
||||
if (!data[i].Size())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Packet Fragments::Combine() const
|
||||
{
|
||||
UInt_64 rSize = 0;
|
||||
for (UInt_64 i = 0; i < size; ++i)
|
||||
rSize += data[i].Size();
|
||||
|
||||
Packet result =
|
||||
{
|
||||
header,
|
||||
{Endianness::LE, rSize}
|
||||
};
|
||||
result.header.fragments = 0;
|
||||
|
||||
for (UInt_64 i = 0; i < size; ++i)
|
||||
result.payload.WriteSer(data[i]);
|
||||
|
||||
result.payload.SetOffset(0);
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
47
src/IO/Socket/Operation.cpp
Normal file
47
src/IO/Socket/Operation.cpp
Normal file
@@ -0,0 +1,47 @@
|
||||
#include "../../../include/IO/Socket/Operation.h"
|
||||
#include "../../../include/IO/Socket/CommsSystem.h"
|
||||
#include "../../../include/IO/Socket/Comms.h"
|
||||
#include "../../../include/IO/Socket/Endpoint.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
Operation::Operation()
|
||||
: hashId(0)
|
||||
{
|
||||
}
|
||||
|
||||
Operation::Operation(const Str_8& id)
|
||||
: id(id), hashId(id.Hash_64())
|
||||
{
|
||||
}
|
||||
|
||||
Operation::Operation(const Operation& cmd)
|
||||
: id(cmd.id), hashId(cmd.hashId)
|
||||
{
|
||||
}
|
||||
|
||||
Operation& Operation::operator=(const Operation& cmd)
|
||||
{
|
||||
if (this == &cmd)
|
||||
return *this;
|
||||
|
||||
id = cmd.id;
|
||||
hashId = cmd.hashId;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Operation::Process(Comms* comms, Endpoint* endpoint, CommsSystem* sys, Serializer<>& payload)
|
||||
{
|
||||
}
|
||||
|
||||
Str_8 Operation::GetId() const
|
||||
{
|
||||
return id;
|
||||
}
|
||||
|
||||
UInt_64 Operation::GetHashId() const
|
||||
{
|
||||
return hashId;
|
||||
}
|
||||
}
|
337
src/IO/Socket/Request.cpp
Normal file
337
src/IO/Socket/Request.cpp
Normal file
@@ -0,0 +1,337 @@
|
||||
#include "../../../include/IO/Socket/Request.h"
|
||||
#include "../../../include/Base64.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
Request::Request()
|
||||
: verb(Verb::GET), cType(ContentType::NONE)
|
||||
{
|
||||
}
|
||||
|
||||
Request::Request(const Verb verb, const Str_8& rsrc)
|
||||
: verb(verb), rsrc(rsrc), cType(ContentType::NONE)
|
||||
{
|
||||
}
|
||||
|
||||
Request::Request(const char* data, const UInt_64 size)
|
||||
: verb(Verb::POST), cType(ContentType::NONE)
|
||||
{
|
||||
ReadData(Str_8(data, size));
|
||||
}
|
||||
|
||||
Request::Request(const Str_8& data)
|
||||
: verb(Verb::POST), cType(ContentType::NONE)
|
||||
{
|
||||
ReadData(data);
|
||||
}
|
||||
|
||||
Request& Request::operator=(const Request &req)
|
||||
{
|
||||
if (this == &req)
|
||||
return *this;
|
||||
|
||||
verb = req.verb;
|
||||
rsrc = req.rsrc;
|
||||
queries = req.queries;
|
||||
header = req.header;
|
||||
cType = req.cType;
|
||||
body = req.body;
|
||||
|
||||
return* this;
|
||||
}
|
||||
|
||||
Verb Request::GetVerb() const
|
||||
{
|
||||
return verb;
|
||||
}
|
||||
|
||||
void Request::SetContentType(const ContentType cType)
|
||||
{
|
||||
if (body.Size())
|
||||
body.Resize(0);
|
||||
|
||||
this->cType = cType;
|
||||
}
|
||||
|
||||
ContentType Request::GetContentType() const
|
||||
{
|
||||
return cType;
|
||||
}
|
||||
|
||||
void Request::SetResource(const Str_8& rsrc)
|
||||
{
|
||||
this->rsrc = rsrc;
|
||||
}
|
||||
|
||||
Str_8 Request::GetResource() const
|
||||
{
|
||||
return rsrc;
|
||||
}
|
||||
|
||||
void Request::AddQuery(const Str_8& var, const Str_8& value)
|
||||
{
|
||||
queries.Push(var + "=" + value);
|
||||
}
|
||||
|
||||
Str_8 Request::GetQuery(const Str_8& var)
|
||||
{
|
||||
for (UInt_64 i = 0; i < queries.Size(); ++i)
|
||||
{
|
||||
Vector<Str_8> data = queries[i].Split("=");
|
||||
|
||||
if (data[0] == var)
|
||||
return data[1];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Vector<Str_8> Request::GetQueries() const
|
||||
{
|
||||
return queries;
|
||||
}
|
||||
|
||||
void Request::BasicAuth(const Str_8& id, const Str_8& secret)
|
||||
{
|
||||
AddToHeader("Authorization", Str_8("Basic ") + Base64::Encode(id + ":" + secret));
|
||||
}
|
||||
|
||||
void Request::BearerAuth(const Str_8& token)
|
||||
{
|
||||
AddToHeader("Authorization", "Bearer " + token);
|
||||
}
|
||||
|
||||
void Request::BearerAuth(const Str_8& token, const Str_8& clientId)
|
||||
{
|
||||
AddToHeader("Authorization", "Bearer " + token);
|
||||
AddToHeader("Client-Id", clientId);
|
||||
}
|
||||
|
||||
void Request::BotAuth(const Str_8& token)
|
||||
{
|
||||
AddToHeader("Authorization", "Bot " + token);
|
||||
}
|
||||
|
||||
void Request::AddToHeader(const Str_8& var, const Str_8& value)
|
||||
{
|
||||
header.Push(var + ": " + value);
|
||||
}
|
||||
|
||||
Str_8 Request::GetHeader(const Str_8& var) const
|
||||
{
|
||||
for (UInt_64 i = 0; i < header.Size(); ++i)
|
||||
{
|
||||
Vector<Str_8> data = header[i].Split(": ");
|
||||
|
||||
if (data[0] == var)
|
||||
return data[1];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Vector<Str_8> Request::GetHeader() const
|
||||
{
|
||||
return header;
|
||||
}
|
||||
|
||||
void Request::AddToBody(const Str_8& var, const Str_8& value)
|
||||
{
|
||||
if (body.Size())
|
||||
{
|
||||
if (cType == ContentType::APP_FORMURLENCODED)
|
||||
body.Push('&');
|
||||
}
|
||||
|
||||
body += var;
|
||||
|
||||
if (cType == ContentType::APP_FORMURLENCODED)
|
||||
body += "=";
|
||||
|
||||
body += value;
|
||||
}
|
||||
|
||||
void Request::AddToBody(const Str_8& data)
|
||||
{
|
||||
if (body.Size())
|
||||
{
|
||||
if (cType == ContentType::APP_FORMURLENCODED)
|
||||
body.Push('&');
|
||||
}
|
||||
|
||||
body += data;
|
||||
}
|
||||
|
||||
void Request::SetBody(const Str_8& body)
|
||||
{
|
||||
this->body = body;
|
||||
}
|
||||
|
||||
Str_8 Request::GetVar(const Str_8& var) const
|
||||
{
|
||||
Vector<Str_8> vars;
|
||||
|
||||
if (cType == ContentType::APP_FORMURLENCODED)
|
||||
vars = body.Split("&");
|
||||
|
||||
for (UInt_64 i = 0; i < vars.Size(); ++i)
|
||||
{
|
||||
Vector<Str_8> data = vars[i].Split("=");
|
||||
|
||||
if (data[0] == var)
|
||||
return data[1];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Str_8 Request::GetBody() const
|
||||
{
|
||||
return body;
|
||||
}
|
||||
|
||||
Json Request::GetJson() const
|
||||
{
|
||||
return {body, 5};
|
||||
}
|
||||
|
||||
Str_8 Request::FormResult() const
|
||||
{
|
||||
Str_8 result = VerbToStr(verb) + " " + rsrc;
|
||||
|
||||
if (queries.Size())
|
||||
result += "?" + queries[0];
|
||||
|
||||
for (UInt_64 i = 1; i < queries.Size(); ++i)
|
||||
result += "&" + queries[i];
|
||||
|
||||
result += " HTTP/1.1\r\n";
|
||||
|
||||
for (UInt_64 i = 0; i < header.Size(); ++i)
|
||||
{
|
||||
result += header[i] + "\r\n";
|
||||
}
|
||||
|
||||
result += "Content-Type: " + ContentTypeToStr(cType) + "\r\n";
|
||||
|
||||
if (verb == Verb::GET)
|
||||
result += "\r\n";
|
||||
else
|
||||
result += "Content-Length: " + Str_8::FromNum(body.Size()) + "\r\n\r\n" + body;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Request::IsValid() const
|
||||
{
|
||||
return rsrc.Size() || queries.Size() || header.Size() || body.Size();
|
||||
}
|
||||
|
||||
Str_8 Request::VerbToStr(const Verb verb)
|
||||
{
|
||||
switch (verb)
|
||||
{
|
||||
case Verb::POST:
|
||||
return "POST";
|
||||
case Verb::GET:
|
||||
return "GET";
|
||||
case Verb::PUT:
|
||||
return "PUT";
|
||||
case Verb::DEL:
|
||||
return "DELETE";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
Str_8 Request::ContentTypeToStr(const ContentType cType)
|
||||
{
|
||||
switch (cType)
|
||||
{
|
||||
case ContentType::APP_MULTIPART_FORMDATA:
|
||||
return "multipart/form-data";
|
||||
case ContentType::APP_FORMURLENCODED:
|
||||
return "application/x-www-form-urlencoded";
|
||||
case ContentType::APP_JAVASCRIPT:
|
||||
return "application/javascript";
|
||||
case ContentType::APP_JSON:
|
||||
return "application/json";
|
||||
case ContentType::APP_XML:
|
||||
return "application/xml";
|
||||
case ContentType::TEXT_PLAIN:
|
||||
return "text/plain";
|
||||
case ContentType::TEXT_HTML:
|
||||
return "text/html";
|
||||
case ContentType::TEXT_XML:
|
||||
return "text/xml";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
ContentType Request::StrToContentType(const Str_8& value)
|
||||
{
|
||||
if (value == "multipart/form-data")
|
||||
return ContentType::APP_MULTIPART_FORMDATA;
|
||||
else if (value == "application/x-www-form-urlencoded")
|
||||
return ContentType::APP_FORMURLENCODED;
|
||||
else if (value == "application/javascript")
|
||||
return ContentType::APP_JAVASCRIPT;
|
||||
else if (value == "application/json")
|
||||
return ContentType::APP_JSON;
|
||||
else if (value == "application/xml")
|
||||
return ContentType::APP_XML;
|
||||
else if (value == "text/plain")
|
||||
return ContentType::TEXT_PLAIN;
|
||||
else if (value == "text/html")
|
||||
return ContentType::TEXT_HTML;
|
||||
else if (value == "text/xml")
|
||||
return ContentType::TEXT_XML;
|
||||
else
|
||||
return ContentType::NONE;
|
||||
}
|
||||
|
||||
void Request::ReadData(const Str_8& data)
|
||||
{
|
||||
Vector<Str_8> lines = data.Split("\r\n");
|
||||
Vector<Str_8> meta = lines[0].Split(" ");
|
||||
|
||||
if (meta[0] == "POST")
|
||||
verb = Verb::POST;
|
||||
else if (meta[0] == "GET")
|
||||
verb = Verb::GET;
|
||||
else if (meta[0] == "PUT")
|
||||
verb = Verb::PUT;
|
||||
|
||||
UInt_64 queryIndex = 0;
|
||||
if (meta[1].Find("?", &queryIndex))
|
||||
{
|
||||
rsrc = meta[1].Sub(0, queryIndex);
|
||||
cType = ContentType::APP_FORMURLENCODED;
|
||||
queries = meta[1].Sub(queryIndex + 1).Split("&");
|
||||
}
|
||||
else
|
||||
{
|
||||
rsrc = meta[1];
|
||||
}
|
||||
|
||||
for (UInt_64 i = 1; i < lines.Size(); ++i)
|
||||
{
|
||||
if (!lines[i].Size())
|
||||
break;
|
||||
|
||||
Vector<Str_8> var = lines[i].Split(": ");
|
||||
|
||||
if (var[0] == "Content-Type")
|
||||
{
|
||||
cType = StrToContentType(var[1]);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (var[0] == "Content-Length")
|
||||
continue;
|
||||
|
||||
header.Push(lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
403
src/IO/Socket/Response.cpp
Normal file
403
src/IO/Socket/Response.cpp
Normal file
@@ -0,0 +1,403 @@
|
||||
#include "../../../include/IO/Socket/Response.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
Response::Response(const UInt_32 code, const Str_8& server)
|
||||
: code(code), server(server), cType(ContentType::NONE)
|
||||
{
|
||||
}
|
||||
|
||||
Response::Response(const char* data, const UInt_64 size)
|
||||
: code(0), cType(ContentType::NONE)
|
||||
{
|
||||
ReadData(Str_8(data, size));
|
||||
}
|
||||
|
||||
Response::Response(const Str_8& data)
|
||||
: code(0), cType(ContentType::NONE)
|
||||
{
|
||||
ReadData(data);
|
||||
}
|
||||
|
||||
Response::Response()
|
||||
: code(0), cType(ContentType::NONE)
|
||||
{
|
||||
}
|
||||
|
||||
Response& Response::operator=(const Response& res)
|
||||
{
|
||||
if (this == &res)
|
||||
return *this;
|
||||
|
||||
code = res.code;
|
||||
server = res.server;
|
||||
cType = res.cType;
|
||||
header = res.header;
|
||||
body = res.body;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void Response::SetCode(const UInt_32 code)
|
||||
{
|
||||
this->code = code;
|
||||
}
|
||||
|
||||
UInt_32 Response::GetCode() const
|
||||
{
|
||||
return code;
|
||||
}
|
||||
|
||||
void Response::SetServer(const Str_8& server)
|
||||
{
|
||||
this->server = server;
|
||||
}
|
||||
|
||||
Str_8 Response::GetServer() const
|
||||
{
|
||||
return server;
|
||||
}
|
||||
|
||||
void Response::SetContentType(const ContentType cType)
|
||||
{
|
||||
this->cType = cType;
|
||||
}
|
||||
|
||||
ContentType Response::GetContentType() const
|
||||
{
|
||||
return cType;
|
||||
}
|
||||
|
||||
void Response::AddToHeader(const Str_8& var, const Str_8& value)
|
||||
{
|
||||
header.Push(var + ": " + value);
|
||||
}
|
||||
|
||||
Str_8 Response::GetHeader(const Str_8& var) const
|
||||
{
|
||||
Str_8 lIdentity = var.GetLower();
|
||||
|
||||
for (UInt_64 i = 0; i < header.Size(); ++i)
|
||||
{
|
||||
Vector<Str_8> data = header[i].Split(": ");
|
||||
|
||||
if (data[0].GetLower() == lIdentity)
|
||||
return data[1];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Vector<Str_8> Response::GetHeader() const
|
||||
{
|
||||
return header;
|
||||
}
|
||||
|
||||
void Response::AddToBody(const Str_8& var, const Str_8& value)
|
||||
{
|
||||
if (body.Size())
|
||||
{
|
||||
if (cType == ContentType::APP_FORMURLENCODED)
|
||||
body.Push('&');
|
||||
}
|
||||
|
||||
body += var;
|
||||
|
||||
if (cType == ContentType::APP_FORMURLENCODED)
|
||||
body += "=";
|
||||
|
||||
body += value;
|
||||
}
|
||||
|
||||
void Response::AddToBody(const Str_8& data)
|
||||
{
|
||||
if (body.Size())
|
||||
{
|
||||
if (cType == ContentType::APP_FORMURLENCODED)
|
||||
body.Push('&');
|
||||
}
|
||||
|
||||
body += data;
|
||||
}
|
||||
|
||||
void Response::SetBody(const Str_8& body)
|
||||
{
|
||||
this->body = body;
|
||||
}
|
||||
|
||||
Str_8 Response::GetVar(const Str_8& var) const
|
||||
{
|
||||
Vector<Str_8> vars;
|
||||
|
||||
if (cType == ContentType::APP_FORMURLENCODED)
|
||||
vars = body.Split("&");
|
||||
|
||||
for (UInt_64 i = 0; i < vars.Size(); ++i)
|
||||
{
|
||||
Vector<Str_8> data = vars[i].Split("=");
|
||||
|
||||
if (data[0] == var)
|
||||
return data[1];
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
Str_8 Response::GetBody() const
|
||||
{
|
||||
return body;
|
||||
}
|
||||
|
||||
Json Response::GetJson() const
|
||||
{
|
||||
return {body, 5};
|
||||
}
|
||||
|
||||
Str_8 Response::FormResult() const
|
||||
{
|
||||
Str_8 result = "HTTP/1.1 " + Str_8::FromNum(code) + " " + CodeToStr(code) + "\r\nServer: " + server + "\r\n";
|
||||
|
||||
for (UInt_64 i = 0; i < header.Size(); ++i)
|
||||
{
|
||||
if (header[i].Find("Content-Length", nullptr, SearchPattern::LEFT_RIGHT, IndexResult::ENDING))
|
||||
continue;
|
||||
else if (header[i].Find("Server", nullptr, SearchPattern::LEFT_RIGHT, IndexResult::ENDING))
|
||||
continue;
|
||||
else if (header[i].Find("Content-Type", nullptr, SearchPattern::LEFT_RIGHT, IndexResult::ENDING))
|
||||
continue;
|
||||
|
||||
result += header[i] + "\r\n";
|
||||
}
|
||||
|
||||
result += "Content-Type: " + ContentTypeToStr(cType) + "\r\nContent-Length: " + Str_8::FromNum(body.Size()) + "\r\n\r\n" + body;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Response::IsValid() const
|
||||
{
|
||||
return server.Size() || header.Size() || body.Size();
|
||||
}
|
||||
|
||||
Str_8 Response::CodeToStr(const UInt_32 code)
|
||||
{
|
||||
if (code == 100)
|
||||
return "Continue";
|
||||
else if (code == 101)
|
||||
return "Switching Protocols";
|
||||
else if (code == 102)
|
||||
return "Processing (WebDAV)";
|
||||
else if (code == 200)
|
||||
return "OK";
|
||||
else if (code == 201)
|
||||
return "Created";
|
||||
else if (code == 202)
|
||||
return "Accepted";
|
||||
else if (code == 203)
|
||||
return "Non-Authoritative Information";
|
||||
else if (code == 204)
|
||||
return "No Content";
|
||||
else if (code == 205)
|
||||
return "Reset Content";
|
||||
else if (code == 206)
|
||||
return "Partial Content";
|
||||
else if (code == 207)
|
||||
return "Multi-Status (WebDAV)";
|
||||
else if (code == 208)
|
||||
return "Already Reported (WebDAV)";
|
||||
else if (code == 226)
|
||||
return "IM Used";
|
||||
else if (code == 300)
|
||||
return "Multiple Choices";
|
||||
else if (code == 301)
|
||||
return "Moved Permanently";
|
||||
else if (code == 302)
|
||||
return "Found";
|
||||
else if (code == 303)
|
||||
return "See Others";
|
||||
else if (code == 304)
|
||||
return "Not Modified";
|
||||
else if (code == 305)
|
||||
return "Use Proxy";
|
||||
else if (code == 306)
|
||||
return "(Unused)";
|
||||
else if (code == 307)
|
||||
return "Temporary Redirect";
|
||||
else if (code == 308)
|
||||
return "Permanent Redirect (experimental)";
|
||||
else if (code == 400)
|
||||
return "Bad Request";
|
||||
else if (code == 401)
|
||||
return "Unauthorized";
|
||||
else if (code == 402)
|
||||
return "Payment Required";
|
||||
else if (code == 403)
|
||||
return "Forbidden";
|
||||
else if (code == 404)
|
||||
return "Not Found";
|
||||
else if (code == 405)
|
||||
return "Method Not Allowed";
|
||||
else if (code == 406)
|
||||
return "Not Acceptable";
|
||||
else if (code == 407)
|
||||
return "Proxy Authentication Required";
|
||||
else if (code == 408)
|
||||
return "Request Timeout";
|
||||
else if (code == 409)
|
||||
return "Conflict";
|
||||
else if (code == 410)
|
||||
return "Gone";
|
||||
else if (code == 411)
|
||||
return "Length Required";
|
||||
else if (code == 412)
|
||||
return "Precondition Failed";
|
||||
else if (code == 413)
|
||||
return "Request Entity Too Large";
|
||||
else if (code == 414)
|
||||
return "Request-URI Too Long";
|
||||
else if (code == 415)
|
||||
return "Unsupported Media Type";
|
||||
else if (code == 416)
|
||||
return "Requested Range Not Satisfiable";
|
||||
else if (code == 417)
|
||||
return "Expectation Failed";
|
||||
else if (code == 418)
|
||||
return "I'm a teapot (RFC 2324)";
|
||||
else if (code == 420)
|
||||
return "Enhance Your Calm (Twitter)";
|
||||
else if (code == 422)
|
||||
return "Unprocessable Entity (WebDAV)";
|
||||
else if (code == 423)
|
||||
return "Locked (WebDAV)";
|
||||
else if (code == 424)
|
||||
return "Failed Dependency (Nginx)";
|
||||
else if (code == 425)
|
||||
return "Reserved for WebDAV";
|
||||
else if (code == 426)
|
||||
return "Upgrade Required";
|
||||
else if (code == 428)
|
||||
return "Precondition Required";
|
||||
else if (code == 429)
|
||||
return "Too Many Requests";
|
||||
else if (code == 431)
|
||||
return "Request Header Fields Too Large";
|
||||
else if (code == 444)
|
||||
return "No Response (Nginx)";
|
||||
else if (code == 449)
|
||||
return "Retry With (Microsoft)";
|
||||
else if (code == 450)
|
||||
return "Blocked by Windows Parental Controls (Microsoft)";
|
||||
else if (code == 451)
|
||||
return "Unavailable For Legal Reasons";
|
||||
else if (code == 499)
|
||||
return "Client Closed Request (Nginx)";
|
||||
else if (code == 500)
|
||||
return "Internal Server Error";
|
||||
else if (code == 501)
|
||||
return "Not Implemented";
|
||||
else if (code == 502)
|
||||
return "Bad Gateway";
|
||||
else if (code == 503)
|
||||
return "Service Unavailable";
|
||||
else if (code == 504)
|
||||
return "Gateway Timeout";
|
||||
else if (code == 505)
|
||||
return "HTTP Version Not Supported";
|
||||
else if (code == 506)
|
||||
return "Variant Also Negotiates (Experimental)";
|
||||
else if (code == 507)
|
||||
return "Insufficient Storage (WebDAV)";
|
||||
else if (code == 508)
|
||||
return "Loop Detected (WebDAV)";
|
||||
else if (code == 509)
|
||||
return "Bandwidth Limit Exceeded (Apache)";
|
||||
else if (code == 510)
|
||||
return "Not Extended";
|
||||
else if (code == 511)
|
||||
return "Network Authentication Required";
|
||||
else if (code == 598)
|
||||
return "Network read timeout error";
|
||||
else if (code == 599)
|
||||
return "Network connect timeout error";
|
||||
else
|
||||
return "Unused Status Code";
|
||||
}
|
||||
|
||||
Str_8 Response::ContentTypeToStr(const ContentType cType)
|
||||
{
|
||||
switch (cType)
|
||||
{
|
||||
case ContentType::APP_MULTIPART_FORMDATA:
|
||||
return "multipart/form-data";
|
||||
case ContentType::APP_FORMURLENCODED:
|
||||
return "application/x-www-form-urlencoded";
|
||||
case ContentType::APP_JAVASCRIPT:
|
||||
return "application/javascript";
|
||||
case ContentType::APP_JSON:
|
||||
return "application/json";
|
||||
case ContentType::APP_XML:
|
||||
return "application/xml";
|
||||
case ContentType::TEXT_PLAIN:
|
||||
return "text/plain";
|
||||
case ContentType::TEXT_HTML:
|
||||
return "text/html";
|
||||
case ContentType::TEXT_XML:
|
||||
return "text/xml";
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
ContentType Response::StrToContentType(const Str_8& value)
|
||||
{
|
||||
if (value == "multipart/form-data")
|
||||
return ContentType::APP_MULTIPART_FORMDATA;
|
||||
else if (value == "application/x-www-form-urlencoded")
|
||||
return ContentType::APP_FORMURLENCODED;
|
||||
else if (value == "application/javascript")
|
||||
return ContentType::APP_JAVASCRIPT;
|
||||
else if (value == "application/json")
|
||||
return ContentType::APP_JSON;
|
||||
else if (value == "application/xml")
|
||||
return ContentType::APP_XML;
|
||||
else if (value == "text/plain")
|
||||
return ContentType::TEXT_PLAIN;
|
||||
else if (value == "text/html")
|
||||
return ContentType::TEXT_HTML;
|
||||
else if (value == "text/xml")
|
||||
return ContentType::TEXT_XML;
|
||||
else
|
||||
return ContentType::NONE;
|
||||
}
|
||||
|
||||
void Response::ReadData(const Str_8& data)
|
||||
{
|
||||
Vector<Str_8> lines = data.Split("\r\n");
|
||||
Vector<Str_8> meta = lines[0].Split(" ");
|
||||
|
||||
code = meta[1].ToDecimal<UInt_32>();
|
||||
|
||||
for (UInt_64 i = 1; i < lines.Size(); ++i)
|
||||
{
|
||||
if (!lines[i].Size())
|
||||
break;
|
||||
|
||||
Vector<Str_8> var = lines[i].Split(": ");
|
||||
|
||||
if (var[0].GetLower() == "server")
|
||||
{
|
||||
server = var[1];
|
||||
continue;
|
||||
}
|
||||
else if (var[0].GetLower() == "content-type")
|
||||
{
|
||||
Vector<Str_8> ctData = var[1].Split(";");
|
||||
|
||||
cType = StrToContentType(ctData[0].GetLower());
|
||||
continue;
|
||||
}
|
||||
|
||||
header.Push(lines[i]);
|
||||
}
|
||||
}
|
||||
}
|
653
src/IO/Socket/RestAPIs/Spotify.cpp
Normal file
653
src/IO/Socket/RestAPIs/Spotify.cpp
Normal file
@@ -0,0 +1,653 @@
|
||||
#include "../../../../include/IO/Socket/RestAPIs/Spotify.h"
|
||||
#include "../../../../include/IO/Socket/DNS.h"
|
||||
#include "../../../../include/System/System.h"
|
||||
#include "../../../../include/URI.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
const Str_8 Spotify::trackUriPrefix = "https://open.spotify.com/track/";
|
||||
|
||||
Spotify::~Spotify()
|
||||
{
|
||||
client.Release();
|
||||
}
|
||||
|
||||
Spotify::Spotify()
|
||||
: forceVerify(false)
|
||||
{
|
||||
}
|
||||
|
||||
Spotify::Spotify(const Str_8& clientId, const Str_8& secret, const Str_8& redURI, const Array<Str_8>& scopes, const bool forceVerify)
|
||||
: client(AddrType::IPV4), clientId(clientId), secret(secret), redURI(redURI), scopes(scopes), forceVerify(forceVerify)
|
||||
{
|
||||
}
|
||||
|
||||
bool Spotify::Authorize()
|
||||
{
|
||||
Str_8 scopesFinal;
|
||||
|
||||
for (UInt_64 i = 0; i < scopes.Size(); ++i)
|
||||
{
|
||||
scopesFinal += scopes[i];
|
||||
|
||||
if (i < scopes.Size() - 1)
|
||||
scopesFinal += "%20";
|
||||
}
|
||||
|
||||
Str_8 rURI = URI::Encode(redURI);
|
||||
|
||||
Str_8 uri = "https://accounts.spotify.com/authorize?client_id=" + clientId + "&redirect_uri=" + rURI +
|
||||
"&response_type=code&show_dialog=" + (forceVerify ? "true" : "false") + "&scope=" +
|
||||
scopesFinal;
|
||||
|
||||
TCP server(AddrType::IPV4);
|
||||
server.Initialize();
|
||||
server.Bind(DNS::Resolve(server.GetAddressType(), "localhost"), 65534);
|
||||
server.Listen();
|
||||
|
||||
System::OpenURI(uri);
|
||||
|
||||
TCP* cbClient = server.Accept();
|
||||
|
||||
Request cbReq = cbClient->RecvReq();
|
||||
|
||||
if (cbReq.GetResource() != "/callback")
|
||||
{
|
||||
Response resp(423, "Event Horizon");
|
||||
resp.SetContentType(ContentType::TEXT_HTML);
|
||||
resp.SetBody("<!DOCTYPE html><html><head><title>LWE Response</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn3.iconfinder.com/data/icons/contour-animals-2/512/wolf-512.png\" /></head><body>Hostile Information Received</body></html>");
|
||||
|
||||
cbClient->SendRes(resp);
|
||||
cbClient->Release();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Response resp(200, "Event Horizon");
|
||||
resp.SetContentType(ContentType::TEXT_HTML);
|
||||
resp.SetBody("<!DOCTYPE html><html><head><title>LWE Response</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn3.iconfinder.com/data/icons/contour-animals-2/512/wolf-512.png\" /></head><body>Authentication Successful</body></html>");
|
||||
|
||||
cbClient->SendRes(resp);
|
||||
cbClient->Release();
|
||||
|
||||
server.Release();
|
||||
|
||||
SSL accounts(AddrType::IPV4);
|
||||
accounts.Initialize();
|
||||
accounts.Connect("accounts.spotify.com", SSL::HTTPS_Port);
|
||||
|
||||
Request authReq(Verb::POST, "/api/token");
|
||||
authReq.SetContentType(ContentType::APP_FORMURLENCODED);
|
||||
authReq.BasicAuth(clientId, secret);
|
||||
authReq.AddToBody("grant_type", "authorization_code");
|
||||
authReq.AddToBody("code", cbReq.GetQuery("code"));
|
||||
authReq.AddToBody("redirect_uri", rURI);
|
||||
|
||||
accounts.SendReq(authReq);
|
||||
|
||||
Response authRes = accounts.RecvRes();
|
||||
|
||||
accounts.Release();
|
||||
|
||||
if (authRes.GetCode() == 400)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Could not authorize with Spotify because the client id was invalid.");
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (authRes.GetCode() == 403)
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Could not authorize with Spotify because the secret was invalid.");
|
||||
|
||||
return false;
|
||||
}
|
||||
else if (authRes.GetCode() != 200)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Could not authorize with Spotify with code " + Str_8::FromNum(authRes.GetCode()) + ".");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Json authResJson = authRes.GetJson();
|
||||
|
||||
JsonObj* value = (JsonObj*)authResJson.GetValue();
|
||||
if (!value)
|
||||
return false;
|
||||
|
||||
JsonVar* tokenVar = value->GetVar("access_token");
|
||||
if (!tokenVar)
|
||||
return false;
|
||||
|
||||
JsonVar* rTokenVar = value->GetVar("refresh_token");
|
||||
if (!rTokenVar)
|
||||
return false;
|
||||
|
||||
token = ((JsonStr*)tokenVar->GetValue())->value;
|
||||
rToken = ((JsonStr*)rTokenVar->GetValue())->value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UInt_32 Spotify::SetVolume(const UInt_8 level)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/volume");
|
||||
req.AddQuery("volume_percent", Str_8::FromNum(level));
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Play()
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/play");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.Release();
|
||||
client.Initialize();
|
||||
client.Connect("", SSL::HTTPS_Port);
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Pause()
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/pause");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::SetRepeat(const SpotifyState state)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Str_8 result;
|
||||
switch (state)
|
||||
{
|
||||
case SpotifyState::TRACK:
|
||||
result = "track";
|
||||
break;
|
||||
case SpotifyState::CONTEXT:
|
||||
result = "context";
|
||||
break;
|
||||
case SpotifyState::OFF:
|
||||
result = "off";
|
||||
break;
|
||||
}
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/repeat");
|
||||
req.AddQuery("state", result);
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::SetShuffle(const bool state)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/repeat");
|
||||
req.AddQuery("state", state ? "true" : "false");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::SearchTrack(Vector<Str_8>& artists, Str_8& id, Str_8& name)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::GET, "/v1/search");
|
||||
|
||||
Str_8 q = "artist%3A";
|
||||
|
||||
for (UInt_64 i = 0; i < artists.Size(); ++i)
|
||||
{
|
||||
q += artists[i].ReplaceAll(" ", "+");
|
||||
if (i != artists.Size() - 1)
|
||||
q += "%2C+";
|
||||
}
|
||||
|
||||
q += "+track%3A" + name.ReplaceAll(" ", "+");
|
||||
|
||||
req.AddQuery("q", q);
|
||||
req.AddQuery("type", "track");
|
||||
req.AddQuery("limit", "1");
|
||||
req.AddQuery("offset", "0");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return SearchTrack(artists, name, id);
|
||||
}
|
||||
|
||||
Json body = res.GetJson();
|
||||
|
||||
JsonNum* total = (JsonNum*)body.RetrieveValue("tracks.total");
|
||||
if (!total || total->value == 0.0f)
|
||||
return 0;
|
||||
|
||||
JsonObj* item = (JsonObj*)body.RetrieveValue("tracks.items[0]");
|
||||
if (!item)
|
||||
return 0;
|
||||
|
||||
JsonVar* artistsVar = item->GetVar("artists");
|
||||
if (!artistsVar)
|
||||
return 0;
|
||||
|
||||
JsonArray* artistsArray = (JsonArray*)artistsVar->GetValue();
|
||||
if (!artistsArray)
|
||||
return 0;
|
||||
|
||||
JsonVar* trackIdVar = item->GetVar("id");
|
||||
if (!trackIdVar)
|
||||
return 0;
|
||||
|
||||
JsonStr* trackIdValue = (JsonStr*)trackIdVar->GetValue();
|
||||
if (!trackIdValue)
|
||||
return 0;
|
||||
|
||||
JsonVar* trackNameVar = item->GetVar("name");
|
||||
if (!trackNameVar)
|
||||
return 0;
|
||||
|
||||
JsonStr* trackNameValue = (JsonStr*)trackNameVar->GetValue();
|
||||
if (!trackNameValue)
|
||||
return 0;
|
||||
|
||||
artists.Resize(artistsArray->Size());
|
||||
|
||||
for (UInt_64 i = 0; i < artistsArray->Size(); ++i)
|
||||
{
|
||||
JsonObj* artistObj = (JsonObj*)(*artistsArray)[i];
|
||||
|
||||
JsonVar* artistNameVar = artistObj->GetVar("name");
|
||||
|
||||
JsonStr* artistNameValue = (JsonStr*)artistNameVar->GetValue();
|
||||
|
||||
artists[i] = artistNameValue->value;
|
||||
}
|
||||
|
||||
id = trackIdValue->value;
|
||||
|
||||
name = trackNameValue->value;
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::GetPlayingTrack(Vector<Str_8>& artists, Str_8& id, Str_8& name)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::GET, "/v1/me/player/currently-playing");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
|
||||
return GetPlayingTrack(artists, id, name);
|
||||
}
|
||||
|
||||
Json result = res.GetJson();
|
||||
|
||||
JsonObj* itemObj = (JsonObj*)result.RetrieveValue("item");
|
||||
if (!itemObj)
|
||||
return {};
|
||||
|
||||
JsonVar* artistsVar = itemObj->GetVar("artists");
|
||||
if (!artistsVar)
|
||||
return 0;
|
||||
|
||||
JsonArray* artistsArray = (JsonArray*)artistsVar->GetValue();
|
||||
if (!artistsArray)
|
||||
return 0;
|
||||
|
||||
JsonVar* trackIdVar = itemObj->GetVar("id");
|
||||
if (!trackIdVar)
|
||||
return 0;
|
||||
|
||||
JsonStr* trackIdValue = (JsonStr*)trackIdVar->GetValue();
|
||||
if (!trackIdValue)
|
||||
return 0;
|
||||
|
||||
JsonVar* trackNameVar = itemObj->GetVar("name");
|
||||
if (!trackNameVar)
|
||||
return 0;
|
||||
|
||||
JsonStr* trackNameValue = (JsonStr*)trackNameVar->GetValue();
|
||||
if (!trackNameValue)
|
||||
return 0;
|
||||
|
||||
artists.Resize(artistsArray->Size());
|
||||
for (UInt_64 i = 0; i < artists.Size(); ++i)
|
||||
{
|
||||
JsonObj* artistObj = (JsonObj*)(*artistsArray)[i];
|
||||
|
||||
JsonVar* artistNameVar = artistObj->GetVar("name");
|
||||
|
||||
JsonStr* artistNameValue = (JsonStr*)artistNameVar->GetValue();
|
||||
|
||||
artists[i] = artistNameValue->value;
|
||||
}
|
||||
|
||||
id = trackIdValue->value;
|
||||
|
||||
name = trackNameValue->value;
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::GetQueue(Array<Track>& tracks)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::GET, "/v1/me/player/queue");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return GetQueue(tracks);
|
||||
}
|
||||
|
||||
Json json = res.GetJson();
|
||||
|
||||
JsonObj* root = (JsonObj*)json.GetValue();
|
||||
|
||||
JsonVar* currentVar = root->GetVar("currently_playing");
|
||||
if (!currentVar->GetValue())
|
||||
return res.GetCode();
|
||||
|
||||
JsonObj* currentObj = (JsonObj*)currentVar->GetValue();
|
||||
|
||||
JsonArray* cArtists = (JsonArray*)currentObj->GetVar("artists")->GetValue();
|
||||
|
||||
JsonArray* queue = (JsonArray*)root->GetVar("queue")->GetValue();
|
||||
|
||||
tracks.Resize(queue->Size() + 1);
|
||||
tracks[0].artists.Resize(cArtists->Size());
|
||||
|
||||
for (UInt_64 i = 0; i < cArtists->Size(); ++i)
|
||||
tracks[0].artists[i] = ((JsonStr*)((JsonObj*)(*cArtists)[i])->GetVar("name")->GetValue())->value;
|
||||
|
||||
tracks[0].id = ((JsonStr*)currentObj->GetVar("id")->GetValue())->value;
|
||||
tracks[0].name = ((JsonStr*)currentObj->GetVar("name")->GetValue())->value;
|
||||
|
||||
for (UInt_64 i = 1; i < queue->Size(); ++i)
|
||||
{
|
||||
JsonObj* trackObj = (JsonObj*)(*queue)[i - 1];
|
||||
|
||||
JsonArray* artists = (JsonArray*)trackObj->GetVar("artists")->GetValue();
|
||||
tracks[i].artists.Resize(artists->Size());
|
||||
|
||||
for (UInt_64 a = 0; a < artists->Size(); ++a)
|
||||
tracks[i].artists[a] = ((JsonStr*)((JsonObj*)(*artists)[a])->GetVar("name")->GetValue())->value;
|
||||
|
||||
tracks[i].id = ((JsonStr*)trackObj->GetVar("id")->GetValue())->value;
|
||||
tracks[i].name = ((JsonStr*)trackObj->GetVar("name")->GetValue())->value;
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::QueueTrack(const Str_8& id)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::POST, "/v1/me/player/queue");
|
||||
req.AddQuery("uri", "spotify:track:" + id);
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return QueueTrack(id);
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::AddTracks(const Str_8& playlistId, const Array<Str_8>& trackIds, const UInt_32 pos)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
JsonObj obj(0);
|
||||
|
||||
JsonArray tracks(trackIds.Size(), 0);
|
||||
for (UInt_64 i = 0; i < trackIds.Size(); ++i)
|
||||
tracks[i] = (JsonBase*)new JsonStr("spotify:track:" + trackIds[i]);
|
||||
obj.AddVar({"uris", tracks});
|
||||
obj.AddVar({"position", (float)pos});
|
||||
|
||||
Json json(obj);
|
||||
|
||||
Request req(Verb::POST, "/v1/playlists/" + playlistId + "/tracks");
|
||||
req.BearerAuth(token);
|
||||
req.SetContentType(ContentType::APP_JSON);
|
||||
req.SetBody(json.ToStr(true));
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return AddTracks(playlistId, trackIds, pos);
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::AddTrack(const Str_8& playlistId, const Str_8& trackId, const UInt_32 pos)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::POST, "/v1/playlists/" + playlistId + "/tracks");
|
||||
req.AddQuery("position", Str_8::FromNum(pos));
|
||||
req.AddQuery("uris", "spotify:track:" + trackId);
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return AddTrack(playlistId, trackId, pos);
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Skip()
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::POST, "/v1/me/player/next");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Skip();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Previous()
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::POST, "/v1/me/player/previous");
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Previous();
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
UInt_32 Spotify::Seek(const UInt_32 pos)
|
||||
{
|
||||
StartConnection();
|
||||
|
||||
Request req(Verb::PUT, "/v1/me/player/seek");
|
||||
req.AddQuery("position_ms", Str_8::FromNum(pos));
|
||||
req.BearerAuth(token);
|
||||
|
||||
client.SendReq(req);
|
||||
|
||||
Response res = client.RecvRes();
|
||||
if (res.GetCode() == 401)
|
||||
{
|
||||
ReAuthorize();
|
||||
return Seek(pos);
|
||||
}
|
||||
|
||||
return res.GetCode();
|
||||
}
|
||||
|
||||
void Spotify::StartConnection()
|
||||
{
|
||||
client.Release();
|
||||
client.Initialize();
|
||||
client.Connect("api.spotify.com", SSL::HTTPS_Port);
|
||||
}
|
||||
|
||||
bool Spotify::ReAuthorize()
|
||||
{
|
||||
SSL accounts;
|
||||
accounts.Initialize();
|
||||
accounts.Connect("accounts.spotify.com", SSL::HTTPS_Port);
|
||||
|
||||
Request reAuth(Verb::POST, "/api/token");
|
||||
reAuth.SetContentType(ContentType::APP_FORMURLENCODED);
|
||||
reAuth.BasicAuth(clientId, secret);
|
||||
reAuth.AddToBody("grant_type", "refresh_token");
|
||||
reAuth.AddToBody("refresh_token", rToken);
|
||||
|
||||
accounts.SendReq(reAuth);
|
||||
|
||||
Response res = accounts.RecvRes();
|
||||
|
||||
accounts.Release();
|
||||
|
||||
if (res.GetCode() != 200)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to reauthorize with Spotify with code #" + Str_8::FromNum(res.GetCode()) + ".");
|
||||
client.Release();
|
||||
return false;
|
||||
}
|
||||
|
||||
Json json = res.GetJson();
|
||||
|
||||
JsonObj* obj = (JsonObj*)json.GetValue();
|
||||
|
||||
JsonVar* tokenVar = obj->GetVar("access_token");
|
||||
if (!tokenVar)
|
||||
return false;
|
||||
|
||||
token = ((JsonStr*)tokenVar->GetValue())->value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Spotify::IsActive() const
|
||||
{
|
||||
return client.IsConnected();
|
||||
}
|
||||
|
||||
Str_8 Spotify::GetClientId() const
|
||||
{
|
||||
return clientId;
|
||||
}
|
||||
|
||||
Str_8 Spotify::GetSecret() const
|
||||
{
|
||||
return secret;
|
||||
}
|
||||
|
||||
Str_8 Spotify::GetRedURI() const
|
||||
{
|
||||
return redURI;
|
||||
}
|
||||
|
||||
bool Spotify::IsVerificationForced() const
|
||||
{
|
||||
return forceVerify;
|
||||
}
|
||||
}
|
150
src/IO/Socket/RestAPIs/Twitch.cpp
Normal file
150
src/IO/Socket/RestAPIs/Twitch.cpp
Normal file
@@ -0,0 +1,150 @@
|
||||
#include "../../../../include/IO/Socket/RestAPIs/Twitch.h"
|
||||
#include "../../../../include/IO/Socket/DNS.h"
|
||||
#include "../../../../include/System/System.h"
|
||||
#include "../../../../include/URI.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
Twitch::~Twitch()
|
||||
{
|
||||
client.Release();
|
||||
}
|
||||
|
||||
Twitch::Twitch()
|
||||
: forceVerify(false)
|
||||
{
|
||||
}
|
||||
|
||||
Twitch::Twitch(const Str_8& clientId, const Str_8& secret, const Str_8& redURI, const Array<Str_8>& scopes, const bool forceVerify)
|
||||
: client(AddrType::IPV4), clientId(clientId), secret(secret), redURI(redURI), scopes(scopes), forceVerify(forceVerify)
|
||||
{
|
||||
}
|
||||
|
||||
bool Twitch::Authorize()
|
||||
{
|
||||
Str_8 scopesFinal;
|
||||
|
||||
for (UInt_64 i = 0; i < scopes.Size(); ++i)
|
||||
{
|
||||
scopesFinal += scopes[i];
|
||||
|
||||
if (i < scopes.Size() - 1)
|
||||
scopesFinal += "%20";
|
||||
}
|
||||
|
||||
Str_8 rURI = URI::Encode(redURI);
|
||||
|
||||
Str_8 uri = "https://id.twitch.tv/oauth2/authorize?client_id=" + clientId + "&redirect_uri=" + rURI +
|
||||
"&response_type=code&force_verify=" + (forceVerify ? "true" : "false") + "&scope=" +
|
||||
scopesFinal;
|
||||
|
||||
TCP server(AddrType::IPV4);
|
||||
server.Bind(DNS::Resolve(client.GetAddressType(), "localhost"), 65535);
|
||||
server.Listen();
|
||||
|
||||
System::OpenURI(uri);
|
||||
|
||||
TCP* cbClient = server.Accept();
|
||||
|
||||
Request cbReq = cbClient->RecvReq();
|
||||
|
||||
if (cbReq.GetResource() != "/callback")
|
||||
{
|
||||
Response resp(423, "Event Horizon");
|
||||
resp.SetContentType(ContentType::TEXT_HTML);
|
||||
resp.SetBody(
|
||||
"<!DOCTYPE html><html><head><title>LWE Response</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn3.iconfinder.com/data/icons/contour-animals-2/512/wolf-512.png\" /></head><body>Hostile Information Received</body></html>");
|
||||
|
||||
cbClient->SendRes(resp);
|
||||
cbClient->Release();
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Response resp(200, "Event Horizon");
|
||||
resp.SetContentType(ContentType::TEXT_HTML);
|
||||
resp.SetBody(
|
||||
"<!DOCTYPE html><html><head><title>LWE Response</title><link rel=\"icon\" type=\"image/png\" href=\"https://cdn3.iconfinder.com/data/icons/contour-animals-2/512/wolf-512.png\" /></head><body>Authentication Successful</body></html>");
|
||||
|
||||
cbClient->SendRes(resp);
|
||||
cbClient->Release();
|
||||
|
||||
server.Release();
|
||||
|
||||
client.Initialize();
|
||||
client.Connect("id.twitch.tv", SSL::HTTPS_Port);
|
||||
|
||||
Request authReq(Verb::POST, "/oauth2/token");
|
||||
authReq.SetContentType(ContentType::APP_FORMURLENCODED);
|
||||
authReq.AddToBody("client_id", clientId);
|
||||
authReq.AddToBody("client_secret", secret);
|
||||
authReq.AddToBody("code", cbReq.GetQuery("code"));
|
||||
authReq.AddToBody("grant_type", "authorization_code");
|
||||
authReq.AddToBody("redirect_uri", redURI);
|
||||
|
||||
client.SendReq(authReq);
|
||||
|
||||
Response authRes = client.RecvRes();
|
||||
if (authRes.GetCode() == 400)
|
||||
{
|
||||
client.Release();
|
||||
|
||||
LWE_LOG_INT("Error", 0, "Could not authorize with Twitch because the client id was invalid.");
|
||||
|
||||
return false;
|
||||
} else if (authRes.GetCode() == 403)
|
||||
{
|
||||
client.Release();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Could not authorize with Twitch because the secret was invalid.");
|
||||
|
||||
return false;
|
||||
} else if (authRes.GetCode() != 200)
|
||||
{
|
||||
client.Release();
|
||||
|
||||
LWE_LOG_INT("Error", 2, "Could not authorize with Twitch.");
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Json authResJson = authRes.GetJson();
|
||||
|
||||
JsonObj* value = (JsonObj*) authResJson.GetValue();
|
||||
if (!value)
|
||||
return false;
|
||||
|
||||
JsonVar* var = value->GetVar("access_token");
|
||||
if (!var)
|
||||
return false;
|
||||
|
||||
token = ((JsonStr*) var->GetValue())->value;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
Str_8 Twitch::GetClientId() const
|
||||
{
|
||||
return clientId;
|
||||
}
|
||||
|
||||
Str_8 Twitch::GetSecret() const
|
||||
{
|
||||
return secret;
|
||||
}
|
||||
|
||||
Str_8 Twitch::GetRedURI() const
|
||||
{
|
||||
return redURI;
|
||||
}
|
||||
|
||||
bool Twitch::IsVerificationForced() const
|
||||
{
|
||||
return forceVerify;
|
||||
}
|
||||
|
||||
Str_8 Twitch::GetToken() const
|
||||
{
|
||||
return token;
|
||||
}
|
||||
}
|
177
src/IO/Socket/RestAPIs/TwitchChat.cpp
Normal file
177
src/IO/Socket/RestAPIs/TwitchChat.cpp
Normal file
@@ -0,0 +1,177 @@
|
||||
#include "../../../../include/IO/Socket/RestAPIs/TwitchChat.h"
|
||||
#include "../../../../include/IO/Socket/DNS.h"
|
||||
#include "../../../../include/IO/Console.h"
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
TwitchChat::~TwitchChat()
|
||||
{
|
||||
UnInitialize();
|
||||
}
|
||||
|
||||
TwitchChat::TwitchChat()
|
||||
: initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
TwitchChat::TwitchChat(const Str_8& username)
|
||||
: username(username), initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
TwitchChat::TwitchChat(const Str_8& username, const Str_8& token)
|
||||
: username(username), token(token), initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
TwitchChat::TwitchChat(const TwitchChat& chat)
|
||||
: username(chat.username), token(chat.token), initialized(false)
|
||||
{
|
||||
}
|
||||
|
||||
TwitchChat& TwitchChat::operator=(const TwitchChat& chat)
|
||||
{
|
||||
if (this == &chat)
|
||||
return *this;
|
||||
|
||||
client = TCP();
|
||||
username = chat.username;
|
||||
token = chat.token;
|
||||
channel = Str_8();
|
||||
initialized = false;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TwitchChat::SetToken(const Str_8& newToken)
|
||||
{
|
||||
token = newToken;
|
||||
}
|
||||
|
||||
void TwitchChat::Initialize()
|
||||
{
|
||||
if (initialized)
|
||||
return;
|
||||
|
||||
client = TCP(lwe::AddrType::IPV4);
|
||||
client.Connect(DNS::Resolve(AddrType::IPV4, "irc.chat.twitch.tv"), 6667);
|
||||
client.SetBlocking(false);
|
||||
|
||||
Str_8 r("PASS oauth:" + token + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
|
||||
r = "NICK " + username + "\r\n";
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
|
||||
initialized = true;
|
||||
}
|
||||
|
||||
void TwitchChat::UnInitialize()
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
client.Release();
|
||||
|
||||
initialized = false;
|
||||
}
|
||||
|
||||
void TwitchChat::JoinChannel(const Str_8& newChannel)
|
||||
{
|
||||
if (!initialized || channel == newChannel)
|
||||
return;
|
||||
|
||||
channel = newChannel;
|
||||
|
||||
Str_8 r("Join #" + newChannel + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
}
|
||||
|
||||
void TwitchChat::LeaveChannel()
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
Str_8 r("PART #" + channel + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
}
|
||||
|
||||
void TwitchChat::SendPong()
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
Str_8 r("PONG :tmi.twitch.tv\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
}
|
||||
|
||||
void TwitchChat::SendMsg(const Str_8& msg)
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
Str_8 r("PRIVMSG #" + channel + " :" + msg + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
|
||||
//irc.SendStr(":" + username + "!" + username + "@" + username + ".tmi.twitch.tv PRIVMSG #" + username + " :" + msg + "\r\n");
|
||||
}
|
||||
|
||||
void TwitchChat::WhisperMsg(const Str_8& user, const Str_8& msg)
|
||||
{
|
||||
if (!initialized)
|
||||
return;
|
||||
|
||||
Str_8 r("PRIVMSG #jtv :/w " + user + " " + msg + "\r\n");
|
||||
|
||||
Console::Write_8("< " + r, false);
|
||||
|
||||
client.Send(r.ToBytes(), (int) r.Size());
|
||||
}
|
||||
|
||||
Str_8 TwitchChat::RecvMsg()
|
||||
{
|
||||
Str_8 result;
|
||||
|
||||
Byte received[1024];
|
||||
UInt_64 recvSize = 0;
|
||||
|
||||
do
|
||||
{
|
||||
recvSize = client.Receive(received, 1024);
|
||||
if (recvSize)
|
||||
result.Push((Char_8*) received, recvSize);
|
||||
else
|
||||
break;
|
||||
} while (!result.Find("\r\n", nullptr, SearchPattern::RIGHT_LEFT));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
Str_8 TwitchChat::GetUsername() const
|
||||
{
|
||||
return username;
|
||||
}
|
||||
|
||||
Str_8 TwitchChat::GetChannel() const
|
||||
{
|
||||
return channel;
|
||||
}
|
||||
}
|
236
src/IO/Socket/SSL.cpp
Normal file
236
src/IO/Socket/SSL.cpp
Normal file
@@ -0,0 +1,236 @@
|
||||
#include "../../../include/IO/Socket/SSL.h"
|
||||
|
||||
#include <openssl/ssl.h>
|
||||
#include <openssl/bio.h>
|
||||
#include <openssl/err.h>
|
||||
#include <openssl/pem.h>
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
SSL::~SSL()
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
if (sslHdl)
|
||||
{
|
||||
if (connection)
|
||||
SSL_shutdown(sslHdl);
|
||||
|
||||
SSL_free(sslHdl);
|
||||
}
|
||||
|
||||
if (ctx)
|
||||
SSL_CTX_free(ctx);
|
||||
}
|
||||
|
||||
SSL::SSL()
|
||||
: ctx(nullptr), sslHdl(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SSL::SSL(const AddrType type)
|
||||
: TCP(type), ctx(nullptr), sslHdl(nullptr)
|
||||
{
|
||||
SSL::Initialize();
|
||||
}
|
||||
|
||||
SSL::SSL(TCP&& tcp) noexcept
|
||||
: TCP(std::move(tcp)), ctx(nullptr), sslHdl(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SSL::SSL(const TCP& tcp)
|
||||
: TCP(tcp), ctx(nullptr), sslHdl(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SSL::SSL(const SSL& ssl)
|
||||
: TCP(ssl), ctx(nullptr), sslHdl(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
SSL& SSL::operator=(const SSL& ssl)
|
||||
{
|
||||
if (this == &ssl)
|
||||
return *this;
|
||||
|
||||
TCP::operator=(ssl);
|
||||
|
||||
ctx = nullptr;
|
||||
sslHdl = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void SSL::Initialize()
|
||||
{
|
||||
TCP::Initialize();
|
||||
|
||||
if (IsValid())
|
||||
return;
|
||||
|
||||
SSL_library_init();
|
||||
}
|
||||
|
||||
void SSL::Release()
|
||||
{
|
||||
TCP::Release();
|
||||
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
if (sslHdl)
|
||||
{
|
||||
if (connection)
|
||||
SSL_shutdown(sslHdl);
|
||||
|
||||
SSL_free(sslHdl);
|
||||
|
||||
sslHdl = nullptr;
|
||||
}
|
||||
|
||||
if (ctx)
|
||||
{
|
||||
SSL_CTX_free(ctx);
|
||||
ctx = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void SSL::Bind(const Str_8& address, unsigned short port)
|
||||
{
|
||||
if (bound)
|
||||
return;
|
||||
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
ctx = SSL_CTX_new(SSLv23_server_method());
|
||||
|
||||
sslHdl = SSL_new(ctx);
|
||||
SSL_set_fd(sslHdl, hdl);
|
||||
|
||||
TCP::Bind(address, port);
|
||||
}
|
||||
|
||||
SSL* SSL::Accept()
|
||||
{
|
||||
if (!bound)
|
||||
return nullptr;
|
||||
|
||||
TCP* tcp = TCP::Accept();
|
||||
|
||||
SSL* client = new SSL(std::move(*tcp));
|
||||
|
||||
delete tcp;
|
||||
|
||||
client->ctx = nullptr;
|
||||
client->sslHdl = SSL_new(ctx);
|
||||
SSL_set_fd(client->sslHdl, client->hdl);
|
||||
|
||||
int err = SSL_accept(client->sslHdl);
|
||||
if (!err)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed SSL handshake with error #" + Str_8::FromNum(SSL_get_error(client->sslHdl, err)) + ".");
|
||||
return {};
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void SSL::Connect(const Str_8& address, const UInt_16 port)
|
||||
{
|
||||
if (bound)
|
||||
return;
|
||||
|
||||
TCP::Connect(address, port);
|
||||
OpenSSL_add_all_algorithms();
|
||||
SSL_load_error_strings();
|
||||
ctx = SSL_CTX_new(SSLv23_client_method());
|
||||
sslHdl = SSL_new(ctx);
|
||||
SSL_set_fd(sslHdl, hdl);
|
||||
SSL_connect(sslHdl);
|
||||
}
|
||||
|
||||
UInt_64 SSL::Send(const Byte* const buffer, const UInt_32 size)
|
||||
{
|
||||
int written = SSL_write(sslHdl, buffer, (int)size);
|
||||
if (written <= 0)
|
||||
{
|
||||
int code = SSL_get_error(sslHdl, written);
|
||||
ERR_print_errors_fp(stderr);
|
||||
LWE_LOG_INT("Error", 0, "Failed to send data with error #" + Str_8::FromNum(code) + ".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return written;
|
||||
}
|
||||
|
||||
UInt_64 SSL::Receive(Byte* const buffer, const UInt_32 size)
|
||||
{
|
||||
int received = SSL_read(sslHdl, buffer, (int)size);
|
||||
if (received <= 0)
|
||||
{
|
||||
int code = SSL_get_error(sslHdl, received);
|
||||
ERR_print_errors_fp(stderr);
|
||||
LWE_LOG_INT("Error", 0, "Failed to receive data with error #" + Str_8::FromNum(code) + ".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
void SSL::UseCertificate(const Byte* data, const UInt_64 size)
|
||||
{
|
||||
X509 *cert = d2i_X509(nullptr, &data, (long)size);
|
||||
if (!cert)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Invalid certificate.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_certificate(ctx, cert) != 1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Failed to use certificate.");
|
||||
return;
|
||||
}
|
||||
|
||||
X509_free(cert);
|
||||
}
|
||||
|
||||
void SSL::UsePrivateKey(const Byte* data, const UInt_64 size)
|
||||
{
|
||||
EVP_PKEY *key = d2i_PrivateKey(EVP_PKEY_RSA, nullptr, &data, (long)size);
|
||||
if (!key)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Invalid private key.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (SSL_CTX_use_PrivateKey(ctx, key) != 1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Failed to use private key.");
|
||||
return;
|
||||
}
|
||||
|
||||
EVP_PKEY_free(key);
|
||||
}
|
||||
|
||||
bool SSL::IsValid()
|
||||
{
|
||||
return TCP::IsValid() && sslHdl;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
1316
src/IO/Socket/Socket.cpp
Normal file
1316
src/IO/Socket/Socket.cpp
Normal file
File diff suppressed because it is too large
Load Diff
478
src/IO/Socket/TCP_BSD.cpp
Normal file
478
src/IO/Socket/TCP_BSD.cpp
Normal file
@@ -0,0 +1,478 @@
|
||||
#include "../../../include/IO/Socket/TCP_BSD.h"
|
||||
#include "../../../include/IO/Socket/DNS.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cerrno>
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
TCP::~TCP()
|
||||
{
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
if (connection)
|
||||
{
|
||||
if (shutdown(hdl, SHUT_RDWR) == -1)
|
||||
LWE_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
if (close(hdl) == -1)
|
||||
LWE_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
TCP::TCP()
|
||||
: hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
TCP::TCP(const AddrType addrType)
|
||||
: BaseTCP(addrType), hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
TCP::Initialize();
|
||||
}
|
||||
|
||||
TCP::TCP(TCP&& tcp) noexcept
|
||||
: BaseTCP(std::move(tcp)), hdl(tcp.hdl)
|
||||
{
|
||||
tcp.hdl = LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
TCP::TCP(const TCP& tcp)
|
||||
: BaseTCP(tcp), hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
TCP& TCP::operator=(TCP&& tcp) noexcept
|
||||
{
|
||||
if (this == &tcp)
|
||||
return *this;
|
||||
|
||||
BaseTCP::operator=(std::move(tcp));
|
||||
|
||||
hdl = tcp.hdl;
|
||||
|
||||
tcp.hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TCP& TCP::operator=(const TCP& tcp)
|
||||
{
|
||||
if (this == &tcp)
|
||||
return *this;
|
||||
|
||||
BaseTCP::operator=(tcp);
|
||||
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TCP::Initialize()
|
||||
{
|
||||
if (IsValid())
|
||||
return;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
hdl = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
hdl = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
else
|
||||
return;
|
||||
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
{
|
||||
UInt_32 code = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Release()
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
if (connection)
|
||||
{
|
||||
if (shutdown(hdl, SHUT_RDWR) == -1)
|
||||
LWE_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
if (close(hdl) == -1)
|
||||
LWE_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
connection = false;
|
||||
bound = false;
|
||||
listening = false;
|
||||
connected = false;
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void TCP::Bind(const Str_8& address, unsigned short port)
|
||||
{
|
||||
if (!IsValid() || bound || connection)
|
||||
return;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
Bind_v6(address, port);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
Bind_v4(address, port);
|
||||
|
||||
this->localAddr = address;
|
||||
this->localPort = port;
|
||||
|
||||
bound = true;
|
||||
}
|
||||
|
||||
void TCP::Listen()
|
||||
{
|
||||
if (connection || !IsValid() || !bound || listening)
|
||||
return;
|
||||
|
||||
int code = listen(hdl, SOMAXCONN);
|
||||
if (code == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to listen with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
listening = true;
|
||||
}
|
||||
|
||||
TCP* TCP::Accept()
|
||||
{
|
||||
if (connection || !IsValid() || !bound || !listening)
|
||||
return nullptr;
|
||||
|
||||
sockaddr_in6 remote = {};
|
||||
UInt_32 addrLen = sizeof(sockaddr_in6);
|
||||
|
||||
TCP* client = new TCP();
|
||||
client->addrType = addrType;
|
||||
client->localAddr = localAddr;
|
||||
client->localPort = localPort;
|
||||
client->connection = true;
|
||||
client->hdl = accept(hdl, (sockaddr*)&remote, &addrLen);
|
||||
|
||||
if (client->hdl == LWE_INVALID_SOCKET)
|
||||
{
|
||||
if (errno != EWOULDBLOCK)
|
||||
LWE_LOG_INT("Error", 0, "Failed to accept client with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (addrLen == sizeof(sockaddr_in6))
|
||||
{
|
||||
char tmpAddr[INET6_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN))
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert IPv6 address with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
client->remoteAddr = tmpAddr;
|
||||
client->remotePort = ntohs(remote.sin6_port);
|
||||
}
|
||||
else if (addrLen == sizeof(sockaddr_in))
|
||||
{
|
||||
char tmpAddr[INET_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN))
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert IPv4 address with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
client->remoteAddr = tmpAddr;
|
||||
client->remotePort = ntohs(((sockaddr_in*)&remote)->sin_port);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void TCP::Connect(const Str_8& address, const unsigned short port)
|
||||
{
|
||||
if (connection || !IsValid() || listening)
|
||||
return;
|
||||
|
||||
remoteHostName = address;
|
||||
remoteAddr = DNS::Resolve(addrType, address);
|
||||
remotePort = port;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
Connect_v6(remoteAddr, port);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
Connect_v4(remoteAddr, port);
|
||||
|
||||
connected = true;
|
||||
}
|
||||
|
||||
UInt_64 TCP::Send(const Byte *const buffer, const UInt_32 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((!connection && !connected))
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Attempted to send while socket is not connected or a connection.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SInt_64 sent = send(hdl, (char*)buffer, size, 0);
|
||||
if (sent == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if (err == ECONNRESET)
|
||||
{
|
||||
Release();
|
||||
LWE_LOG_INT("Information", 0, "Connection dropped.");
|
||||
}
|
||||
else
|
||||
LWE_LOG_INT("Error", 1, "Failed to send with error #" + Str_8::FromNum(err) + ".");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt_64)sent;
|
||||
}
|
||||
|
||||
UInt_64 TCP::Receive(Byte* const buffer, const UInt_32 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((!connection && !connected))
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Attempted to receive while socket is not connected or a connection.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SInt_64 received = recv(hdl, (char*)buffer, size, 0);
|
||||
if (received == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if (err == ECONNRESET)
|
||||
{
|
||||
Release();
|
||||
LWE_LOG_INT("Information", 0, "Connection dropped.");
|
||||
}
|
||||
else if (err != EWOULDBLOCK)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to receive with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt_64)received;
|
||||
}
|
||||
|
||||
void TCP::SetBlocking(const bool blocking)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
int flags = fcntl(hdl, F_GETFL, 0);
|
||||
if (flags == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to retrieve flags.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (blocking)
|
||||
flags ^= O_NONBLOCK;
|
||||
else
|
||||
flags |= O_NONBLOCK;
|
||||
|
||||
if (fcntl(hdl, F_SETFL, flags) == -1)
|
||||
LWE_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
bool TCP::IsBlocking() const
|
||||
{
|
||||
int flags = fcntl(hdl, F_GETFL, 0);
|
||||
if (flags == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to retrieve flags.");
|
||||
return true;
|
||||
}
|
||||
|
||||
return !(flags & O_NONBLOCK);
|
||||
}
|
||||
|
||||
bool TCP::IsValid() const
|
||||
{
|
||||
return hdl != LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void TCP::Bind_v6(const Str_8& address, unsigned short port)
|
||||
{
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
int code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sin6_addr = in6addr_any;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (code == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Bind_v4(const Str_8& address, unsigned short port)
|
||||
{
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
{
|
||||
result.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (code == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Connect_v6(const Str_8& address, unsigned short port)
|
||||
{
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (code == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
LWE_LOG_INT("Information", 2, "Connection attempt timed-out.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LWE_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Connect_v4(const Str_8& address, unsigned short port)
|
||||
{
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (code == -1)
|
||||
{
|
||||
int err = errno;
|
||||
if (err == ETIMEDOUT)
|
||||
{
|
||||
LWE_LOG_INT("Information", 2, "Connection attempt timed-out.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LWE_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
500
src/IO/Socket/TCP_W32.cpp
Normal file
500
src/IO/Socket/TCP_W32.cpp
Normal file
@@ -0,0 +1,500 @@
|
||||
#include "../../../include/IO/Socket/TCP_W32.h"
|
||||
#include "../../../include/IO/Socket/DNS.h"
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
TCP::~TCP()
|
||||
{
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
Int_32 code;
|
||||
|
||||
if (connection)
|
||||
{
|
||||
code = shutdown(hdl, SD_SEND);
|
||||
if (code == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||||
}
|
||||
|
||||
code = closesocket(hdl);
|
||||
if (code == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||||
|
||||
if (!connection && WSACleanup() == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
}
|
||||
|
||||
TCP::TCP()
|
||||
: hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
TCP::TCP(const AddrType addrType)
|
||||
: BaseTCP(addrType), hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
TCP::Initialize();
|
||||
}
|
||||
|
||||
TCP::TCP(TCP&& tcp) noexcept
|
||||
: BaseTCP(std::move(tcp)), hdl(tcp.hdl)
|
||||
{
|
||||
tcp.hdl = LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
TCP::TCP(const TCP& tcp)
|
||||
: BaseTCP(tcp), hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
TCP::Initialize();
|
||||
}
|
||||
|
||||
TCP& TCP::operator=(TCP&& tcp) noexcept
|
||||
{
|
||||
if (this == &tcp)
|
||||
return *this;
|
||||
|
||||
BaseTCP::operator=(tcp);
|
||||
|
||||
hdl = tcp.hdl;
|
||||
|
||||
tcp.hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
TCP& TCP::operator=(const TCP& tcp)
|
||||
{
|
||||
if (this == &tcp)
|
||||
return *this;
|
||||
|
||||
BaseTCP::operator=(tcp);
|
||||
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
TCP::Initialize();
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void TCP::Initialize()
|
||||
{
|
||||
WSADATA data = {};
|
||||
|
||||
int code = WSAStartup(MAKEWORD(2, 2), &data);
|
||||
if (code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "WSAStartup failed with the error #" + Str_8::FromNum(code) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
hdl = socket(AF_INET6, SOCK_STREAM, IPPROTO_TCP);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
hdl = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
|
||||
else
|
||||
return;
|
||||
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
{
|
||||
UInt_32 code = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
if (WSACleanup() == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Release()
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
Int_32 code;
|
||||
|
||||
if (connection)
|
||||
{
|
||||
code = shutdown(hdl, SD_SEND);
|
||||
if (code == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 0, "Failed to shutdown socket with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||||
}
|
||||
|
||||
code = closesocket(hdl);
|
||||
if (code == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 1, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||||
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
if (!connection && WSACleanup() == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
|
||||
connection = false;
|
||||
bound = false;
|
||||
listening = false;
|
||||
connected = false;
|
||||
}
|
||||
|
||||
void TCP::Bind(const Str_8& address, unsigned short port)
|
||||
{
|
||||
if (!IsValid() || bound || connection)
|
||||
return;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
Bind_v6(address, port);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
Bind_v4(address, port);
|
||||
|
||||
this->localAddr = address;
|
||||
this->localPort = port;
|
||||
|
||||
bound = true;
|
||||
}
|
||||
|
||||
void TCP::Listen()
|
||||
{
|
||||
if (connection || !IsValid() || !bound || listening)
|
||||
return;
|
||||
|
||||
int code = listen(hdl, SOMAXCONN);
|
||||
if (code == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Failed to listen with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
listening = true;
|
||||
}
|
||||
|
||||
TCP* TCP::Accept()
|
||||
{
|
||||
if (connection || !IsValid() || !bound || !listening)
|
||||
return nullptr;
|
||||
|
||||
sockaddr_in6 remote = {};
|
||||
UInt_32 addrLen = sizeof(sockaddr_in6);
|
||||
|
||||
TCP* client = new TCP();
|
||||
client->addrType = addrType;
|
||||
client->localAddr = localAddr;
|
||||
client->localPort = localPort;
|
||||
client->connection = true;
|
||||
client->hdl = accept(hdl, (sockaddr*)&remote, (int*)&addrLen);
|
||||
|
||||
if (client->hdl == LWE_INVALID_SOCKET)
|
||||
{
|
||||
Int_32 code = WSAGetLastError();
|
||||
|
||||
if (code != WSAEWOULDBLOCK)
|
||||
LWE_LOG_INT("Error", 0, "Failed to accept client with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if (addrLen == sizeof(sockaddr_in6))
|
||||
{
|
||||
char tmpAddr[INET6_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN))
|
||||
{
|
||||
Int_32 code = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
client->remoteAddr = tmpAddr;
|
||||
client->remotePort = ntohs(remote.sin6_port);
|
||||
}
|
||||
else if (addrLen == sizeof(sockaddr_in))
|
||||
{
|
||||
char tmpAddr[INET_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN))
|
||||
{
|
||||
Int_32 code = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
delete client;
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
client->remoteAddr = tmpAddr;
|
||||
client->remotePort = ntohs(((sockaddr_in*)&remote)->sin_port);
|
||||
}
|
||||
|
||||
return client;
|
||||
}
|
||||
|
||||
void TCP::Connect(const Str_8& address, const unsigned short port)
|
||||
{
|
||||
if (connection || !IsValid() || listening)
|
||||
return;
|
||||
|
||||
remoteHostName = address;
|
||||
remoteAddr = DNS::Resolve(addrType, address);
|
||||
remotePort = port;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
Connect_v6(remoteAddr, port);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
Connect_v4(remoteAddr, port);
|
||||
|
||||
connected = true;
|
||||
}
|
||||
|
||||
UInt_64 TCP::Send(const Byte *const buffer, const UInt_32 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((!connection && !connected))
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Attempted to send while socket is not connected or a connection.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SInt_64 sent = (SInt_64)send(hdl, (char*)buffer, (int)size, 0);
|
||||
if (sent == SOCKET_ERROR)
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if (err == WSAECONNRESET)
|
||||
{
|
||||
Release();
|
||||
LWE_LOG_INT("Information", 0, "Connection dropped.");
|
||||
}
|
||||
else
|
||||
LWE_LOG_INT("Error", 1, "Failed to send with error #" + Str_8::FromNum(err) + ".");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt_64)sent;
|
||||
}
|
||||
|
||||
UInt_64 TCP::Receive(Byte* const buffer, const UInt_32 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
if ((!connection && !connected))
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "Attempted to receive while socket is not connected or a connection.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
SInt_64 received = (SInt_64)recv(hdl, (char*)buffer, (int)size, 0);
|
||||
if (received == SOCKET_ERROR)
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if (err == WSAECONNRESET)
|
||||
{
|
||||
Release();
|
||||
LWE_LOG_INT("Information", 0, "Connection dropped.");
|
||||
}
|
||||
else if (err == WSAECONNABORTED)
|
||||
{
|
||||
LWE_LOG_INT("Information", 1, "Receiving timed-out.");
|
||||
}
|
||||
else if (err != WSAEWOULDBLOCK)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to receive with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (UInt_64)received;
|
||||
}
|
||||
|
||||
void TCP::SetBlocking(const bool blocking)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
u_long r = (u_long)!blocking;
|
||||
|
||||
int result = ioctlsocket(hdl, FIONBIO, &r);
|
||||
if (result != NO_ERROR)
|
||||
LWE_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(result) + ".");
|
||||
}
|
||||
|
||||
bool TCP::IsBlocking() const
|
||||
{
|
||||
u_long r = 0;
|
||||
if (ioctlsocket(hdl, FIONREAD, &r) == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 0, "Failed to retrieve socket info.");
|
||||
|
||||
return (bool)r;
|
||||
}
|
||||
|
||||
bool TCP::IsValid() const
|
||||
{
|
||||
return hdl != LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void TCP::Bind_v6(const Str_8& address, unsigned short port)
|
||||
{
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
int code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sin6_addr = in6addr_any;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (code == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Bind_v4(const Str_8& address, unsigned short port)
|
||||
{
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sin_addr.S_un.S_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (code == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Connect_v6(const Str_8& address, unsigned short port)
|
||||
{
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (code == SOCKET_ERROR)
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if (err == WSAETIMEDOUT)
|
||||
{
|
||||
LWE_LOG_INT("Information", 2, "Connection attempt timed-out.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LWE_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void TCP::Connect_v4(const Str_8& address, unsigned short port)
|
||||
{
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
code = connect(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (code == SOCKET_ERROR)
|
||||
{
|
||||
int err = WSAGetLastError();
|
||||
if (err == WSAETIMEDOUT)
|
||||
{
|
||||
LWE_LOG_INT("Information", 2, "Connection attempt timed-out.");
|
||||
}
|
||||
else
|
||||
{
|
||||
LWE_LOG_INT("Error", 3, "Failed to connect with error #" + Str_8::FromNum(err) + ".");
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
362
src/IO/Socket/UDP_BSD.cpp
Normal file
362
src/IO/Socket/UDP_BSD.cpp
Normal file
@@ -0,0 +1,362 @@
|
||||
#include "../../../include/IO/Socket/UDP_BSD.h"
|
||||
#include "../../../include/Log.h"
|
||||
|
||||
#include <sys/socket.h>
|
||||
#include <netinet/in.h>
|
||||
#include <netinet/tcp.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <netdb.h>
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <cerrno>
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
UDP::~UDP()
|
||||
{
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
Int_32 code = close(hdl);
|
||||
if (code == -1)
|
||||
LWE_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
UDP::UDP()
|
||||
: hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
UDP::UDP(const AddrType addrType)
|
||||
: BaseUDP(addrType), hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
if (addrType == AddrType::IPV6)
|
||||
hdl = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
hdl = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
else
|
||||
return;
|
||||
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
{
|
||||
UInt_32 code = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UDP::UDP(UDP&& udp) noexcept
|
||||
: BaseUDP(std::move(udp)), hdl(udp.hdl)
|
||||
{
|
||||
udp.hdl = LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
UDP::UDP(const UDP& udp)
|
||||
: BaseUDP(udp), hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
UDP& UDP::operator=(UDP&& udp) noexcept
|
||||
{
|
||||
if (this == &udp)
|
||||
return *this;
|
||||
|
||||
BaseUDP::operator=(std::move(udp));
|
||||
|
||||
hdl = udp.hdl;
|
||||
|
||||
udp.hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
UDP& UDP::operator=(const UDP& udp)
|
||||
{
|
||||
if (this == &udp)
|
||||
return *this;
|
||||
|
||||
BaseUDP::operator=(udp);
|
||||
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void UDP::Release()
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
Int_32 code = close(hdl);
|
||||
if (code == -1)
|
||||
LWE_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
bound = false;
|
||||
}
|
||||
|
||||
void UDP::Bind(const Str_8& address, const UInt_16 port)
|
||||
{
|
||||
if (!IsValid() || bound)
|
||||
return;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
Bind_v6(address, port);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
Bind_v4(address, port);
|
||||
|
||||
this->address = address;
|
||||
this->port = port;
|
||||
|
||||
bound = true;
|
||||
}
|
||||
|
||||
UInt_64 UDP::Send(const Str_8& address, const UInt_16 port, const Byte *const data, const UInt_64 size)
|
||||
{
|
||||
if (addrType == AddrType::IPV6)
|
||||
return Send_v6(address, port, data, size);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
return Send_v4(address, port, data, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UInt_64 UDP::Receive(Str_8* const address, UInt_16* const port, Byte* const data, const UInt_64 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in6 remote = {};
|
||||
UInt_32 addrLen = sizeof(sockaddr_in);
|
||||
SInt_64 received = 0;
|
||||
|
||||
received = recvfrom(hdl, data, size, 0, (sockaddr*)&remote, &addrLen);
|
||||
if (received == -1)
|
||||
{
|
||||
int code = errno;
|
||||
if (code != ECONNRESET && code != EWOULDBLOCK)
|
||||
{
|
||||
Release();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to receive with error #" + Str_8::FromNum(code) + ".");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (addrLen == sizeof(sockaddr_in6))
|
||||
{
|
||||
char tmpAddr[INET6_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN))
|
||||
{
|
||||
Int_32 code = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 2, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
*address = tmpAddr;
|
||||
*port = ntohs(remote.sin6_port);
|
||||
}
|
||||
else if (addrLen == sizeof(sockaddr_in))
|
||||
{
|
||||
char tmpAddr[INET_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN))
|
||||
{
|
||||
Int_32 code = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 2, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
*address = tmpAddr;
|
||||
*port = ntohs(((sockaddr_in*)&remote)->sin_port);
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
void UDP::SetBlocking(const bool blocking)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (fcntl(hdl, F_SETFL, O_NONBLOCK, blocking) == -1)
|
||||
LWE_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(errno) + ".");
|
||||
}
|
||||
|
||||
bool UDP::IsBlocking() const
|
||||
{
|
||||
return (bool)fcntl(hdl, F_GETFL, O_NONBLOCK);
|
||||
}
|
||||
|
||||
bool UDP::IsValid() const
|
||||
{
|
||||
return hdl != LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void UDP::Bind_v6(const Str_8& address, const UInt_16 port)
|
||||
{
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sin6_addr = in6addr_any;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (code == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UDP::Bind_v4(const Str_8& address, const UInt_16 port)
|
||||
{
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sin_addr.s_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (code == -1)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(errno) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UInt_64 UDP::Send_v6(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Info", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "The given address, \"" + address + "\" is not valid.");
|
||||
return 0;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int_32 sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (sent == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
||||
|
||||
Release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
UInt_64 UDP::Send_v4(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Info", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "The given address, \"" + address + "\" is not valid.");
|
||||
return 0;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (sent == -1)
|
||||
{
|
||||
Int_32 dCode = errno;
|
||||
|
||||
LWE_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
||||
|
||||
Release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
};
|
380
src/IO/Socket/UDP_W32.cpp
Normal file
380
src/IO/Socket/UDP_W32.cpp
Normal file
@@ -0,0 +1,380 @@
|
||||
#include "../../../include/IO/Socket/UDP_W32.h"
|
||||
#include "../../../include/Log.h"
|
||||
|
||||
#include <WinSock2.h>
|
||||
#include <WS2tcpip.h>
|
||||
|
||||
namespace lwe
|
||||
{
|
||||
UDP::~UDP()
|
||||
{
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
return;
|
||||
|
||||
Int_32 code = closesocket(hdl);
|
||||
if (code == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||||
|
||||
if (WSACleanup() == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 1, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
}
|
||||
|
||||
UDP::UDP()
|
||||
: hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
UDP::UDP(const AddrType addrType)
|
||||
: BaseUDP(addrType), hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
WSADATA data = {};
|
||||
|
||||
int code = WSAStartup(MAKEWORD(2, 2), &data);
|
||||
if (code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "WSAStartup failed with the error #" + Str_8::FromNum(code) + ".");
|
||||
return;
|
||||
}
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
hdl = socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
hdl = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
|
||||
else
|
||||
return;
|
||||
|
||||
if (hdl == LWE_INVALID_SOCKET)
|
||||
{
|
||||
UInt_32 code = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to create socket with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
if (WSACleanup() == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 2, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UDP::UDP(UDP&& udp) noexcept
|
||||
: BaseUDP(std::move(udp)), hdl(udp.hdl)
|
||||
{
|
||||
udp.hdl = LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
UDP::UDP(const UDP& udp)
|
||||
: BaseUDP(udp), hdl(LWE_INVALID_SOCKET)
|
||||
{
|
||||
}
|
||||
|
||||
UDP& UDP::operator=(UDP&& udp) noexcept
|
||||
{
|
||||
if (this == &udp)
|
||||
return *this;
|
||||
|
||||
BaseUDP::operator=(std::move(udp));
|
||||
|
||||
hdl = udp.hdl;
|
||||
|
||||
udp.hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
UDP& UDP::operator=(const UDP& udp)
|
||||
{
|
||||
if (this == &udp)
|
||||
return *this;
|
||||
|
||||
BaseUDP::operator=(udp);
|
||||
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
void UDP::Release()
|
||||
{
|
||||
if (!IsValid())
|
||||
return;
|
||||
|
||||
Int_32 code = closesocket(hdl);
|
||||
if (code == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 0, "Failed to close socket with error #" + Str_8::FromNum(GetLastError()) + ".");
|
||||
|
||||
hdl = LWE_INVALID_SOCKET;
|
||||
|
||||
if (WSACleanup() == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 1, "Failed to shutdown WSA with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
|
||||
bound = false;
|
||||
}
|
||||
|
||||
void UDP::Bind(const Str_8& address, const UInt_16 port)
|
||||
{
|
||||
if (!IsValid() || bound)
|
||||
return;
|
||||
|
||||
if (addrType == AddrType::IPV6)
|
||||
Bind_v6(address, port);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
Bind_v4(address, port);
|
||||
|
||||
this->address = address;
|
||||
this->port = port;
|
||||
|
||||
bound = true;
|
||||
}
|
||||
|
||||
UInt_64 UDP::Send(const Str_8& address, const UInt_16 port, const Byte *const data, const UInt_64 size)
|
||||
{
|
||||
if (addrType == AddrType::IPV6)
|
||||
return Send_v6(address, port, data, size);
|
||||
else if (addrType == AddrType::IPV4)
|
||||
return Send_v4(address, port, data, size);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
UInt_64 UDP::Receive(Str_8* const address, UInt_16* const port, Byte* const data, const UInt_64 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to receive while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in6 remote = {};
|
||||
UInt_32 addrLen = sizeof(sockaddr_in);
|
||||
|
||||
SInt_64 received = recvfrom(hdl, (char*)data, (int)size, 0, (sockaddr*)&remote, (int*)&addrLen);
|
||||
if (received == SOCKET_ERROR)
|
||||
{
|
||||
int code = WSAGetLastError();
|
||||
if (code != WSAECONNRESET && code != WSAEWOULDBLOCK)
|
||||
{
|
||||
Release();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to receive with error #" + Str_8::FromNum(code) + ".");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (addrLen == sizeof(sockaddr_in6))
|
||||
{
|
||||
char tmpAddr[INET6_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(remote.sin6_family, &remote.sin6_addr, tmpAddr, INET6_ADDRSTRLEN))
|
||||
{
|
||||
Int_32 code = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 2, "Failed to convert IPv6 address with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
*address = tmpAddr;
|
||||
*port = ntohs(remote.sin6_port);
|
||||
}
|
||||
else if (addrLen == sizeof(sockaddr_in))
|
||||
{
|
||||
char tmpAddr[INET_ADDRSTRLEN];
|
||||
|
||||
if (!inet_ntop(((sockaddr_in*)&remote)->sin_family, &((sockaddr_in*)&remote)->sin_addr, tmpAddr, INET_ADDRSTRLEN))
|
||||
{
|
||||
Int_32 code = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 2, "Failed to convert IPv4 address with error #" + Str_8::FromNum(code) + ".");
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
*address = tmpAddr;
|
||||
*port = ntohs(((sockaddr_in*)&remote)->sin_port);
|
||||
}
|
||||
|
||||
return received;
|
||||
}
|
||||
|
||||
void UDP::SetBlocking(const bool blocking)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "Attempted to toggle blocking while socket is not initialized.");
|
||||
return;
|
||||
}
|
||||
|
||||
u_long r = (u_long)!blocking;
|
||||
|
||||
int result = ioctlsocket(hdl, FIONBIO, &r);
|
||||
if (result != NO_ERROR)
|
||||
LWE_LOG_INT("Error", 1, "Failed to toggle non-blocking mode with error #" + Str_8::FromNum(result) + ".");
|
||||
}
|
||||
|
||||
bool UDP::IsBlocking() const
|
||||
{
|
||||
u_long r = 0;
|
||||
if (ioctlsocket(hdl, FIONREAD, &r) == SOCKET_ERROR)
|
||||
LWE_LOG_INT("Error", 0, "Failed to retrieve socket info.");
|
||||
|
||||
return (bool)r;
|
||||
}
|
||||
|
||||
bool UDP::IsValid() const
|
||||
{
|
||||
return hdl != LWE_INVALID_SOCKET;
|
||||
}
|
||||
|
||||
void UDP::Bind_v6(const Str_8& address, const UInt_16 port)
|
||||
{
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
Int_32 code = inet_pton(AF_INET6, address, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sin6_addr = in6addr_any;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (code == SOCKET_ERROR)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void UDP::Bind_v4(const Str_8& address, const UInt_16 port)
|
||||
{
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
if (address.Size())
|
||||
{
|
||||
int code = inet_pton(AF_INET, address, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 0, "The given address, \"" + address + "\" is not valid.");
|
||||
return;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
result.sin_addr.S_un.S_addr = INADDR_ANY;
|
||||
}
|
||||
|
||||
int code = bind(hdl, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (code == SOCKET_ERROR)
|
||||
{
|
||||
LWE_LOG_INT("Error", 2, "Failed to bind socket with error #" + Str_8::FromNum(WSAGetLastError()) + ".");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
UInt_64 UDP::Send_v6(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Info", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in6 result = {};
|
||||
result.sin6_family = AF_INET6;
|
||||
result.sin6_port = htons(port);
|
||||
|
||||
Int_32 code = inet_pton(AF_INET6, addr, &result.sin6_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "The given address, \"" + address + "\" is not valid.");
|
||||
return 0;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
Int_32 sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in6));
|
||||
if (sent == SOCKET_ERROR)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
||||
|
||||
Release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
|
||||
UInt_64 UDP::Send_v4(const Str_8& addr, const UInt_16 port, const Byte* const data, const UInt_64 size)
|
||||
{
|
||||
if (!IsValid())
|
||||
{
|
||||
LWE_LOG_INT("Info", 0, "Attempted to send while socket is not initialized.");
|
||||
return 0;
|
||||
}
|
||||
|
||||
sockaddr_in result = {};
|
||||
result.sin_family = AF_INET;
|
||||
result.sin_port = htons(port);
|
||||
|
||||
int code = inet_pton(AF_INET, addr, &result.sin_addr);
|
||||
if (!code)
|
||||
{
|
||||
LWE_LOG_INT("Error", 1, "The given address, \"" + address + "\" is not valid.");
|
||||
return 0;
|
||||
}
|
||||
else if (code == -1)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 1, "Failed to convert address with error #" + Str_8::FromNum(dCode) + ".");
|
||||
return 0;
|
||||
}
|
||||
|
||||
int sent = sendto(hdl, (char*)&data[0], (int)size, 0, (sockaddr*)&result, sizeof(sockaddr_in));
|
||||
if (sent == SOCKET_ERROR)
|
||||
{
|
||||
Int_32 dCode = WSAGetLastError();
|
||||
|
||||
LWE_LOG_INT("Error", 3, "Failed to send with error #" + Str_8::FromNum(dCode) + ".");
|
||||
|
||||
Release();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
return sent;
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user