Meta: Add linktest seg fault issue to TASKS list.
[link.git] / Source / Thread.cpp
blobaaae2d1411fe807faff0eea665e0a15dacba380c
1 #include <Link.hpp>
2 #include <iostream>
3 #include <sys/socket.h>
4 #include <netinet/in.h>
5 #include <arpa/inet.h>
6 #include <netdb.h>
7 #include <openssl/ssl.h>
8 #include <iomanip>
9 #include <unistd.h>
10 #include <fcntl.h>
11 #include <fstream>
13 int Link::Thread::Write(const void* buf, size_t count) {
14 if (this->sslEnabled) return SSL_write((SSL*)ssl, buf, count);
15 else {
16 int sent = 0;
17 while (sent < count) {
18 int toSend = count - sent;
19 if (toSend > 1024) toSend = 1024;
20 int res = send(sock, (char*)buf + sent, toSend, MSG_NOSIGNAL);
21 if (res < 0) return res;
22 sent += res;
24 return sent;
28 int Link::Thread::Read(void* buf, size_t count) {
29 try {
30 if (this->sslEnabled) return SSL_read((SSL*)ssl, buf, count);
31 else return recv(sock, buf, count, 0);
32 } catch (std::exception& e) {
33 std::cout << e.what() << std::endl;
34 return -1;
38 Link::Thread::Thread(Server* server, int sock, bool sslEnabled) {
39 this->server = server;
40 this->sock = sock;
41 this->sslEnabled = sslEnabled;
44 Link::Thread::Thread(Server* server, SSL* ssl, bool sslEnabled) {
45 this->server = server;
46 this->ssl = ssl;
47 this->sslEnabled = sslEnabled;
50 std::vector<std::string> split(std::string data, std::string delimiter) {
51 std::vector<std::string> result;
52 size_t pos = 0;
53 std::string token;
54 while ((pos = data.find(delimiter)) != std::string::npos) {
55 token = data.substr(0, pos);
56 if (token != "") result.push_back(token);
57 data.erase(0, pos + delimiter.length());
59 if (data != "") result.push_back(data);
60 return result;
63 void Link::Thread::SetIP(std::string ip) {
64 this->ip = ip;
67 void Link::Thread::Run() {
68 struct timespec start, end;
69 clock_gettime(CLOCK_MONOTONIC, &start);
70 char buffer[1];
71 std::string request = "";
72 while (Read(buffer, 1) > 0) {
73 request += buffer[0];
74 if (request.find("\r\n\r\n") != std::string::npos) break;
77 if (request == "") {
78 if (server->IsMultiThreaded()) pthread_exit(NULL);
79 return;
82 std::string lower;
83 std::transform(request.begin(), request.end(), std::back_inserter(lower), ::tolower);
84 std::string headers = request.substr(0, request.find("\r\n\r\n"));
86 Link::Request* req = new Link::Request(headers, "");
87 req->SetProtocol(this->sslEnabled ? "https" : "http");
89 std::string body;
90 if (req->GetHeader("Content-Length") != "") {
91 for (int x=0;x<std::stoi(req->GetHeader("Content-Length"));x++) {
92 Read(buffer, 1);
93 body += buffer[0];
97 req->SetBody(body);
98 req->SetIP(this->ip);
100 Link::Response* response = new Link::Response();
101 response->SetHeader("Content-Type", "text/html");
102 if (req->GetMethod() == "" || req->GetPath() == "") {
103 response->SetStatus(400);
104 response->SetBody("400 Bad Request");
105 std::string res = response->GetVersion() + " " + std::to_string(response->GetStatus()) + " " + Link::Status(response->GetStatus()) + "\r\n";
106 res = response->GetHeadersRaw() + "\r\n" + response->GetBody();
107 Write(res.c_str(), res.length());
108 if (server->IsMultiThreaded()) pthread_exit(NULL);
109 else return;
112 std::vector<std::string> staticPages = server->GetStaticPages();
114 bool found = false;
116 for (auto middleware : server->GetMiddlewares()) {
117 middleware(req, response, server);
120 if (response->isClosed()) found = true;
122 if (!found) for (auto route : server->GetCallbacks()) {
123 if (route.first[0] == req->GetMethod()) {
124 std::string routePath = route.first[1];
125 std::string reqPath = req->GetPath();
126 if (routePath == reqPath) {
127 found = true;
128 route.second(req, response);
129 if (response->isClosed()) found = true;
130 break;
132 std::vector<std::string> routePathSplit = split(routePath, "/");
133 std::vector<std::string> reqPathSplit = split(reqPath, "/");
134 if (routePathSplit.size() == reqPathSplit.size()) {
135 bool match = true;
136 for (int x=0;x<routePathSplit.size();x++) {
137 if (routePathSplit[x] != reqPathSplit[x]) {
138 if (routePathSplit[x][0] == ':') {
139 req->SetParam(routePathSplit[x].substr(1, routePathSplit[x].length()), reqPathSplit[x]);
140 } else {
141 match = false;
142 break;
146 if (match) {
147 found = true;
148 try {
149 route.second(req, response);
150 if (response->isClosed()) found = true;
151 } catch (std::exception e) {
152 perror("Link: Error while executing route!");
154 break;
160 if (!found) {
161 for (int i=0;i<staticPages.size();i++) {
162 std::string route = staticPages[i].substr(server->GetStaticPagesDirectory().length());
163 if (route[0] != '/') route = "/" + route;
164 if (req->GetPath() == route) {
165 std::string path = staticPages[i];
166 std::ifstream file(path);
167 if (file.is_open()) {
168 found = true;
169 std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
170 response->SetBody(content);
171 response->SetStatus(200);
172 response->SetHeader("Content-Type", Link::GetMIMEType(path));
173 if (response->GetHeader("Content-Type").substr(0,4) == "font" || req->GetPath().substr(0, 5) == "/font") {
174 response->SetHeader("Access-Control-Allow-Origin", "*")->SetHeader("Cache-Control", "public, max-age=31536000")->SetHeader("Accept-Ranges", "bytes");
177 break;
182 if (!found) {
183 response->SetStatus(404);
184 response->SetBody("404 Not Found");
185 try {
186 if (server->GetErrors()[404] != NULL) server->GetErrors()[404](req, response);
187 } catch (std::exception e) {
188 perror("Link: No 404 error handler found!");
192 std::string res = response->GetVersion() + " " + std::to_string(response->GetStatus()) + " " + Link::Status(response->GetStatus()) + "\r\n";
193 res = response->GetHeadersRaw() + "\r\n" + response->GetBody();
195 if (server->IsDebugging()) {
196 clock_gettime(CLOCK_MONOTONIC, &end);
197 double time = (end.tv_sec - start.tv_sec) + (end.tv_nsec - start.tv_nsec) / 1000000000.0;
198 std::string color = "\033[0m";
199 if (response->GetStatus() >= 200 && response->GetStatus() < 300) color = "\033[32m";
200 else if (response->GetStatus() >= 300 && response->GetStatus() < 400) color = "\033[33m";
201 else if (response->GetStatus() >= 400 && response->GetStatus() < 500) color = "\033[31m";
202 else if (response->GetStatus() >= 500 && response->GetStatus() < 600) color = "\033[35m";
203 std::cout << "\033[36m[Link]" << color << " [" << req->GetMethod() << "] " << req->GetPath() << " \033[35m" << std::setprecision(2) << time << "s" << "\033[0m" << std::endl;
206 int error = 0;
207 socklen_t len = sizeof(error);
208 int retval = getsockopt(this->sock, SOL_SOCKET, SO_ERROR, &error, &len);
210 if (error != 0) {
211 this->server->Status = 5;
212 } else {
213 int ret = Write(res.c_str(), res.length());
216 if (this->sslEnabled) {
217 SSL_clear(ssl);
218 SSL_shutdown(ssl);
219 SSL_free(ssl);
221 close(sock);
223 if (server->IsMultiThreaded()) pthread_exit(NULL);
226 Link::Thread::Thread() {}