URL Redirect
This commit is contained in:
71
src/Path.cpp
71
src/Path.cpp
@ -1,71 +0,0 @@
|
|||||||
|
|
||||||
#include "Path.h"
|
|
||||||
#include "necronda-server.h"
|
|
||||||
#include <utility>
|
|
||||||
|
|
||||||
using namespace std;
|
|
||||||
|
|
||||||
Path::Path(string webroot, string reqpath) {
|
|
||||||
unsigned long pos = reqpath.find('?');
|
|
||||||
if (pos != string::npos) {
|
|
||||||
query = reqpath.substr(pos + 1, reqpath.length() - pos);
|
|
||||||
reqpath.erase(pos + 1, reqpath.length() - pos);
|
|
||||||
}
|
|
||||||
if (webroot[webroot.length() - 1] == '/') {
|
|
||||||
webroot.erase(webroot.length() - 1);
|
|
||||||
}
|
|
||||||
if (reqpath.find("/../") != string::npos) {
|
|
||||||
throw (char *) "Invalid path";
|
|
||||||
}
|
|
||||||
if (reqpath[0] != '/') {
|
|
||||||
reqpath = '/' + reqpath;
|
|
||||||
}
|
|
||||||
this->webroot = webroot;
|
|
||||||
this->relpath = reqpath;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Path::getWebRoot() {
|
|
||||||
return webroot;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Path::getRelativePath() {
|
|
||||||
return relpath;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Path::getAbsolutePath() {
|
|
||||||
return webroot + relpath;
|
|
||||||
}
|
|
||||||
|
|
||||||
string Path::getFilePath() {
|
|
||||||
string abs = webroot;
|
|
||||||
// TODO
|
|
||||||
return getAbsolutePath();
|
|
||||||
}
|
|
||||||
|
|
||||||
string Path::getRelativeFilePath() {
|
|
||||||
string rel = getRelativePath();
|
|
||||||
// TODO
|
|
||||||
}
|
|
||||||
|
|
||||||
string Path::getNewPath() {
|
|
||||||
string rel = getRelativeFilePath();
|
|
||||||
// TODO
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
FILE *Path::openFile() {
|
|
||||||
return fopen64(getFilePath().c_str(), "r");
|
|
||||||
}
|
|
||||||
|
|
||||||
string Path::getFilePathInfo() {
|
|
||||||
return getAbsolutePath().erase(getFilePath().length(), getAbsolutePath().length());
|
|
||||||
}
|
|
||||||
|
|
||||||
string Path::getFileType() {
|
|
||||||
return getMimeType(getFilePath());
|
|
||||||
}
|
|
||||||
|
|
||||||
bool Path::isStatic() {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
159
src/URI.cpp
Normal file
159
src/URI.cpp
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
|
||||||
|
#include "URI.h"
|
||||||
|
#include "necronda-server.h"
|
||||||
|
#include <utility>
|
||||||
|
#include <sys/stat.h>
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
|
||||||
|
string getExtension(string path) {
|
||||||
|
long pos = path.find_last_of('.');
|
||||||
|
if (pos == string::npos) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return path.substr(pos + 1, path.length() - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
string getFilename(string path) {
|
||||||
|
long pos = path.find_last_of('/');
|
||||||
|
if (pos == string::npos) {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
return path.substr(pos + 1, path.length() - pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isDirectory(string path) {
|
||||||
|
struct stat statbuf;
|
||||||
|
return stat(path.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode) != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool isFile(string path) {
|
||||||
|
struct stat statbuf;
|
||||||
|
return stat(path.c_str(), &statbuf) == 0 && S_ISDIR(statbuf.st_mode) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool fileExists(string path) {
|
||||||
|
struct stat statbuf;
|
||||||
|
return stat(path.c_str(), &statbuf) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
URI::URI(string webroot, string reqpath) {
|
||||||
|
unsigned long pos = reqpath.find('?');
|
||||||
|
if (pos != string::npos) {
|
||||||
|
queryinit = true;
|
||||||
|
query = reqpath.substr(pos + 1, reqpath.length() - pos);
|
||||||
|
reqpath.erase(pos, reqpath.length() - pos);
|
||||||
|
} else {
|
||||||
|
query = "";
|
||||||
|
queryinit = false;
|
||||||
|
}
|
||||||
|
if (webroot[webroot.length() - 1] == '/') {
|
||||||
|
webroot.erase(webroot.length() - 1);
|
||||||
|
}
|
||||||
|
if (reqpath.find("/../") != string::npos) {
|
||||||
|
throw (char *) "Invalid path";
|
||||||
|
}
|
||||||
|
if (reqpath[0] != '/') {
|
||||||
|
reqpath = '/' + reqpath;
|
||||||
|
}
|
||||||
|
this->webroot = webroot;
|
||||||
|
this->reqpath = reqpath;
|
||||||
|
|
||||||
|
string abs = reqpath;
|
||||||
|
if (fileExists(webroot + abs)) {
|
||||||
|
string ext = getExtension(abs);
|
||||||
|
if (ext == "php" || ext == "html") {
|
||||||
|
abs.erase(abs.length() - ext.length() - 1, abs.length());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string fname = getFilename(abs);
|
||||||
|
if (fname == "index") {
|
||||||
|
abs.erase(abs.length() - fname.length() - 1, abs.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
this->filepath = webroot + reqpath;
|
||||||
|
|
||||||
|
if (isDirectory(webroot + abs)) {
|
||||||
|
if (abs[abs.length() - 1] != '/') {
|
||||||
|
abs += "/";
|
||||||
|
}
|
||||||
|
this->relpath = abs;
|
||||||
|
abs += "index";
|
||||||
|
if (fileExists(webroot + abs + ".php")) {
|
||||||
|
this->filepath = webroot + abs + ".php";
|
||||||
|
} else if (fileExists(webroot + abs + ".html")) {
|
||||||
|
this->filepath = webroot + abs + ".html";
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (abs[abs.length() - 1] == '/') {
|
||||||
|
abs.erase(abs.length() - 1, abs.length() - 1);
|
||||||
|
}
|
||||||
|
this->relpath = abs;
|
||||||
|
if (fileExists(webroot + abs + ".php")) {
|
||||||
|
this->filepath = webroot + abs + ".php";
|
||||||
|
} else if (fileExists(webroot + abs + ".html")) {
|
||||||
|
this->filepath = webroot + abs + ".html";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getWebRoot() {
|
||||||
|
return webroot;
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getRelativePath() {
|
||||||
|
return relpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getAbsolutePath() {
|
||||||
|
return webroot + relpath;
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getFilePath() {
|
||||||
|
return filepath;
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getRelativeFilePath() {
|
||||||
|
string rel = getRelativePath();
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getNewPath() {
|
||||||
|
if (isStatic()) {
|
||||||
|
if (hasQuery()) {
|
||||||
|
return getRelativePath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (relpath != reqpath) {
|
||||||
|
return relpath + (queryinit? "?" + query : "");
|
||||||
|
} else {
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *URI::openFile() {
|
||||||
|
return fopen64(getFilePath().c_str(), "rb");
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getFilePathInfo() {
|
||||||
|
return getAbsolutePath().erase(getFilePath().length(), getAbsolutePath().length());
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getFileType() {
|
||||||
|
return getMimeType(getFilePath());
|
||||||
|
}
|
||||||
|
|
||||||
|
bool URI::isStatic() {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
string URI::getQuery() {
|
||||||
|
return query;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool URI::hasQuery() {
|
||||||
|
return queryinit;
|
||||||
|
}
|
||||||
|
|
@ -6,14 +6,17 @@
|
|||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
class Path {
|
class URI {
|
||||||
private:
|
private:
|
||||||
string webroot;
|
string webroot;
|
||||||
|
string reqpath;
|
||||||
string relpath;
|
string relpath;
|
||||||
string query;
|
string query;
|
||||||
|
string filepath;
|
||||||
|
bool queryinit;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Path(string webroot, string reqpath);
|
URI(string webroot, string reqpath);
|
||||||
|
|
||||||
string getWebRoot();
|
string getWebRoot();
|
||||||
|
|
||||||
@ -35,6 +38,10 @@ public:
|
|||||||
|
|
||||||
bool isStatic();
|
bool isStatic();
|
||||||
|
|
||||||
|
string getQuery();
|
||||||
|
|
||||||
|
bool hasQuery();
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif
|
#endif
|
242
src/client.cpp
242
src/client.cpp
@ -9,12 +9,16 @@
|
|||||||
#include <iostream>
|
#include <iostream>
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
|
#include <fstream>
|
||||||
|
#include <openssl/md5.h>
|
||||||
|
#include <cstring>
|
||||||
|
|
||||||
#include "network/Socket.h"
|
#include "network/Socket.h"
|
||||||
#include "network/http/HttpRequest.h"
|
#include "network/http/HttpRequest.h"
|
||||||
#include "network/http/HttpConnection.h"
|
#include "network/http/HttpConnection.h"
|
||||||
#include "necronda-server.h"
|
#include "necronda-server.h"
|
||||||
#include "network/http/HttpStatusCode.h"
|
#include "network/http/HttpStatusCode.h"
|
||||||
#include "Path.h"
|
#include "URI.h"
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -27,6 +31,81 @@ void log(const char *prefix, const string &string) {
|
|||||||
flush(cout);
|
flush(cout);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
string getETag(string filename) {
|
||||||
|
ifstream etags = ifstream("/var/necronda/ETags");
|
||||||
|
|
||||||
|
ifstream a = ifstream();
|
||||||
|
string line;
|
||||||
|
int index = 0;
|
||||||
|
int i = 0;
|
||||||
|
string timestamp = getTimestamp(filename);
|
||||||
|
long size = getFileSize(filename);
|
||||||
|
while (getline(etags, line)) {
|
||||||
|
i++;
|
||||||
|
if (line == filename) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
long p1 = line.find(':');
|
||||||
|
if (p1 == string::npos) continue;
|
||||||
|
long p2 = line.find(':', (unsigned) p1 + 1);
|
||||||
|
if (p2 == string::npos) continue;
|
||||||
|
long p3 = line.find(':', (unsigned) p2 + 1);
|
||||||
|
if (p3 == string::npos) continue;
|
||||||
|
string FILENAME = line.substr(0, (unsigned) p1);
|
||||||
|
string HASH = line.substr((unsigned) p1 + 1, (unsigned) (p2 - p1));
|
||||||
|
string TIMESTAMP = line.substr((unsigned) p2 + 1, (unsigned) (p3 - p2));
|
||||||
|
long SIZE = strtol(line.substr((unsigned) p3 + 1, line.length() - p3).c_str(), nullptr, 10);
|
||||||
|
if (FILENAME == filename) {
|
||||||
|
index = i;
|
||||||
|
if (timestamp != TIMESTAMP || size != SIZE) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
etags.close();
|
||||||
|
return HASH;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
etags.close();
|
||||||
|
|
||||||
|
MD5_CTX mdContext;
|
||||||
|
MD5_Init(&mdContext);
|
||||||
|
size_t bytes;
|
||||||
|
char buffer[4096];
|
||||||
|
FILE *file = fopen(filename.c_str(), "rb");
|
||||||
|
if (file == nullptr) {
|
||||||
|
throw (char *) "Invalid file";
|
||||||
|
}
|
||||||
|
while ((bytes = fread(buffer, 1, 4096, file)) != 0) {
|
||||||
|
MD5_Update(&mdContext, buffer, bytes);
|
||||||
|
}
|
||||||
|
fclose(file);
|
||||||
|
unsigned char md[16];
|
||||||
|
MD5_Final(md, &mdContext);
|
||||||
|
char md5buff[32];
|
||||||
|
for (int i = 0; i < 16; i++) {
|
||||||
|
sprintf(md5buff + i * 2, "%02x", md[i]);
|
||||||
|
}
|
||||||
|
string md5 = string(md5buff);
|
||||||
|
|
||||||
|
if (index == 0) {
|
||||||
|
char buff[256];
|
||||||
|
sprintf(buff, "%s:%s:%s:%ld\n", filename.c_str(), md5.c_str(), timestamp.c_str(), size);
|
||||||
|
FILE *f = fopen("/var/necronda/ETags", "a");
|
||||||
|
if (f == nullptr) {
|
||||||
|
throw (char *) strerror(errno);
|
||||||
|
}
|
||||||
|
fseek(f, 0, SEEK_END);
|
||||||
|
fwrite(buff, 1, strlen(buff), f);
|
||||||
|
fflush(f);
|
||||||
|
fclose(f);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return md5;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Handles (keep-alive) HTTP connections
|
* Handles (keep-alive) HTTP connections
|
||||||
* @param prefix The connection prefix
|
* @param prefix The connection prefix
|
||||||
@ -35,7 +114,7 @@ void log(const char *prefix, const string &string) {
|
|||||||
* @param num The Connection Number in the client
|
* @param num The Connection Number in the client
|
||||||
* @return Should the server wait for another header?
|
* @return Should the server wait for another header?
|
||||||
*/
|
*/
|
||||||
bool connection_handler(const char *prefix, Socket socket, long id, long num) {
|
bool connection_handler(const char *prefix, Socket *socket, long id, long num) {
|
||||||
bool error = false;
|
bool error = false;
|
||||||
try {
|
try {
|
||||||
HttpConnection req(socket);
|
HttpConnection req(socket);
|
||||||
@ -61,81 +140,103 @@ bool connection_handler(const char *prefix, Socket socket, long id, long num) {
|
|||||||
sprintf(buffer, "%s", n.c_str());
|
sprintf(buffer, "%s", n.c_str());
|
||||||
prefix = buffer;
|
prefix = buffer;
|
||||||
|
|
||||||
Path path = Path(getWebRoot(host), req.getPath());
|
URI path = URI(getWebRoot(host), req.getPath());
|
||||||
log(prefix, req.getMethod() + " " + req.getPath());
|
log(prefix, req.getMethod() + " " + req.getPath());
|
||||||
|
|
||||||
FILE *file = path.openFile();
|
FILE *file = path.openFile();
|
||||||
|
|
||||||
if (file == nullptr) {
|
if (!path.getNewPath().empty()) {
|
||||||
req.setField("Cache-Control", "public, max-age=60");
|
req.redirect(302, path.getNewPath());
|
||||||
req.respond(404);
|
|
||||||
} else {
|
} else {
|
||||||
string type = path.getFileType();
|
|
||||||
|
|
||||||
if (type.find("inode/") == 0) {
|
if (file == nullptr) {
|
||||||
req.respond(403);
|
req.setField("Cache-Control", "public, max-age=60");
|
||||||
|
req.respond(404);
|
||||||
} else {
|
} else {
|
||||||
req.setField("Content-Type", type);
|
string type = path.getFileType();
|
||||||
req.setField("Last-Modified", getHttpDate(path.getAbsolutePath()));
|
|
||||||
|
|
||||||
bool invalidMethod = false;
|
if (type.find("inode/") == 0) {
|
||||||
|
req.respond(403);
|
||||||
if (path.isStatic()) {
|
|
||||||
req.setField("Accept-Ranges", "bytes");
|
|
||||||
req.setField("Cache-Control", "public, max-age=10");
|
|
||||||
req.setField("Allow", "GET");
|
|
||||||
if (req.getMethod() != "GET") {
|
|
||||||
invalidMethod = true;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
req.setField("Accept-Ranges", "none");
|
req.setField("Content-Type", type);
|
||||||
req.setField("Cache-Control", "private, no-cache");
|
req.setField("Last-Modified", getHttpDate(path.getFilePath()));
|
||||||
req.setField("Allow", "GET, POST, PUT");
|
|
||||||
if (req.getMethod() != "GET" && req.getMethod() != "POST" && req.getMethod() != "PUT") {
|
|
||||||
invalidMethod = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (invalidMethod) {
|
bool invalidMethod = false;
|
||||||
req.respond(405);
|
bool etag = false;
|
||||||
} else {
|
|
||||||
|
|
||||||
bool compress = type.find("text/") == 0 && req.isExistingField("Accept-Encoding") &&
|
if (path.isStatic()) {
|
||||||
req.getField("Accept-Encoding").find("deflate") != string::npos;
|
string hash = getETag(path.getFilePath());
|
||||||
|
req.setField("ETag", hash);
|
||||||
if (req.isExistingField("Range")) {
|
req.setField("Accept-Ranges", "bytes");
|
||||||
string range = req.getField("Range");
|
req.setField("Cache-Control", "public, max-age=30");
|
||||||
if (range.find("bytes=") != 0 || !path.isStatic()) {
|
req.setField("Allow", "GET");
|
||||||
req.respond(416);
|
if (req.getMethod() != "GET") {
|
||||||
} else {
|
invalidMethod = true;
|
||||||
fseek(file, 0L, SEEK_END);
|
}
|
||||||
long len = ftell(file);
|
if (req.isExistingField("If-None-Match") && req.getField("If-None-Match") == hash) {
|
||||||
fseek(file, 0L, SEEK_SET);
|
etag = true;
|
||||||
long p = range.find('-');
|
|
||||||
if (p == string::npos) {
|
|
||||||
req.respond(416);
|
|
||||||
} else {
|
|
||||||
string part1 = range.substr(6, p - 6);
|
|
||||||
string part2 = range.substr(p + 1, range.length() - p - 1);
|
|
||||||
long num1 = stol(part1, nullptr, 10);
|
|
||||||
long num2 = len - 1;
|
|
||||||
if (!part2.empty()) {
|
|
||||||
num2 = stol(part2, nullptr, 10);
|
|
||||||
}
|
|
||||||
if (num1 < 0 || num1 >= len || num2 < 0 || num2 >= len) {
|
|
||||||
req.respond(416);
|
|
||||||
} else {
|
|
||||||
req.setField("Content-Range", (string) "bytes " + to_string(num1) + "-" + to_string(num2) + "/" + to_string(len));
|
|
||||||
req.respond(206, file, compress, num1, num2);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
req.respond(200, file, compress);
|
req.setField("Accept-Ranges", "none");
|
||||||
|
req.setField("Cache-Control", "private, no-cache");
|
||||||
|
req.setField("Allow", "GET, POST, PUT");
|
||||||
|
if (req.getMethod() != "GET" && req.getMethod() != "POST" && req.getMethod() != "PUT") {
|
||||||
|
invalidMethod = true;
|
||||||
|
}
|
||||||
|
system("php");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalidMethod) {
|
||||||
|
req.respond(405);
|
||||||
|
} else if (etag) {
|
||||||
|
req.respond(304);
|
||||||
|
} else {
|
||||||
|
|
||||||
|
bool compress = type.find("text/") == 0 && req.isExistingField("Accept-Encoding") &&
|
||||||
|
req.getField("Accept-Encoding").find("deflate") != string::npos;
|
||||||
|
|
||||||
|
if (compress) {
|
||||||
|
req.setField("Accept-Ranges", "none");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.isExistingField("Range")) {
|
||||||
|
string range = req.getField("Range");
|
||||||
|
if (range.find("bytes=") != 0 || !path.isStatic()) {
|
||||||
|
req.respond(416);
|
||||||
|
} else {
|
||||||
|
fseek(file, 0L, SEEK_END);
|
||||||
|
long len = ftell(file);
|
||||||
|
fseek(file, 0L, SEEK_SET);
|
||||||
|
long p = range.find('-');
|
||||||
|
if (p == string::npos) {
|
||||||
|
req.respond(416);
|
||||||
|
} else {
|
||||||
|
string part1 = range.substr(6, (unsigned long) (p - 6));
|
||||||
|
string part2 = range.substr((unsigned long) (p + 1),
|
||||||
|
range.length() - p - 1);
|
||||||
|
long num1 = stol(part1, nullptr, 10);
|
||||||
|
long num2 = len - 1;
|
||||||
|
if (!part2.empty()) {
|
||||||
|
num2 = stol(part2, nullptr, 10);
|
||||||
|
}
|
||||||
|
if (num1 < 0 || num1 >= len || num2 < 0 || num2 >= len) {
|
||||||
|
req.respond(416);
|
||||||
|
} else {
|
||||||
|
req.setField("Content-Range",
|
||||||
|
(string) "bytes " + to_string(num1) + "-" +
|
||||||
|
to_string(num2) +
|
||||||
|
"/" + to_string(len));
|
||||||
|
req.respond(206, file, compress, num1, num2);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
req.respond(200, file, compress);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
fclose(file);
|
||||||
}
|
}
|
||||||
fclose(file);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
HttpStatusCode status = req.getStatusCode();
|
HttpStatusCode status = req.getStatusCode();
|
||||||
@ -151,6 +252,7 @@ bool connection_handler(const char *prefix, Socket socket, long id, long num) {
|
|||||||
error = true;
|
error = true;
|
||||||
} else if (msg == "Invalid path") {
|
} else if (msg == "Invalid path") {
|
||||||
log(prefix, "Timeout!");
|
log(prefix, "Timeout!");
|
||||||
|
req.setField("Connection", "close");
|
||||||
req.respond(400);
|
req.respond(400);
|
||||||
} else {
|
} else {
|
||||||
log(prefix, (string) "Unable to receive from socket: " + msg);
|
log(prefix, (string) "Unable to receive from socket: " + msg);
|
||||||
@ -164,11 +266,11 @@ bool connection_handler(const char *prefix, Socket socket, long id, long num) {
|
|||||||
try {
|
try {
|
||||||
if (msg == "Malformed header") {
|
if (msg == "Malformed header") {
|
||||||
log(prefix, "Unable to parse header: Malformed header");
|
log(prefix, "Unable to parse header: Malformed header");
|
||||||
socket << "HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n";
|
socket->send("HTTP/1.1 400 Bad Request\r\nConnection: close\r\n\r\n");
|
||||||
error = true;
|
error = true;
|
||||||
} else if (msg == "timeout") {
|
} else if (msg == "timeout") {
|
||||||
log(prefix, "Timeout!");
|
log(prefix, "Timeout!");
|
||||||
socket << "HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n";
|
socket->send("HTTP/1.1 408 Request Timeout\r\nConnection: close\r\n\r\n");
|
||||||
error = true;
|
error = true;
|
||||||
} else {
|
} else {
|
||||||
log(prefix, (string) "Unable to receive from socket: " + msg);
|
log(prefix, (string) "Unable to receive from socket: " + msg);
|
||||||
@ -186,12 +288,12 @@ bool connection_handler(const char *prefix, Socket socket, long id, long num) {
|
|||||||
* @param socket The socket
|
* @param socket The socket
|
||||||
* @param id The client ID
|
* @param id The client ID
|
||||||
*/
|
*/
|
||||||
void client_handler(Socket *socket, long id) {
|
void client_handler(Socket *socket, long id, bool ssl) {
|
||||||
const char *prefix;
|
const char *prefix;
|
||||||
{
|
{
|
||||||
char const *col1;
|
char const *col1;
|
||||||
char const *col2 = "\x1B[0m";
|
char const *col2 = "\x1B[0m";
|
||||||
int group = (int) (id % 6);
|
auto group = (int) (id % 6);
|
||||||
if (group == 0) {
|
if (group == 0) {
|
||||||
col1 = "\x1B[1;31m";
|
col1 = "\x1B[1;31m";
|
||||||
} else if (group == 1) {
|
} else if (group == 1) {
|
||||||
@ -225,22 +327,24 @@ void client_handler(Socket *socket, long id) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (socket->getSocketPort() == 443) {
|
if (ssl) {
|
||||||
socket->sslHandshake("/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/privkey.pem",
|
socket->sslHandshake("/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/privkey.pem",
|
||||||
"/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/fullchain.pem");
|
"/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/fullchain.pem");
|
||||||
}
|
}
|
||||||
} catch (char *msg) {
|
} catch (char *msg) {
|
||||||
log(prefix, (string) "Unable to perform handhsake: " + msg);
|
log(prefix, (string) "Unable to perform handshake: " + msg);
|
||||||
err = true;
|
err = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
long reqnum = 0;
|
long reqnum = 0;
|
||||||
if (!err) {
|
if (!err) {
|
||||||
while (connection_handler(prefix, *socket, id, ++reqnum));
|
while (connection_handler(prefix, socket, id, ++reqnum));
|
||||||
reqnum--;
|
reqnum--;
|
||||||
}
|
}
|
||||||
|
|
||||||
log(prefix, "Connection terminated (#:" + to_string(reqnum) + ", R:, S:, T: " + formatTime(socket->getDuration()) + ")");
|
log(prefix,
|
||||||
|
"Connection terminated (#:" + to_string(reqnum) + ", R: " + formatSize(socket->getBytesReceived()) + ", S: " +
|
||||||
|
formatSize(socket->getBytesSent()) + ", T: " + formatTime(socket->getDuration()) + ")");
|
||||||
socket->close();
|
socket->close();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,6 +80,26 @@ string getHttpDate(time_t time) {
|
|||||||
return string(buffer);
|
return string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string getTimestamp(string path) {
|
||||||
|
struct stat attrib;
|
||||||
|
stat(path.c_str(), &attrib);
|
||||||
|
return getTimestamp(attrib.st_ctime);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string getTimestamp(time_t time) {
|
||||||
|
char buffer[64];
|
||||||
|
struct tm *timeinfo = gmtime(&time);
|
||||||
|
strftime(buffer, sizeof(buffer), "%Y%m%d%H%M%S", timeinfo);
|
||||||
|
return string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
|
long getFileSize(string filename) {
|
||||||
|
struct stat stat_buf;
|
||||||
|
int rc = stat(filename.c_str(), &stat_buf);
|
||||||
|
return rc == 0 ? stat_buf.st_size : -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns a formatted time string
|
* Returns a formatted time string
|
||||||
* @param micros Delta time to be formatted
|
* @param micros Delta time to be formatted
|
||||||
@ -101,6 +121,22 @@ std::string formatTime(long micros) {
|
|||||||
return std::string(buffer);
|
return std::string(buffer);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string formatSize(unsigned long bytes) {
|
||||||
|
char buffer[64];
|
||||||
|
if (bytes > 0x10000000000) {
|
||||||
|
sprintf(buffer, "%.1f TiB", (double) bytes / 0x10000000000);
|
||||||
|
} else if (bytes > 0x40000000) {
|
||||||
|
sprintf(buffer, "%.1f GiB", (double) bytes / 0x40000000);
|
||||||
|
} else if (bytes > 0x100000) {
|
||||||
|
sprintf(buffer, "%.1f MiB", (double) bytes / 0x100000);
|
||||||
|
} else if (bytes > 0x400) {
|
||||||
|
sprintf(buffer, "%.1f KiB", (double) bytes / 0x400);
|
||||||
|
} else {
|
||||||
|
sprintf(buffer, "%ld B", bytes);
|
||||||
|
}
|
||||||
|
return std::string(buffer);
|
||||||
|
}
|
||||||
|
|
||||||
string getWebRoot(string host) {
|
string getWebRoot(string host) {
|
||||||
return "/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/webroot";
|
return "/home/lorenz/Documents/Projects/Necronda-Server/necronda-server-3.0/webroot";
|
||||||
}
|
}
|
||||||
@ -108,7 +144,7 @@ string getWebRoot(string host) {
|
|||||||
|
|
||||||
#include "network/Address.cpp"
|
#include "network/Address.cpp"
|
||||||
#include "network/Socket.cpp"
|
#include "network/Socket.cpp"
|
||||||
#include "Path.cpp"
|
#include "URI.cpp"
|
||||||
#include "network/http/HttpStatusCode.cpp"
|
#include "network/http/HttpStatusCode.cpp"
|
||||||
#include "network/http/HttpHeader.cpp"
|
#include "network/http/HttpHeader.cpp"
|
||||||
#include "network/http/HttpRequest.cpp"
|
#include "network/http/HttpRequest.cpp"
|
||||||
@ -121,54 +157,74 @@ string getWebRoot(string host) {
|
|||||||
long clientnum = 0;
|
long clientnum = 0;
|
||||||
|
|
||||||
int main() {
|
int main() {
|
||||||
|
cout << "Necronda Server 3.0" << endl << "by Lorenz Stechauner" << endl << endl;
|
||||||
|
|
||||||
|
signal(SIGPIPE, SIG_IGN);
|
||||||
|
|
||||||
SSL_load_error_strings();
|
SSL_load_error_strings();
|
||||||
SSL_library_init();
|
SSL_library_init();
|
||||||
ERR_load_crypto_strings();
|
ERR_load_crypto_strings();
|
||||||
OpenSSL_add_all_algorithms();
|
OpenSSL_add_all_algorithms();
|
||||||
|
|
||||||
signal(SIGPIPE, SIG_IGN);
|
int ret = system("mkdir -p /var/necronda /etc/necronda /tmp/necronda; touch /var/necronda/ETags");
|
||||||
|
|
||||||
cout << "Necronda Server 3.0" << endl << "by Lorenz Stechauner" << endl << endl;
|
if (ret != 0) {
|
||||||
|
cout << "Unable to create server files" << endl;
|
||||||
unsigned short PORT = 443;
|
|
||||||
|
|
||||||
Socket *s;
|
|
||||||
try {
|
|
||||||
s = new Socket();
|
|
||||||
} catch (char *msg) {
|
|
||||||
cout << "Unable to create socket: " << msg << endl;
|
|
||||||
exit(1);
|
exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
list<unsigned short> ports = {80, 443};
|
||||||
s->setReuseAddress(true);
|
|
||||||
} catch (char *msg) {
|
list<Socket> servers = {};
|
||||||
cout << "Unable to set socket option: " << msg << endl;
|
auto it = ports.begin();
|
||||||
exit(1);
|
|
||||||
|
for (int i = 0; i < ports.size(); i++) {
|
||||||
|
unsigned short port = *it;
|
||||||
|
advance(it, 1);
|
||||||
|
Socket server = Socket();
|
||||||
|
servers.push_back(server);
|
||||||
|
|
||||||
|
try {
|
||||||
|
server.setReuseAddress(true);
|
||||||
|
server.setReceiveTimeout(0);
|
||||||
|
server.setSendTimeout(0);
|
||||||
|
} catch (char *msg) {
|
||||||
|
cout << "Unable to set socket option: " << msg << endl;
|
||||||
|
exit(2);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
server.bind(port);
|
||||||
|
} catch (char *msg) {
|
||||||
|
cout << "Unable to bind socket to port " << port << ": " << msg << endl;
|
||||||
|
exit(3);
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
server.listen(256);
|
||||||
|
} catch (char *msg) {
|
||||||
|
cout << "Unable to listen on socket: " << msg << endl;
|
||||||
|
exit(4);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
cout << "Ready for connections" << endl;
|
||||||
s->bind(PORT);
|
|
||||||
} catch (char *msg) {
|
|
||||||
cout << "Unable to bind socket to port " << PORT << ": " << msg << endl;
|
|
||||||
exit(2);
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
|
||||||
s->listen(256);
|
|
||||||
} catch (char *msg) {
|
|
||||||
cout << "Unable to listen on socket: " << msg << endl;
|
|
||||||
exit(3);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
try {
|
try {
|
||||||
Socket *socket = s->accept();
|
Socket::select(servers, {});
|
||||||
clientnum++;
|
for (Socket server : servers) {
|
||||||
thread *t = new thread(client_handler, socket, clientnum);
|
try {
|
||||||
|
Socket *socket = server.accept();
|
||||||
|
clientnum++;
|
||||||
|
thread *t = new thread(client_handler, socket, clientnum, server.getSocketPort() == 443);
|
||||||
|
} catch (char *msg) {
|
||||||
|
// Nothing
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (char *msg) {
|
} catch (char *msg) {
|
||||||
cout << msg << endl;
|
cout << "Select: " << msg << endl;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ unsigned long getMicros();
|
|||||||
|
|
||||||
string formatTime(long micros);
|
string formatTime(long micros);
|
||||||
|
|
||||||
|
string formatSize(unsigned long bytes);
|
||||||
|
|
||||||
string getWebRoot(string host);
|
string getWebRoot(string host);
|
||||||
|
|
||||||
string getMimeType(string path);
|
string getMimeType(string path);
|
||||||
@ -25,4 +27,11 @@ string getHttpDate();
|
|||||||
|
|
||||||
string getHttpDate(string filename);
|
string getHttpDate(string filename);
|
||||||
|
|
||||||
|
string getTimestamp(string path);
|
||||||
|
|
||||||
|
string getTimestamp(time_t time);
|
||||||
|
|
||||||
|
long getFileSize(string filename);
|
||||||
|
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
|
Reference in New Issue
Block a user