Initial commit

This commit is contained in:
2025-11-22 16:25:58 +01:00
commit 44f8d48356
19 changed files with 1510 additions and 0 deletions

82
.gitignore vendored Normal file
View File

@ -0,0 +1,82 @@
# Snippets taken from https://github.com/github/gitignore/blob/main/VisualStudio.gitignore
# Visual Studio 2015/2017 cache/options directory
.vs/
# Build results
[Dd]ebug/
[Dd]ebugPublic/
[Rr]elease/
[Rr]eleases/
[Dd]ebug/x64/
[Dd]ebugPublic/x64/
[Rr]elease/x64/
[Rr]eleases/x64/
bin/x64/
obj/x64/
[Dd]ebug/x86/
[Dd]ebugPublic/x86/
[Rr]elease/x86/
[Rr]eleases/x86/
bin/x86/
obj/x86/
[Ww][Ii][Nn]32/
[Aa][Rr][Mm]/
[Aa][Rr][Mm]64/
[Aa][Rr][Mm]64[Ee][Cc]/
bld/
[Oo]bj/
[Oo]ut/
[Ll]og/
[Ll]ogs/
# Files built by Visual Studio
*_i.c
*_p.c
*_h.h
*.ilk
*.meta
*.obj
*.idb
*.iobj
*.pch
*.pdb
*.ipdb
*.pgc
*.pgd
*.rsp
# but not Directory.Build.rsp, as it configures directory-level build defaults
!Directory.Build.rsp
*.sbr
*.tlb
*.tli
*.tlh
*.tmp
*.tmp_proj
*_wpftmp.csproj
*.log
*.tlog
*.vspscc
*.vssscc
.builds
*.pidb
*.svclog
*.scc
# Visual C++ cache files
ipch/
*.aps
*.ncb
*.opendb
*.opensdf
*.sdf
*.cachefile
*.VC.db
*.VC.VC.opendb

48
Main.cpp Normal file
View File

@ -0,0 +1,48 @@
#include <iostream>
#include <string>
#include <sstream>
#include "Windows.h"
#include "Router.h"
#include "Terminal.h"
#include "Resource.h"
int main()
{
SetConsoleMode((HANDLE)(size_t)STD_OUTPUT_HANDLE, ENABLE_VIRTUAL_TERMINAL_PROCESSING);
std::string req_raw =
"GET /url/path HTTP/1.1\r\n"
"Host: example.com\r\n"
"Host: www.example.com\r\n"
"User - Agent : Mozilla / 5.0\r\n"
"Accept : text / html, application / xhtml + xml, application / xml; q = 0.9, image / avif, image / webp, */*;q=0.8\r\n"
"Accept-Language: en-GB,en;q=0.5\r\n"
"Accept-Encoding: gzip, deflate, br\r\n"
"Connection: keep-alive\r\n"
"\r\n";
std::stringstream req(req_raw);
Router router;
router.on_get("/", [&](Request& req, Response& res, Next next) {
Resource src(INDEX, HTML_FILE);
res.header("Content-Type", "text/html");
res.send(src.data(), src.size());
});
router.on_get("/url/path", [&](Request& req, Response& res, Next next) {
res.send("Hello, World!");
});
std::stringstream res;
router.handle(req, res);
std::cout << CYAN << res.str() << RESET << std::endl;
router.listen(8080);
}

17
RC.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
class RC
{
public:
RC() : _rc(new int[1]) { *_rc = 1; };
RC(const RC &r) noexcept : _rc(r._rc) { *_rc += 1; }
RC(RC &&r) noexcept : _rc(r._rc) { }
~RC() { if (!--*_rc) delete[] _rc; };
int rc() { return *_rc; }
bool last() { return *_rc == 1; }
private:
int* _rc;
};

106
Resource.cpp Normal file
View File

