Renamed HLAPI to VoidNet_HL and VoidNet to VoidNet_LL
This commit is contained in:
84
src/VoidNet_LL/Cookies.cpp
Normal file
84
src/VoidNet_LL/Cookies.cpp
Normal file
@ -0,0 +1,84 @@
|
||||
// https://github.com/mfichman/http
|
||||
|
||||
#include "VoidNet/Cookies.hpp"
|
||||
#include "VoidNet/Parse.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
ParseResult<std::string> ParseName(const char* str)
|
||||
{
|
||||
return ParseUntil(str, [](char ch)
|
||||
{
|
||||
return isspace(ch) || ch == '=';
|
||||
});
|
||||
}
|
||||
|
||||
ParseResult<std::string> ParseValue(const char* str)
|
||||
{
|
||||
return ParseUntil(str, [](char ch)
|
||||
{
|
||||
return ch == ';' || ch == '=';
|
||||
});
|
||||
}
|
||||
|
||||
ParseResult<std::string> ParseSeparator(const char* str)
|
||||
{
|
||||
if (*str)
|
||||
{
|
||||
assert(*str == ';' || *str == '=');
|
||||
return ParseWhitespace(str + 1);
|
||||
}
|
||||
else
|
||||
{
|
||||
auto result = ParseResult<std::string>{};
|
||||
result.ch = str;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
Cookie ParseCookie(const char* str)
|
||||
{
|
||||
auto name = ParseName(str);
|
||||
auto ch = ParseSeparator(name.ch).ch;
|
||||
auto value = ParseValue(ch);
|
||||
ch = ParseSeparator(value.ch).ch;
|
||||
|
||||
auto cookie = Cookie();
|
||||
cookie.SetName(name.value);
|
||||
cookie.SetValue(value.value);
|
||||
while (*ch)
|
||||
{
|
||||
auto flag = ParseValue(ch);
|
||||
if (flag.value == "Path")
|
||||
{
|
||||
ch = ParseSeparator(flag.ch).ch;
|
||||
flag = ParseValue(ch);
|
||||
cookie.SetPath(flag.value);
|
||||
}
|
||||
else if (flag.value == "HttpOnly")
|
||||
cookie.SetHttpOnly(true);
|
||||
else if (flag.value == "Secure")
|
||||
cookie.SetSecure(true);
|
||||
ch = ParseSeparator(flag.ch).ch;
|
||||
}
|
||||
return cookie;
|
||||
}
|
||||
|
||||
Cookie::Cookie(const std::string& text)
|
||||
{
|
||||
*this = ParseCookie(text.c_str());
|
||||
}
|
||||
|
||||
const Cookie Cookies::operator[](const std::string & name) const
|
||||
{
|
||||
auto i = m_cookie.find(name);
|
||||
return (i == m_cookie.end()) ? Cookie() : i->second;
|
||||
}
|
||||
|
||||
void Cookies::SetCookie(const Cookie& cookie)
|
||||
{
|
||||
m_cookie[cookie.GetName()] = cookie;
|
||||
}
|
||||
}
|
||||
22
src/VoidNet_LL/Headers.cpp
Normal file
22
src/VoidNet_LL/Headers.cpp
Normal file
@ -0,0 +1,22 @@
|
||||
// https://github.com/mfichman/http
|
||||
|
||||
#include "VoidNet/Headers.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
std::string const Headers::HOST("Host");
|
||||
std::string const Headers::CONTENT_LENGTH("Content-Length");
|
||||
std::string const Headers::ACCEPT_ENCODING("Accept-Encoding");
|
||||
std::string const Headers::CONNECTION("Connection");
|
||||
|
||||
const std::string Headers::operator[](const std::string & name) const
|
||||
{
|
||||
auto i = m_header.find(name);
|
||||
return (i == m_header.end()) ? "" : i->second;
|
||||
}
|
||||
|
||||
void Headers::AddHeader(std::string const& name, std::string const& value)
|
||||
{
|
||||
m_header.emplace(name, value);
|
||||
}
|
||||
}
|
||||
120
src/VoidNet_LL/Http.cpp
Normal file
120
src/VoidNet_LL/Http.cpp
Normal file
@ -0,0 +1,120 @@
|
||||
// https://github.com/mfichman/http
|
||||
|
||||
#include "VoidNet/Http.hpp"
|
||||
|
||||
#include <cassert>
|
||||
#include <vector>
|
||||
#include <sstream>
|
||||
|
||||
#include "VoidNet/Socket.hpp"
|
||||
#include "VoidNet/SecureSocket.hpp"
|
||||
|
||||
#undef DELETE
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
Response Http::Send(Request const& request)
|
||||
{
|
||||
// Send an HTTP request. Auto-fill the content-length headers.
|
||||
std::string string = Str(request);
|
||||
|
||||
uint16_t port = 0;
|
||||
std::unique_ptr<Socket> socket;
|
||||
std::unique_ptr<SecureSocket> secure_socket;
|
||||
bool secure = false;
|
||||
if (request.GetUri().GetScheme() == "https")
|
||||
{
|
||||
secure_socket.reset(new SecureSocket());
|
||||
port = 443;
|
||||
secure = true;
|
||||
}
|
||||
else if (request.GetUri().GetScheme() == "http")
|
||||
{
|
||||
socket.reset(new Socket(SocketType::Streaming));
|
||||
port = 80;
|
||||
}
|
||||
else
|
||||
assert(!"unknown http scheme");
|
||||
|
||||
if (request.GetUri().GetPort())
|
||||
port = request.GetUri().GetPort();
|
||||
|
||||
secure ? secure_socket->Connect(IPAddress(request.GetUri().GetHost(), port)) : socket->Connect(IPAddress(request.GetUri().GetHost(), port));
|
||||
int32_t sent;
|
||||
secure ? secure_socket->Recv((uint8_t*)string.c_str(), string.size(), sent) : socket->Send((uint8_t*)string.c_str(), string.size(), sent);
|
||||
|
||||
std::vector<char> buffer(16384); // 16 KiB
|
||||
std::stringstream ss;
|
||||
|
||||
int32_t read;
|
||||
do
|
||||
{
|
||||
secure ? secure_socket->Recv((uint8_t*)&buffer[0], buffer.size(), read) : socket->Recv((uint8_t*)&buffer[0], buffer.size(), read);
|
||||
ss.write(&buffer[0], read);
|
||||
}
|
||||
while (read > 0);
|
||||
secure ? secure_socket->Close() : socket->Close();
|
||||
|
||||
return Response(ss.str());
|
||||
}
|
||||
|
||||
Response Http::Get(std::string const& path, std::string const& data)
|
||||
{
|
||||
// Shortcut for simple GET requests
|
||||
Request request;
|
||||
request.SetMethod(Method::GET);
|
||||
request.SetUri(Uri(path));
|
||||
request.SetData(data);
|
||||
return Send(request);
|
||||
}
|
||||
|
||||
Response Http::Post(std::string const& path, std::string const& data)
|
||||
{
|
||||
// Shortcut for simple POST requests
|
||||
Request request;
|
||||
request.SetMethod(Method::POST);
|
||||
request.SetUri(Uri(path));
|
||||
request.SetData(data);
|
||||
return Send(request);
|
||||
}
|
||||
|
||||
static std::string str_impl(Method method) {
|
||||
switch (method)
|
||||
{
|
||||
case Method::GET:
|
||||
return "GET";
|
||||
case Method::HEAD:
|
||||
return "HEAD";
|
||||
case Method::POST:
|
||||
return "POST";
|
||||
case Method::PUT:
|
||||
return "PUT";
|
||||
case Method::DELETE:
|
||||
return "DELETE";
|
||||
case Method::TRACE:
|
||||
return "TRACE";
|
||||
case Method::CONNECT:
|
||||
return "CONNECT";
|
||||
default:
|
||||
assert(!"unknown request method");
|
||||
}
|
||||
return "";
|
||||
}
|
||||
|
||||
std::string Http::Str(Request const& request)
|
||||
{
|
||||
// Serialize a request to a string
|
||||
std::stringstream ss;
|
||||
auto path = request.GetPath().empty() ? "/" : request.GetPath();
|
||||
ss << str_impl(request.GetMethod()) << ' ' << path << " HTTP/1.1\n";
|
||||
ss << Headers::HOST << ": " << request.GetUri().GetHost() << "\n";
|
||||
ss << Headers::CONTENT_LENGTH << ": " << request.GetData().size() << "\n";
|
||||
ss << Headers::CONNECTION << ": close\n";
|
||||
ss << Headers::ACCEPT_ENCODING << ": identity\n";
|
||||
for (auto header : request.GetHeaders())
|
||||
ss << header.first << ": " << header.second << "\n";
|
||||
ss << "\n";
|
||||
ss << request.GetData();
|
||||
return ss.str();
|
||||
}
|
||||
}
|
||||
62
src/VoidNet_LL/IPAddress.cpp
Normal file
62
src/VoidNet_LL/IPAddress.cpp
Normal file
@ -0,0 +1,62 @@
|
||||
#include "VoidNet/IPAddress.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
const IPAddress IPAddress::None;
|
||||
const IPAddress IPAddress::Any(0, 0, 0, 0);
|
||||
const IPAddress IPAddress::LocalHost(127, 0, 0, 1);
|
||||
const IPAddress IPAddress::Broadcast(255, 255, 255, 255);
|
||||
|
||||
std::string IPAddress::ToString() const
|
||||
{
|
||||
in_addr address;
|
||||
address.s_addr = m_address;
|
||||
|
||||
return inet_ntoa(address);
|
||||
}
|
||||
|
||||
void IPAddress::Resolve(const std::string& address)
|
||||
{
|
||||
m_address = 0;
|
||||
m_valid = false;
|
||||
|
||||
if (address == "255.255.255.255")
|
||||
{
|
||||
// The broadcast address needs to be handled explicitly,
|
||||
// because it is also the value returned by inet_addr on error
|
||||
m_address = INADDR_BROADCAST;
|
||||
m_valid = true;
|
||||
}
|
||||
else if (address == "0.0.0.0")
|
||||
{
|
||||
m_address = INADDR_ANY;
|
||||
m_valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
uint32_t ip = inet_addr(address.c_str());
|
||||
if (ip != INADDR_NONE)
|
||||
{
|
||||
m_address = ip;
|
||||
m_valid = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
addrinfo hints;
|
||||
std::memset(&hints, 0, sizeof(hints));
|
||||
hints.ai_family = AF_INET;
|
||||
addrinfo* result = NULL;
|
||||
if (getaddrinfo(address.c_str(), NULL, &hints, &result) == 0)
|
||||
{
|
||||
if (result)
|
||||
{
|
||||
ip = reinterpret_cast<sockaddr_in*>(result->ai_addr)->sin_addr.s_addr;
|
||||
freeaddrinfo(result);
|
||||
m_address = ip;
|
||||
m_valid = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/VoidNet_LL/Request.cpp
Normal file
31
src/VoidNet_LL/Request.cpp
Normal file
@ -0,0 +1,31 @@
|
||||
// https://github.com/mfichman/http
|
||||
|
||||
#include "VoidNet/Request.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
const std::string Request::GetHeaderElement(const std::string& name) const
|
||||
{
|
||||
return m_headers[name];
|
||||
}
|
||||
|
||||
void Request::SetMethod(Method method)
|
||||
{
|
||||
m_method = method;
|
||||
}
|
||||
|
||||
void Request::SetUri(const Uri& uri)
|
||||
{
|
||||
m_uri = uri;
|
||||
}
|
||||
|
||||
void Request::SetData(const std::string& data)
|
||||
{
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
void Request::AddHeader(const std::string& name, const std::string& value)
|
||||
{
|
||||
m_headers.AddHeader(name, value);
|
||||
}
|
||||
}
|
||||
97
src/VoidNet_LL/Response.cpp
Normal file
97
src/VoidNet_LL/Response.cpp
Normal file
@ -0,0 +1,97 @@
|
||||
// https://github.com/mfichman/http
|
||||
|
||||
#include "VoidNet/Response.hpp"
|
||||
#include "VoidNet/Parse.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
static ParseResult<HttpStatus> ParseStatus(const char* str)
|
||||
{
|
||||
ParseResult<HttpStatus> result
|
||||
{
|
||||
};
|
||||
|
||||
auto code = ParseToken(str);
|
||||
|
||||
result.value = (HttpStatus)std::atoi(code.value.c_str());
|
||||
result.ch = code.ch;
|
||||
return result;
|
||||
}
|
||||
|
||||
Response ParseResponse(const char* str)
|
||||
{
|
||||
// Parse an HTTP response
|
||||
auto version = ParseToken(str);
|
||||
auto code = ParseStatus(version.ch);
|
||||
auto message = ParseUntil(code.ch, [](char ch)
|
||||
{
|
||||
return ch == '\r';
|
||||
});
|
||||
|
||||
auto response = Response();
|
||||
if (version.value != "HTTP/1.1")
|
||||
throw std::runtime_error("bad HTTP version");
|
||||
|
||||
auto ch = parseCrLf(message.ch).ch;
|
||||
while (*ch != '\0' && *ch != '\r')
|
||||
{
|
||||
auto name = ParseUntil(ch, [](char ch)
|
||||
{
|
||||
return ch == ':';
|
||||
});
|
||||
|
||||
if (*name.ch)
|
||||
name.ch++; // For ":"
|
||||
auto ws = ParseWhile(name.ch, isspace);
|
||||
auto value = ParseUntil(ws.ch, [](char ch)
|
||||
{
|
||||
return ch == '\r';
|
||||
});
|
||||
|
||||
response.SetHeader(name.value, value.value);
|
||||
if (name.value == "Set-Cookie")
|
||||
response.SetCookie(Cookie(value.value));
|
||||
ch = parseCrLf(value.ch).ch;
|
||||
}
|
||||
ch = parseCrLf(ch).ch;
|
||||
|
||||
response.SetStatus(code.value);
|
||||
response.SetData(ch);
|
||||
return response;
|
||||
}
|
||||
|
||||
Response::Response(const std::string& response)
|
||||
{
|
||||
*this = ParseResponse(response.c_str());
|
||||
}
|
||||
|
||||
const std::string Response::GetHeader(const std::string& name) const
|
||||
{
|
||||
return m_headers[name];
|
||||
}
|
||||
|
||||
const Cookie Response::GetCookie(const std::string& name) const
|
||||
{
|
||||
return m_cookies[name];
|
||||
}
|
||||
|
||||
void Response::SetStatus(HttpStatus status)
|
||||
{
|
||||
m_status = status;
|
||||
}
|
||||
|
||||
void Response::SetData(const std::string& data)
|
||||
{
|
||||
m_data = data;
|
||||
}
|
||||
|
||||
void Response::SetHeader(const std::string& name, const std::string& value)
|
||||
{
|
||||
m_headers.AddHeader(name, value);
|
||||
}
|
||||
|
||||
void Response::SetCookie(const Cookie& cookie)
|
||||
{
|
||||
m_cookies.SetCookie(cookie);
|
||||
}
|
||||
}
|
||||
163
src/VoidNet_LL/SecureSocket.cpp
Normal file
163
src/VoidNet_LL/SecureSocket.cpp
Normal file
@ -0,0 +1,163 @@
|
||||
#include "VoidNet/SecureSocket.hpp"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
namespace inl::net::sockets
|
||||
{
|
||||
/*SecureSocket::SecureSocket()
|
||||
: m_context(0), m_conn(0), m_eof(false)
|
||||
{
|
||||
m_socket = std::make_unique<Socket>(SocketType::Streaming);
|
||||
|
||||
// Intitialize the SSL client-side socket
|
||||
SSL_library_init();
|
||||
SSL_load_error_strings();
|
||||
ERR_load_BIO_strings();
|
||||
}
|
||||
|
||||
bool SecureSocket::Connect(const IPAddress & addrStr)
|
||||
{
|
||||
if (!m_context)
|
||||
{
|
||||
m_context = SSL_CTX_new(SSLv23_client_method());
|
||||
assert(m_context);
|
||||
}
|
||||
if (!m_conn)
|
||||
{
|
||||
m_conn = SSL_new(m_context);
|
||||
assert(m_conn);
|
||||
|
||||
m_in = BIO_new(BIO_s_mem());
|
||||
m_out = BIO_new(BIO_s_mem());
|
||||
SSL_set_bio(m_conn, m_in, m_out);
|
||||
SSL_set_connect_state(m_conn);
|
||||
}
|
||||
|
||||
return m_socket->Connect(addrStr);
|
||||
}
|
||||
|
||||
bool SecureSocket::Close() const
|
||||
{
|
||||
return m_socket->Close();
|
||||
}
|
||||
|
||||
bool SecureSocket::HasPendingData(uint32_t & pendingDataSize) const
|
||||
{
|
||||
return m_socket->HasPendingData(pendingDataSize);
|
||||
}
|
||||
|
||||
bool SecureSocket::Send(uint8_t* data, int32_t count, int32_t &sent, int flags)
|
||||
{
|
||||
sent = SSL_write(m_conn, data, count);
|
||||
SendFromBio(); // Write data if available
|
||||
if (sent < 0)
|
||||
{
|
||||
HandleReturn(sent);
|
||||
return true;
|
||||
}
|
||||
return sent > 0;
|
||||
}
|
||||
|
||||
bool SecureSocket::SendRaw(uint8_t * buf, size_t len, int flags)
|
||||
{
|
||||
int32_t sent;
|
||||
return m_socket->Send(buf, len, sent) && sent == len;
|
||||
|
||||
}
|
||||
|
||||
bool SecureSocket::SendFromBio(int flags)
|
||||
{
|
||||
uint8_t buf[4096];
|
||||
size_t pending = BIO_ctrl_pending(m_out);
|
||||
if (!pending)
|
||||
return true;
|
||||
size_t bytes = BIO_read(m_out, buf, sizeof(buf));
|
||||
if (bytes > 0)
|
||||
return SendRaw(buf, bytes, flags);
|
||||
else if (bytes == -1 || bytes == 0)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
bool SecureSocket::RecvToBio(int flags)
|
||||
{
|
||||
uint8_t buf[4096];
|
||||
size_t bytes = m_socket->Recv(buf, sizeof(buf), flags);
|
||||
if (bytes > 0)
|
||||
{
|
||||
size_t written = BIO_write(m_in, buf, int(bytes));
|
||||
assert(bytes == written);
|
||||
return true;
|
||||
}
|
||||
else if (bytes == 0)
|
||||
{
|
||||
// No data
|
||||
m_eof = true;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void SecureSocket::HandleReturn(size_t ret)
|
||||
{
|
||||
int32_t err = SSL_get_error(m_conn, ret);
|
||||
if (SSL_ERROR_WANT_WRITE == err)
|
||||
SendFromBio();
|
||||
else if (SSL_ERROR_WANT_READ == err)
|
||||
RecvToBio();
|
||||
else if (SSL_ERROR_SSL == err)
|
||||
throw inl::RuntimeException();
|
||||
else
|
||||
assert(!"unexpected error");
|
||||
}
|
||||
|
||||
bool SecureSocket::Recv(uint8_t* data, int32_t count, int32_t &read, int flags)
|
||||
{
|
||||
read = SSL_read(m_conn, data, count);
|
||||
if (read < 0)
|
||||
{
|
||||
HandleReturn(read);
|
||||
if (m_eof)
|
||||
return false;
|
||||
}
|
||||
return read > 0;
|
||||
}
|
||||
|
||||
bool SecureSocket::Wait(SocketWaitConditions cond, std::chrono::milliseconds t) const
|
||||
{
|
||||
return m_socket->Wait(cond, t);
|
||||
}
|
||||
|
||||
SocketConnectionState SecureSocket::GetConnectionState() const
|
||||
{
|
||||
return m_socket->GetConnectionState();
|
||||
}
|
||||
|
||||
void SecureSocket::GetAddress(IPAddress & outAddr) const
|
||||
{
|
||||
m_socket->GetAddress(outAddr);
|
||||
}
|
||||
|
||||
int32_t SecureSocket::GetPort() const
|
||||
{
|
||||
return m_socket->GetPort();
|
||||
}
|
||||
|
||||
void SecureSocket::UseCertificateFile(std::string const & path)
|
||||
{
|
||||
if (!m_context)
|
||||
assert(!"not initialized yet");
|
||||
if (SSL_CTX_use_certificate_file(m_context, path.c_str(), SSL_FILETYPE_PEM) <= 0)
|
||||
throw inl::RuntimeException();
|
||||
}
|
||||
|
||||
void SecureSocket::UsePrivateKeyFile(std::string const & path)
|
||||
{
|
||||
if (!m_context)
|
||||
assert(!"not initialized yet");
|
||||
if (SSL_CTX_use_PrivateKey_file(m_context, path.c_str(), SSL_FILETYPE_PEM) <= 0)
|
||||
throw inl::RuntimeException();
|
||||
if (!SSL_CTX_check_private_key(m_context))
|
||||
throw inl::RuntimeException();
|
||||
}*/
|
||||
}
|
||||
511
src/VoidNet_LL/Socket.cpp
Normal file
511
src/VoidNet_LL/Socket.cpp
Normal file
@ -0,0 +1,511 @@
|
||||
#include "VoidNet/Socket.hpp"
|
||||
#include "VoidNet/IPAddress.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
void Socket::init()
|
||||
{
|
||||
if (GetSocketType() == SocketType::Unknown)
|
||||
throw std::invalid_argument("Unknown socket type");
|
||||
|
||||
if (m_socket == INVALID_SOCKET)
|
||||
{
|
||||
m_socket = socket(AF_INET, (int)GetSocketType(), 0);
|
||||
|
||||
if (m_socket == INVALID_SOCKET)
|
||||
throw std::runtime_error("Couldnt create socket");
|
||||
}
|
||||
|
||||
if (GetSocketType() == SocketType::Streaming)
|
||||
{
|
||||
int yes = 1;
|
||||
// Disable the Nagle algorithm (i.e. removes buffering of TCP packets)
|
||||
setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char*>(&yes), sizeof(yes));
|
||||
|
||||
// On Mac OS X, disable the SIGPIPE signal on disconnection
|
||||
#if defined(__APPLE__) && defined(__MACH__)
|
||||
setsockopt(m_socket, SOL_SOCKET, SO_NOSIGPIPE, reinterpret_cast<char*>(&yes), sizeof(yes));
|
||||
#endif
|
||||
}
|
||||
else
|
||||
{
|
||||
// Enable broadcast by default for UDP sockets
|
||||
int yes = 1;
|
||||
setsockopt(m_socket, SOL_SOCKET, SO_BROADCAST, reinterpret_cast<char*>(&yes), sizeof(yes));
|
||||
}
|
||||
}
|
||||
|
||||
bool Socket::Close()
|
||||
{
|
||||
if (m_socket != INVALID_SOCKET)
|
||||
{
|
||||
int32_t error = closesocket(m_socket);
|
||||
m_socket = INVALID_SOCKET;
|
||||
return error == 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool Socket::Bind(const IPAddress &addr)
|
||||
{
|
||||
sockaddr_in addr_in = addr.ToCAddr();
|
||||
return bind(m_socket, (sockaddr*)&addr_in, sizeof(sockaddr_in)) == 0;
|
||||
}
|
||||
|
||||
bool Socket::Connect(const IPAddress& addr)
|
||||
{
|
||||
sockaddr_in addr_in = addr.ToCAddr();
|
||||
int32_t Return = connect(m_socket, (sockaddr*)&addr_in, sizeof(sockaddr_in));
|
||||
SocketErrors Error = TranslateErrorCode(Return);
|
||||
|
||||
// "would block" is not an error
|
||||
return ((Error == SocketErrors::SE_NO_ERROR) || (Error == SocketErrors::SE_EWOULDBLOCK));
|
||||
}
|
||||
|
||||
bool Socket::WaitForPendingConnection(bool& hasPendingConnection, std::chrono::milliseconds t)
|
||||
{
|
||||
bool hasSucceeded = false;
|
||||
hasPendingConnection = false;
|
||||
|
||||
if (HasState(SocketParam::HasError) == SocketReturn::No)
|
||||
{
|
||||
SocketReturn state = HasState(SocketParam::CanRead, t);
|
||||
|
||||
hasSucceeded = state != SocketReturn::EncounteredError;
|
||||
hasPendingConnection = state == SocketReturn::Yes;
|
||||
}
|
||||
|
||||
return hasSucceeded;
|
||||
}
|
||||
|
||||
bool Socket::HasPendingData(uint32_t& pendingDataSize)
|
||||
{
|
||||
pendingDataSize = 0;
|
||||
|
||||
if (HasState(SocketParam::CanRead) == SocketReturn::Yes)
|
||||
{
|
||||
if (ioctl(m_socket, FIONREAD,
|
||||
#if defined(_WIN32)
|
||||
(u_long*)
|
||||
#endif
|
||||
&pendingDataSize) == 0)
|
||||
return (pendingDataSize > 0);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
std::unique_ptr<Socket> Socket::Accept()
|
||||
{
|
||||
SOCKET newSocket = accept(m_socket, nullptr, nullptr);
|
||||
|
||||
if (newSocket != INVALID_SOCKET)
|
||||
{
|
||||
return std::make_unique<Socket>(newSocket, GetSocketType());
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
bool Socket::SendTo(const uint8_t* data, int32_t count, int32_t& sent, const IPAddress& addrDest)
|
||||
{
|
||||
sockaddr_in addr = addrDest.ToCAddr();
|
||||
sent = sendto(m_socket, (const char*)data, count, 0, (sockaddr*)&addr, sizeof(sockaddr_in));
|
||||
|
||||
bool result = sent >= 0;
|
||||
if (result)
|
||||
m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Socket::Send(const uint8_t* data, int32_t count, int32_t& sent)
|
||||
{
|
||||
sent = send(m_socket, (const char*)data, count, 0);
|
||||
|
||||
bool result = sent != SOCKET_ERROR;
|
||||
if (result)
|
||||
m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool Socket::RecvFrom(uint8_t* data, int32_t size, int32_t& read, IPAddress& srcAddr, SocketReceiveFlags flags)
|
||||
{
|
||||
socklen_t len = sizeof(sockaddr_in);
|
||||
sockaddr_in addr = srcAddr.ToCAddr();
|
||||
const int translatedFlags = TranslateFlags(flags);
|
||||
|
||||
read = recvfrom(m_socket, (char*)data, size, translatedFlags, (sockaddr*)&addr, &len);
|
||||
|
||||
if (read < 0 && TranslateErrorCode(read) == SocketErrors::SE_EWOULDBLOCK)
|
||||
read = 0;
|
||||
else if (read <= 0) // 0 means gracefully closed
|
||||
{
|
||||
read = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Socket::Recv(uint8_t* data, int32_t size, int32_t& read, SocketReceiveFlags flags)
|
||||
{
|
||||
const int translatedFlags = TranslateFlags(flags);
|
||||
read = recv(m_socket, (char*)data, size, translatedFlags);
|
||||
|
||||
if (read < 0 && TranslateErrorCode(read) == SocketErrors::SE_EWOULDBLOCK)
|
||||
read = 0;
|
||||
else if (read <= 0) // 0 means gracefully closed
|
||||
{
|
||||
read = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool Socket::Wait(SocketWaitConditions cond, std::chrono::milliseconds t)
|
||||
{
|
||||
if ((cond == SocketWaitConditions::WaitForRead) || (cond == SocketWaitConditions::WaitForReadOrWrite))
|
||||
{
|
||||
if (HasState(SocketParam::CanRead, t) == SocketReturn::Yes)
|
||||
return true;
|
||||
}
|
||||
|
||||
if ((cond == SocketWaitConditions::WaitForWrite) || (cond == SocketWaitConditions::WaitForReadOrWrite))
|
||||
{
|
||||
if (HasState(SocketParam::CanWrite, t) == SocketReturn::Yes)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
SocketConnectionState Socket::GetConnectionState()
|
||||
{
|
||||
SocketConnectionState currentState = SocketConnectionState::ConnectionError;
|
||||
|
||||
if (HasState(SocketParam::HasError) == SocketReturn::No)
|
||||
{
|
||||
if (std::chrono::system_clock::now().time_since_epoch().count() - m_lastActivityTime > std::chrono::milliseconds(5).count())
|
||||
{
|
||||
SocketReturn writeState = HasState(SocketParam::CanWrite);
|
||||
SocketReturn readState = HasState(SocketParam::CanRead);
|
||||
|
||||
if (writeState == SocketReturn::Yes || readState == SocketReturn::Yes)
|
||||
{
|
||||
currentState = SocketConnectionState::Connected;
|
||||
m_lastActivityTime = std::chrono::system_clock::now().time_since_epoch().count();
|
||||
}
|
||||
else if (writeState == SocketReturn::No && readState == SocketReturn::No)
|
||||
currentState = SocketConnectionState::NotConnected;
|
||||
}
|
||||
else
|
||||
currentState = SocketConnectionState::Connected;
|
||||
}
|
||||
|
||||
return currentState;
|
||||
}
|
||||
|
||||
void Socket::GetAddress(IPAddress& outAddr)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t Size = sizeof(sockaddr_in);
|
||||
|
||||
if (getsockname(m_socket, (sockaddr*)&addr, &Size) != 0)
|
||||
return;
|
||||
|
||||
outAddr = IPAddress(inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
||||
}
|
||||
|
||||
bool Socket::GetPeerAddress(IPAddress& outAddr)
|
||||
{
|
||||
struct sockaddr_in addr;
|
||||
socklen_t size = sizeof(sockaddr_in);
|
||||
|
||||
int result = getpeername(m_socket, (sockaddr*)&addr, &size);
|
||||
if (result != 0)
|
||||
return false;
|
||||
|
||||
outAddr = IPAddress(inet_ntoa(addr.sin_addr), ntohs(addr.sin_port));
|
||||
|
||||
return result == 0;
|
||||
}
|
||||
|
||||
bool Socket::SetNonBlocking(bool isNonBlocking)
|
||||
{
|
||||
#if PLATFORM_HTML5 // if we have more platforms later (html5, android, ios) later we need to do some changes to networking
|
||||
throw std::exception("Can't have blocking sockets on HTML5");
|
||||
return false;
|
||||
#else
|
||||
|
||||
#if _WIN32
|
||||
return ioctl(m_socket, FIONBIO, (u_long*)&isNonBlocking) == 0;
|
||||
#else
|
||||
int flags = fcntl(m_socket, F_GETFL, 0);
|
||||
flags = isNonBlocking ? flags | O_NONBLOCK : flags ^ (flags & O_NONBLOCK);
|
||||
int err = fcntl(m_socket, F_SETFL, flags);
|
||||
return (err == 0 ? true : false);
|
||||
#endif
|
||||
#endif
|
||||
}
|
||||
|
||||
bool Socket::JoinMulticastGroup(const IPAddress& addrStr)
|
||||
{
|
||||
sockaddr_in addr = addrStr.ToCAddr();
|
||||
|
||||
ip_mreq imr;
|
||||
imr.imr_interface.s_addr = INADDR_ANY;
|
||||
imr.imr_multiaddr = addr.sin_addr;
|
||||
|
||||
return (setsockopt(m_socket, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char*)&imr, sizeof(imr)) == 0);
|
||||
}
|
||||
|
||||
bool Socket::LeaveMulticastGroup(const IPAddress& addrStr)
|
||||
{
|
||||
sockaddr_in addr = addrStr.ToCAddr();
|
||||
|
||||
ip_mreq imr;
|
||||
imr.imr_interface.s_addr = INADDR_ANY;
|
||||
imr.imr_multiaddr = addr.sin_addr;
|
||||
|
||||
return (setsockopt(m_socket, IPPROTO_IP, IP_DROP_MEMBERSHIP, (char*)&imr, sizeof(imr)) == 0);
|
||||
}
|
||||
|
||||
bool Socket::SetLinger(bool shouldLinger, int32_t t)
|
||||
{
|
||||
linger ling;
|
||||
ling.l_onoff = shouldLinger;
|
||||
ling.l_linger = t;
|
||||
|
||||
return setsockopt(m_socket, SOL_SOCKET, SO_LINGER, (char*)&ling, sizeof(ling)) == 0;
|
||||
}
|
||||
|
||||
bool Socket::SetSendBufferSize(int32_t size, int32_t& newSize)
|
||||
{
|
||||
socklen_t len = sizeof(int32_t);
|
||||
bool success = setsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)&size, sizeof(int32_t)) == 0;
|
||||
|
||||
getsockopt(m_socket, SOL_SOCKET, SO_SNDBUF, (char*)&newSize, &len);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
bool Socket::SetReceiveBufferSize(int32_t size, int32_t& newSize)
|
||||
{
|
||||
socklen_t len = sizeof(int32_t);
|
||||
bool success = setsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&size, sizeof(int32_t)) == 0;
|
||||
|
||||
getsockopt(m_socket, SOL_SOCKET, SO_RCVBUF, (char*)&newSize, &len);
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
uint32_t Socket::GetPort()
|
||||
{
|
||||
sockaddr_in addr;
|
||||
socklen_t size = sizeof(sockaddr_in);
|
||||
if (getsockname(m_socket, (sockaddr*)&addr, &size) != 0)
|
||||
throw std::runtime_error("Invalid port");
|
||||
return ntohs(addr.sin_port);
|
||||
}
|
||||
|
||||
SocketReturn Socket::HasState(SocketParam state, std::chrono::milliseconds t)
|
||||
{
|
||||
timeval time;
|
||||
time.tv_sec = t.count();
|
||||
time.tv_usec = t.count() * 1000 + t.count();
|
||||
|
||||
fd_set socketSet;
|
||||
|
||||
FD_ZERO(&socketSet);
|
||||
FD_SET(m_socket, &socketSet);
|
||||
|
||||
int32_t SelectStatus = 0;
|
||||
switch (state)
|
||||
{
|
||||
case SocketParam::CanRead:
|
||||
SelectStatus = select(m_socket + 1, &socketSet, nullptr, nullptr, &time);
|
||||
break;
|
||||
|
||||
case SocketParam::CanWrite:
|
||||
SelectStatus = select(m_socket + 1, nullptr, &socketSet, nullptr, &time);
|
||||
break;
|
||||
|
||||
case SocketParam::HasError:
|
||||
SelectStatus = select(m_socket + 1, nullptr, nullptr, &socketSet, &time);
|
||||
break;
|
||||
}
|
||||
|
||||
return SelectStatus > 0 ? SocketReturn::Yes :
|
||||
SelectStatus == 0 ? SocketReturn::No :
|
||||
SocketReturn::EncounteredError;
|
||||
}
|
||||
|
||||
SocketErrors Socket::TranslateErrorCode(int32_t code)
|
||||
{
|
||||
#if !_WIN32
|
||||
if (code == SOCKET_ERROR)
|
||||
{
|
||||
return SE_SOCKET_ERROR;
|
||||
}
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 0: return SocketErrors::SE_NO_ERROR;
|
||||
case EINTR: return SocketErrors::SE_EINTR;
|
||||
case EBADF: return SocketErrors::SE_EBADF;
|
||||
case EACCES: return SocketErrors::SE_EACCES;
|
||||
case EFAULT: return SocketErrors::SE_EFAULT;
|
||||
case EINVAL: return SocketErrors::SE_EINVAL;
|
||||
case EMFILE: return SocketErrors::SE_EMFILE;
|
||||
case EWOULDBLOCK: return SocketErrors::SE_EWOULDBLOCK;
|
||||
case EINPROGRESS: return SocketErrors::SE_EINPROGRESS;
|
||||
case EALREADY: return SocketErrors::SE_EALREADY;
|
||||
case ENOTSOCK: return SocketErrors::SE_ENOTSOCK;
|
||||
case EDESTADDRREQ: return SocketErrors::SE_EDESTADDRREQ;
|
||||
case EMSGSIZE: return SocketErrors::SE_EMSGSIZE;
|
||||
case EPROTOTYPE: return SocketErrors::SE_EPROTOTYPE;
|
||||
case ENOPROTOOPT: return SocketErrors::SE_ENOPROTOOPT;
|
||||
case EPROTONOSUPPORT: return SocketErrors::SE_EPROTONOSUPPORT;
|
||||
case ESOCKTNOSUPPORT: return SocketErrors::SE_ESOCKTNOSUPPORT;
|
||||
case EOPNOTSUPP: return SocketErrors::SE_EOPNOTSUPP;
|
||||
case EPFNOSUPPORT: return SocketErrors::SE_EPFNOSUPPORT;
|
||||
case EAFNOSUPPORT: return SocketErrors::SE_EAFNOSUPPORT;
|
||||
case EADDRINUSE: return SocketErrors::SE_EADDRINUSE;
|
||||
case EADDRNOTAVAIL: return SocketErrors::SE_EADDRNOTAVAIL;
|
||||
case ENETDOWN: return SocketErrors::SE_ENETDOWN;
|
||||
case ENETUNREACH: return SocketErrors::SE_ENETUNREACH;
|
||||
case ENETRESET: return SocketErrors::SE_ENETRESET;
|
||||
case ECONNABORTED: return SocketErrors::SE_ECONNABORTED;
|
||||
case ECONNRESET: return SocketErrors::SE_ECONNRESET;
|
||||
case ENOBUFS: return SocketErrors::SE_ENOBUFS;
|
||||
case EISCONN: return SocketErrors::SE_EISCONN;
|
||||
case ENOTCONN: return SocketErrors::SE_ENOTCONN;
|
||||
case ESHUTDOWN: return SocketErrors::SE_ESHUTDOWN;
|
||||
case ETOOMANYREFS: return SocketErrors::SE_ETOOMANYREFS;
|
||||
case ETIMEDOUT: return SocketErrors::SE_ETIMEDOUT;
|
||||
case ECONNREFUSED: return SocketErrors::SE_ECONNREFUSED;
|
||||
case ELOOP: return SocketErrors::SE_ELOOP;
|
||||
case ENAMETOOLONG: return SocketErrors::SE_ENAMETOOLONG;
|
||||
case EHOSTDOWN: return SocketErrors::SE_EHOSTDOWN;
|
||||
case EHOSTUNREACH: return SocketErrors::SE_EHOSTUNREACH;
|
||||
case ENOTEMPTY: return SocketErrors::SE_ENOTEMPTY;
|
||||
case EUSERS: return SocketErrors::SE_EUSERS;
|
||||
case EDQUOT: return SocketErrors::SE_EDQUOT;
|
||||
case ESTALE: return SocketErrors::SE_ESTALE;
|
||||
case EREMOTE: return SocketErrors::SE_EREMOTE;
|
||||
case ENODEV: return SocketErrors::SE_NODEV;
|
||||
#if !PLATFORM_HAS_NO_EPROCLIM
|
||||
case EPROCLIM: return SocketErrors::SE_EPROCLIM;
|
||||
#endif
|
||||
// case EDISCON: return SE_EDISCON;
|
||||
// case SYSNOTREADY: return SE_SYSNOTREADY;
|
||||
// case VERNOTSUPPORTED: return SE_VERNOTSUPPORTED;
|
||||
// case NOTINITIALISED: return SE_NOTINITIALISED;
|
||||
|
||||
#if PLATFORM_HAS_BSD_SOCKET_FEATURE_GETHOSTNAME
|
||||
case HOST_NOT_FOUND: return SocketErrors::SE_HOST_NOT_FOUND;
|
||||
case TRY_AGAIN: return SocketErrors::SE_TRY_AGAIN;
|
||||
case NO_RECOVERY: return SocketErrors::SE_NO_RECOVERY;
|
||||
#endif
|
||||
|
||||
// case NO_DATA: return SE_NO_DATA;
|
||||
// case : return SE_UDP_ERR_PORT_UNREACH; //@TODO Find it's replacement
|
||||
}
|
||||
|
||||
return SocketErrors::SE_EINVAL;
|
||||
#else
|
||||
// handle the generic -1 error
|
||||
if (code == SOCKET_ERROR)
|
||||
{
|
||||
return SocketErrors::SE_SOCKET_ERROR;
|
||||
}
|
||||
|
||||
switch (code)
|
||||
{
|
||||
case 0: return SocketErrors::SE_NO_ERROR;
|
||||
case ERROR_INVALID_HANDLE: return SocketErrors::SE_ECONNRESET; // invalid socket handle catch
|
||||
case WSAEINTR: return SocketErrors::SE_EINTR;
|
||||
case WSAEBADF: return SocketErrors::SE_EBADF;
|
||||
case WSAEACCES: return SocketErrors::SE_EACCES;
|
||||
case WSAEFAULT: return SocketErrors::SE_EFAULT;
|
||||
case WSAEINVAL: return SocketErrors::SE_EINVAL;
|
||||
case WSAEMFILE: return SocketErrors::SE_EMFILE;
|
||||
case WSAEWOULDBLOCK: return SocketErrors::SE_EWOULDBLOCK;
|
||||
case WSAEINPROGRESS: return SocketErrors::SE_EINPROGRESS;
|
||||
case WSAEALREADY: return SocketErrors::SE_EALREADY;
|
||||
case WSAENOTSOCK: return SocketErrors::SE_ENOTSOCK;
|
||||
case WSAEDESTADDRREQ: return SocketErrors::SE_EDESTADDRREQ;
|
||||
case WSAEMSGSIZE: return SocketErrors::SE_EMSGSIZE;
|
||||
case WSAEPROTOTYPE: return SocketErrors::SE_EPROTOTYPE;
|
||||
case WSAENOPROTOOPT: return SocketErrors::SE_ENOPROTOOPT;
|
||||
case WSAEPROTONOSUPPORT: return SocketErrors::SE_EPROTONOSUPPORT;
|
||||
case WSAESOCKTNOSUPPORT: return SocketErrors::SE_ESOCKTNOSUPPORT;
|
||||
case WSAEOPNOTSUPP: return SocketErrors::SE_EOPNOTSUPP;
|
||||
case WSAEPFNOSUPPORT: return SocketErrors::SE_EPFNOSUPPORT;
|
||||
case WSAEAFNOSUPPORT: return SocketErrors::SE_EAFNOSUPPORT;
|
||||
case WSAEADDRINUSE: return SocketErrors::SE_EADDRINUSE;
|
||||
case WSAEADDRNOTAVAIL: return SocketErrors::SE_EADDRNOTAVAIL;
|
||||
case WSAENETDOWN: return SocketErrors::SE_ENETDOWN;
|
||||
case WSAENETUNREACH: return SocketErrors::SE_ENETUNREACH;
|
||||
case WSAENETRESET: return SocketErrors::SE_ENETRESET;
|
||||
case WSAECONNABORTED: return SocketErrors::SE_ECONNABORTED;
|
||||
case WSAECONNRESET: return SocketErrors::SE_ECONNRESET;
|
||||
case WSAENOBUFS: return SocketErrors::SE_ENOBUFS;
|
||||
case WSAEISCONN: return SocketErrors::SE_EISCONN;
|
||||
case WSAENOTCONN: return SocketErrors::SE_ENOTCONN;
|
||||
case WSAESHUTDOWN: return SocketErrors::SE_ESHUTDOWN;
|
||||
case WSAETOOMANYREFS: return SocketErrors::SE_ETOOMANYREFS;
|
||||
case WSAETIMEDOUT: return SocketErrors::SE_ETIMEDOUT;
|
||||
case WSAECONNREFUSED: return SocketErrors::SE_ECONNREFUSED;
|
||||
case WSAELOOP: return SocketErrors::SE_ELOOP;
|
||||
case WSAENAMETOOLONG: return SocketErrors::SE_ENAMETOOLONG;
|
||||
case WSAEHOSTDOWN: return SocketErrors::SE_EHOSTDOWN;
|
||||
case WSAEHOSTUNREACH: return SocketErrors::SE_EHOSTUNREACH;
|
||||
case WSAENOTEMPTY: return SocketErrors::SE_ENOTEMPTY;
|
||||
case WSAEPROCLIM: return SocketErrors::SE_EPROCLIM;
|
||||
case WSAEUSERS: return SocketErrors::SE_EUSERS;
|
||||
case WSAEDQUOT: return SocketErrors::SE_EDQUOT;
|
||||
case WSAESTALE: return SocketErrors::SE_ESTALE;
|
||||
case WSAEREMOTE: return SocketErrors::SE_EREMOTE;
|
||||
case WSAEDISCON: return SocketErrors::SE_EDISCON;
|
||||
case WSASYSNOTREADY: return SocketErrors::SE_SYSNOTREADY;
|
||||
case WSAVERNOTSUPPORTED: return SocketErrors::SE_VERNOTSUPPORTED;
|
||||
case WSANOTINITIALISED: return SocketErrors::SE_NOTINITIALISED;
|
||||
case WSAHOST_NOT_FOUND: return SocketErrors::SE_HOST_NOT_FOUND;
|
||||
case WSATRY_AGAIN: return SocketErrors::SE_TRY_AGAIN;
|
||||
case WSANO_RECOVERY: return SocketErrors::SE_NO_RECOVERY;
|
||||
case WSANO_DATA: return SocketErrors::SE_NO_DATA;
|
||||
// case : return SE_UDP_ERR_PORT_UNREACH;
|
||||
}
|
||||
|
||||
return SocketErrors::SE_NO_ERROR;
|
||||
#endif
|
||||
}
|
||||
|
||||
int Socket::TranslateFlags(SocketReceiveFlags flags)
|
||||
{
|
||||
int translatedFlags = 0;
|
||||
|
||||
if ((int)flags & (int)SocketReceiveFlags::Peek)
|
||||
{
|
||||
translatedFlags |= MSG_PEEK;
|
||||
#if !_WIN32
|
||||
translatedFlags |= MSG_DONTWAIT;
|
||||
#endif
|
||||
}
|
||||
|
||||
if ((int)flags & (int)SocketReceiveFlags::WaitAll)
|
||||
{
|
||||
translatedFlags |= MSG_WAITALL;
|
||||
}
|
||||
|
||||
return translatedFlags;
|
||||
}
|
||||
}
|
||||
60
src/VoidNet_LL/TcpClient.cpp
Normal file
60
src/VoidNet_LL/TcpClient.cpp
Normal file
@ -0,0 +1,60 @@
|
||||
#include "VoidNet/TcpClient.hpp"
|
||||
#include "VoidNet/TcpSocketBuilder.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
TcpClient::TcpClient(Socket *soc)
|
||||
{
|
||||
m_socket = std::unique_ptr<Socket>(soc); // will this work
|
||||
}
|
||||
|
||||
TcpClient::TcpClient(SocketProtocol protocol)
|
||||
{
|
||||
m_socket = TcpSocketBuilder().AsNonBlocking().AsReusable().Protocol(protocol).Build();
|
||||
}
|
||||
|
||||
bool TcpClient::Connect(const IPAddress& addrStr)
|
||||
{
|
||||
return m_socket->Connect(addrStr);
|
||||
}
|
||||
|
||||
bool TcpClient::Close() const
|
||||
{
|
||||
return m_socket->Close();
|
||||
}
|
||||
|
||||
bool TcpClient::HasPendingData(uint32_t& pendingDataSize) const
|
||||
{
|
||||
return m_socket->HasPendingData(pendingDataSize);
|
||||
}
|
||||
|
||||
bool TcpClient::Send(const uint8_t* data, int32_t count, int32_t& sent) const
|
||||
{
|
||||
return m_socket->Send(data, count, sent);
|
||||
}
|
||||
|
||||
bool TcpClient::Recv(uint8_t* data, int32_t size, int32_t& read, SocketReceiveFlags flags) const
|
||||
{
|
||||
return m_socket->Recv(data, size, read, flags);
|
||||
}
|
||||
|
||||
bool TcpClient::Wait(SocketWaitConditions cond, std::chrono::milliseconds t) const
|
||||
{
|
||||
return m_socket->Wait(cond, t);
|
||||
}
|
||||
|
||||
SocketConnectionState TcpClient::GetConnectionState() const
|
||||
{
|
||||
return m_socket->GetConnectionState();
|
||||
}
|
||||
|
||||
void TcpClient::GetAddress(IPAddress& outAddr) const
|
||||
{
|
||||
return m_socket->GetAddress(outAddr);
|
||||
}
|
||||
|
||||
int32_t TcpClient::GetPort() const
|
||||
{
|
||||
return m_socket->GetPort();
|
||||
}
|
||||
}
|
||||
52
src/VoidNet_LL/TcpListener.cpp
Normal file
52
src/VoidNet_LL/TcpListener.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "VoidNet/TcpListener.hpp"
|
||||
#include "VoidNet/TcpSocketBuilder.hpp"
|
||||
#include "VoidNet/Socket.hpp"
|
||||
#include "VoidNet/TcpClient.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
TcpListener::TcpListener(uint16_t port, std::chrono::milliseconds inSleepTime)
|
||||
: m_port(port)
|
||||
, m_sleepTime(inSleepTime)
|
||||
{
|
||||
m_socket = TcpSocketBuilder().AsNonBlocking().AsReusable().Bind(IPAddress(0, 0, 0, 0, port)).Listening().Build();
|
||||
}
|
||||
|
||||
TcpListener::TcpListener(Socket *InSocket, std::chrono::milliseconds inSleepTime)
|
||||
: m_sleepTime(inSleepTime)
|
||||
{
|
||||
m_socket = std::unique_ptr<Socket>(InSocket);
|
||||
}
|
||||
|
||||
TcpClient *TcpListener::AcceptClient()
|
||||
{
|
||||
if (m_socket == nullptr)
|
||||
m_socket = TcpSocketBuilder().AsReusable().Bind(IPAddress(0, 0, 0, 0, m_port)).Listening().Build();
|
||||
|
||||
if (m_socket == nullptr)
|
||||
return nullptr;
|
||||
|
||||
std::string remoteAddress;
|
||||
|
||||
const bool hasZeroSleepTime = (m_sleepTime == std::chrono::milliseconds(0));
|
||||
|
||||
bool pending = false;
|
||||
|
||||
if (m_socket->WaitForPendingConnection(pending, m_sleepTime))
|
||||
{
|
||||
if (pending)
|
||||
{
|
||||
std::unique_ptr<Socket> connectionSocket = m_socket->Accept();
|
||||
|
||||
if (connectionSocket != nullptr)
|
||||
{
|
||||
return new TcpClient(connectionSocket.release());
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(m_sleepTime));
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
52
src/VoidNet_LL/TcpSocketBuilder.cpp
Normal file
52
src/VoidNet_LL/TcpSocketBuilder.cpp
Normal file
@ -0,0 +1,52 @@
|
||||
#include "VoidNet/TcpSocketBuilder.hpp"
|
||||
|
||||
#include "VoidNet/Socket.hpp"
|
||||
#include "VoidNet/TcpClient.hpp"
|
||||
#include "VoidNet/TcpListener.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
std::unique_ptr<Socket> TcpSocketBuilder::Build() const
|
||||
{
|
||||
std::unique_ptr<Socket> socket = std::make_unique<Socket>(SocketType::Streaming, m_socketProtocol);
|
||||
|
||||
if (socket != nullptr)
|
||||
{
|
||||
bool Error = !socket->SetReuseAddr(m_reusable) ||
|
||||
!socket->SetLinger(m_linger, m_lingerTimeout);
|
||||
|
||||
if (!Error)
|
||||
Error = m_bound && !socket->Bind(m_boundAddr);
|
||||
if (!Error)
|
||||
Error = m_listen && !socket->Listen();
|
||||
if (!Error)
|
||||
Error = !socket->SetNonBlocking(!m_blocking);
|
||||
|
||||
if (!Error)
|
||||
{
|
||||
int32_t out_new_size;
|
||||
if (m_receiveBufferSize > 0)
|
||||
socket->SetReceiveBufferSize(m_receiveBufferSize, out_new_size);
|
||||
if (m_sendBufferSize > 0)
|
||||
socket->SetSendBufferSize(m_sendBufferSize, out_new_size);
|
||||
}
|
||||
|
||||
if (Error)
|
||||
throw std::runtime_error("Couldnt create socket"); // make parameter a string depending on the error
|
||||
}
|
||||
|
||||
return socket;
|
||||
}
|
||||
|
||||
std::unique_ptr<TcpClient> TcpSocketBuilder::BuildClient() const
|
||||
{
|
||||
std::unique_ptr<Socket> socket = Build();
|
||||
return std::make_unique<TcpClient>(socket.release());
|
||||
}
|
||||
|
||||
std::unique_ptr<TcpListener> TcpSocketBuilder::BuildListener() const
|
||||
{
|
||||
std::unique_ptr<Socket> socket = Build();
|
||||
return std::make_unique<TcpListener>(socket.release());
|
||||
}
|
||||
}
|
||||
64
src/VoidNet_LL/UdpSocket.cpp
Normal file
64
src/VoidNet_LL/UdpSocket.cpp
Normal file
@ -0,0 +1,64 @@
|
||||
#include "VoidNet/UdpSocket.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
UdpSocket::UdpSocket(Socket * soc)
|
||||
{
|
||||
m_socket = std::unique_ptr<Socket>(soc); // will this work
|
||||
}
|
||||
|
||||
UdpSocket::UdpSocket(SocketProtocol protocol)
|
||||
{
|
||||
m_socket = std::make_unique<Socket>(SocketType::Datagram, protocol);
|
||||
}
|
||||
|
||||
bool UdpSocket::Bind(const IPAddress & addr)
|
||||
{
|
||||
return m_socket->Bind(addr);
|
||||
}
|
||||
|
||||
bool UdpSocket::SendTo(const uint8_t * data, int32_t count, int32_t & sent, const IPAddress & addrDest)
|
||||
{
|
||||
return m_socket->SendTo(data, count, sent, addrDest);
|
||||
}
|
||||
|
||||
bool UdpSocket::RecvFrom(uint8_t * data, int32_t size, int32_t & read, IPAddress & srcAddr, SocketReceiveFlags flags)
|
||||
{
|
||||
return m_socket->RecvFrom(data, size, read, srcAddr, flags);
|
||||
}
|
||||
|
||||
bool UdpSocket::GetPeerAddress(IPAddress & outAddr)
|
||||
{
|
||||
return m_socket->GetPeerAddress(outAddr);
|
||||
}
|
||||
|
||||
bool UdpSocket::JoinMulticastGroup(const IPAddress & addrStr)
|
||||
{
|
||||
return m_socket->JoinMulticastGroup(addrStr);
|
||||
}
|
||||
|
||||
bool UdpSocket::LeaveMulticastGroup(const IPAddress & addrStr)
|
||||
{
|
||||
return m_socket->LeaveMulticastGroup(addrStr);
|
||||
}
|
||||
|
||||
bool UdpSocket::SetMulticastLoopback(bool loopback)
|
||||
{
|
||||
return m_socket->SetMulticastLoopback(loopback);
|
||||
}
|
||||
|
||||
bool UdpSocket::SetMulticastTtl(uint8_t timeToLive)
|
||||
{
|
||||
return m_socket->SetMulticastTtl(timeToLive);
|
||||
}
|
||||
|
||||
uint32_t UdpSocket::GetPort()
|
||||
{
|
||||
return m_socket->GetPort();
|
||||
}
|
||||
|
||||
bool UdpSocket::SetReuseAddr(bool allowReuse)
|
||||
{
|
||||
return m_socket->SetReuseAddr(allowReuse);
|
||||
}
|
||||
}
|
||||
188
src/VoidNet_LL/Uri.cpp
Normal file
188
src/VoidNet_LL/Uri.cpp
Normal file
@ -0,0 +1,188 @@
|
||||
// https://github.com/mfichman/http
|
||||
|
||||
#include "VoidNet/Uri.hpp"
|
||||
#include "VoidNet/Parse.hpp"
|
||||
|
||||
namespace std::net
|
||||
{
|
||||
static bool IsReserved(char ch)
|
||||
{
|
||||
switch (ch)
|
||||
{
|
||||
case '/':
|
||||
return true;
|
||||
case '#':
|
||||
return true;
|
||||
case '?':
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static ParseResult<std::string> ParseScheme(char const* str)
|
||||
{
|
||||
auto result = ParseWhile(str, [](char ch)
|
||||
{
|
||||
return ch != ':' && !IsReserved(ch);
|
||||
});
|
||||
|
||||
result.ch = (result.ch[0] == ':') ? (result.ch + 1) : (result.ch);
|
||||
return result;
|
||||
}
|
||||
|
||||
static ParseResult<std::string> ParseUser(char const* str)
|
||||
{
|
||||
auto result = ParseWhile(str, [](char ch)
|
||||
{
|
||||
return ch != '@' && !IsReserved(ch);
|
||||
});
|
||||
|
||||
if (result.ch[0] == '@')
|
||||
result.ch = result.ch + 1;
|
||||
else {
|
||||
result.ch = str;
|
||||
result.value = "";
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
static ParseResult<std::string> ParseHost(char const* str)
|
||||
{
|
||||
return ParseWhile(str, [](char ch)
|
||||
{
|
||||
return ch != ':' && !IsReserved(ch);
|
||||
});
|
||||
}
|
||||
|
||||
static ParseResult<uint16_t> ParsePort(char const* str)
|
||||
{
|
||||
ParseResult<uint16_t> result;
|
||||
if (str[0] != ':')
|
||||
{
|
||||
result.value = 0;
|
||||
result.ch = str;
|
||||
return result;
|
||||
}
|
||||
|
||||
auto tmp = ParseWhile(str + 1, [](char ch)
|
||||
{
|
||||
return !IsReserved(ch);
|
||||
});
|
||||
|
||||
result.value = uint16_t(strtol(tmp.value.c_str(), 0, 10));
|
||||
result.ch = tmp.ch;
|
||||
return result;
|
||||
}
|
||||
|
||||
static ParseResult<Authority> ParseAuthority(char const* str)
|
||||
{
|
||||
ParseResult<Authority> result
|
||||
{
|
||||
};
|
||||
|
||||
if (str[0] == '\0' || str[0] != '/' || str[1] != '/')
|
||||
{
|
||||
result.ch = str;
|
||||
return result;
|
||||
}
|
||||
|
||||
auto user = ParseUser(str + 2); // For "//"
|
||||
auto host = ParseHost(user.ch);
|
||||
auto port = ParsePort(host.ch);
|
||||
|
||||
result.value.SetUser(user.value);
|
||||
result.value.SetHost(host.value);
|
||||
result.value.SetPort(port.value);
|
||||
result.ch = port.ch;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static ParseResult<std::string> ParsePath(char const* str)
|
||||
{
|
||||
// Return query/frag as part of path for now
|
||||
ParseResult<std::string> result = ParseWhile(str, [](char ch)
|
||||
{
|
||||
return true;
|
||||
});
|
||||
/*
|
||||
ParseResult<std::string> result = parseWhile(str, [](char ch) {
|
||||
return ch != '/' && !isReserved(ch);
|
||||
});
|
||||
result.ch = (result.ch[0] == '?') ? (result.ch+1) : (result.ch);
|
||||
*/
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
static Uri parseUri(char const* str)
|
||||
{
|
||||
Uri uri;
|
||||
|
||||
auto scheme = ParseScheme(str);
|
||||
auto authority = ParseAuthority(scheme.ch);
|
||||
auto path = ParsePath(authority.ch);
|
||||
|
||||
uri.SetScheme(scheme.value);
|
||||
uri.SetAuthority(authority.value);
|
||||
uri.SetPath(path.value);
|
||||
return uri;
|
||||
}
|
||||
|
||||
|
||||
Authority::Authority(std::string const& user, std::string const& host, uint16_t port)
|
||||
{
|
||||
m_user = user;
|
||||
m_host = host;
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
Authority::Authority()
|
||||
{
|
||||
m_port = 0;
|
||||
}
|
||||
|
||||
void Authority::SetUser(std::string const& user)
|
||||
{
|
||||
m_user = user;
|
||||
}
|
||||
|
||||
void Authority::SetHost(std::string const& host)
|
||||
{
|
||||
m_host = host;
|
||||
}
|
||||
|
||||
void Authority::SetPort(uint16_t port)
|
||||
{
|
||||
m_port = port;
|
||||
}
|
||||
|
||||
Uri::Uri(const char* value)
|
||||
{
|
||||
*this = parseUri(value);
|
||||
}
|
||||
|
||||
Uri::Uri(const std::string& value)
|
||||
{
|
||||
*this = parseUri(value.c_str());
|
||||
}
|
||||
|
||||
Uri::Uri() {
|
||||
}
|
||||
|
||||
void Uri::SetScheme(const std::string& scheme)
|
||||
{
|
||||
m_scheme = scheme;
|
||||
}
|
||||
|
||||
void Uri::SetAuthority(const Authority& authority)
|
||||
{
|
||||
m_authority = authority;
|
||||
}
|
||||
|
||||
void Uri::SetPath(const std::string& path)
|
||||
{
|
||||
m_path = path;
|
||||
}
|
||||
}
|
||||
29
src/VoidNet_LL/Util.cpp
Normal file
29
src/VoidNet_LL/Util.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include "VoidNet/Util.hpp"
|
||||
|
||||
std::vector<std::string> std::net::Split(const std::string& str, const std::string& delimiter)
|
||||
{
|
||||
std::vector<std::string> splited;
|
||||
if (str.empty() && delimiter.empty())
|
||||
return std::vector<std::string>();
|
||||
std::string::size_type lastPos = str.find_first_not_of(delimiter, 0);
|
||||
std::string::size_type pos = str.find_first_of(delimiter, lastPos);
|
||||
|
||||
while (std::string::npos != pos || std::string::npos != lastPos)
|
||||
{
|
||||
splited.push_back(str.substr(lastPos, pos - lastPos));
|
||||
lastPos = str.find_first_not_of(delimiter, pos);
|
||||
pos = str.find_first_of(delimiter, lastPos);
|
||||
}
|
||||
return splited;
|
||||
}
|
||||
|
||||
sockaddr_in std::net::CreateAddress(uint32_t address, uint16_t port)
|
||||
{
|
||||
sockaddr_in addr;
|
||||
std::memset(&addr, 0, sizeof(addr));
|
||||
addr.sin_addr.s_addr = htonl(address);
|
||||
addr.sin_family = AF_INET;
|
||||
addr.sin_port = htons(port);
|
||||
|
||||
return addr;
|
||||
}
|
||||
Reference in New Issue
Block a user