Clean client main handler function

This commit is contained in:
2020-11-30 18:17:04 +01:00
parent 60e3b4bfe0
commit 6e21f0fbd4

View File

@ -221,24 +221,55 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
bool error = false; bool error = false;
char buffer[1024]; char buffer[1024];
char *prefix = (char *) preprefix; char *prefix = (char *) preprefix;
HttpConnection *req = nullptr;
try { try {
HttpConnection req(socket); *req = HttpConnection(socket);
} catch (char *msg) {
try { try {
if (req.isExistingField("Connection") && req.getField("Connection") == "keep-alive") { if (msg == "Malformed header") {
req.setField("Connection", "keep-alive"); log(prefix, "Unable to parse header: Malformed header");
req.setField("Keep-Alive", "timeout=3600, max=100"); socket->send("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
error = true;
} else if (msg == "timeout") {
log(prefix, "Timeout!");
socket->send("HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n");
error = true;
} else { } else {
req.setField("Connection", "close"); log(prefix, (string) "Unable to receive from socket: " + msg);
error = true;
}
} catch (char *msg2) {
}
}
try {
bool noRedirect, redir, invalidMethod, etag;
URI *path;
pid_t childpid;
FILE *file;
int statuscode;
string hash, type, host;
thread *t;
long pos;
if (req->isExistingField("Connection") && req->getField("Connection") == "keep-alive") {
req->setField("Connection", "keep-alive");
req->setField("Keep-Alive", "timeout=3600, max=100");
} else {
req->setField("Connection", "close");
error = true; error = true;
} }
string host = ""; host = "";
if (!req.isExistingField("Host")) { if (!req->isExistingField("Host")) {
req.respond(400); req->respond(400);
} else { goto respond;
host = req.getField("Host"); }
long pos = host.find(':'); host = req->getField("Host");
pos = host.find(':');
if (pos != string::npos) { if (pos != string::npos) {
host.erase(pos, host.length() - pos); host.erase(pos, host.length() - pos);
} }
@ -257,17 +288,17 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
info->host.c_str(), socket->getPeerPort(), col2); info->host.c_str(), socket->getPeerPort(), col2);
prefix = buffer; prefix = buffer;
log(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m"); log(prefix, "\x1B[1m" + req->getMethod() + " " + req->getPath() + "\x1B[0m");
log_to_file(prefix, "\x1B[1m" + req.getMethod() + " " + req.getPath() + "\x1B[0m", host); log_to_file(prefix, "\x1B[1m" + req->getMethod() + " " + req->getPath() + "\x1B[0m", host);
bool noRedirect = req.getPath().find("/.well-known/") == 0 || (req.getPath().find("/files/") == 0); noRedirect = req->getPath().find("/.well-known/") == 0 || (req->getPath().find("/files/") == 0);
bool redir = true; redir = true;
if (!noRedirect) { if (!noRedirect) {
if (getWebRoot(host).empty()) { if (getWebRoot(host).empty()) {
req.redirect(303, "https://www.necronda.net" + req.getPath()); req->redirect(303, "https://www.necronda.net" + req->getPath());
} else if (socket->getSocketPort() != 443) { } else if (socket->getSocketPort() != 443) {
req.redirect(302, "https://" + host + req.getPath()); req->redirect(302, "https://" + host + req->getPath());
} else { } else {
redir = false; redir = false;
} }
@ -275,85 +306,94 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
redir = false; redir = false;
} }
URI path = URI(getWebRoot(host), req.getPath()); *path = URI(getWebRoot(host), req->getPath());
pid_t childpid = 0; childpid = 0;
if (redir) { if (redir) {
goto respond;
} else if (!path->getNewPath().empty() && req->getMethod() != "POST") {
req->redirect(303, path->getNewPath());
goto respond;
}
} else if (!path.getNewPath().empty() && req.getMethod() != "POST") { file = path->openFile();
req.redirect(303, path.getNewPath());
} else {
FILE *file = path.openFile();
if (file == nullptr) { if (file == nullptr) {
req.setField("Cache-Control", "public, max-age=60"); req->setField("Cache-Control", "public, max-age=60");
req.respond(404); req->respond(404);
} else { goto respond;
string type = path.getFileType(); }
type = path->getFileType();
if (type.find("inode/") == 0) { if (type.find("inode/") == 0) {
req.respond(403); req->respond(403);
} else if (path.getRelativeFilePath().find("/.") != string::npos && !noRedirect) { goto respond;
req.respond(403); } else if (path->getRelativeFilePath().find("/.") != string::npos && !noRedirect) {
} else { req->respond(403);
req.setField("Content-Type", type); goto respond;
req.setField("Last-Modified", getHttpDate(path.getFilePath()));
bool invalidMethod = false;
bool etag = false;
if (path.isStatic()) {
string hash = getETag(path.getFilePath());
req.setField("ETag", hash);
req.setField("Accept-Ranges", "bytes");
if (type.find("text/") == 0) {
req.setField("Cache-Control", "public, max-age=3600");
} else {
req.setField("Cache-Control", "public, max-age=86400");
} }
req.setField("Allow", "GET");
if (req.getMethod() != "GET") { req->setField("Content-Type", type);
req->setField("Last-Modified", getHttpDate(path->getFilePath()));
invalidMethod = false;
etag = false;
if (path->isStatic()) {
hash = getETag(path->getFilePath());
req->setField("ETag", hash);
req->setField("Accept-Ranges", "bytes");
if (type.find("text/") == 0) {
req->setField("Cache-Control", "public, max-age=3600");
} else {
req->setField("Cache-Control", "public, max-age=86400");
}
req->setField("Allow", "GET");
if (req->getMethod() != "GET") {
invalidMethod = true; invalidMethod = true;
} }
if (req.isExistingField("If-None-Match") && req.getField("If-None-Match") == hash) { if (req->isExistingField("If-None-Match") && req->getField("If-None-Match") == hash) {
etag = true; etag = true;
} }
} else { } else {
req.setField("Accept-Ranges", "none"); req->setField("Accept-Ranges", "none");
req.setField("Cache-Control", "private, no-cache"); req->setField("Cache-Control", "private, no-cache");
req.setField("Allow", "GET, POST, PUT"); req->setField("Allow", "GET, POST, PUT");
if (req.getMethod() != "GET" && req.getMethod() != "POST" && req.getMethod() != "PUT") { if (req->getMethod() != "GET" && req->getMethod() != "POST" && req->getMethod() != "PUT") {
invalidMethod = true; invalidMethod = true;
} }
} }
if (invalidMethod) { if (invalidMethod) {
req.respond(405); req->respond(405);
goto respond;
} else if (etag) { } else if (etag) {
req.respond(304); req->respond(304);
} else { goto respond;
int statuscode = 0; }
if (!path.isStatic()) {
statuscode = 0;
if (!path->isStatic()) {
string cmd = (string) "env -i" + string cmd = (string) "env -i" +
" REDIRECT_STATUS=" + cli_encode("CGI") + " REDIRECT_STATUS=" + cli_encode("CGI") +
" DOCUMENT_ROOT=" + cli_encode(getWebRoot(host)) + " DOCUMENT_ROOT=" + cli_encode(getWebRoot(host)) +
" " + req.cgiExport() + " " + req->cgiExport() +
(req.isExistingField("Content-Length") ? " CONTENT_LENGTH=" + (req->isExistingField("Content-Length") ? " CONTENT_LENGTH=" +
cli_encode(req.getField( cli_encode(req->getField(
"Content-Length")) "Content-Length"))
: "") + : "") +
(req.isExistingField("Content-Type") ? " CONTENT_TYPE=" + cli_encode( (req->isExistingField("Content-Type") ? " CONTENT_TYPE=" + cli_encode(
req.getField("Content-Type")) : "") + req->getField("Content-Type")) : "") +
((socket->isSecured()) ? " HTTPS=on" : "") + ((socket->isSecured()) ? " HTTPS=on" : "") +
" PATH_INFO=" + cli_encode(path.getFilePathInfo()) + " PATH_INFO=" + cli_encode(path->getFilePathInfo()) +
" PATH_TRANSLATED=" + cli_encode(path.getAbsolutePath()) + " PATH_TRANSLATED=" + cli_encode(path->getAbsolutePath()) +
" QUERY_STRING=" + cli_encode(path.getQuery()) + " QUERY_STRING=" + cli_encode(path->getQuery()) +
" REMOTE_ADDR=" + cli_encode(socket->getPeerAddress()->toString()) + " REMOTE_ADDR=" + cli_encode(socket->getPeerAddress()->toString()) +
" REMOTE_HOST=" + cli_encode(info->host) + " REMOTE_HOST=" + cli_encode(info->host) +
" REMOTE_PORT=" + cli_encode(to_string(socket->getPeerPort())) + " REMOTE_PORT=" + cli_encode(to_string(socket->getPeerPort())) +
" REQUEST_METHOD=" + cli_encode(req.getMethod()) + " REQUEST_METHOD=" + cli_encode(req->getMethod()) +
" REQUEST_URI=" + cli_encode(req.getPath()) + " REQUEST_URI=" + cli_encode(req->getPath()) +
" SCRIPT_FILENAME=" + cli_encode(path.getFilePath()) + " SCRIPT_FILENAME=" + cli_encode(path->getFilePath()) +
" SCRIPT_NAME=" + cli_encode(path.getRelativePath()) + " SCRIPT_NAME=" + cli_encode(path->getRelativePath()) +
" SERVER_ADMIN=" + cli_encode("lorenz.stechauner@gmail.com") + " SERVER_ADMIN=" + cli_encode("lorenz.stechauner@gmail.com") +
" SERVER_NAME=" + cli_encode(host) + " SERVER_NAME=" + cli_encode(host) +
" SERVER_PORT=" + cli_encode(to_string(socket->getSocketPort())) + " SERVER_PORT=" + cli_encode(to_string(socket->getSocketPort())) +
@ -365,11 +405,11 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
stds pipes = procopen(cmd.c_str()); stds pipes = procopen(cmd.c_str());
childpid = pipes.pid; childpid = pipes.pid;
long len = req.isExistingField("Content-Length") ? strtol(req.getField("Content-Length").c_str(), nullptr, 10) : (req.getMethod() == "POST" || req.getMethod() == "PUT")?-1:0; long len = req->isExistingField("Content-Length") ? strtol(req->getField("Content-Length").c_str(), nullptr, 10) : (req->getMethod() == "POST" || req->getMethod() == "PUT")?-1:0;
socket->receive(pipes.stdin, len); socket->receive(pipes.stdin, len);
fclose(pipes.stdin); fclose(pipes.stdin);
thread *t = new thread(php_error_handler, prefix, pipes.stderr); t = new thread(php_error_handler, prefix, pipes.stderr);
string line; string line;
while (!(line = read_line(pipes.stdout)).empty()) { while (!(line = read_line(pipes.stdout)).empty()) {
@ -390,7 +430,7 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
} else if (index == "Content-Type") { } else if (index == "Content-Type") {
type = data; type = data;
} }
req.setField(index, data); req->setField(index, data);
} }
} }
@ -398,7 +438,7 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
int c = fgetc(pipes.stdout); int c = fgetc(pipes.stdout);
if (c == -1) { if (c == -1) {
// No Data -> Error // No Data -> Error
req.respond((statuscode == 0) ? 500 : statuscode); req->respond((statuscode == 0) ? 500 : statuscode);
statuscode = -1; statuscode = -1;
} else { } else {
ungetc(c, pipes.stdout); ungetc(c, pipes.stdout);
@ -413,26 +453,26 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
(type.find("application/") == 0 && type.find("+xml") != string::npos) || (type.find("application/") == 0 && type.find("+xml") != string::npos) ||
type == "application/json" || type == "application/json" ||
type == "application/javascript") && type == "application/javascript") &&
req.isExistingField("Accept-Encoding") && req->isExistingField("Accept-Encoding") &&
req.getField("Accept-Encoding").find("deflate") != string::npos; req->getField("Accept-Encoding").find("deflate") != string::npos;
if (compress) { if (compress) {
req.setField("Accept-Ranges", "none"); req->setField("Accept-Ranges", "none");
} }
if (compress && req.isExistingField("Range")) { if (compress && req->isExistingField("Range")) {
req.respond(416); req->respond(416);
} else if (req.isExistingField("Range")) { } else if (req->isExistingField("Range")) {
string range = req.getField("Range"); string range = req->getField("Range");
if (range.find("bytes=") != 0 || !path.isStatic()) { if (range.find("bytes=") != 0 || !path->isStatic()) {
req.respond(416); req->respond(416);
} else { } else {
fseek(file, 0L, SEEK_END); fseek(file, 0L, SEEK_END);
long len = ftell(file); long len = ftell(file);
fseek(file, 0L, SEEK_SET); fseek(file, 0L, SEEK_SET);
long p = range.find('-'); long p = range.find('-');
if (p == string::npos) { if (p == string::npos) {
req.respond(416); req->respond(416);
} else { } else {
string part1 = range.substr(6, (unsigned long) (p - 6)); string part1 = range.substr(6, (unsigned long) (p - 6));
string part2 = range.substr((unsigned long) (p + 1), string part2 = range.substr((unsigned long) (p + 1),
@ -443,30 +483,29 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
num2 = stol(part2, nullptr, 10); num2 = stol(part2, nullptr, 10);
} }
if (num1 < 0 || num1 >= len || num2 < 0 || num2 >= len) { if (num1 < 0 || num1 >= len || num2 < 0 || num2 >= len) {
req.respond(416); req->respond(416);
} else { } else {
req.setField("Content-Range", req->setField("Content-Range",
(string) "bytes " + to_string(num1) + "-" + (string) "bytes " + to_string(num1) + "-" +
to_string(num2) + to_string(num2) +
"/" + to_string(len)); "/" + to_string(len));
req.respond(206, file, compress, num1, num2); req->respond(206, file, compress, num1, num2);
} }
} }
} }
} else { } else {
req.respond(statuscode, file, compress); req->respond(statuscode, file, compress);
}
}
} }
} }
fclose(file); fclose(file);
if (childpid > 0) { if (childpid > 0) {
waitpid(childpid, nullptr, 0); waitpid(childpid, nullptr, 0);
} }
}
} respond:
}
HttpStatusCode status = req.getStatusCode(); HttpStatusCode status = req->getStatusCode();
int code = status.code; int code = status.code;
string color = ""; string color = "";
string comment = ""; string comment = "";
@ -477,50 +516,32 @@ bool connection_handler(const char *preprefix, const char *col1, const char *col
} else if (code >= 300 && code < 400) { } else if (code >= 300 && code < 400) {
color = "\x1B[1;93m"; // Redirect: Yellow color = "\x1B[1;93m"; // Redirect: Yellow
comment = " -> " + comment = " -> " +
(req.isExistingResponseField("Location") ? req.getResponseField("Location") : "<invalid>"); (req->isExistingResponseField("Location") ? req->getResponseField("Location") : "<invalid>");
} else if (code >= 400 && code < 500) { } else if (code >= 400 && code < 500) {
color = "\x1B[1;31m"; // Client Error: Red color = "\x1B[1;31m"; // Client Error: Red
//comment = " -> " + req.getPath(); //comment = " -> " + req->getPath();
} else if (code >= 500 & code < 600) { } else if (code >= 500 & code < 600) {
color = "\x1B[1;31m"; // Server Error: Red color = "\x1B[1;31m"; // Server Error: Red
//comment = " -> " + req.getPath(); //comment = " -> " + req->getPath();
} }
string msg = color + to_string(status.code) + " " + status.message + comment + " (" + formatTime(req.getDuration()) + ")\x1B[0m"; string msg = color + to_string(status.code) + " " + status.message + comment + " (" + formatTime(req->getDuration()) + ")\x1B[0m";
log(prefix, msg); log(prefix, msg);
if (!host.empty()) { if (!host.empty()) {
log_to_file(prefix, msg, host); log_to_file(prefix, msg, host);
} }
} catch (char *msg) { } catch (char *msg) {
HttpStatusCode status = req.getStatusCode(); HttpStatusCode status = req->getStatusCode();
log(prefix, to_string(status.code) + " " + status.message + " (" + formatTime(req.getDuration()) + ")"); log(prefix, to_string(status.code) + " " + status.message + " (" + formatTime(req->getDuration()) + ")");
try { try {
if (strncmp(msg, "timeout", strlen(msg)) == 0) { if (strncmp(msg, "timeout", strlen(msg)) == 0) {
log(prefix, "Timeout!"); log(prefix, "Timeout!");
req.setField("Connection", "close"); req->setField("Connection", "close");
req.respond(408); req->respond(408);
error = true; error = true;
} else if (strncmp(msg, "Invalid path", strlen(msg)) == 0) { } else if (strncmp(msg, "Invalid path", strlen(msg)) == 0) {
log(prefix, "Timeout!"); log(prefix, "Timeout!");
req.setField("Connection", "close"); req->setField("Connection", "close");
req.respond(400); req->respond(400);
} else {
log(prefix, (string) "Unable to receive from socket: " + msg);
error = true;
}
} catch (char *msg2) {
}
}
} catch (char *msg) {
try {
if (msg == "Malformed header") {
log(prefix, "Unable to parse header: Malformed header");
socket->send("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
error = true;
} else if (msg == "timeout") {
log(prefix, "Timeout!");
socket->send("HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n");
error = true;
} else { } else {
log(prefix, (string) "Unable to receive from socket: " + msg); log(prefix, (string) "Unable to receive from socket: " + msg);
error = true; error = true;