Meta: Remove linktest seg fault issue
[link.git] / Source / Server.cpp
blob85d6041e23b572ab041199703431a1f409b917c7
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 <pthread.h>
8 #include <openssl/ssl.h>
9 #include <openssl/err.h>
10 #include <unistd.h>
11 #include <fcntl.h>
12 #include <functional>
13 #include <fstream>
15 #ifndef FS_EXPERIMENTAL
16 #include <filesystem>
17 #else
18 #include <experimental/filesystem>
19 #endif
21 Link::Server::Server() {
22 this->port = 0;
23 this->multiThreaded = false;
24 this->sslEnabled = false;
25 this->debugging = false;
26 this->Status = 0;
29 Link::Server* Link::Server::EnableDebugging() {
30 this->debugging = true;
31 return this;
34 bool Link::Server::IsDebugging() {
35 return this->debugging;
38 Link::Server::Server(int port) {
39 this->port = port;
40 this->multiThreaded = false;
41 this->sslEnabled = false;
42 this->debugging = false;
43 this->Status = 0;
46 Link::Server* Link::Server::SetPort(int port) {
47 this->port = port;
48 return this;
51 Link::Server* Link::Server::EnableMultiThreading() {
52 this->multiThreaded = true;
53 return this;
56 Link::Server* Link::Server::Stop() {
57 this->running = false;
58 return this;
61 Link::Server* Link::Server::Use(std::function<void(Link::Request*, Link::Response*, Link::Server*)> middleware) {
62 this->middlewares.push_back(middleware);
63 return this;
66 std::vector<std::function<void(Link::Request*, Link::Response*, Link::Server*)>> Link::Server::GetMiddlewares() {
67 return this->middlewares;
70 Link::Server* Link::Server::DisableMultiThreading() {
71 this->multiThreaded = false;
72 return this;
75 Link::Server* Link::Server::EnableSSL(std::string certPath, std::string keyPath) {
76 this->sslEnabled = true;
77 this->certPath = certPath;
78 this->keyPath = keyPath;
79 SSL_library_init();
80 SSLeay_add_ssl_algorithms();
81 SSL_load_error_strings();
82 SSL_CTX* ctx = SSL_CTX_new(TLS_server_method());
83 if (ctx == NULL) {
84 std::cout << "SSL_CTX_new failed" << std::endl;
85 return this;
87 SSL_CTX_set_ecdh_auto(ctx, 1);
88 if (SSL_CTX_use_certificate_file(ctx, this->certPath.c_str(), SSL_FILETYPE_PEM) <= 0) {
89 std::cout << "SSL_CTX_use_certificate_file failed" << std::endl;
90 return this;
92 if (SSL_CTX_use_PrivateKey_file(ctx, this->keyPath.c_str(), SSL_FILETYPE_PEM) <= 0) {
93 std::cout << "SSL_CTX_use_PrivateKey_file failed" << std::endl;
94 return this;
96 this->ctx = ctx;
97 return this;
100 bool Link::Server::IsRunning() {
101 return this->running;
104 bool Link::Server::IsMultiThreaded() {
105 return this->multiThreaded;
108 bool Link::Server::IsSSL() {
109 return this->sslEnabled;
112 int Link::Server::GetPort() {
113 return this->port;
116 struct HandlerArgs {
117 Link::Thread* thread;
120 void HandlerWrapper(void* raw) {
121 HandlerArgs* args = (HandlerArgs*) raw;
122 Link::Thread* thread = args->thread;
123 thread->Run();
126 Link::Server* Link::Server::Get(std::string path, std::function<void(Request*, Response*)> callback) {
127 std::vector<std::string> key = {"GET", path};
128 this->callbacks[key] = callback;
129 return this;
132 Link::Server* Link::Server::Post(std::string path, std::function<void(Request*, Response*)> callback) {
133 std::vector<std::string> key = {"POST", path};
134 this->callbacks[key] = callback;
135 return this;
138 Link::Server* Link::Server::Route(std::string method, std::string path, std::function<void(Request*, Response*)> callback) {
139 std::vector<std::string> key = {method, path};
140 this->callbacks[key] = callback;
141 return this;
144 Link::Server* Link::Server::Error(int code, std::function<void(Request*, Response*)> callback) {
145 this->errors[code] = callback;
146 return this;
149 std::map<int, std::function<void(Link::Request*, Link::Response*)>> Link::Server::GetErrors() {
150 return this->errors;
153 std::map<std::vector<std::string>, std::function<void(Link::Request*, Link::Response*)>> Link::Server::GetCallbacks() {
154 return this->callbacks;
157 Link::Server* Link::Server::SetStaticPages(std::string path) {
158 this->staticPages = path;
159 return this;
162 std::string Link::Server::GetStaticPagesDirectory() {
163 return this->staticPages;
166 std::vector<std::string> Link::Server::GetStaticPages() {
167 #ifndef FS_EXPERIMENTAL
168 if (this->staticPages == "" || !std::filesystem::exists(this->staticPages)) return std::vector<std::string>();
169 #else
170 if (this->staticPages == "" || !std::experimental::filesystem::exists(this->staticPages)) return std::vector<std::string>();
171 #endif
172 std::vector<std::string> pages;
173 #ifndef FS_EXPERIMENTAL
174 for (std::filesystem::recursive_directory_iterator i(staticPages), end; i != end; ++i)
175 if (!std::filesystem::is_directory(i->path())) {
176 #else
177 for (std::experimental::filesystem::recursive_directory_iterator i(staticPages), end; i != end; ++i)
178 if(!std::experimental::filesystem::is_directory(i->path())) {
179 #endif
180 pages.push_back(i->path().parent_path().string()+'/'+i->path().filename().string());
182 return pages;
185 Link::Server* Link::Server::SetStartMessage(std::string msg) {
186 this->startMessage = msg;
187 return this;
190 Link::Server* Link::Server::Start() {
191 running = true;
192 if (this->port == 0) port = sslEnabled ? 443 : 80;
194 sock = socket(AF_INET, SOCK_STREAM, 0);
195 int opt = 1;
196 if (setsockopt(this->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) return this;
198 if (sock < 0) {
199 Status = 1;
200 return this;
203 struct sockaddr_in server;
204 server.sin_family = AF_INET;
205 server.sin_addr.s_addr = INADDR_ANY;
206 server.sin_port = htons(this->port);
208 if (bind(sock, (struct sockaddr*) &server, sizeof(server)) < 0) {
209 Status = 2;
210 return this;
213 if (listen(sock, 128) < 0) {
214 Status = 3;
215 return this;
218 if (startMessage.length() > 0) std::cout << startMessage << std::endl;
220 while (this->running) {
221 struct sockaddr_in client;
222 socklen_t clientLen = sizeof(client);
223 int clientSock = accept(sock, (struct sockaddr*) &client, &clientLen);
224 if (clientSock < 0) {
225 Status = 4;
226 return this;
228 SSL* ssl;
230 bool ClientSSL = false;
232 if (sslEnabled) {
233 char buffer[1];
234 int bytes = recv(clientSock, buffer, sizeof(buffer), MSG_PEEK);
235 if (buffer[0] == '\x16') {
236 ssl = (SSL*) malloc(sizeof(SSL*));
237 ssl = SSL_new(ctx);
238 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
239 if (SSL_set_fd(ssl, clientSock) <= 0) {
240 printf("ERROR: could not set SSL file descriptor\n");
241 } else {
242 int ret = SSL_accept(ssl);
243 if (ret <= 0) {
244 // int err = SSL_get_error(ssl, ret);
245 // printf("ERROR: could not accept SSL connection: %d\n", err);
246 } else {
247 ClientSSL = true;
250 } else {
251 close(clientSock);
252 continue;
256 if (this->multiThreaded) {
257 pthread_t thread;
258 std::string ip = inet_ntoa(client.sin_addr);
259 Link::Thread t;
260 if (ClientSSL&&sslEnabled) {
261 t = Link::Thread(this, ssl, ClientSSL);
262 t.SetIP(ip);
263 } else {
264 t = Link::Thread(this, clientSock, ClientSSL);
265 t.SetIP(ip);
268 HandlerArgs* args = new HandlerArgs();
269 args->thread = &t;
270 pthread_create(&thread, NULL, (void* (*)(void*)) HandlerWrapper, (void*) args);
271 pthread_join(thread, NULL);
272 } else {
273 std::string ip = inet_ntoa(client.sin_addr);
274 if (ClientSSL) {
275 Link::Thread thread(this, ssl, ClientSSL);
276 thread.SetIP(ip);
277 thread.Run();
278 } else {
279 Link::Thread thread(this, clientSock, ClientSSL);
280 thread.SetIP(ip);
281 thread.Run();
286 shutdown(sock, SHUT_RDWR);
287 close(sock);
288 EVP_cleanup();
290 return this;