@ -0,0 +1,106 @@
#include "Resource.h"
#include <streambuf>
#include <istream>
#include <Windows.h>
static HMODULE GetCurrentModule()
{
HMODULE hModule = NULL;
GetModuleHandleEx(
GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS,
(LPCTSTR)GetCurrentModule,
&hModule
);
return hModule;
}
namespace
{
class ResBuf : public std::streambuf
{
public:
ResBuf(int id, int type)
{
HMODULE hMod = GetCurrentModule();
HRSRC hRes = FindResource(hMod, MAKEINTRESOURCE(id), MAKEINTRESOURCE(type));
if (!hRes)
{
throw std::exception("Could not find resource");
}
HGLOBAL hResLoad = LoadResource(hMod, hRes);
if (!hResLoad)
{
throw std::exception("Could not load resource");
}
LPVOID lpResLock = LockResource(hResLoad);
if (!lpResLock)
{
throw std::exception("Could not lock resource");
}
DWORD dwResSize = SizeofResource(hMod, hRes);
if (dwResSize == 0)
{
throw std::exception("Could not aquire resource size");
}
_hRes = hRes;
_buff = (char*)lpResLock;
_size = dwResSize;
setg(_buff, _buff, _buff + _size);
}
virtual ~ResBuf()
{
FreeResource(_hRes);
}
virtual int underflow()
{
return EOF;
}
char* buff()
{
return _buff;
}
size_t size()
{
return _size;
}
private:
HRSRC _hRes;
char* _buff;
size_t _size;
};
}
Resource::Resource(int id, int type) : std::istream(new ResBuf(id, type))
{
}
Resource::~Resource()
{
delete rdbuf();
}
const char* Resource::data()
{
return (const char*)(((ResBuf*)rdbuf())->buff());
}
size_t Resource::size()
{
return ((ResBuf*)rdbuf())->size();
}

19
Resource.h Normal file
View File

@ -0,0 +1,19 @@
#pragma once
#include <istream>
#include "_resource.h"
class Resource : public std::istream
{
public:
Resource(int id, int type);
virtual ~Resource();
Resource(const Resource&) = delete;
Resource(Resource&&) = delete;
const char* data();
size_t size();
};

507
Router.cpp Normal file
View File

