๐Ÿ A very simple static Gemini server, now with Titan support!
cpp gemini titan gemini-protocol titan-protocol
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor: tighen clang-tidy

Fuwn 7a2a8f07 970099af

+72 -51
+3 -3
Tupfile
··· 8 8 # Compiler Configuration 9 9 CXX = clang++ 10 10 CXX_EXTENSION = cc 11 - CXX_FLAGS = -std=c++23 -I $(INCLUDE_DIRECTORY) -Weverything -Wno-padded -Wno-c++98-compat -MMD -fno-diagnostics-show-note-include-stack -Wno-c++98-compat-pedantic 11 + CXX_FLAGS = -std=c++23 -I $(INCLUDE_DIRECTORY) -Weverything -Wno-padded -Wno-c++98-compat -MMD -Wno-c++98-compat-pedantic 12 12 LD_FLAGS = -lssl -lcrypto 13 13 14 14 # Clang-Tidy Configuration 15 - CLANG_TIDY_CHECKS = '-*,bugprone-*,clang-analyzer-*,concurrency-*,cppcoreguildelines-*,llvm-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-readability-magic-numbers,-llvm-header-guard,-bugprone-suspicious-include,-readability-function-cognitive-complexity,-bugprone-exception-escape' 15 + CLANG_TIDY_CHECKS = '-*,bugprone-*,clang-analyzer-*,concurrency-*,cppcoreguildelines-*,llvm-*,misc-*,modernize-*,performance-*,portability-*,readability-*,-readability-function-cognitive-complexity,-bugprone-easily-swappable-parameters,-concurrency-mt-unsafe' 16 16 CLANG_TIDY_FLAGS = -checks=$(CLANG_TIDY_CHECKS) -warnings-as-errors=* -quiet 17 17 18 18 # : foreach $(SOURCE_DIRECTORY)/*.$(CXX_EXTENSION) $(INCLUDE_DIRECTORY)/*.hh |> clang-format -i -style=LLVM %f |> 19 - # : foreach $(SOURCE_DIRECTORY)/*.$(CXX_EXTENSION) |> clang-tidy $(CLANG_TIDY_FLAGS) %f -- $(CXX_FLAGS) |> 19 + : foreach $(SOURCE_DIRECTORY)/*.$(CXX_EXTENSION) |> clang-tidy $(CLANG_TIDY_FLAGS) %f -- $(CXX_FLAGS) |> 20 20 : foreach $(SOURCE_DIRECTORY)/*.$(CXX_EXTENSION) |> ^j^ $(CXX) $(CXX_FLAGS) -MF $(BUILD_DIRECTORY)/%B.d -c %f -o %o |> $(BUILD_DIRECTORY)/%B.o | $(BUILD_DIRECTORY)/%B.d 21 21 : $(BUILD_DIRECTORY)/*.o |> $(CXX) $(LD_FLAGS) %f -o %o |> $(BUILD_DIRECTORY)/$(NAME) 22 22
+7 -5
maple/gemini.cc
··· 18 18 * SPDX-License-Identifier: GPL-3.0-only 19 19 */ 20 20 21 + #include <algorithm> 21 22 #include <fstream> 22 23 #include <iostream> 24 + #include <sstream> 25 + #include <string> 26 + #include <vector> 23 27 24 28 #include "gemini.hh" 25 29 26 - namespace maple { 27 - namespace gemini { 30 + namespace maple::gemini { 28 31 auto handle_client(std::vector<std::string> gemini_files, std::string path, 29 32 std::stringstream &response) -> void { 30 33 // Check if the route is a file being served ··· 43 46 } 44 47 } 45 48 46 - std::cout << "requested " << path << std::endl; 49 + std::cout << "requested " << path << '\n'; 47 50 } 48 - } // namespace gemini 49 - } // namespace maple 51 + } // namespace maple::gemini
+48 -30
maple/maple.cc
··· 18 18 * SPDX-License-Identifier: GPL-3.0-only 19 19 */ 20 20 21 - #include <arpa/inet.h> 21 + #include <algorithm> 22 + #include <array> 23 + #include <asm-generic/socket.h> 24 + #include <cctype> 22 25 #include <csignal> 26 + #include <cstddef> 27 + #include <cstdio> 28 + #include <cstdlib> 23 29 #include <filesystem> 24 30 #include <iostream> 25 - #include <map> 31 + #include <netinet/in.h> 26 32 #include <openssl/err.h> 33 + #include <openssl/ssl.h> 34 + #include <sstream> 35 + #include <string> 27 36 #include <sys/socket.h> 28 37 #include <unistd.h> 29 38 #include <vector> ··· 36 45 std::vector<std::string> gemini_files; 37 46 bool titan = false; 38 47 std::string titan_token; 39 - size_t titan_max_size = 0; 48 + std::size_t titan_max_size = 0; 40 49 const std::string GEMINI_FILE_EXTENSION = "gmi"; 41 50 42 51 // Check if the user is want to support Titan and set it up ··· 46 55 47 56 // Try a graceful shutdown when a SIGINT is detected 48 57 signal(SIGINT, [](int _signal) -> void { 49 - std::cout << "shutdown(" << _signal << ")" << std::endl; 58 + std::cout << "shutdown(" << _signal << ")\n"; 50 59 51 60 close(maple::maple_socket); 52 61 SSL_CTX_free(maple::ssl_context); ··· 61 70 // Only keep track of file if it is a Gemini file 62 71 if (std::equal(file_extension.begin(), file_extension.end(), 63 72 GEMINI_FILE_EXTENSION.begin(), GEMINI_FILE_EXTENSION.end(), 64 - [](char a, char b) -> bool { 65 - return std::tolower(a) == std::tolower(b); 73 + [](char character_a, char character_b) -> bool { 74 + return std::tolower(character_a) == 75 + std::tolower(character_b); 66 76 })) { 67 77 gemini_files.push_back(entry.path()); 68 78 } ··· 70 80 71 81 // Inform user of which files will be served 72 82 for (const std::string &file : gemini_files) { 73 - std::cout << "serving " << file << std::endl; 83 + std::cout << "serving " << file << '\n'; 74 84 } 75 85 76 86 // Setup SSL ··· 83 93 sockaddr_in socket_address_{}; 84 94 unsigned int socket_address_length = sizeof(socket_address_); 85 95 SSL *ssl; 86 - int client = accept(maple::maple_socket, 87 - reinterpret_cast<sockaddr *>(&socket_address_), 88 - &socket_address_length); 96 + const int client = accept(maple::maple_socket, 97 + reinterpret_cast<sockaddr *>(&socket_address_), 98 + &socket_address_length); 89 99 90 100 if (client < 0) { 91 101 return maple::prepare_exit_with("unable to accept", false); ··· 99 109 ERR_print_errors_fp(stderr); 100 110 } else { 101 111 std::stringstream response; 102 - size_t index_of_junk; 112 + std::size_t index_of_junk; 103 113 int request_scheme; // Gemini = 1, Titan = 2, Error = 0 104 - size_t bytes_read; 105 - char request[1024]{}; 114 + std::size_t bytes_read; 115 + constexpr std::size_t GEMINI_MAXIMUM_REQUEST_SIZE = 1024; 116 + std::array<char, GEMINI_MAXIMUM_REQUEST_SIZE> request{}; 106 117 107 - SSL_read_ex(ssl, request, sizeof(request), &bytes_read); 118 + SSL_read_ex(ssl, request.begin(), request.size(), &bytes_read); 108 119 109 - std::string path(request); 120 + std::string path(request.data()); 110 121 111 122 if (path.starts_with("gemini://")) { 112 123 request_scheme = 1; ··· 125 136 } 126 137 127 138 if (request_scheme == 1) { 128 - path.erase(0, 9); // Remove "gemini://" 139 + constexpr std::size_t GEMINI_PROTOCOL_LENGTH = 9; 140 + 141 + path.erase(0, GEMINI_PROTOCOL_LENGTH); // Remove "gemini://" 129 142 } else { 130 - path.erase(0, 8); // Remove "titan://" 143 + constexpr std::size_t TITAN_PROTOCOL_LENGTH = 8; 144 + 145 + path.erase(0, TITAN_PROTOCOL_LENGTH); // Remove "titan://" 131 146 } 132 147 133 148 // Try to remove the host, if you cannot; it must be a trailing 134 149 // slash-less hostname, so we will respond with the index. 135 - size_t found_first = path.find_first_of('/'); 150 + const std::size_t found_first = path.find_first_of('/'); 136 151 137 152 if (found_first != std::string::npos) { 138 153 path = path.substr(found_first, ··· 166 181 SSL_write(ssl, response.str().c_str(), 167 182 static_cast<int>(response.str().size())); 168 183 } else { 169 - std::cout << "received a request with an unsupported url scheme" 170 - << std::endl; 184 + std::cout << "received a request with an unsupported url scheme\n"; 171 185 } 172 186 } 173 187 ··· 189 203 } 190 204 191 205 auto setup_environment(bool &titan, std::string &titan_token, 192 - size_t &titan_max_size) -> int { 206 + std::size_t &titan_max_size) -> int { 193 207 char *titan_environment = std::getenv("TITAN"); 194 208 195 209 if (titan_environment == nullptr) { ··· 197 211 } else { 198 212 std::string valid_titan_environment(titan_environment); 199 213 200 - std::transform(valid_titan_environment.begin(), 201 - valid_titan_environment.end(), 202 - valid_titan_environment.begin(), 203 - [](unsigned char c) -> int { return std::tolower(c); }); 214 + std::transform( 215 + valid_titan_environment.begin(), valid_titan_environment.end(), 216 + valid_titan_environment.begin(), 217 + [](unsigned char character) -> int { return std::tolower(character); }); 204 218 205 219 if (valid_titan_environment == "true" || valid_titan_environment == "1") { 206 220 char *unvalidated_titan_token = std::getenv("TITAN_TOKEN"); ··· 213 227 } 214 228 215 229 if (unvalidated_titan_max_size == nullptr) { 216 - titan_max_size = 1024; 230 + constexpr std::size_t TITAN_MAX_SIZE = 1024; 231 + 232 + titan_max_size = TITAN_MAX_SIZE; 217 233 218 - std::cout << "no TITAN_MAX_SIZE set, defaulting to 1024" << std::endl; 234 + std::cout << "no TITAN_MAX_SIZE set, defaulting to 1024\n"; 219 235 } else { 220 236 try { 221 237 titan_max_size = 222 - static_cast<size_t>(std::stoi(unvalidated_titan_max_size)); 238 + static_cast<std::size_t>(std::stoi(unvalidated_titan_max_size)); 223 239 } catch (...) { 224 240 return maple::prepare_exit_with( 225 241 "TITAN_MAX_SIZE could not be interpreted as an integer", false); ··· 242 258 243 259 maple::ssl_context = SSL_CTX_new(TLS_server_method()); 244 260 245 - if (!maple::ssl_context) { 261 + if (maple::ssl_context == nullptr) { 246 262 return maple::prepare_exit_with("unable to create ssl context", true); 247 263 } 248 264 ··· 256 272 return maple::prepare_exit_with("unable to use private key file", true); 257 273 } 258 274 275 + constexpr std::size_t GEMINI_PORT = 1965; 276 + 259 277 socket_address.sin_family = AF_INET; 260 - socket_address.sin_port = htons(1965); 278 + socket_address.sin_port = htons(GEMINI_PORT); 261 279 socket_address.sin_addr.s_addr = htonl(INADDR_ANY); 262 280 263 281 maple::maple_socket = socket(AF_INET, SOCK_STREAM, 0);
+1 -1
maple/maple.hh
··· 29 29 static SSL_CTX *ssl_context; 30 30 31 31 auto prepare_exit_with(const char *, bool) -> int; 32 - auto setup_environment(bool &, std::string &, size_t &) -> int; 32 + auto setup_environment(bool &, std::string &, std::size_t &) -> int; 33 33 auto setup_ssl() -> int; 34 34 } // namespace maple 35 35
+12 -11
maple/titan.cc
··· 18 18 * SPDX-License-Identifier: GPL-3.0-only 19 19 */ 20 20 21 + #include <cstddef> 21 22 #include <fstream> 22 23 #include <map> 24 + #include <sstream> 25 + #include <string> 23 26 #include <vector> 24 27 25 28 #include "titan.hh" 26 29 27 - namespace maple { 28 - namespace titan { 30 + namespace maple::titan { 29 31 auto parameters_to_map(const std::vector<std::string> &parameters) 30 32 -> std::map<std::string, std::string> { 31 33 std::map<std::string, std::string> parameters_map; 32 34 33 35 for (auto parameter : parameters) { 34 36 // Find the key in `parameter` 35 - size_t parameter_delimiter_position = parameter.find('='); 36 - std::string key = parameter.substr(0, parameter_delimiter_position); 37 + const std::size_t parameter_delimiter_position = parameter.find('='); 38 + const std::string key = parameter.substr(0, parameter_delimiter_position); 37 39 38 40 // Remove the key in `parameter` 39 41 parameter.erase(0, parameter_delimiter_position + 1); ··· 47 49 48 50 auto handle_client(std::stringstream &response, std::string path, 49 51 const std::string &titan_token, 50 - size_t titan_max_size) -> void { 52 + std::size_t titan_max_size) -> void { 51 53 std::vector<std::string> parameters; 52 54 // Find path in `path` 53 - size_t delimiter_position = path.find(';'); 55 + std::size_t delimiter_position = path.find(';'); 54 56 std::string update_path = path.substr(0, delimiter_position); 55 - std::string body = path.substr(path.find('\n') + 1, path.length() - 1); 57 + const std::string body = path.substr(path.find('\n') + 1, path.length() - 1); 56 58 57 59 path.erase(path.find('\n') - 1, path.length() - 1); 58 60 // parameters.push_back(update_path); ··· 121 123 } 122 124 123 125 try { 124 - size_t body_size = 125 - static_cast<size_t>(std::stoi(parameters_map.at("size"))); 126 + const std::size_t body_size = 127 + static_cast<std::size_t>(std::stoi(parameters_map.at("size"))); 126 128 127 129 if (body_size > titan_max_size) { 128 130 response << "20 text/gemini\r\nThe server (Maple) received a body " ··· 157 159 break; 158 160 } 159 161 } 160 - } // namespace titan 161 - } // namespace maple 162 + } // namespace maple::titan
+1 -1
maple/titan.hh
··· 32 32 -> std::map<std::string, std::string>; 33 33 34 34 auto handle_client(std::stringstream &, std::string, const std::string &, 35 - size_t) -> void; 35 + std::size_t) -> void; 36 36 } // namespace titan 37 37 } // namespace maple 38 38