split into src and lib and socket changes
* libanthracite files are now all in lib/ * anthracite-bin + anthracite-api-bin files are now all in src/ * socket split into header and source properly
This commit is contained in:
parent
fba87f3fbb
commit
71be773d49
24 changed files with 49 additions and 121 deletions
244
lib/http/constants.hpp
Normal file
244
lib/http/constants.hpp
Normal file
|
@ -0,0 +1,244 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
|
||||
namespace anthracite::http {
|
||||
|
||||
constexpr int HEADER_BYTES = 8190;
|
||||
|
||||
enum method {
|
||||
GET,
|
||||
POST,
|
||||
DELETE,
|
||||
PUT,
|
||||
PATCH,
|
||||
HEAD,
|
||||
OPTIONS,
|
||||
CONNECT,
|
||||
TRACE,
|
||||
COPY,
|
||||
LINK,
|
||||
UNLINK,
|
||||
PURGE,
|
||||
LOCK,
|
||||
UNLOCK,
|
||||
PROPFIND,
|
||||
VIEW,
|
||||
UNKNOWN
|
||||
};
|
||||
|
||||
enum status_codes {
|
||||
CONTINUE = 100,
|
||||
SWITCHING_PROTOCOLS = 101,
|
||||
PROCESSING = 102,
|
||||
EARLY_HINTS = 103,
|
||||
OK = 200,
|
||||
CREATED = 201,
|
||||
ACCEPTED = 202,
|
||||
NON_AUTHORITATIVE_INFORMATION = 203,
|
||||
NO_CONTENT = 204,
|
||||
RESET_CONTENT = 205,
|
||||
PARTIAL_CONTENT = 206,
|
||||
MULTI_STATUS = 207,
|
||||
ALREADY_REPORTED = 208,
|
||||
IM_USED = 226,
|
||||
MULTIPLE_CHOICES = 300,
|
||||
MOVED_PERMANENTLY = 301,
|
||||
FOUND = 302,
|
||||
SEE_OTHER = 303,
|
||||
NOT_MODIFIED = 304,
|
||||
USE_PROXY = 305,
|
||||
TEMPORARY_REDIRECT = 307,
|
||||
PERMANENT_REDIRECT = 308,
|
||||
BAD_REQUEST = 400,
|
||||
UNAUTHORIZED = 401,
|
||||
PAYMENT_REQUIRED = 402,
|
||||
FORBIDDEN = 403,
|
||||
NOT_FOUND = 404,
|
||||
METHOD_NOT_ALLOWED = 405,
|
||||
NOT_ACCEPTABLE = 406,
|
||||
PROXY_AUTHENTICATION_REQUIRED = 407,
|
||||
REQUEST_TIMEOUT = 408,
|
||||
CONFLICT = 409,
|
||||
GONE = 410,
|
||||
LENGTH_REQUIRED = 411,
|
||||
PRECONDITION_FAILED = 412,
|
||||
PAYLOAD_TOO_LARGE = 413,
|
||||
URI_TOO_LONG = 414,
|
||||
UNSUPPORTED_MEDIA_TYPE = 415,
|
||||
RANGE_NOT_SATISFIABLE = 416,
|
||||
EXPECTATION_FAILED = 417,
|
||||
I_AM_A_TEAPOT = 418,
|
||||
ENHANCE_YOUR_CALM = 420,
|
||||
MISDIRECTED_REQUEST = 421,
|
||||
UNPROCESSABLE_ENTITY = 422,
|
||||
LOCKED = 423,
|
||||
FAILED_DEPENDENCY = 424,
|
||||
TOO_EARLY = 425,
|
||||
UPGRADE_REQUIRED = 426,
|
||||
PRECONDITION_REQUIRED = 428,
|
||||
TOO_MANY_REQUESTS = 429,
|
||||
REQUEST_HEADER_FIELDS_TOO_LARGE = 431,
|
||||
UNAVAILABLE_FOR_LEGAL_REASONS = 451,
|
||||
INTERNAL_SERVER_ERROR = 500,
|
||||
NOT_IMPLEMENTED = 501,
|
||||
BAD_GATEWAY = 502,
|
||||
SERVICE_UNAVAILABLE = 503,
|
||||
GATEWAY_TIMEOUT = 504,
|
||||
HTTP_VERSION_NOT_SUPPORTED = 505,
|
||||
VARIANT_ALSO_NEGOTIATES = 506,
|
||||
INSUFFICIENT_STORAGE = 507,
|
||||
LOOP_DETECTED = 508,
|
||||
NOT_EXTENDED = 510,
|
||||
NETWORK_AUTHENTICATION_REQUIRED = 511
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, method> const method_map = {
|
||||
{ "GET", method::GET },
|
||||
{ "POST", method::POST },
|
||||
{ "DELETE", method::DELETE },
|
||||
{ "PUT", method::PUT },
|
||||
{ "PATCH", method::PATCH },
|
||||
{ "HEAD", method::HEAD },
|
||||
{ "OPTIONS", method::OPTIONS },
|
||||
{ "CONNECT", method::CONNECT },
|
||||
{ "TRACE", method::TRACE },
|
||||
{ "COPY", method::COPY },
|
||||
{ "LINK", method::LINK },
|
||||
{ "UNLINK", method::UNLINK },
|
||||
{ "PURGE", method::PURGE },
|
||||
{ "LOCK", method::LOCK },
|
||||
{ "UNLOCK", method::UNLOCK },
|
||||
{ "PROPFIND", method::PROPFIND },
|
||||
{ "VIEW", method::VIEW },
|
||||
{ "UNKNOWN", method::UNKNOWN }
|
||||
};
|
||||
|
||||
static std::unordered_map<method, std::string> const reverse_method_map = {
|
||||
{ method::GET, "GET" },
|
||||
{ method::POST, "POST" },
|
||||
{ method::DELETE, "DELETE" },
|
||||
{ method::PUT, "PUT" },
|
||||
{ method::PATCH, "PATCH" },
|
||||
{ method::HEAD, "HEAD" },
|
||||
{ method::OPTIONS, "OPTIONS" },
|
||||
{ method::CONNECT, "CONNECT" },
|
||||
{ method::TRACE, "TRACE" },
|
||||
{ method::COPY, "COPY" },
|
||||
{ method::LINK, "LINK" },
|
||||
{ method::UNLINK, "UNLINK" },
|
||||
{ method::PURGE, "PURGE" },
|
||||
{ method::LOCK, "LOCK" },
|
||||
{ method::UNLOCK, "UNLOCK" },
|
||||
{ method::PROPFIND, "PROPFIND" },
|
||||
{ method::VIEW, "VIEW" },
|
||||
{ method::UNKNOWN, "UNKNOWN" }
|
||||
};
|
||||
|
||||
static std::unordered_map<int, std::string> const status_map = {
|
||||
{ 100, "CONTINUE" },
|
||||
{ 101, "SWITCHING PROTOCOLS" },
|
||||
{ 200, "OK" },
|
||||
{ 201, "CREATED" },
|
||||
{ 202, "ACCEPTED" },
|
||||
{ 203, "NON-AUTHORITATIVE INFORMATION" },
|
||||
{ 204, "NO CONTENT" },
|
||||
{ 205, "RESET CONTENT" },
|
||||
{ 206, "PARTIAL CONTENT" },
|
||||
{ 300, "MULTIPLE CHOICES" },
|
||||
{ 301, "MOVED PERMANENTLY" },
|
||||
{ 302, "FOUND" },
|
||||
{ 303, "SEE OTHER" },
|
||||
{ 304, "NOT MODIFIED" },
|
||||
{ 305, "USE PROXY" },
|
||||
{ 307, "TEMPORARY 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, "PAYLOAD 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 ENTITY" },
|
||||
{ 423, "LOCKED" },
|
||||
{ 424, "FAILED DEPENDENCY" },
|
||||
{ 426, "UPGRADE REQUIRED" },
|
||||
{ 428, "PRECONDITION REQUIRED" },
|
||||
{ 429, "TOO MANY REQUESTS" },
|
||||
{ 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" },
|
||||
{ 510, "NOT EXTENDED" },
|
||||
{ 511, "NETWORK AUTHENTICATION REQUIRED" },
|
||||
{ 420, "ENHANCE YOUR CALM" }
|
||||
};
|
||||
|
||||
static std::unordered_map<std::string, std::string> const mime_types = {
|
||||
{ "html", "text/html" },
|
||||
{ "css", "text/css" },
|
||||
|
||||
{ "js", "application/javascript" },
|
||||
{ "json", "application/json" },
|
||||
{ "pdf", "application/pdf" },
|
||||
|
||||
{ "ico", "image/x-icon" },
|
||||
{ "jpg", "image/jpeg" },
|
||||
{ "jpeg", "image/jpeg" },
|
||||
{ "png", "image/png" },
|
||||
{ "gif", "image/gif" },
|
||||
{ "bmp", "image/bmp" },
|
||||
|
||||
{ "mp4", "video/mp4" },
|
||||
{ "avi", "video/x-msvideo" },
|
||||
{ "mkv", "video/x-matroska" },
|
||||
{ "mov", "video/quicktime" },
|
||||
{ "wmv", "video/x-ms-wmv" },
|
||||
};
|
||||
|
||||
enum version { HTTP_0_9,
|
||||
HTTP_1_0,
|
||||
HTTP_1_1,
|
||||
HTTP_2_0,
|
||||
HTTP_3_0 };
|
||||
|
||||
static std::unordered_map<std::string, version> const version_map = {
|
||||
// This is because HTTP 0.9 didn't specify version in the header
|
||||
{ "", HTTP_0_9 },
|
||||
{ "HTTP/0.9", HTTP_0_9 },
|
||||
{ "HTTP/1.0", HTTP_1_0 },
|
||||
{ "HTTP/1.1", HTTP_1_1 },
|
||||
{ "HTTP/2.0", HTTP_2_0 },
|
||||
{ "HTTP/3.0", HTTP_3_0 }
|
||||
};
|
||||
|
||||
static std::unordered_map<version, std::string> const reverse_version_map = {
|
||||
{ HTTP_0_9, "HTTP/0.9" },
|
||||
{ HTTP_1_0, "HTTP/1.0" },
|
||||
{ HTTP_1_1, "HTTP/1.1" },
|
||||
{ HTTP_2_0, "HTTP/2.0" },
|
||||
{ HTTP_3_0, "HTTP/3.0" }
|
||||
};
|
||||
|
||||
};
|
60
lib/http/header_query.hpp
Normal file
60
lib/http/header_query.hpp
Normal file
|
@ -0,0 +1,60 @@
|
|||
#pragma once
|
||||
#include <string>
|
||||
|
||||
namespace anthracite::http {
|
||||
|
||||
class name_value {
|
||||
private:
|
||||
std::string _name;
|
||||
std::string _value;
|
||||
|
||||
protected:
|
||||
name_value() {}
|
||||
|
||||
public:
|
||||
name_value(std::string name, std::string value)
|
||||
: _name(std::move(name))
|
||||
, _value(std::move(value))
|
||||
{
|
||||
}
|
||||
virtual ~name_value() = default;
|
||||
name_value(const name_value&) = default;
|
||||
name_value& operator=(name_value const&) = default;
|
||||
name_value(name_value&&) = default;
|
||||
name_value& operator=(name_value&&) = default;
|
||||
|
||||
std::string& name() { return _name; }
|
||||
std::string& value() { return _value; }
|
||||
|
||||
virtual std::string to_string() { return ""; }
|
||||
};
|
||||
|
||||
class header : public name_value {
|
||||
public:
|
||||
header()
|
||||
: name_value()
|
||||
{
|
||||
}
|
||||
header(std::string name, std::string value)
|
||||
: name_value(name, value)
|
||||
{
|
||||
}
|
||||
|
||||
std::string to_string() override { return name() + ": " + value() + "\r\n"; }
|
||||
};
|
||||
|
||||
class query_param : public name_value {
|
||||
public:
|
||||
query_param()
|
||||
: name_value()
|
||||
{
|
||||
}
|
||||
query_param(std::string name, std::string value)
|
||||
: name_value(name, value)
|
||||
{
|
||||
}
|
||||
|
||||
std::string to_string() override { return name() + "=" + value(); }
|
||||
};
|
||||
|
||||
};
|
0
lib/http/http_request.cpp
Normal file
0
lib/http/http_request.cpp
Normal file
165
lib/http/request.cpp
Normal file
165
lib/http/request.cpp
Normal file
|
@ -0,0 +1,165 @@
|
|||
#include "request.hpp"
|
||||
#include "constants.hpp"
|
||||
#include "../log/log.hpp"
|
||||
#include <stdio.h>
|
||||
|
||||
namespace anthracite::http {
|
||||
|
||||
request::request(std::string& raw_data, const std::string& client_ip)
|
||||
: _path(""), _client_ipaddr(client_ip)
|
||||
{
|
||||
|
||||
parser_state state = METHOD;
|
||||
|
||||
std::string scratch = "";
|
||||
std::string scratch_2 = "";
|
||||
for (int i = 0; i < raw_data.length(); i++) {
|
||||
switch (state) {
|
||||
case METHOD: {
|
||||
if (raw_data[i] == ' ') {
|
||||
if (method_map.find(scratch) == method_map.end()) {
|
||||
_method = method::UNKNOWN;
|
||||
} else {
|
||||
_method = method_map.find(scratch)->second;
|
||||
}
|
||||
scratch = "";
|
||||
state = PATH;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case PATH: {
|
||||
switch (raw_data[i]) {
|
||||
case ' ':
|
||||
state = VERSION;
|
||||
break;
|
||||
case '?':
|
||||
state = QUERY_PARAM_NAME;
|
||||
break;
|
||||
default:
|
||||
_path += raw_data[i];
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
|
||||
case QUERY_PARAM_NAME: {
|
||||
if (raw_data[i] == ' ') {
|
||||
scratch = "";
|
||||
state = VERSION;
|
||||
} else if (raw_data[i] == '=') {
|
||||
state = QUERY_PARAM_VALUE;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case QUERY_PARAM_VALUE: {
|
||||
if (raw_data[i] == ' ') {
|
||||
_query_params[scratch] = query_param(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = VERSION;
|
||||
} else if (raw_data[i] == '&') {
|
||||
_query_params[scratch] = query_param(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = QUERY_PARAM_NAME;
|
||||
} else {
|
||||
scratch_2 += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case VERSION: {
|
||||
if (raw_data[i] == '\n') {
|
||||
_http_version = version_map.find(scratch)->second;
|
||||
scratch = "";
|
||||
state = HEADER_NAME;
|
||||
} else if (raw_data[i] != '\r') {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case HEADER_NAME: {
|
||||
if (raw_data[i] == '\n') {
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = BODY_CONTENT;
|
||||
break;
|
||||
} else if (raw_data[i] == ' ') {
|
||||
scratch = "";
|
||||
break;
|
||||
} else if (raw_data[i] == ':') {
|
||||
state = HEADER_VALUE;
|
||||
i++;
|
||||
} else {
|
||||
scratch += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case HEADER_VALUE: {
|
||||
if (raw_data[i] == '\n') {
|
||||
_headers[scratch] = header(scratch, scratch_2);
|
||||
scratch = "";
|
||||
scratch_2 = "";
|
||||
state = HEADER_NAME;
|
||||
} else if (raw_data[i] != '\r') {
|
||||
scratch_2 += raw_data[i];
|
||||
}
|
||||
} break;
|
||||
|
||||
case BODY_CONTENT: {
|
||||
_body_content += raw_data[i];
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::string request::path() { return _path; }
|
||||
|
||||
method request::get_method() { return _method; }
|
||||
|
||||
std::string request::client_ip() { return _client_ipaddr; }
|
||||
|
||||
version request::get_http_version() {
|
||||
return _http_version;
|
||||
}
|
||||
|
||||
bool request::is_supported_version() {
|
||||
//log::err << reverse_version_map.find(_http_version)->second << std::endl;
|
||||
return _http_version == HTTP_1_1 || _http_version == HTTP_1_0;
|
||||
}
|
||||
|
||||
bool request::close_connection() {
|
||||
const auto& header = _headers.find("Connection");
|
||||
const bool found = header != _headers.end();
|
||||
|
||||
if(found && header->second.value() == "keep-alive") {
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string request::to_string()
|
||||
{
|
||||
std::string response = "";
|
||||
response += reverse_method_map.find(_method)->second + " " + _path + "?";
|
||||
|
||||
for (auto qp : _query_params) {
|
||||
response += qp.second.to_string() + "&";
|
||||
}
|
||||
|
||||
response += " " + reverse_version_map.find(_http_version)->second + "\r\n";
|
||||
|
||||
for (auto header : _headers) {
|
||||
response += header.second.to_string();
|
||||
}
|
||||
|
||||
response += "\r\n";
|
||||
response += _body_content;
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
};
|
39
lib/http/request.hpp
Normal file
39
lib/http/request.hpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "./header_query.hpp"
|
||||
#include "./constants.hpp"
|
||||
|
||||
namespace anthracite::http {
|
||||
|
||||
class request {
|
||||
private:
|
||||
enum parser_state { METHOD,
|
||||
PATH,
|
||||
QUERY_PARAM_NAME,
|
||||
QUERY_PARAM_VALUE,
|
||||
VERSION,
|
||||
HEADER_NAME,
|
||||
HEADER_VALUE,
|
||||
BODY_CONTENT };
|
||||
method _method;
|
||||
version _http_version;
|
||||
std::string _path;
|
||||
std::string _client_ipaddr;
|
||||
std::string _body_content;
|
||||
std::unordered_map<std::string, header> _headers; // kinda goofy, whatever
|
||||
std::unordered_map<std::string, query_param> _query_params; // kinda goofy, whatever
|
||||
|
||||
public:
|
||||
request(std::string& raw_data, const std::string& client_ip);
|
||||
std::string path();
|
||||
method get_method();
|
||||
std::string client_ip();
|
||||
version get_http_version();
|
||||
bool is_supported_version();
|
||||
bool close_connection();
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
};
|
58
lib/http/response.cpp
Normal file
58
lib/http/response.cpp
Normal file
|
@ -0,0 +1,58 @@
|
|||
#include "response.hpp"
|
||||
#include "../version.hpp"
|
||||
|
||||
namespace anthracite::http {
|
||||
|
||||
response::response() {};
|
||||
|
||||
int response::status_code() { return _status_code; }
|
||||
|
||||
void response::add_body(const std::string body) {
|
||||
_content_noref = body;
|
||||
_content = &_content_noref;
|
||||
}
|
||||
|
||||
void response::add_body_ref(std::string* body) {
|
||||
_content = body;
|
||||
}
|
||||
|
||||
void response::add_header(header header, bool override_existing)
|
||||
{
|
||||
if (override_existing || _headers.find(header.name()) == _headers.end()) {
|
||||
_headers[header.name()] = header;
|
||||
}
|
||||
}
|
||||
|
||||
void response::add_status(int code) {
|
||||
_status_code = code;
|
||||
}
|
||||
|
||||
std::string& response::content()
|
||||
{
|
||||
return *_content;
|
||||
}
|
||||
|
||||
std::string response::header_to_string()
|
||||
{
|
||||
std::string response = "";
|
||||
response += "HTTP/1.1 " + std::to_string(_status_code) + " " + status_map.find(_status_code)->second + "\r\n";
|
||||
|
||||
add_header(header("Content-Length", std::to_string(_content->length())), false);
|
||||
add_header(header("Server", ANTHRACITE_FULL_VERSION_STRING), false);
|
||||
add_header(header("Origin-Server", ANTHRACITE_FULL_VERSION_STRING), false);
|
||||
|
||||
for (auto header : _headers) {
|
||||
response += header.second.to_string();
|
||||
}
|
||||
|
||||
response += "\r\n";
|
||||
|
||||
return response;
|
||||
}
|
||||
|
||||
std::string response::to_string()
|
||||
{
|
||||
return header_to_string() + *_content;
|
||||
}
|
||||
|
||||
};
|
31
lib/http/response.hpp
Normal file
31
lib/http/response.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#pragma once
|
||||
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
#include "header_query.hpp"
|
||||
#include "constants.hpp"
|
||||
#include <optional>
|
||||
|
||||
namespace anthracite::http {
|
||||
|
||||
class response {
|
||||
private:
|
||||
int _status_code;
|
||||
std::string* _content;
|
||||
std::string _content_noref;
|
||||
std::unordered_map<std::string, header> _headers; // kinda goofy, whatever
|
||||
|
||||
public:
|
||||
response();
|
||||
int status_code();
|
||||
|
||||
void add_body(const std::string body);
|
||||
void add_body_ref(std::string* body);
|
||||
void add_status(int);
|
||||
void add_header(header header, bool override_existing = true);
|
||||
std::string& content();
|
||||
std::string header_to_string();
|
||||
std::string to_string();
|
||||
};
|
||||
|
||||
};
|
Loading…
Add table
Add a link
Reference in a new issue