@ -0,0 +1,507 @@
#include "Router.h"
#include <algorithm>
#include <streambuf>
#include <iostream> // Only for warning when handling client error
#include <istream>
#include <ostream>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
#include <map>
#include "Socket.h"
#include "StringUtil.h"
static std::map<int, const char*> STATUS_STR =
{
{ 100, "Continue" },
{ 101, "Switching Protocols" },
{ 102, "Processing" },
{ 103, "Early Hints" },
{ 104, "Upload Resumption Supported" },
{ 200, "OK" },
{ 201, "Created" },
{ 202, "Accepted" },
{ 203, "Non - Authoritative Information" },
{ 204, "No Content" },
{ 205, "Reset Content" },
{ 206, "Partial Content" },
{ 207, "Multi - Status" },
{ 208, "Already Reported" },
{ 226, "IM Used" },
{ 300, "Multiple Choices" },
{ 301, "Moved Permanently" },
{ 302, "Found" },
{ 303, "See Other" },
{ 304, "Not Modified" },
{ 305, "Use Proxy" },
{ 306, "Switch Proxy" },
{ 307, "Temporary Redirect" },
{ 308, "Permanent Redirect" },
{ 400, "Bad Request" },
{ 401, "Unauthorized" },
{ 402, "Payment Required" },
{ 403, "Forbidden" },
{ 404, "Not Found" },
{ 405, "Method Not Allowed" },
{ 406, "Not Acceptable" },
{ 407, "Proxy Authentication Required" },
{ 408, "Request Timeout" },
{ 409, "Conflict" },
{ 410, "Gone" },
{ 411, "Length Required" },
{ 412, "Precondition Failed" },
{ 413, "Content Too Large" },
{ 414, "URI Too Long" },
{ 415, "Unsupported Media Type" },
{ 416, "Range Not Satisfiable" },
{ 417, "Expectation Failed" },
{ 418, "I<EFBFBD>m a Teapot" },
{ 421, "Misdirected Request" },
{ 422, "Unprocessable Content" },
{ 423, "Locked" },
{ 424, "Failed Dependency" },
{ 425, "Too Early" },
{ 426, "Upgrade Required" },
{ 427, "Unassigned" },
{ 428, "Precondition Required" },
{ 429, "Too Many Requests" },
{ 430, "Unassigned" },
{ 431, "Request Header Fields Too Large" },
{ 451, "Unavailable For Legal Reasons" },
{ 500, "Internal Server Error" },
{ 501, "Not Implemented" },
{ 502, "Bad Gateway" },
{ 503, "Service Unavailable" },
{ 504, "Gateway Timeout" },
{ 505, "HTTP Version Not Supported" },
{ 506, "Variant Also Negotiates" },
{ 507, "Insufficient Storage" },
{ 508, "Loop Detected" },
{ 509, "Unassigned" },
{ 510, "Not Extended" },
{ 511, "Network Authentication Required" },
};
namespace
{
class ClientError : public std::exception
{
public:
ClientError(const char* what) : std::exception(what)
{
}
};
class ReqBuf : public std::streambuf
{
public:
ReqBuf(std::istream& stream) : _stream(stream)
{
constexpr int header_max = 64;
constexpr int line_max = 512;
for (int n = 0;; n++)
{
if (n == header_max)
{
throw ClientError("Too many headers");
}
std::string line;
line += (char)_stream.get();
line += (char)_stream.get();
while (line.substr(line.size() - 2, 2) != "\r\n")
{
int byte = _stream.get();
if (byte == EOF)
{
throw ClientError("Connection closed prmaturely");
}
if (line.size() > line_max)
{
throw ClientError("Header field too long");
}
line += (char)byte;
}
if (line == "\r\n")
break;
line = line.substr(0, line.size() - 2);
if (n == 0)
{
std::vector<std::string> req = StringUtil::splitClean(line, ' ');
method = req[0];
url = req[1];
http_version = req[2];
std::transform(method.begin(), method.end(), method.begin(), ::toupper);
}
else
{
size_t sep = line.find(':');
std::string name = StringUtil::strip(line.substr(0, sep));
std::string value = StringUtil::strip(line.substr(sep + 1));
headers[name] = value;
}
}
}
virtual int underflow()
{
return _stream.get();
}
std::string method, url, http_version;
std::map<std::string, std::string> headers;
private:
std::istream& _stream;
};
}
#define buf ((ReqBuf*)rdbuf())
Request::Request(std::istream& stream) : std::istream(new ReqBuf(stream))
{
}
Request::~Request()
{
delete rdbuf();
}
std::string Request::url() const
{
return buf->url;
}
std::string Request::method() const
{
return buf->method;
}
const std::string* Request::header(std::string name) const
{
auto field = buf->headers.find(name);
if (field == buf->headers.end())
return nullptr;
return &field->second;
}
#undef buf
namespace
{
class ResBuf : public std::streambuf
{
public:
ResBuf(std::ostream& stream) : _stream(stream)
{
}
void send_head()
{
if (head_sent)
throw std::exception("Head has already been sent");
head_sent = true;
_stream
<< "HTTP/1.1 " << status << ' ' << STATUS_STR[status] << "\r\n";
for (auto& pair : headers)
_stream << pair.first << ": " << pair.second << "\r\n";
_stream << "\r\n";
}
int status = 200;
bool head_sent = false;
std::map<std::string, std::string> headers = {
{"Connection", "Close"},
};
protected:
virtual int overflow(int c) override
{
if (!head_sent)
send_head();
_stream.put(c);
return c;
}
virtual int sync() override
{
_stream.flush();
return 0;
}
private:
std::ostream& _stream;
};
}
#define buf ((ResBuf*)rdbuf())
Response::Response(std::ostream& stream) : std::ostream(new ResBuf(stream))
{
}
Response::~Response()
{
delete rdbuf();
}
void Response::status(int status)
{
buf->status = status;
}
void Response::header(std::string name, std::string value)
{
if (buf->head_sent)
throw std::exception("Headers already sent.");
buf->headers[name] = value;
}
void Response::send(std::string text)
{
if (buf->headers.find("Content-Type") == buf->headers.end())
header("Content-Type", "text/plain");
header("Content-Length", text.size());
*this << text;
flush();
}
void Response::send(const void* data, size_t size)
{
if (buf->headers.find("Content-Type") == buf->headers.end())
header("Content-Type", "application/octet-stream");
header("Content-Length", size);
write((const char*)data, size);
flush();
}
void Response::sendFile(std::string filename)
{
std::ifstream fs(filename, std::ifstream::binary | std::ifstream::ate);
if (!fs.is_open())
throw std::exception(("Could not open \""+filename+"\"").c_str());
header("Content-Length", (size_t)fs.tellg());
fs.seekg(0, std::ifstream::beg);
*this << fs.rdbuf();
flush();
}
#undef buf
static void default_404(Request& req, Response& res)
{
std::stringstream ss;
ss << "<html>"
<< "<head>"
<< "<title>Not Found</title>"
<< "</head>"
<< "<body>"
<< "<h1>Not Found</h1>"
<< "<p>"
<< '"' << req.url() << '"'
<< " could not be found."
<< "</p>"
<< "</body>"
<< "</html>";
res.status(404);
res.header("Content-Type", "text/html");
res.send(ss.str());
}
static void default_500(Request& req, Response& res, std::string msg)
{
std::stringstream ss;
ss << "<html>"
<< "<head>"
<< "<title>Error</title>"
<< "</head>"
<< "<body>"
<< "<h1>Error</h1>"
<< "<p>"
<< msg
<< "</p>"
<< "</body>"
<< "</html>";
res.status(500);
res.header("Content-Type", "text/html");
res.send(ss.str());
}
Router::Router()
{
}
void Router::handle(std::istream& req_s, std::ostream& res_s)
{
Request req(req_s);
Response res(res_s);
auto f = _handlers.find(req.url());
if (f == _handlers.end())
{
default_404(req, res);
return;
}
else
{
std::vector<Handler>& handlers = f->second;
auto h = handlers.begin();
jmp_buf next;
setjmp(next);
if (h == handlers.end())
{
default_404(req, res);
return;
}
else
{
try
{
(*h)(req, res, [&]() { h += 1; longjmp(next, 0); });
}
catch (std::exception& ex)
{
default_500(req, res, std::string(ex.what()));
}
catch (std::string& str)
{
default_500(req, res, str);
}
catch (const char* str)
{
default_500(req, res, std::string(str));
}
catch (...)
{
default_500(req, res, "Unhandeled exception has accrued");
}
}
}
}
void Router::on(std::string url, Handler handler)
{
// if vector already existed it is returned.
// If not, a new one is created and returned.
auto pair = _handlers.emplace(std::piecewise_construct, std::make_tuple(url), std::make_tuple());
// First is the key-value pair iterator,
// second is true if new vector was created.
auto& iter = pair.first;
iter
// First is the key, second is the vector.
->second
.push_back(handler);
}
void Router::on_get(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "GET") handler(req, res, next); else next(); });
}
void Router::on_head(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "HEAD") handler(req, res, next); else next(); });
}
void Router::on_options(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "OPTIONS") handler(req, res, next); else next(); });
}
void Router::on_trace(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "TRACE") handler(req, res, next); else next(); });
}
void Router::on_put(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "PUT") handler(req, res, next); else next(); });
}
void Router::on_delete(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "DELETE") handler(req, res, next); else next(); });
}
void Router::on_post(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "POST") handler(req, res, next); else next(); });
}
void Router::on_patch(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "PATCH") handler(req, res, next); else next(); });
}
void Router::on_connect(std::string url, Handler handler)
{
on(url, [=](Request& req, Response& res, Next next) { if (req.method() == "CONNECT") handler(req, res, next); else next(); });
}
void Router::listen(std::string address, short port)
{
Socket server;
server.bind(address.c_str(), port);
server.listen();
while (true)
{
Socket client = server.accept();
SocketStream stream(client);
handle(stream, stream);
}
}
void Router::listen(short port)
{
Socket server;
server.bind(port);
server.listen();
while (true) try
{
Socket client = server.accept();
SocketStream stream(client);
handle(stream, stream);
}
catch (ClientError& err)
{
// Nothing major, ignore
std::cout << "Client Error: " << err.what() << std::endl;
}
}

