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;
 | 
			
		||||
 | 
			
		||||
class Path {
 | 
			
		||||
class URI {
 | 
			
		||||
private:
 | 
			
		||||
	string webroot;
 | 
			
		||||
	string reqpath;
 | 
			
		||||
	string relpath;
 | 
			
		||||
	string query;
 | 
			
		||||
	string filepath;
 | 
			
		||||
	bool queryinit;
 | 
			
		||||
 | 
			
		||||
public:
 | 
			
		||||
	Path(string webroot, string reqpath);
 | 
			
		||||
	URI(string webroot, string reqpath);
 | 
			
		||||
 | 
			
		||||
	string getWebRoot();
 | 
			
		||||
 | 
			
		||||
@@ -35,6 +38,10 @@ public:
 | 
			
		||||
 | 
			
		||||
	bool isStatic();
 | 
			
		||||
 | 
			
		||||
	string getQuery();
 | 
			
		||||
 | 
			
		||||
	bool hasQuery();
 | 
			
		||||
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
							
								
								
									
										242
									
								
								src/client.cpp
									
									
									
									
									
								
							
							
						
						
									
										242
									
								
								src/client.cpp
									
									
									
									
									
								
							@@ -9,12 +9,16 @@
 | 
			
		||||
#include <iostream>
 | 
			
		||||
#include <zlib.h>
 | 
			
		||||
#include <cassert>
 | 
			
		||||
#include <fstream>
 | 
			
		||||
#include <openssl/md5.h>
 | 
			
		||||
#include <cstring>
 | 
			
		||||
 | 
			
		||||
#include "network/Socket.h"
 | 
			
		||||
#include "network/http/HttpRequest.h"
 | 
			
		||||
#include "network/http/HttpConnection.h"
 | 
			
		||||
#include "necronda-server.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);
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
 * @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
 | 
			
		||||
 * @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;
 | 
			
		||||
	try {
 | 
			
		||||
		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());
 | 
			
		||||
				prefix = buffer;
 | 
			
		||||
 | 
			
		||||
				Path path = Path(getWebRoot(host), req.getPath());
 | 
			
		||||
				URI path = URI(getWebRoot(host), req.getPath());
 | 
			
		||||
				log(prefix, req.getMethod() + " " + req.getPath());
 | 
			
		||||
 | 
			
		||||
				FILE *file = path.openFile();
 | 
			
		||||
 | 
			
		||||
				if (file == nullptr) {
 | 
			
		||||
					req.setField("Cache-Control", "public, max-age=60");
 | 
			
		||||
					req.respond(404);
 | 
			
		||||
				if (!path.getNewPath().empty()) {
 | 
			
		||||
					req.redirect(302, path.getNewPath());
 | 
			
		||||
				} else {
 | 
			
		||||
					string type = path.getFileType();
 | 
			
		||||
 | 
			
		||||
					if (type.find("inode/") == 0) {
 | 
			
		||||
						req.respond(403);
 | 
			
		||||
					if (file == nullptr) {
 | 
			
		||||
						req.setField("Cache-Control", "public, max-age=60");
 | 
			
		||||
						req.respond(404);
 | 
			
		||||
					} else {
 | 
			
		||||
						req.setField("Content-Type", type);
 | 
			
		||||
						req.setField("Last-Modified", getHttpDate(path.getAbsolutePath()));
 | 
			
		||||
						string type = path.getFileType();
 | 
			
		||||
 | 
			
		||||
						bool invalidMethod = false;
 | 
			
		||||
 | 
			
		||||
						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;
 | 
			
		||||
							}
 | 
			
		||||
						if (type.find("inode/") == 0) {
 | 
			
		||||
							req.respond(403);
 | 
			
		||||
						} else {
 | 
			
		||||
							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;
 | 
			
		||||
							}
 | 
			
		||||
						}
 | 
			
		||||
							req.setField("Content-Type", type);
 | 
			
		||||
							req.setField("Last-Modified", getHttpDate(path.getFilePath()));
 | 
			
		||||
 | 
			
		||||
						if (invalidMethod) {
 | 
			
		||||
							req.respond(405);
 | 
			
		||||
						} else {
 | 
			
		||||
							bool invalidMethod = false;
 | 
			
		||||
							bool etag = false;
 | 
			
		||||
 | 
			
		||||
							bool compress = type.find("text/") == 0 && req.isExistingField("Accept-Encoding") &&
 | 
			
		||||
											req.getField("Accept-Encoding").find("deflate") != string::npos;
 | 
			
		||||
 | 
			
		||||
							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, 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);
 | 
			
		||||
										}
 | 
			
		||||
									}
 | 
			
