Skip to content

Commit c3ba336

Browse files
author
lh5053
committed
Added support for a few basic mime types.
Added support for cgi scripts. Added some test content (a sample image, icon and script).
1 parent e837df4 commit c3ba336

File tree

8 files changed

+219
-37
lines changed

8 files changed

+219
-37
lines changed

errors/415.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 - 415 Unsupported Media Type</title>
4+
</head>
5+
<body>
6+
<h1>Unfortunately the resource you requested is not supported (Error 415).</h1>
7+
<p>Error: %s</p>
8+
</body>
9+
</html>

favicon.ico

4.18 KB
Binary file not shown.

src/DataHandler.cpp

Lines changed: 158 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
#include <unistd.h>
21
#include <cstring>
32
#include <fcntl.h>
3+
#include <unistd.h>
44
#include <linux/limits.h>
55

66
#include "DataHandler.h"
@@ -36,45 +36,184 @@ bool DataHandler::verify_path(std::string path) {
3636
return path_ok;
3737
}
3838

39-
char * DataHandler::read_resource(std::string path) {
39+
bool is(char * mime, std::string type) {
40+
return (strstr(mime, type.c_str()) != NULL);
41+
}
42+
43+
resource DataHandler::read_resource(std::string path) {
4044
// prepend cwd() to path
4145

4246
if (!verify_path(path))
4347
path = "/index.html";
4448

45-
path = get_working_path() + path;
46-
this->logger->debug("Reading file at: " + path);
49+
std::string cwd = get_working_path();
50+
path = cwd + path;
51+
this->logger->debug("Checking resource at: " + path);
52+
53+
// check mime type of resource
54+
std::string args[2] = { "/usr/bin/file", path };
55+
resource file_mime = run_command(args);
56+
char * mime = file_mime.data;
57+
58+
resource output;
59+
// now check for known mime types
60+
if (is(mime, "executable")) {
61+
// run the script
62+
std::string args[2] = { path, "" }; // TODO: Fix me too!
63+
resource script_output = run_command(args);
64+
output.data = script_output.data;
65+
output.size = script_output.size;
66+
output.type = "executable";
67+
}
68+
// TODO: Move this definition to some more reasonable place
69+
else if (is(mime, "HTML")) { output.type = "text/html; charset=UTF-8"; }
70+
else if (is(mime, "ERROR") || is(mime, "ASCII")) { output.type = "text/plain; charset=UTF-8"; }
71+
else if (is(mime, "JPEG")) { output.type = "image/jpeg"; }
72+
else if (is(mime, "PNG")) { output.type = "image/png"; }
73+
else if (is(mime, "MS Windows icon")) { output.type = "image/vnd.microsoft.icon"; }
74+
75+
if (output.type.length() > 0 && output.type != "executable") {
76+
resource f = get_file(path);
77+
output.data = f.data;
78+
output.size = f.size;
79+
}
80+
else if (output.type.length() == 0){
81+
std::string error_str = "Unsupported mime type: " + std::string(mime);
82+
// drop 'local' part of path
83+
size_t pos = 0;
84+
while ((pos = error_str.find(cwd)) != std::string::npos )
85+
error_str.erase(pos, cwd.length());
86+
87+
throw DataHandler::Unsupported(error_str);
88+
}
89+
90+
return output;
91+
}
92+
93+
std::string DataHandler::get_working_path() {
94+
char temp[PATH_MAX];
95+
return ( getcwd(temp, PATH_MAX) ? std::string( temp ) : std::string("") );
96+
}
4797

98+
resource DataHandler::get_file(std::string path) {
99+
this->logger->debug("Read file: " + path);
48100
int fd = open(path.c_str(), O_RDONLY);
49101
if (fd < 0) {
50102
// TODO: replace with a proper HTTP CODE
51103
char * err = std::strerror(errno);
52104
throw DataHandler::FileNotFound("Error while reading file contents at " + path + ": " + std::string(err ? err : "unknown error"));
53105
}
54106

55-
lseek(fd, 0, SEEK_END);
56-
long fsize = lseek(fd, 0, SEEK_CUR);
107+
long fsize = lseek(fd, 0, SEEK_END);
57108
lseek(fd, 0, SEEK_SET);
58109

59-
char * data = (char *) malloc(fsize + 1);
60-
read(fd, data, fsize);
110+
resource data;
111+
data.data = (char *) malloc(fsize+1);
112+
read(fd, data.data, fsize);
61113
close(fd);
62-
data[fsize] = '\0';
63114

64-
return data;
65-
}
115+
data.data[fsize] = '\0';
116+
data.size = fsize;
66117

67-
std::string DataHandler::get_working_path() {
68-
char temp[PATH_MAX];
69-
return ( getcwd(temp, PATH_MAX) ? std::string( temp ) : std::string("") );
118+
return data;
70119
}
71120

72-
char * DataHandler::get_error_file(int error_code, std::string param) {
121+
resource DataHandler::get_error_file(int error_code, std::string param) {
73122
// start by reading the error template
74-
char * contents = read_resource("/errors/"+ std::to_string(error_code) +".html");
123+
resource output = read_resource("/errors/"+ std::to_string(error_code) +".html");
75124
// now prepare a place to write the filled template
76-
char * data = (char *) malloc(strlen(contents) + param.length() - 3);
125+
long new_size = output.size + param.length() - 3;
126+
char * data = (char *) malloc(new_size * sizeof(char));
77127
// and fill it
78-
sprintf(data, contents, param.c_str());
79-
return data;
128+
sprintf(data, output.data, param.c_str());
129+
130+
// free old structure
131+
free(output.data);
132+
// replace with new pointer
133+
output.data = data;
134+
output.size = new_size;
135+
136+
return output;
137+
}
138+
139+
resource DataHandler::run_command(std::string args[]) {
140+
int comms[2];
141+
// start with a small 1k buffer
142+
int buf_pos = 0, buf_max = 1024, buf_blocks = 1;
143+
resource output;
144+
output.data = (char *) malloc(buf_blocks * buf_max * sizeof(char));
145+
146+
if (pipe(comms) < 0) {
147+
char * err = std::strerror(errno);
148+
throw DataHandler::Exception("Cannot create pipe: " + std::string(err ? err : "unknown error"));
149+
}
150+
151+
int pid = fork();
152+
if (pid == 0) {
153+
// redirect STDOUT to our comms
154+
if (dup2(comms[1], STDOUT_FILENO) == -1) {
155+
char * err = std::strerror(errno);
156+
throw DataHandler::Exception("Cannot redirect STDOUT to pipe: " + std::string(err ? err : "unknown error"));
157+
}
158+
159+
// same with STDERR
160+
if (dup2(comms[1], STDERR_FILENO) == -1) {
161+
char * err = std::strerror(errno);
162+
throw DataHandler::Exception("Cannot redirect STDERR to pipe: " + std::string(err ? err : "unknown error"));
163+
}
164+
165+
// now close the pipe on our side
166+
close(comms[0]);
167+
168+
// now run the target
169+
// TODO: Fix me! I'm ugly!
170+
if (execl(args[0].c_str(), args[0].c_str(), args[1].c_str(), (char *) 0) < 0) {
171+
char * err = std::strerror(errno);
172+
throw DataHandler::Exception(
173+
"Cannot exec '" + std::string(args[0]) + "' due to: " + std::string(err ? err : "unknown error")
174+
);
175+
}
176+
}
177+
else if (pid > 0) {
178+
close(comms[1]);
179+
180+
// Just a char by char read here, you can change it accordingly
181+
char rchar;
182+
while (read(comms[0], &rchar, 1) == 1) {
183+
output.data[buf_pos++] = rchar;
184+
if (buf_pos >= buf_max){
185+
// extend the buffer by another 1k block
186+
buf_blocks++;
187+
// reset the curent position
188+
buf_pos = 0;
189+
// allocate more memory
190+
output.data = (char *) realloc(output.data, buf_blocks * buf_max * sizeof(char));
191+
if (output.data == NULL) {
192+
char * err = std::strerror(errno);
193+
throw DataHandler::Exception(
194+
"Error while reading output from: " + std::string(args[0]) +", "
195+
+ "because of: " + std::string(err ? err : "unknown error")
196+
);
197+
}
198+
}
199+
}
200+
// add NULL termination
201+
output.data[buf_pos] = '\0';
202+
output.size = (buf_blocks - 1) * buf_max + buf_pos + 1;
203+
204+
close(comms[0]);
205+
}
206+
else {
207+
// fork failed
208+
char * err = std::strerror(errno);
209+
close(comms[0]);
210+
close(comms[1]);
211+
throw DataHandler::Exception("Cannot fork: " + std::string(err ? err : "unknown error"));
212+
}
213+
214+
if (strstr(output.data, "DataHandler::Exception") != NULL) {
215+
throw DataHandler::Exception("Fork returned an exception: " + std::string(output.data));
216+
}
217+
218+
return output;
80219
}

src/DataHandler.h

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,25 @@
44
#include "Exceptions.h"
55
#include "Logger.h"
66

7+
struct resource {
8+
std::string type;
9+
char * data;
10+
long size;
11+
};
12+
713
class DataHandler {
814
private:
915
Logger *logger;
1016
std::string get_working_path();
1117
bool verify_path(std::string path);
18+
resource run_command(std::string args[]);
19+
resource get_file(std::string path);
1220

1321
public:
1422
DataHandler();
1523
virtual ~DataHandler();
16-
char * read_resource(std::string path);
17-
char * get_error_file(int error_code, std::string param);
24+
resource read_resource(std::string path);
25+
resource get_error_file(int error_code, std::string param);
1826

1927
class Exception: public BaseException {
2028
public:
@@ -28,6 +36,12 @@ class DataHandler {
2836
this->reason = msg;
2937
}
3038
};
39+
class Unsupported: public Exception {
40+
public:
41+
Unsupported(std::string msg = "Unsupported file found") {
42+
this->reason = msg;
43+
}
44+
};
3145
};
3246

3347
#endif /* SRC_DATAHANDLER_H_ */

src/Worker.cpp

Lines changed: 28 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -53,15 +53,20 @@ void Worker::handle_request() {
5353
}
5454

5555
DataHandler dHandler;
56-
char * data;
56+
resource data;
5757
int return_code = 200;
5858
if (space_count == 2) {
5959
std::string path = req_str.substr(prev_pos, pos-prev_pos);
6060

6161
try {
6262
data = dHandler.read_resource(path);
6363
}
64-
catch (DataHandler::FileNotFound &e) {
64+
catch (DataHandler::Unsupported &e) {
65+
this->logger->debug("Unsupported file while handling request: " + std::string(e.what()));
66+
return_code = 415;
67+
data = dHandler.get_error_file(return_code, std::string(e.what()));
68+
}
69+
catch (DataHandler::Exception &e) {
6570
this->logger->debug("Got exception while handling request: " + std::string(e.what()));
6671
return_code = 404;
6772
data = dHandler.get_error_file(return_code, path.substr(1));
@@ -73,30 +78,38 @@ void Worker::handle_request() {
7378
data = dHandler.get_error_file(return_code, req_str);
7479
}
7580

76-
// apend standard response headers
7781
// TODO: move this to BasicHTTP handling
78-
char * headers = (char *) std::string(
79-
"HTTP/1.x "+std::to_string(return_code)+" OK\n"
80-
"Server: HackTTP\n"
81-
"Connection: close\n"
82-
"Content-Type: text/html; charset=UTF-8\n\n"
83-
).c_str();
82+
// apend standard response headers, but only if it's static content
83+
if (data.type != "executable") {
84+
std::string headers =
85+
"HTTP/1.1 "+std::to_string(return_code)+" OK\n"
86+
"Server: HackTTP\n"
87+
"Connection: close\n"
88+
"Content-Type: "+data.type+"\n";
89+
;
90+
91+
if (data.type.find("image/") != std::string::npos) {
92+
headers += "Accept-Ranges: bytes\n";
93+
headers += "Content-Length: " + std::to_string(data.size) + "\n";
94+
}
8495

85-
// now first send the headers
86-
send_msg(headers);
96+
// now send the headers
97+
headers += "\n";
98+
send_msg((char *) headers.c_str(), headers.length());
99+
}
87100

88101
// then the data
89-
send_msg(data);
102+
send_msg(data.data, data.size);
90103

91-
free(data);
104+
free(data.data);
92105

93106
this->logger->debug("Request handling done");
94107

95108
return;
96109
}
97110

98-
void Worker::send_msg(char * msgc) {
99-
if (send(this->socket_fd, msgc, strlen(msgc), 0) < 0) {
111+
void Worker::send_msg(char * msgc, long size) {
112+
if (send(this->socket_fd, msgc, size, 0) < 0) {
100113
char * err = std::strerror(errno);
101114
throw Worker::Exception("Error while sending response to request: " + std::string(err ? err : "unknown error"));
102115
}

src/Worker.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Worker {
88
private:
99
int socket_fd;
1010
Logger *logger;
11-
void send_msg(char * msg);
11+
void send_msg(char * msg, long size);
1212

1313
public:
1414
Worker(int socket_fd);

test.jpg

67.9 KB
Loading

test.pl

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
#!/usr/bin/perl -w
2+
use strict;
3+
4+
# Example perl script
5+
print "HTTP/1.x 200 OK\n";
6+
print "Content-Type: text/html; charset=UTF-8;\n\n";
7+
print "Today is ".`date`;

0 commit comments

Comments
 (0)