75
Router.h Normal file
View File

@ -0,0 +1,75 @@
#pragma once
#include <istream>
#include <ostream>
#include <functional>
#include <map>
#include <vector>
#include <string>
class Request : public std::istream
{
public:
Request(std::istream& stream);
virtual ~Request();
Request(const Request&) = delete;
Request(Request&&) = delete;
std::string url() const;
std::string method() const;
const std::string* header(std::string name) const;
};
class Response : public std::ostream
{
public:
Response(std::ostream& stream);
virtual ~Response();
Response(const Response&) = delete;
Response(Response&&) = delete;
void status(int status);
template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, bool> = true>
void header(std::string name, T value) { header(name, std::to_string(value)); }
void header(std::string name, std::string value);
void send(std::string text);
void send(const void* data, size_t size);
void sendFile(std::string filename);
};
using Next = std::function<void()>;
class Router
{
public:
using Handler = std::function<void(Request&, Response&, Next)>;
Router();
void handle(std::iostream& req) { handle(req, req); };
void handle(std::istream& req, std::ostream& res);
void on(std::string url, Handler handler);
void on_get(std::string url, Handler handler);
void on_head(std::string url, Handler handler);
void on_options(std::string url, Handler handler);
void on_trace(std::string url, Handler handler);
void on_put(std::string url, Handler handler);
void on_delete(std::string url, Handler handler);
void on_post(std::string url, Handler handler);
void on_patch(std::string url, Handler handler);
void on_connect(std::string url, Handler handler);
void listen(std::string address, short port);
void listen(short port);
private:
std::map<std::string, std::vector<Handler>> _handlers;
};