		||||
							if (path.isStatic()) {
 | 
			
		||||
								string hash = getETag(path.getFilePath());
 | 
			
		||||
								req.setField("ETag", hash);
 | 
			
		||||
								req.setField("Accept-Ranges", "bytes");
 | 
			
		||||
								req.setField("Cache-Control", "public, max-age=30");
 | 
			
		||||
								req.setField("Allow", "GET");
 | 
			
		||||
								if (req.getMethod() != "GET") {
 | 
			
		||||
									invalidMethod = true;
 | 
			
		||||
								}
 | 
			
		||||
								if (req.isExistingField("If-None-Match") && req.getField("If-None-Match") == hash) {
 | 
			
		||||
									etag = true;
 | 
			
		||||
								}
 | 
			
		||||
							} 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();
 | 
			
		||||
@@ -151,6 +252,7 @@ bool connection_handler(const char *prefix, Socket socket, long id, long num) {
 | 
			
		||||
					error = true;
 | 
			
		||||
				} else if (msg == "Invalid path") {
 | 
			
		||||
					log(prefix, "Timeout!");
 | 
			
		||||
					req.setField("Connection", "close");
 | 
			
		||||
					req.respond(400);
 | 
			
		||||
				} else {
 | 
			
		||||
					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 {
 | 
			
		||||
			if (msg == "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;
 | 
			
		||||
			} else if (msg == "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;
 | 
			
		||||
			} else {
 | 
			
		||||
				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 id The client ID
 | 
			
		||||
 */
 | 
			
		||||
void client_handler(Socket *socket, long id) {
 | 
			
		||||
void client_handler(Socket *socket, long id, bool ssl) {
 | 
			
		||||
	const char *prefix;
 | 
			
		||||
	{
 | 
			
		||||
		char const *col1;
 | 
			
		||||
		char const *col2 = "\x1B[0m";
 | 
			
		||||
		int group = (int) (id % 6);
 | 
			
		||||
		auto group = (int) (id % 6);
 | 
			
		||||
		if (group == 0) {
 | 
			
		||||
			col1 = "\x1B[1;31m";
 | 
			
		||||
		} else if (group == 1) {
 | 
			
		||||
@@ -225,22 +327,24 @@ void client_handler(Socket *socket, long id) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		if (socket->getSocketPort() == 443) {
 | 
			
		||||
		if (ssl) {
 | 
			
		||||
			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");
 | 
			
		||||
		}
 | 
			
		||||
	} catch (char *msg) {
 | 
			
		||||
		log(prefix, (string) "Unable to perform handhsake: " + msg);
 | 
			
		||||
		log(prefix, (string) "Unable to perform handshake: " + msg);
 | 
			
		||||
		err = true;
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	long reqnum = 0;
 | 
			
		||||
	if (!err) {
 | 
			
		||||
		while (connection_handler(prefix, *socket, id, ++reqnum));
 | 
			
		||||
		while (connection_handler(prefix, socket, id, ++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();
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -80,6 +80,26 @@ string getHttpDate(time_t time) {
 | 
			
		||||
	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
 | 
			
		||||
 * @param micros Delta time to be formatted
 | 
			
		||||
@@ -101,6 +121,22 @@ std::string formatTime(long micros) {
 | 
			
		||||
	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) {
 | 
			
		||||
	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/Socket.cpp"
 | 
			
		||||
#include "Path.cpp"
 | 
			
		||||
#include "URI.cpp"
 | 
			
		||||
#include "network/http/HttpStatusCode.cpp"
 | 
			
		||||
#include "network/http/HttpHeader.cpp"
 | 
			
		||||
#include "network/http/HttpRequest.cpp"
 | 
			
		||||
@@ -121,54 +157,74 @@ string getWebRoot(string host) {
 | 
			
		||||
long clientnum = 0;
 | 
			
		||||
 | 
			
		||||
int main() {
 | 
			
		||||
	cout << "Necronda Server 3.0" << endl << "by Lorenz Stechauner" << endl << endl;
 | 
			
		||||
 | 
			
		||||
	signal(SIGPIPE, SIG_IGN);
 | 
			
		||||
 | 
			
		||||
	SSL_load_error_strings();
 | 
			
		||||
	SSL_library_init();
 | 
			
		||||
	ERR_load_crypto_strings();
 | 
			
		||||
	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;
 | 
			
		||||
 | 
			
		||||
	unsigned short PORT = 443;
 | 
			
		||||
 | 
			
		||||
	Socket *s;
 | 
			
		||||
	try {
 | 
			
		||||
		s = new Socket();
 | 
			
		||||
	} catch (char *msg) {
 | 
			
		||||
		cout << "Unable to create socket: " << msg << endl;
 | 
			
		||||
	if (ret != 0) {
 | 
			
		||||
		cout << "Unable to create server files" << endl;
 | 
			
		||||
		exit(1);
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	try {
 | 
			
		||||
		s->setReuseAddress(true);
 | 
			
		||||
	} catch (char *msg) {
 | 
			
		||||
		cout << "Unable to set socket option: " << msg << endl;
 | 
			
		||||
		exit(1);
 | 
			
		||||
	list<unsigned short> ports = {80, 443};
 | 
			
		||||
 | 
			
		||||
	list<Socket> servers = {};
 | 
			
		||||
	auto it = ports.begin();
 | 
			
		||||
 | 
			
		||||
	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 {
 | 
			
		||||
		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);
 | 
			
		||||
	}
 | 
			
		||||
	cout << "Ready for connections" << endl;
 | 
			
		||||
 | 
			
		||||
	while (true) {
 | 
			
		||||
		try {
 | 
			
		||||
			Socket *socket = s->accept();
 | 
			
		||||
			clientnum++;
 | 
			
		||||
			thread *t = new thread(client_handler, socket, clientnum);
 | 
			
		||||
			Socket::select(servers, {});
 | 
			
		||||
			for (Socket server : servers) {
 | 
			
		||||
				try {
 | 
			
		||||
					Socket *socket = server.accept();
 | 
			
		||||
					clientnum++;
 | 
			
		||||
					thread *t = new thread(client_handler, socket, clientnum, server.getSocketPort() == 443);
 | 
			
		||||
				} catch (char *msg) {
 | 
			
		||||
					// Nothing
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		} catch (char *msg) {
 | 
			
		||||
			cout << msg << endl;
 | 
			
		||||
			cout << "Select: " << msg << endl;
 | 
			
		||||
			break;
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -15,6 +15,8 @@ unsigned long getMicros();
 | 
			
		||||
 | 
			
		||||
string formatTime(long micros);
 | 
			
		||||
 | 
			
		||||
string formatSize(unsigned long bytes);
 | 
			
		||||
 | 
			
		||||
string getWebRoot(string host);
 | 
			
		||||
 | 
			
		||||
string getMimeType(string path);
 | 
			
		||||
@@ -25,4 +27,11 @@ string getHttpDate();
 | 
			
		||||
 | 
			
		||||
string getHttpDate(string filename);
 | 
			
		||||
 | 
			
		||||
string getTimestamp(string path);
 | 
			
		||||
 | 
			
		||||
string getTimestamp(time_t time);
 | 
			
		||||
 | 
			
		||||
long getFileSize(string filename);
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#endif
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user