From 44f8d483561b5692756698e2a11fd0efcdead81f Mon Sep 17 00:00:00 2001 From: Tomas Date: Sat, 22 Nov 2025 16:25:58 +0100 Subject: [PATCH] Initial commit --- .gitignore | 82 +++++++ Main.cpp | 48 ++++ RC.h | 17 ++ Resource.cpp | 106 +++++++++ Resource.h | 19 ++ Router.cpp | 507 +++++++++++++++++++++++++++++++++++++++++++ Router.h | 75 +++++++ Socket.cpp | 133 ++++++++++++ Socket.h | 86 ++++++++ StringUtil.cpp | 100 +++++++++ StringUtil.h | 18 ++ Terminal.h | 42 ++++ _resource.h | 19 ++ hate.rc | Bin 0 -> 124 bytes hate.sln | 31 +++ hate.vcxproj | 151 +++++++++++++ hate.vcxproj.filters | 62 ++++++ hate.vcxproj.user | 4 + res/index.html | 10 + 19 files changed, 1510 insertions(+) create mode 100644 .gitignore create mode 100644 Main.cpp create mode 100644 RC.h create mode 100644 Resource.cpp create mode 100644 Resource.h create mode 100644 Router.cpp create mode 100644 Router.h create mode 100644 Socket.cpp create mode 100644 Socket.h create mode 100644 StringUtil.cpp create mode 100644 StringUtil.h create mode 100644 Terminal.h create mode 100644 _resource.h create mode 100644 hate.rc create mode 100644 hate.sln create mode 100644 hate.vcxproj create mode 100644 hate.vcxproj.filters create mode 100644 hate.vcxproj.user create mode 100644 res/index.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..08d9f52 --- /dev/null +++ b/.gitignore @@ -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 \ No newline at end of file diff --git a/Main.cpp b/Main.cpp new file mode 100644 index 0000000..3b22483 --- /dev/null +++ b/Main.cpp @@ -0,0 +1,48 @@ +#include +#include +#include + +#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); +} \ No newline at end of file diff --git a/RC.h b/RC.h new file mode 100644 index 0000000..3078df3 --- /dev/null +++ b/RC.h @@ -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; +}; \ No newline at end of file diff --git a/Resource.cpp b/Resource.cpp new file mode 100644 index 0000000..0f99935 --- /dev/null +++ b/Resource.cpp @@ -0,0 +1,106 @@ +#include "Resource.h" + +#include +#include + +#include + + +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(); +} \ No newline at end of file diff --git a/Resource.h b/Resource.h new file mode 100644 index 0000000..366d0c6 --- /dev/null +++ b/Resource.h @@ -0,0 +1,19 @@ +#pragma once + +#include + +#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(); +}; \ No newline at end of file diff --git a/Router.cpp b/Router.cpp new file mode 100644 index 0000000..50add5c --- /dev/null +++ b/Router.cpp @@ -0,0 +1,507 @@ +#include "Router.h" + +#include +#include +#include // Only for warning when handling client error +#include +#include +#include +#include +#include +#include +#include + +#include "Socket.h" +#include "StringUtil.h" + + +static std::map 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’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 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 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 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 << "" + << "" + << "Not Found" + << "" + << "" + << "

Not Found

" + << "

" + << '"' << req.url() << '"' + << " could not be found." + << "

" + << "" + << ""; + + 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 << "" + << "" + << "Error" + << "" + << "" + << "

Error

" + << "

" + << msg + << "