133
Socket.cpp Normal file
View File

@ -0,0 +1,133 @@
#include "Socket.h"
#include "WinSock2.h"
#include "WS2tcpip.h"
#include "Windows.h"
#include <exception>
#include <cassert>
static bool wsa_initialized = false;
Socket::Socket()
{
// Move out to a separate function
if (!wsa_initialized)
{
WSAData data;
if (WSAStartup(MAKEWORD(2, 2), &data))
throw std::exception("Failed to initialize WSA");
wsa_initialized = true;
}
_sock = socket(
AF_INET,
SOCK_STREAM,
0 // IPPROTO_TCP
);
if (_sock == INVALID_SOCKET)
throw std::exception("Failed to create socket");
}
Socket::~Socket()
{
if (!_rc.last())
return;
// Flush comunication before closing
shutdown(_sock, SD_SEND);
char buffer[512];
while (read(buffer, sizeof(buffer))) {}
shutdown(_sock, SD_RECEIVE);
closesocket(_sock);
}
void Socket::bind(const char *address, unsigned short port)
{
sockaddr_in addr = { 0 };
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
if (address)
inet_pton(AF_INET, address, &addr.sin_addr);
else
addr.sin_addr.s_addr = htonl(INADDR_ANY);
if (::bind(_sock, (sockaddr*)&addr, sizeof(addr)) == SOCKET_ERROR)
throw std::exception("Failed to bind socket");
}
void Socket::listen(int backlog)
{
if (::listen(_sock, backlog) == SOCKET_ERROR)
throw std::exception("Failed to listen on socket");
}
Socket Socket::accept()
{
SOCKET sock = ::accept(_sock, nullptr, nullptr);
if (sock == INVALID_SOCKET)
throw std::exception("Failed to accept socket");
Socket socket(sock);
return std::move(socket);
}
int Socket::read(void* buffer, int size)
{
int ret = ::recv(_sock, (char*)buffer, size, 0);
return max(0, ret); // Clamp to 0, as socket is always synchronious
}
int Socket::write(const char *string)
{
return write(string, (int)strlen(string));
}
int Socket::write(const void *data, int size)
{
return ::send(_sock, (const char *)data, size, 0);
}
void Socket::end()
{
::shutdown(_sock, SD_SEND);
}
SocketStreamBuf::~SocketStreamBuf()
{
sync();
}
int SocketStreamBuf::underflow()
{
// System should be buffering received data
if (!_sock.read(&_c, 1))
return EOF;
setg(&_c, &_c, &_c+1);
return _c;
}
int SocketStreamBuf::overflow(int c)
{
if (_buf_size >= _buf_size_max)
sync();
// This check is not required, but it muffles a warning in VisualStudio
if (_buf_size < _buf_size_max)
_buf[_buf_size++] = c;
else
assert(0);
return c;
}
int SocketStreamBuf::sync()
{
_sock.write(_buf, _buf_size);
_buf_size = 0;
return 0;
}

