docs: Update contributing to allow PR's.
[link.git] / Source / Server.cpp
blob3414a88227384b5e8d74421056bafc8b99879740
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>
14 #include <filesystem>
16 Link::Server::Server() {
17 this->port = 0;
18 this->multiThreaded = true;
19 this->sslEnabled = false;
20 this->debugging = false;
23 Link::Server* Link::Server::EnableDebugging() {
24 this->debugging = true;
25 return this;
28 bool Link::Server::IsDebugging() {
29 return this->debugging;
32 Link::Server::Server(int port) {
33 this->port = port;
34 this->multiThreaded = true;
35 this->sslEnabled = false;
36 this->debugging = false;
39 Link::Server* Link::Server::SetPort(int port) {
40 this->port = port;
41 this->multiThreaded = true;
42 this->sslEnabled = false;
43 return this;
46 Link::Server* Link::Server::EnableMultiThreading() {
47 this->multiThreaded = true;
48 return this;
51 Link::Server* Link::Server::Stop() {
52 this->running = false;
53 return this;
56 Link::Server* Link::Server::DisableMultiThreading() {
57 this->multiThreaded = false;
58 return this;
61 Link::Server* Link::Server::EnableSSL(std::string certPath, std::string keyPath) {
62 this->sslEnabled = true;
63 this->certPath = certPath;
64 this->keyPath = keyPath;
65 SSL_library_init();
66 SSLeay_add_ssl_algorithms();
67 SSL_load_error_strings();
68 SSL_CTX* ctx = SSL_CTX_new(TLS_server_method());
69 if (ctx == NULL) {
70 std::cout << "SSL_CTX_new failed" << std::endl;
71 return this;
73 SSL_CTX_set_ecdh_auto(ctx, 1);
74 if (SSL_CTX_use_certificate_file(ctx, this->certPath.c_str(), SSL_FILETYPE_PEM) <= 0) {
75 std::cout << "SSL_CTX_use_certificate_file failed" << std::endl;
76 return this;
78 if (SSL_CTX_use_PrivateKey_file(ctx, this->keyPath.c_str(), SSL_FILETYPE_PEM) <= 0) {
79 std::cout << "SSL_CTX_use_PrivateKey_file failed" << std::endl;
80 return this;
82 this->ctx = ctx;
83 return this;
86 bool Link::Server::IsRunning() {
87 return this->running;
90 bool Link::Server::IsMultiThreaded() {
91 return this->multiThreaded;
94 bool Link::Server::IsSSL() {
95 return this->sslEnabled;
98 int Link::Server::GetPort() {
99 return this->port;
102 struct HandlerArgs {
103 Link::Thread* thread;
106 void HandlerWrapper(void* raw) {
107 HandlerArgs* args = (HandlerArgs*) raw;
108 Link::Thread* thread = args->thread;
109 thread->Run();
112 Link::Server* Link::Server::Get(std::string path, std::function<void(Request*, Response*)> callback) {
113 std::vector<std::string> key = {"GET", path};
114 this->callbacks[key] = callback;
115 return this;
118 Link::Server* Link::Server::Post(std::string path, std::function<void(Request*, Response*)> callback) {
119 std::vector<std::string> key = {"POST", path};
120 this->callbacks[key] = callback;
121 return this;
124 Link::Server* Link::Server::Route(std::string method, std::string path, std::function<void(Request*, Response*)> callback) {
125 std::vector<std::string> key = {method, path};
126 this->callbacks[key] = callback;
127 return this;
130 Link::Server* Link::Server::Error(int code, std::function<void(Request*, Response*)> callback) {
131 this->errors[code] = callback;
132 return this;
135 std::map<int, std::function<void(Link::Request*, Link::Response*)>> Link::Server::GetErrors() {
136 return this->errors;
139 std::map<std::vector<std::string>, std::function<void(Link::Request*, Link::Response*)>> Link::Server::GetCallbacks() {
140 return this->callbacks;
143 Link::Server* Link::Server::SetStaticPages(std::string path) {
144 this->staticPages = path;
145 return this;
148 std::string Link::Server::GetStaticPagesDirectory() {
149 return this->staticPages;
152 std::vector<std::string> Link::Server::GetStaticPages() {
153 if (this->staticPages == "" || !std::filesystem::exists(this->staticPages)) return std::vector<std::string>();
154 std::vector<std::string> pages;
155 for (std::filesystem::recursive_directory_iterator i(staticPages), end; i != end; ++i)
156 if (!std::filesystem::is_directory(i->path())) {
157 pages.push_back(i->path().parent_path().string()+'/'+i->path().filename().string());
159 return pages;
162 Link::Server* Link::Server::SetStartMessage(std::string msg) {
163 this->startMessage = msg;
164 return this;
167 Link::Server* Link::Server::Start() {
168 running = true;
169 if (this->port == 0) port = sslEnabled ? 443 : 80;
171 sock = socket(AF_INET, SOCK_STREAM, 0);
172 int opt = 1;
173 if (setsockopt(this->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) return this;
175 if (sock < 0) {
176 perror("Socket error");
177 exit(1);
180 struct sockaddr_in server;
181 server.sin_family = AF_INET;
182 server.sin_addr.s_addr = INADDR_ANY;
183 server.sin_port = htons(this->port);
185 if (bind(sock, (struct sockaddr*) &server, sizeof(server)) < 0) {
186 perror("Bind error");
187 exit(1);
190 if (listen(sock, 128) < 0) {
191 perror("Listen error");
192 exit(1);
195 if (startMessage.length() > 0) std::cout << startMessage << std::endl;
197 while (this->running) {
198 struct sockaddr_in client;
199 socklen_t clientLen = sizeof(client);
200 int clientSock = accept(sock, (struct sockaddr*) &client, &clientLen);
201 if (clientSock < 0) {
202 perror("Accept error");
203 exit(1);
205 SSL* ssl;
207 bool ClientSSL = false;
209 if (sslEnabled) {
210 char buffer[1];
211 int bytes = recv(clientSock, buffer, sizeof(buffer), MSG_PEEK);
212 if (buffer[0] == '\x16') {
213 ssl = (SSL*) malloc(sizeof(SSL*));
214 ssl = SSL_new(ctx);
215 SSL_set_mode(ssl, SSL_MODE_AUTO_RETRY);
216 if (SSL_set_fd(ssl, clientSock) <= 0) {
217 printf("ERROR: could not set SSL file descriptor\n");
218 } else {
219 int ret = SSL_accept(ssl);
220 if (ret <= 0) {
221 // int err = SSL_get_error(ssl, ret);
222 // printf("ERROR: could not accept SSL connection: %d\n", err);
223 } else {
224 ClientSSL = true;
227 } else {
228 close(clientSock);
229 continue;
233 if (this->multiThreaded) {
234 pthread_t thread;
235 std::string ip = inet_ntoa(client.sin_addr);
236 Link::Thread t;
237 if (ClientSSL&&sslEnabled) {
238 t = Link::Thread(this, ssl, ClientSSL);
239 t.SetIP(ip);
240 } else {
241 t = Link::Thread(this, clientSock, ClientSSL);
242 t.SetIP(ip);
245 HandlerArgs* args = new HandlerArgs();
246 args->thread = &t;
247 pthread_create(&thread, NULL, (void* (*)(void*)) HandlerWrapper, (void*) args);
248 pthread_join(thread, NULL);
249 } else {
250 std::string ip = inet_ntoa(client.sin_addr);
251 if (ClientSSL) {
252 Link::Thread thread(this, ssl, ClientSSL);
253 thread.SetIP(ip);
254 thread.Run();
255 } else {
256 Link::Thread thread(this, clientSock, ClientSSL);
257 thread.SetIP(ip);
258 thread.Run();
262 if (sslEnabled&&ClientSSL) {
263 SSL_clear(ssl);
264 SSL_shutdown(ssl);
265 SSL_free(ssl);
266 ClientSSL = false;
268 close(clientSock);
271 shutdown(sock, SHUT_RDWR);
272 close(sock);
273 EVP_cleanup();
275 return this;