" + << "" + << ""; + + 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& 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; + } +} \ No newline at end of file diff --git a/Router.h b/Router.h new file mode 100644 index 0000000..3db6fea --- /dev/null +++ b/Router.h @@ -0,0 +1,75 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + + +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::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; +class Router +{ +public: + using Handler = std::function; + + 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> _handlers; +}; \ No newline at end of file diff --git a/Socket.cpp b/Socket.cpp new file mode 100644 index 0000000..5453903 --- /dev/null +++ b/Socket.cpp @@ -0,0 +1,133 @@ +#include "Socket.h" + +#include "WinSock2.h" +#include "WS2tcpip.h" +#include "Windows.h" + +#include +#include + + +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; +} \ No newline at end of file diff --git a/Socket.h b/Socket.h new file mode 100644 index 0000000..0e01fac --- /dev/null +++ b/Socket.h @@ -0,0 +1,86 @@ +#pragma once + +#include "RC.h" + +#include +#include +#include + + +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(); } +}; \ No newline at end of file diff --git a/StringUtil.cpp b/StringUtil.cpp new file mode 100644 index 0000000..e5919b1 --- /dev/null +++ b/StringUtil.cpp @@ -0,0 +1,100 @@ +#include "StringUtil.h" + + +std::vector StringUtil::split(std::string str, std::string sep) +{ + std::vector 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 StringUtil::split(std::string str, char sep) +{ + std::vector 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 StringUtil::splitClean(std::string str, std::string sep) +{ + std::vector 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 StringUtil::splitClean(std::string str, char sep) +{ + std::vector 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); +} \ No newline at end of file diff --git a/StringUtil.h b/StringUtil.h new file mode 100644 index 0000000..523ba2c --- /dev/null +++ b/StringUtil.h @@ -0,0 +1,18 @@ +#pragma once + +#include +#include + + +namespace StringUtil +{ + std::vector split(std::string str, std::string sep); + std::vector split(std::string str, char sep); + + std::vector splitClean(std::string str, std::string sep); + std::vector 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 = ' '); +} \ No newline at end of file diff --git a/Terminal.h b/Terminal.h new file mode 100644 index 0000000..5887037 --- /dev/null +++ b/Terminal.h @@ -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" \ No newline at end of file diff --git a/_resource.h b/_resource.h new file mode 100644 index 0000000..c9bf563 --- /dev/null +++ b/_resource.h @@ -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 diff --git a/hate.rc b/hate.rc new file mode 100644 index 0000000000000000000000000000000000000000..a955d589b20008fe38a086bad1c4f771db7c329e GIT binary patch literal 124 zcmX|)%?dz36otRF@(yC}DU3{EWnnR8fuj7>pgcXUQ5L6rI=Azk`-&J@XqhdMR42a+Hr@FmB;xpN_LL%UZ|-aiJ8r literal 0 HcmV?d00001 diff --git a/hate.sln b/hate.sln new file mode 100644 index 0000000..7ea14e4 --- /dev/null +++ b/hate.sln @@ -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 diff --git a/hate.vcxproj b/hate.vcxproj new file mode 100644 index 0000000..b98598d --- /dev/null +++ b/hate.vcxproj @@ -0,0 +1,151 @@ + + + + + Debug + Win32 + + + Release + Win32 + + + Debug + x64 + + + Release + x64 + + + + 17.0 + Win32Proj + {0181c661-83f0-48e2-bbaf-d802ccfb2104} + hate + 10.0 + + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + Application + true + v143 + Unicode + + + Application + false + v143 + true + Unicode + + + + + + + + + + + + + + + + + + + + + + Level3 + true + WIN32;_DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + WIN32;NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + true + _DEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + Level3 + true + true + true + NDEBUG;_CONSOLE;%(PreprocessorDefinitions) + true + + + Console + true + ws2_32.lib;%(AdditionalDependencies) + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/hate.vcxproj.filters b/hate.vcxproj.filters new file mode 100644 index 0000000..b340bbd --- /dev/null +++ b/hate.vcxproj.filters @@ -0,0 +1,62 @@ + + + + + {4FC737F1-C7A5-4376-A066-2A32D752A2FF} + cpp;c;cc;cxx;c++;cppm;ixx;def;odl;idl;hpj;bat;asm;asmx + + + {93995380-89BD-4b04-88EB-625FBE52EBFB} + h;hh;hpp;hxx;h++;hm;inl;inc;ipp;xsd + + + {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} + rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms + + + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + Source Files + + + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + Header Files + + + + + Resource Files + + + \ No newline at end of file diff --git a/hate.vcxproj.user b/hate.vcxproj.user new file mode 100644 index 0000000..88a5509 --- /dev/null +++ b/hate.vcxproj.user @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/res/index.html b/res/index.html new file mode 100644 index 0000000..51a91d8 --- /dev/null +++ b/res/index.html @@ -0,0 +1,10 @@ + + + + Hate + + +

H.A.T.E.

+

Time to KILL those pesky little CPU sucking processes!

+ + \ No newline at end of file