Skip to content

Commit f70e5fa

Browse files
committed
Improved GET support and added POST support.
Added a better live example (perl form) for testing/demonstration purposes. Added basic cookies support. Possibly also added a couple new memory leaks, since the server is crashing very often now. This will be addressed in the next push.
1 parent e477e44 commit f70e5fa

File tree

16 files changed

+376
-62
lines changed

16 files changed

+376
-62
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ Debug
3939

4040
.idea
4141
.settings
42+
.includepath
4243

4344
#CLion files
4445
CMakeLists.txt

errors/403.html

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<html>
2+
<head>
3+
<title>HackTTP - 403 Forbidden</title>
4+
</head>
5+
<body>
6+
<h1>Unfortunately we encountered an error while handling your request.</h1>
7+
<p>Error: %s</p>
8+
</body>
9+
</html>

index.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
<html>
22
<head>
33
<title>HackTTP - Example page</title>
4+
<link rel="shortcut icon" href="/favicon.ico?12" />
45
</head>
56
<body>
67
<h1>HackTTP - Example index file</h1>
78
<p>This is a sample page, if you can see this, it means that the servers is up and running.</p>
89
</body>
9-
</html>
10+
</html>

src/BasicHTTP.cpp

Lines changed: 79 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,19 @@ bool is_valid(BasicHTTP::request req, std::string http_ver) {
1616
// if all tests pass, return true
1717
return (
1818
(http_ver == "HTTP/1.0" || http_ver == "HTTP/1.1")
19-
&& req.method == "GET" // || POST
19+
&& (req.method == "GET" || req.method == "POST")
2020
&& req.uri.length() > 0
2121
);
2222
}
2323