86
Socket.h Normal file
View File

@ -0,0 +1,86 @@
#pragma once
#include "RC.h"
#include <streambuf>
#include <istream>
#include <ostream>
class SocketStreamBuf;
class ISocketStream;
class OSocketStream;
class SocketStream;
class Socket
{
public:
Socket();
Socket(size_t socket) : _sock(socket) {};
virtual ~Socket();
void bind(unsigned short port) { bind(nullptr, port); }
void bind(const char* address, unsigned short port);
void listen(int backlog = 1024);
Socket accept();
int read(void* buffer, int size);
int write(const char* string);
int write(const void* data, int size);
void end();
size_t handle() const { return _sock; }
private:
size_t _sock;
RC _rc;
};
class SocketStreamBuf : public std::streambuf
{
public:
SocketStreamBuf(Socket sock) : _sock(sock), _buf() {}
virtual ~SocketStreamBuf();
virtual int underflow();
virtual int overflow(int c);
virtual int sync();
private:
Socket _sock;
// Read buffer
char _c = 0;
// Send buffer
enum { _buf_size_max = 1024 };
char _buf[_buf_size_max] = { 0 };
int _buf_size = 0;
};
class ISocketStream : public std::istream
{
public:
ISocketStream(Socket& sock) : std::istream(new SocketStreamBuf(sock)) {}
virtual ~ISocketStream() { delete rdbuf(); }
};
class OSocketStream : public std::ostream
{
public:
OSocketStream(Socket& sock) : std::ostream(new SocketStreamBuf(sock)) {}
virtual ~OSocketStream() { delete rdbuf(); }
};
class SocketStream : public std::iostream
{
public:
SocketStream(Socket& sock) : std::iostream(new SocketStreamBuf(sock)) {}
virtual ~SocketStream() { delete rdbuf(); }
};

100
StringUtil.cpp Normal file
View File

@ -0,0 +1,100 @@
#include "StringUtil.h"
std::vector<std::string> StringUtil::split(std::string str, std::string sep)
{
std::vector<std::string> vec;
while (str.size() != 0)
{
size_t index = str.find(sep);
if (index)
vec.push_back(str.substr(0, index));
else
vec.push_back("");
str = str.substr(index + sep.size());
}
return vec;
}
std::vector<std::string> StringUtil::split(std::string str, char sep)
{
std::vector<std::string> vec;
int i, j;
for (i = 0, j = 1; j < str.size(); j++)
{
if (str[j] == sep)
{
if (i == j)
vec.push_back("");
else
vec.push_back(str.substr(i, j - i));
i = j + 1;
}
}
if (j + 1 != i)
vec.push_back(str.substr(i, j - 1));
return vec;
}
std::vector<std::string> StringUtil::splitClean(std::string str, std::string sep)
{
std::vector<std::string> vec;
while (str.size() != 0)
{
size_t index = str.find(sep);
if (index)
vec.push_back(str.substr(0, index));
str = str.substr(index + sep.size());
}
return vec;
}
std::vector<std::string> StringUtil::splitClean(std::string str, char sep)
{
std::vector<std::string> vec;
int i, j;
for (i = 0, j = 1; j < str.size(); j++)
{
if (str[j] == sep && i != j)
{
vec.push_back(str.substr(i, j - i));
i = j + 1;
}
}
if (j + 1 != i)
vec.push_back(str.substr(i, j - 1));
return vec;
}
std::string StringUtil::strip(std::string str, char sep)
{
return stripLeft(stripRight(str, sep), sep);
}
std::string StringUtil::stripLeft(std::string str, char sep)
{
int n;
for (n = 0; n < str.size(); n++)
if (str[n] != sep)
break;
return str.substr(n);
}
std::string StringUtil::stripRight(std::string str, char sep)
{
int n;
for (n = (int)str.size()-1; n > -1; n--)
if (str[n] != sep)
break;
return str.substr(0, n+1);
}

