3 #include <sys/socket.h>
4 #include <netinet/in.h>
7 #include <openssl/ssl.h>
13 int Link::Thread::Write(const void* buf
, size_t count
) {
14 if (this->sslEnabled
) return SSL_write((SSL
*)ssl
, buf
, count
);
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
;
28 int Link::Thread::Read(void* buf
, size_t count
) {
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
;
38 Link::Thread::Thread(Server
* server
, int sock
, bool sslEnabled
) {
39 this->server
= server
;
41 this->sslEnabled
= sslEnabled
;
44 Link::Thread::Thread(Server
* server
, SSL
* ssl
, bool sslEnabled
) {
45 this->server
= server
;
47 this->sslEnabled
= sslEnabled
;
50 std::vector
<std::string
> split(std::string data
, std::string delimiter
) {
51 std::vector
<std::string
> result
;
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
);
63 void Link::Thread::SetIP(std::string ip
) {
67 void Link::Thread::Run() {
68 struct timespec start
, end
;
69 clock_gettime(CLOCK_MONOTONIC
, &start
);
71 std::string request
= "";
72 while (Read(buffer
, 1) > 0) {
74 if (request
.find("\r\n\r\n") != std::string::npos
) break;
78 if (server
->IsMultiThreaded()) pthread_exit(NULL
);
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");
90 if (req
->GetHeader("Content-Length") != "") {
91 for (int x
=0;x
<std::stoi(req
->GetHeader("Content-Length"));x
++) {
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
);
112 std::vector
<std::string
> staticPages
= server
->GetStaticPages();
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
) {
128 route
.second(req
, response
);
129 if (response
->isClosed()) found
= true;
132 std::vector
<std::string
> routePathSplit
= split(routePath
, "/");
133 std::vector
<std::string
> reqPathSplit
= split(reqPath
, "/");
134 if (routePathSplit
.size() == reqPathSplit
.size()) {
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
]);
149 route
.second(req
, response
);
150 if (response
->isClosed()) found
= true;
151 } catch (std::exception e
) {
152 perror("Link: Error while executing route!");
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()) {
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");
183 response
->SetStatus(404);
184 response
->SetBody("404 Not Found");
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
;
207 socklen_t len
= sizeof(error
);
208 int retval
= getsockopt(this->sock
, SOL_SOCKET
, SO_ERROR
, &error
, &len
);
211 this->server
->Status
= 5;
213 int ret
= Write(res
.c_str(), res
.length());
216 if (this->sslEnabled
) {
223 if (server
->IsMultiThreaded()) pthread_exit(NULL
);
226 Link::Thread::Thread() {}