Major Update: Link Library
[link.git] / Source / Link.cpp
blob5f067bfd842e18a000e55cf95f2366a54af1d181
1 /*
2 * ______________________________________
3 * | _ _ _ _ |
4 * | | \ | |/ |/ | Date: 06/23/2022 |
5 * | | \| |- |- | Author: Levi Hicks |
6 * | | || || | |
7 * | | |\ || || | |
8 * | |_| \_||_||_| File: Link.cpp |
9 * | |
10 * | |
11 * | Please do not remove this header. |
12 * |______________________________________|
15 #include "../Includes/Link/Link.hpp"
16 #include <iostream>
17 #include <stdlib.h>
18 #include <sys/types.h>
19 #include <sys/stat.h>
20 #include <filesystem>
21 #include <sys/socket.h>
22 #include <netinet/in.h>
23 #include <netinet/tcp.h>
24 #include <arpa/inet.h>
25 #include <unistd.h>
26 #include <string>
27 #include <sstream>
28 #include <fstream>
29 #include <map>
30 #include <stdexcept>
31 #include <iomanip>
32 #include <cstring>
33 #include <chrono>
34 #include <vector>
35 #include <bits/stdc++.h>
36 #include "../Includes/Link/HTTPTools.hpp"
39 * Holds data for the request.
41 class ThreadInfo {
42 public:
43 std::function<void(Request*, Response*)> func;
44 Request* request;
45 std::map<int, std::function<void(Request*, Response*)>> errorHandlers;
49 * Creates a new thread.
50 * We have to use this because pthread_create will not hold the function and the Request.
52 * @param info The information to pass to the thread.
54 void* threadWrapper(void* arg) {
55 ThreadInfo* info = (ThreadInfo*)arg;
56 Response response(info->request, &info->errorHandlers);
57 info->func(info->request, &response);
58 if (!response.IsCancelled()) {
59 int packet = 0;
60 while (packet < response.GetHTTP().size()) {
61 int packetLength = response.GetHTTP().size()<packet+1024? response.GetHTTP().size()-packet: 1024;
62 std::string PacketData = response.GetHTTP().substr(packet, packetLength);
63 int sent = send(info->request->GetSocket(), PacketData.c_str(), PacketData.size(), 0);
64 if (sent < 0) return NULL;
65 packet+=packetLength;
67 close(info->request->GetSocket());
68 } else {
69 close(info->request->GetSocket());
71 return NULL;
75 * Creates a Link server
77 * @param port The port that the server will run on.
79 Link::Link(int port) { this->port = port; }
82 * Set handler for a get request.
84 * @param path The specific path for the handler.
85 * @param callback The function to call when a get request is made.
87 void Link::Get(std::string path, std::function<void(Request*, Response*)> callback) {
88 this->handlers[path+"GET"] = callback;
92 * Sets default request handler.
94 * @param callback The function to call when a request is made.
96 void Link::Default(std::function<void(Request*, Response*)> callback) {
97 this->defaultHandler = callback;
101 * Set handler for a post request.
103 * @param path The specific path for the handler.
104 * @param callback The function to call when a post request is made.
106 void Link::Post(std::string path, std::function<void(Request*, Response*)> callback) {
107 this->handlers[path+"POST"] = callback;
111 * Set handler for a put request.
113 * @param path The specific path for the handler.
114 * @param callback The function to call when a put request is made.
116 void Link::Put(std::string path, std::function<void(Request*, Response*)> callback) {
117 this->handlers[path+"PUT"] = callback;
121 * Set handler for a delete request.
123 * @param path The specific path for the handler.
124 * @param callback The function to call when a delete request is made.
126 void Link::Delete(std::string path, std::function<void(Request*, Response*)> callback) {
127 this->handlers[path+"DELETE"] = callback;
131 * Set handler for an error.
133 * @param code The error code.
134 * @param callback The function to call when an error is made.
136 void Link::Error(int code, std::function<void(Request*, Response*)> callback) {
137 this->errorHandlers[code] = callback;
141 * Binds and listens on specified port
143 * @return the error code.
145 int Link::Start() {
146 this->sock = socket(AF_INET, SOCK_STREAM, 0);
147 if (this->sock == 0) return 1;
148 int opt = 1;
149 if (setsockopt(this->sock, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0) return 2;
150 int time = 7200, interval = 60, retry = 3;
151 struct sockaddr_in addr;
152 addr.sin_family = AF_INET;
153 addr.sin_addr.s_addr = INADDR_ANY;
154 addr.sin_port = htons(this->port);
155 if (bind(this->sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) return 3;
156 if (listen(this->sock, 50) < 0) return 4;
157 while (true) {
158 char buffer[1024] = {0};
159 struct sockaddr_in addr;
160 socklen_t addrSize = sizeof(addr);
161 int sock = accept(this->sock, (struct sockaddr*)&addr, &addrSize);
162 recv(sock, buffer, 1024, 0);
163 std::string bufferStr = buffer, line, path, method;
164 std::map<std::string, std::string> queriesMap;
165 std::istringstream stream(decodeHTTP(bufferStr));
166 getline(stream, line);
167 if (line.substr(0, 3) == "GET" || line.substr(0, 4) == "POST" || line.substr(0, 3) == "PUT" || line.substr(0, 6) == "DELETE") {
168 method = line.substr(0, 3) == "GET" ? "GET" : line.substr(0, 4) == "POST" ? "POST" : line.substr(0, 3) == "PUT"? "PUT" : "DELETE";
169 path = line.substr(method.length()+1);
170 path = path.substr(0, path.find_last_of("HTTP/1.1")-8);
171 } else {
172 close(sock);
173 continue;
175 if (path.find("?") != std::string::npos) {
176 std::string queries = path.substr(path.find_last_of("?")+1);
177 path = path.substr(0, path.find_last_of("?"));
178 std::istringstream stream(queries);
179 std::string query;
180 while(getline(stream, query, '&')) {
181 std::string key = query.substr(0, query.find_first_of("="));
182 std::string value = query.substr(query.find_first_of("=")+1);
183 queriesMap[key] = sanitize(value);
185 std::string newPath = "";
186 for (int i=0; i<path.length(); i++) if (path.substr(i, 2) != "//") newPath += path[i];
187 path = newPath[newPath.length()-1]=='/' ? newPath.substr(0, newPath.length()-1) : newPath;
189 Request request(sock, &addr, path, method, buffer, queriesMap);
190 if (this->handlers.contains(path+method)) {
191 ThreadInfo* info = new ThreadInfo();
192 info->func = this->handlers.find(path+method)->second;
193 info->request = &request;
194 info->errorHandlers = this->errorHandlers;
195 pthread_t thread;
196 pthread_create(&thread, NULL, threadWrapper, info);
197 pthread_join(thread, NULL);
198 } else if (this->defaultHandler != nullptr) {
199 ThreadInfo* info = new ThreadInfo();
200 info->func = this->defaultHandler;
201 info->request = &request;
202 info->errorHandlers = this->errorHandlers;
203 pthread_t thread;
204 pthread_create(&thread, NULL, threadWrapper, info);
205 pthread_join(thread, NULL);
206 } else {
207 close(sock);
208 shutdown(this->sock, SHUT_RDWR);
209 return 5;
212 return 0;
216 * Shuts down the server
218 * @return the error code.
220 int Link::Stop() {
221 return shutdown(this->sock, SHUT_RDWR);
222 return 0;
226 * @return the port number
228 int Link::GetPort() { return this->port; }