diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4164df0196096498ffde75427703fff90177ee16 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required(VERSION 3.10) + +project(cpphttpd) + +file(GLOB SRC_FILES "src/*.cpp") +file(GLOB HEADER_FILES "inc/*.hpp") + +include_directories(inc) + +add_library(cpphttpd STATIC ${SRC_FILES} ${HEADER_FILES}) +set_target_properties(cpphttpd PROPERTIES PREFIX "") + + +add_executable(example.run EXCLUDE_FROM_ALL example/main.cpp) +target_link_libraries(example.run cpphttpd) +target_link_libraries(example.run pthread) +add_custom_target(example example.run) diff --git a/Makefile b/Makefile index 4264a41969b7834f555af406042b88c4a2638568..f76195613065399950c55a8c22de00b375cb9156 100644 --- a/Makefile +++ b/Makefile @@ -1,6 +1,8 @@ # Name of the executable -NAME = prog.run +NAME = cpphttpd.a +# Include directory +INC_DIR = inc # Main source code directory SRC_DIR = src # Build output directory @@ -13,24 +15,27 @@ SRC = $(wildcard $(SRC_DIR)/*.cpp) OBJ = $(addprefix $(BUILD_DIR)/, $(notdir $(SRC:.cpp=.o))) -LD_FLAGS = -g -pthread -COMPILE_FLAGS = -g -c -O3 +LD_FLAGS = -g -pthread -I$(INC_DIR) +COMPILE_FLAGS = -g -c -O3 -I$(INC_DIR) # Build rule for the main target executable $(TARGET): $(OBJ) - g++ $(LD_FLAGS) -o $@ $(OBJ) + ar rcs $@ $(OBJ) + # Build rule for normal source files $(BUILD_DIR)/%.o: $(SRC_DIR)/%.cpp g++ $(COMPILE_FLAGS) -o $@ $< +example: $(TARGET) + g++ $(LD_FLAGS) -o build/example.run example/main.cpp $(TARGET) .PHONY: clean clean: rm $(OBJ) $(TARGET) .PHONY: run -run: $(TARGET) - ./$(TARGET) +run: $(TARGET) example + ./build/example.run diff --git a/src/main.cpp b/example/main.cpp similarity index 96% rename from src/main.cpp rename to example/main.cpp index 31913c41d755125e2101ec5a355f6733aaef6973..e567d8be1d3586c9fc073ba3a41cff5914213402 100644 --- a/src/main.cpp +++ b/example/main.cpp @@ -1,147 +1,147 @@ -#include <iostream> -#include <string> -#include <vector> -#include <unordered_map> -#include <cstring> -#include <sstream> -#include <chrono> - -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <unistd.h> - -#include "httpd.hpp" -#include "http_service.hpp" - - -HttpRouteHandling handle_echo_path(const HttpRequest &req, HttpResponse &res) -{ - std::string echo_text = req.regexMatches().at(1); - - std::string resp = "Echo: " + echo_text; - - res.send((uint8_t*) resp.c_str(), resp.size()); - - return HttpRouteHandling::End; -} - -// Calculate the first N prime numbers where N is extracted from the request uri -HttpRouteHandling handle_prime(const HttpRequest &req, HttpResponse &res) -{ - std::string result; - int number_of_primes_found = 0; - - int max = std::stoi(req.regexMatches().at(1)); - - int num = 2; - while(number_of_primes_found < max) - { - bool is_prime = true; - for (int i = 2; i <= num/2; i++) - { - if (num % i == 0) - { - is_prime = false; - break; - } - } - if (is_prime) - { - result += std::to_string(num) + " "; - number_of_primes_found++; - } - num++; - } - - res.send((uint8_t*) result.c_str(), result.size()); - - return HttpRouteHandling::End; -} - -int main(int argc, char **argv) -{ - // Create the webserver on port 8081. By default listening on 0.0.0.0 - HttpServer srv(8081); - - // Log Middleware example - srv.addRoute(HttpRoute( - "", - [](const HttpRequest &req, HttpResponse &res) { - - std::string log = "Request: " + req.ip() + " => " + req.uri() + "\n"; - log += " Agent: " + req.headers().getHeader(HttpHeader::UserAgent).getValue() + "\n"; - - std::cout << log; - - return HttpRouteHandling::Continue; - }, - HttpRoute::MatchType::MatchAny - )); - - // Example index route with hardcoded html response - srv.addRoute(HttpRoute( - "/(index.html)?", // match "/" or "/index.html" - [](const HttpRequest &req, HttpResponse &res) { - - char body[] = R"EOT( -<!DOCTYPE html> -<html lang="en"> -<head> - <title>Example HTML Document</title> -</head> -<body> - <h1><a href="/example.html">example.html</a></h1> - <h1><a href="/cached/example.html">cached example.html</a></h1> - <h1><a href="/prime/100">first 100 primes</a></h1> - <h1><a href="/echo/Hello">echo hello</a></h1> -</body> -</html> - )EOT"; - res.sendAll((uint8_t*)body, sizeof(body)); - - return HttpRouteHandling::End; - } - )); - - // Static file example - srv.addRoute(serveFile( - "/example.html", - "./static/example.html", - "text/html; charset=utf-8" - )); - - // Cached static file example. The file is loaded into memory at launch - srv.addRoute(serveFileCached( - "/cached/example.html", - "./static/example.html", - "text/html; charset=utf-8" - )); - - // Example for a handler function that uses regex to extract a path segment - srv.addRoute(HttpRoute( - "/prime/(\\d+)", - &handle_prime - )); - - // Example for a handler function that uses regex to extract a path segment - srv.addRoute(HttpRoute( - "/echo/([a-zA-Z0-9_\\-.]+)/?", - &handle_echo_path - )); - - try - { - - // Start serving the content - srv.serveForever(); - - } - catch(const HttpException& e) - { - std::cerr << e.what() << '\n'; - } - - return 0; - -} +#include <iostream> +#include <string> +#include <vector> +#include <unordered_map> +#include <cstring> +#include <sstream> +#include <chrono> + +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <unistd.h> + +#include "httpd.hpp" +#include "http_service.hpp" + + +HttpRouteHandling handle_echo_path(const HttpRequest &req, HttpResponse &res) +{ + std::string echo_text = req.regexMatches().at(1); + + std::string resp = "Echo: " + echo_text; + + res.send((uint8_t*) resp.c_str(), resp.size()); + + return HttpRouteHandling::End; +} + +// Calculate the first N prime numbers where N is extracted from the request uri +HttpRouteHandling handle_prime(const HttpRequest &req, HttpResponse &res) +{ + std::string result; + int number_of_primes_found = 0; + + int max = std::stoi(req.regexMatches().at(1)); + + int num = 2; + while(number_of_primes_found < max) + { + bool is_prime = true; + for (int i = 2; i <= num/2; i++) + { + if (num % i == 0) + { + is_prime = false; + break; + } + } + if (is_prime) + { + result += std::to_string(num) + " "; + number_of_primes_found++; + } + num++; + } + + res.send((uint8_t*) result.c_str(), result.size()); + + return HttpRouteHandling::End; +} + +int main(int argc, char **argv) +{ + // Create the webserver on port 8081. By default listening on 0.0.0.0 + HttpServer srv(8081); + + // Log Middleware example + srv.addRoute(HttpRoute( + "", + [](const HttpRequest &req, HttpResponse &res) { + + std::string log = "Request: " + req.ip() + " => " + req.uri() + "\n"; + log += " Agent: " + req.headers().getHeader(HttpHeader::UserAgent).getValue() + "\n"; + + std::cout << log; + + return HttpRouteHandling::Continue; + }, + HttpRoute::MatchType::MatchAny + )); + + // Example index route with hardcoded html response + srv.addRoute(HttpRoute( + "/(index.html)?", // match "/" or "/index.html" + [](const HttpRequest &req, HttpResponse &res) { + + char body[] = R"EOT( +<!DOCTYPE html> +<html lang="en"> +<head> + <title>Example HTML Document</title> +</head> +<body> + <h1><a href="/example.html">example.html</a></h1> + <h1><a href="/cached/example.html">cached example.html</a></h1> + <h1><a href="/prime/100">first 100 primes</a></h1> + <h1><a href="/echo/Hello">echo hello</a></h1> +</body> +</html> + )EOT"; + res.sendAll((uint8_t*)body, sizeof(body)); + + return HttpRouteHandling::End; + } + )); + + // Static file example + srv.addRoute(serveFile( + "/example.html", + "./static/example.html", + "text/html; charset=utf-8" + )); + + // Cached static file example. The file is loaded into memory at launch + srv.addRoute(serveFileCached( + "/cached/example.html", + "./static/example.html", + "text/html; charset=utf-8" + )); + + // Example for a handler function that uses regex to extract a path segment + srv.addRoute(HttpRoute( + "/prime/(\\d+)", + &handle_prime + )); + + // Example for a handler function that uses regex to extract a path segment + srv.addRoute(HttpRoute( + "/echo/([a-zA-Z0-9_\\-.]+)/?", + &handle_echo_path + )); + + try + { + + // Start serving the content + srv.serveForever(); + + } + catch(const HttpException& e) + { + std::cerr << e.what() << '\n'; + } + + return 0; + +} diff --git a/src/http_err.hpp b/inc/http_err.hpp similarity index 100% rename from src/http_err.hpp rename to inc/http_err.hpp diff --git a/src/http_header.hpp b/inc/http_header.hpp similarity index 100% rename from src/http_header.hpp rename to inc/http_header.hpp diff --git a/src/http_request.hpp b/inc/http_request.hpp similarity index 100% rename from src/http_request.hpp rename to inc/http_request.hpp diff --git a/src/http_response.hpp b/inc/http_response.hpp similarity index 100% rename from src/http_response.hpp rename to inc/http_response.hpp diff --git a/src/http_route.hpp b/inc/http_route.hpp similarity index 100% rename from src/http_route.hpp rename to inc/http_route.hpp diff --git a/src/http_service.hpp b/inc/http_service.hpp similarity index 100% rename from src/http_service.hpp rename to inc/http_service.hpp diff --git a/src/httpd.hpp b/inc/httpd.hpp similarity index 100% rename from src/httpd.hpp rename to inc/httpd.hpp diff --git a/src/semaphore.hpp b/inc/semaphore.hpp similarity index 100% rename from src/semaphore.hpp rename to inc/semaphore.hpp diff --git a/src/threadpool.hpp b/inc/threadpool.hpp similarity index 100% rename from src/threadpool.hpp rename to inc/threadpool.hpp