18
StringUtil.h Normal file
View File

@ -0,0 +1,18 @@
#pragma once
#include <vector>
#include <string>
namespace StringUtil
{
std::vector<std::string> split(std::string str, std::string sep);
std::vector<std::string> split(std::string str, char sep);
std::vector<std::string> splitClean(std::string str, std::string sep);
std::vector<std::string> splitClean(std::string str, char sep);
std::string strip(std::string str, char c = ' ');
std::string stripLeft(std::string str, char c = ' ');
std::string stripRight(std::string str, char c = ' ');
}

42
Terminal.h Normal file
View File

@ -0,0 +1,42 @@
#pragma once
#define __TERM_INT2STR(n) #n
#define FG(r, g, b) ("\x1B[38;2;" __TERM_INT2STR(r) ";" __TERM_INT2STR(g) ";" __TERM_INT2STR(b) "m")
#define BG(r, g, b) ("\x1B[48;2;" __TERM_INT2STR(r) ";" __TERM_INT2STR(g) ";" __TERM_INT2STR(b) "m")
#define RESET "\x1B[0m"
#define BLACK "\x1B[30m"
#define RED "\x1B[31m"
#define GREEN "\x1B[32m"
#define YELLOW "\x1B[33m"
#define BLUE "\x1B[34m"
#define MAGENTA "\x1B[35m"
#define CYAN "\x1B[36m"
#define WHITE "\x1B[37m"
#define BRIGHT_BLACK "\x1B[90m"
#define BRIGHT_RED "\x1B[91m"
#define BRIGHT_GREEN "\x1B[92m"
#define BRIGHT_YELLOW "\x1B[93m"
#define BRIGHT_BLUE "\x1B[94m"
#define BRIGHT_MAGENTA "\x1B[95m"
#define BRIGHT_CYAN "\x1B[96m"
#define BRIGHT_WHITE "\x1B[97m"
#define BBLACK "\x1B[40m"
#define BRED "\x1B[41m"
#define BGREEN "\x1B[42m"
#define BYELLOW "\x1B[43m"
#define BBLUE "\x1B[44m"
#define BMAGENTA "\x1B[45m"
#define BCYAN "\x1B[46m"
#define BWHITE "\x1B[47m"
#define BBRIGHT_BLACK "\x1B[100m"
#define BBRIGHT_RED "\x1B[101m"
#define BBRIGHT_GREEN "\x1B[102m"
#define BBRIGHT_YELLOW "\x1B[103m"
#define BBRIGHT_BLUE "\x1B[104m"
#define BBRIGHT_MAGENTA "\x1B[105m"
#define BBRIGHT_CYAN "\x1B[106m"
#define BBRIGHT_WHITE "\x1B[107m"

19
_resource.h Normal file
View File

@ -0,0 +1,19 @@
//{{NO_DEPENDENCIES}}
// Microsoft Visual C++ generated include file.
// Used by hate.rc
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 101
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif
#define INDEX 1
#define HTML_FILE 256

BIN
hate.rc Normal file

Binary file not shown.

31
hate.sln Normal file
View File

@ -0,0 +1,31 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.14.36511.14 d17.14
MinimumVisualStudioVersion = 10.0.40219.1
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "hate", "hate.vcxproj", "{0181C661-83F0-48E2-BBAF-D802CCFB2104}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{0181C661-83F0-48E2-BBAF-D802CCFB2104}.Debug|x64.ActiveCfg = Debug|x64
{0181C661-83F0-48E2-BBAF-D802CCFB2104}.Debug|x64.Build.0 = Debug|x64
{0181C661-83F0-48E2-BBAF-D802CCFB2104}.Debug|x86.ActiveCfg = Debug|Win32
{0181C661-83F0-48E2-BBAF-D802CCFB2104}.Debug|x86.Build.0 = Debug|Win32
{0181C661-83F0-48E2-BBAF-D802CCFB2104}.Release|x64.ActiveCfg = Release|x64
{0181C661-83F0-48E2-BBAF-D802CCFB2104}.Release|x64.Build.0 = Release|x64
{0181C661-83F0-48E2-BBAF-D802CCFB2104}.Release|x86.ActiveCfg = Release|Win32
{0181C661-83F0-48E2-BBAF-D802CCFB2104}.Release|x86.Build.0 = Release|Win32
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {69BFFB14-18F1-4AF0-80B7-BBF80722B98A}
EndGlobalSection
EndGlobal