2424
BasicHTTP::request BasicHTTP::parse_request(std::string req_str) {
25+
this->logger->debug("Full request: " + req_str);
2526
request req;
27+
if (req_str.length() < 3) {
28+
req.valid = false;
29+
return req;
30+
}
31+
2632
// From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5
2733
// Request-Line = Method SP Request-URI SP HTTP-Version CRLF
2834
size_t pos = 0, prev_pos = 0;
@@ -43,12 +49,24 @@ BasicHTTP::request BasicHTTP::parse_request(std::string req_str) {
4349
std::string http_ver;
4450
if ((pos = req_str.find("\r\n", prev_pos)) != std::string::npos) {
4551
http_ver = req_str.substr(prev_pos, pos-prev_pos);
46-
prev_pos = pos + 1;
52+
prev_pos = pos + 2;
4753
}
4854
this->logger->info("Method: " + req.method + ", HTTP version string: " + http_ver + ", URI: " + req.uri);
4955

5056
// now end with checking if it's actually valid
51-
req.valid = is_valid(req, http_ver);
57+
req.valid = is_valid(req, http_ver);
58+
59+
// find and extract cookies, if available
60+
if (req.valid) {
61+
req.cookies = BasicHTTP::fetch_cookies(req_str);
62+
63+
if (req.method == "GET") {
64+
handle_get_method(&req);
65+
}
66+
if (req.method == "POST") {
67+
handle_post_method(&req, req_str);
68+
}
69+
}
5270

5371
return req;
5472
}
@@ -78,3 +96,61 @@ BasicHTTP::response BasicHTTP::render_headers(int code, DataHandler::resource rs
7896

7997
return resp;
8098
}
99+
100+
std::string BasicHTTP::fetch_cookies(std::string req_str) {
101+
size_t pos = 0, eol = 0;
102+
103+
std::string cookies = "";
104+
// to extract the POST params we first need to find it and it's length
105+
if ((pos = req_str.find("Cookie: ")) != std::string::npos) {
106+
if ((eol = req_str.find("\r\n", pos)) != std::string::npos) {
107+
cookies = req_str.substr(pos + 8, eol - pos - 8);
108+
this->logger->debug("Cookies: " + cookies);
109+
}
110+
}
111+
112+
return cookies;
113+
}
114+
115+
void BasicHTTP::handle_get_method(BasicHTTP::request * req) {
116+
size_t pos = 0;
117+
118+
req->data.type = req->method;
119+
if ((pos = req->uri.find("?", 0)) != std::string::npos) {
120+
req->data.size = req->uri.length() - pos + 1;
121+
req->data.data = (char *) malloc((req->data.size+1) * sizeof(char));
122+
std::string params = req->uri.substr(pos+1);
123+
for (int i = 0; i < req->data.size; i++)
124+
req->data.data[i] = params[i];
125+
req->uri = req->uri.substr(0, pos);
126+
}
127+
else {
128+
req->data.size = 0;
129+
req->data.data = (char *) malloc((req->data.size+1) * sizeof(char));
130+
req->data.data[0] = '\0';
131+
}
132+
}
133+
134+
void BasicHTTP::handle_post_method(BasicHTTP::request * req, std::string req_str) {
135+
size_t pos = 0, prev_pos = 0;
136+
137+
// to extract the POST params we first need to find it and it's length
138+
if ((pos = req_str.find("Content-Length:", prev_pos)) != std::string::npos) {
139+
size_t eol = 0;
140+
int content_length = 0;
141+
if ((eol = req_str.find("\r\n", pos)) != std::string::npos) {
142+
content_length = std::stoi(req_str.substr(pos + 16, eol - pos + 16));
143+
this->logger->debug("Content-Length: " + std::to_string(content_length));
144+
}
145+
// don't read anything, if content data was 0, or wasn't defined at all
146+
if (content_length > 0 && (pos = req_str.find("\r\n\r\n", eol)) != std::string::npos) {
147+
req->data.type = req->method;
148+
req->data.size = content_length;
149+
req->data.data = (char *) malloc((req->data.size + 1)* sizeof(char));
150+
std::string data = req_str.substr(pos+4);
151+
for (int i = 0; i < req->data.size; i++)
152+
req->data.data[i] = data[i];
153+
req->data.data[req->data.size] = '\0';
154+
}
155+
}
156+
}

src/BasicHTTP.h

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,20 +15,26 @@
1515
#define HTTP_NOT_IMPLEMENTED 501
1616

1717
class BasicHTTP {
18-
private:
19-
Logger *logger;
2018
public:
2119
struct request {
2220
std::string method;
2321
std::string uri;
22+
std::string cookies;
23+
DataHandler::resource data;
2424
bool valid;
2525
};
26-
2726
struct response {
2827
std::string headers;
2928
bool has_headers;
3029
};
3130

31+
private:
32+
Logger *logger;
33+
std::string fetch_cookies(std::string req_str);
34+
void handle_get_method(request * req);
35+
void handle_post_method(BasicHTTP::request * req, std::string req_str);
36+
37+
public:
3238
BasicHTTP();
3339
virtual ~BasicHTTP();
3440
request parse_request(std::string req_str);

src/Config.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ std::map<string, string> configMap;
1717

1818
Config::Config() {
1919
loadConfigFileToMap();
20-
printMapContents();
20+
// printMapContents();
2121
}
2222

2323

@@ -75,7 +75,7 @@ Config::~Config() {
7575
int Config::get_int_setting(std::string setting_name) {
7676
if(isSigusr1Recieved){
7777
Config::loadConfigFileToMap();
78-
Config::printMapContents();
78+
// Config::printMapContents();
7979
isSigusr1Recieved = false;
8080
}
8181
int returnInt = -1;

src/DataHandler.cpp

Lines changed: 15 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#include <fcntl.h>
33
#include <unistd.h>
44
#include <linux/limits.h>
5+
#include <cctype>
6+
#include <clocale>
57

68
#include "DataHandler.h"
79

@@ -41,6 +43,12 @@ bool is(char * mime, std::string type) {
4143
}
4244

4345
DataHandler::resource DataHandler::read_resource(std::string path) {
46+
return DataHandler::read_resource(path, "");
47+
}
48+
DataHandler::resource DataHandler::read_resource(std::string path, std::string cookies) {
49+
return DataHandler::read_resource(path, cookies, NULL);
50+
}
51+
DataHandler::resource DataHandler::read_resource(std::string path, std::string cookies, DataHandler::resource * data) {
4452
// prepend cwd() to path
4553

4654
if (!verify_path(path))
@@ -55,19 +63,23 @@ DataHandler::resource DataHandler::read_resource(std::string path) {
5563
std::string args[2] = { "/usr/bin/file", path };
5664
DataHandler::resource file_mime = runner.run_command(args);
5765
char * mime = file_mime.data;
66+
std::string ext = path.substr(path.length()-3);
67+
for(char c : ext)
68+
c = std::toupper(c);
5869

5970
DataHandler::resource output;
6071
// now check for known mime types
6172
if (is(mime, "executable")) {
62-
// run the script
73+
// run the script, pass the data
6374
std::string args[2] = { path, "" }; // TODO: Fix me too!
64-
DataHandler::resource script_output = runner.run_command(args);
75+
DataHandler::resource script_output = runner.run_command(args, data, cookies);
6576
output.data = script_output.data;
6677
output.size = script_output.size;
6778
output.type = "executable";
6879
}
6980
// TODO: Move this definition to some more reasonable place
7081
else if (is(mime, "HTML")) { output.type = "text/html; charset=UTF-8"; }
82+
else if (is(mime, "ASCII") && ext == "CSS") { output.type = "text/css"; }
7183
else if (is(mime, "ERROR") || is(mime, "ASCII")) { output.type = "text/plain; charset=UTF-8"; }
7284
else if (is(mime, "JPEG")) { output.type = "image/jpeg"; }
7385
else if (is(mime, "PNG")) { output.type = "image/png"; }
@@ -102,7 +114,7 @@ DataHandler::resource DataHandler::get_error_file(int error_code, std::string pa
102114
DataHandler::resource output = DataHandler::read_resource("/errors/"+ std::to_string(error_code) +".html");
103115
// now prepare a place to write the filled template
104116
long new_size = output.size + param.length() - 3;
105-
char * data = (char *) malloc(new_size * sizeof(char));
117+
char * data = (char *) malloc((new_size+1) * sizeof(char));
106118
// and fill it
107119
sprintf(data, output.data, param.c_str());
108120

src/DataHandler.h

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,18 @@ class DataHandler {
2020
DataHandler();
2121
virtual ~DataHandler();
2222
resource read_resource(std::string path);
23+
resource read_resource(std::string path, std::string cookies);
24+
resource read_resource(std::string path, std::string cookies, DataHandler::resource * data);
2325
resource get_error_file(int error_code, std::string param);
2426

27+
// Exception base
28+
class Exception: public BaseException {
29+
public:
30+
Exception(std::string msg = "Unknown data handler exception") {
31+
this->reason = msg;
32+
}
33+
};
34+
2535
class Static {
2636
private:
2737
Logger *logger;
@@ -39,15 +49,17 @@ class DataHandler {
3949
Exec();
4050
~Exec();
4151
resource run_command(std::string args[]);
42-
};
52+
resource run_command(std::string args[], DataHandler::resource * data);
53+
resource run_command(std::string args[], DataHandler::resource * data, std::string cookies);
4354

44-
// Exceptions
45-
class Exception: public BaseException {
46-
public:
47-
Exception(std::string msg = "Unknown data handler exception") {
48-
this->reason = msg;
49-
}
55+
class PermissionDenied: public Exception {
56+
public:
57+
PermissionDenied(std::string msg = "Permission denied while trying to execute command") {
58+
this->reason = msg;
59+
}
60+
};
5061
};
62+
5163
class FileNotFound: public Exception {
5264
public:
5365
FileNotFound(std::string msg = "Could not find requested file") {

0 commit comments

Comments
 (0)