Initial commit
This commit is contained in:
82
.gitignore
vendored
Normal file
82
.gitignore
vendored
Normal 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
48
Main.cpp
Normal 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
17
RC.h
Normal 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
106
Resource.cpp
Normal 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
19
Resource.h
Normal 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
507
Router.cpp
Normal 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
75
Router.h
Normal 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
133
Socket.cpp
Normal 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
86
Socket.h
Normal 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
100
StringUtil.cpp
Normal 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
18
StringUtil.h
Normal 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
42
Terminal.h
Normal 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
19
_resource.h
Normal 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
|
||||||
31
hate.sln
Normal file
31
hate.sln
Normal 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
151
hate.vcxproj
Normal 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
62
hate.vcxproj.filters
Normal 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
4
hate.vcxproj.user
Normal 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
10
res/index.html
Normal 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>
|
||||||
Reference in New Issue
Block a user