151
hate.vcxproj Normal file
View File

@ -0,0 +1,151 @@
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup Label="ProjectConfigurations">
<ProjectConfiguration Include="Debug|Win32">
<Configuration>Debug</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|Win32">
<Configuration>Release</Configuration>
<Platform>Win32</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Debug|x64">
<Configuration>Debug</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
<ProjectConfiguration Include="Release|x64">
<Configuration>Release</Configuration>
<Platform>x64</Platform>
</ProjectConfiguration>
</ItemGroup>
<PropertyGroup Label="Globals">
<VCProjectVersion>17.0</VCProjectVersion>
<Keyword>Win32Proj</Keyword>
<ProjectGuid>{0181c661-83f0-48e2-bbaf-d802ccfb2104}</ProjectGuid>
<RootNamespace>hate</RootNamespace>
<WindowsTargetPlatformVersion>10.0</WindowsTargetPlatformVersion>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>true</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
<ConfigurationType>Application</ConfigurationType>
<UseDebugLibraries>false</UseDebugLibraries>
<PlatformToolset>v143</PlatformToolset>
<WholeProgramOptimization>true</WholeProgramOptimization>
<CharacterSet>Unicode</CharacterSet>
</PropertyGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
<ImportGroup Label="ExtensionSettings">
</ImportGroup>
<ImportGroup Label="Shared">
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<ImportGroup Label="PropertySheets" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
</ImportGroup>
<PropertyGroup Label="UserMacros" />
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>_DEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<ClCompile>
<WarningLevel>Level3</WarningLevel>
<FunctionLevelLinking>true</FunctionLevelLinking>
<IntrinsicFunctions>true</IntrinsicFunctions>
<SDLCheck>true</SDLCheck>
<PreprocessorDefinitions>NDEBUG;_CONSOLE;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<ConformanceMode>true</ConformanceMode>
</ClCompile>
<Link>
<SubSystem>Console</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>ws2_32.lib;%(AdditionalDependencies)</AdditionalDependencies>
</Link>
</ItemDefinitionGroup>
<ItemGroup>
<ClCompile Include="Main.cpp" />
<ClCompile Include="Resource.cpp" />
<ClCompile Include="Router.cpp" />
<ClCompile Include="Socket.cpp" />
<ClCompile Include="StringUtil.cpp" />
</ItemGroup>
<ItemGroup>
<ClInclude Include="Resource.h" />
<ClInclude Include="_resource.h" />
<ClInclude Include="Router.h" />
<ClInclude Include="RC.h" />
<ClInclude Include="Socket.h" />
<ClInclude Include="StringUtil.h" />
<ClInclude Include="Terminal.h" />
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="hate.rc" />
</ItemGroup>
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
<ImportGroup Label="ExtensionTargets">
</ImportGroup>
</Project>

62
hate.vcxproj.filters Normal file
View File

@ -0,0 +1,62 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemGroup>
<Filter Include="Source Files">
<UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
<Extensions>cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
</Filter>
<Filter Include="Header Files">
<UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
<Extensions>h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd</Extensions>
</Filter>
<Filter Include="Resource Files">
<UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
<Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
</Filter>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Main.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Socket.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Router.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="StringUtil.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="Resource.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ClInclude Include="Socket.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="RC.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Router.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Terminal.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="StringUtil.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="_resource.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="Resource.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="hate.rc">
<Filter>Resource Files</Filter>
</ResourceCompile>
</ItemGroup>
</Project>

4
hate.vcxproj.user Normal file
View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="Current" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup />
</Project>

10
res/index.html Normal file
View File

@ -0,0 +1,10 @@
<!DOCTYPE html>
<html>
<head>
<title>Hate</title>
</head>
<body>
<h1>H.A.T.E.</h1>
<p>Time to <b>KILL</b> those pesky little CPU sucking processes!</p>
</body>
</html>