diff --git a/CMakeLists.txt b/CMakeLists.txt index fa0b79d..b9379b9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ set(CMAKE_CXX_EXTENSIONS OFF) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -static -g -O2") -include_directories(${PROJECT_SOURCE_DIR}/include) +add_subdirectory(${PROJECT_SOURCE_DIR}/include/ouc_server) -add_executable(test "${PROJECT_SOURCE_DIR}/example/epoll_tcp_loop.cpp") \ No newline at end of file +add_executable(test "${PROJECT_SOURCE_DIR}/example/test_http_request.cpp") +target_link_libraries(test PRIVATE ouc_server_lib) \ No newline at end of file diff --git a/example/test_http_request.cpp b/example/test_http_request.cpp new file mode 100644 index 0000000..2f44d41 --- /dev/null +++ b/example/test_http_request.cpp @@ -0,0 +1,20 @@ +#include + +#include +#include + +int main() +{ + std::string body = "Hello HTTP!"; + + auto builder = ouc_server::http::HttpResponse::create(); + auto res = + builder.body(body) + .header("Content-Length", std::to_string(body.size())) + .header("Content-Type", "text/plain") + .build(); + + printf("%s\n", res.to_string().c_str()); + + return 0; +} \ No newline at end of file diff --git a/include/ouc_server/CMakeLists.txt b/include/ouc_server/CMakeLists.txt new file mode 100644 index 0000000..a849cdc --- /dev/null +++ b/include/ouc_server/CMakeLists.txt @@ -0,0 +1,11 @@ +cmake_minimum_required(VERSION 3.15) + +set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +file(GLOB_RECURSE HEADER_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*/*.cpp) +file(GLOB_RECURSE SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*/*.cpp) + +add_library(ouc_server_lib ${SOURCE_FILE} ${HEADER_FILE}) + +target_include_directories(ouc_server_lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) \ No newline at end of file diff --git a/include/ouc_server/http/http_method_type.cpp b/include/ouc_server/http/http_method_type.cpp new file mode 100644 index 0000000..094a687 --- /dev/null +++ b/include/ouc_server/http/http_method_type.cpp @@ -0,0 +1,53 @@ +#include + +namespace ouc_server +{ + namespace http + { + HttpMethodType str2type(const std::string &str) + { + if (str == "GET") + return HttpMethodType::Get; + else if (str == "HEAD") + return HttpMethodType::Head; + else if (str == "POST") + return HttpMethodType::Post; + else if (str == "PUT") + return HttpMethodType::Put; + else if (str == "DELETE") + return HttpMethodType::Delete; + else if (str == "CONNECT") + return HttpMethodType::Connect; + else if (str == "OPTIONS") + return HttpMethodType::Options; + else if (str == "TRACE") + return HttpMethodType::Trace; + else + return HttpMethodType::Get; + } + + std::string type2str(HttpMethodType type) + { + switch (type) + { + case HttpMethodType::Get: + default: + return "GET"; + case HttpMethodType::Head: + return "HEAD"; + case HttpMethodType::Post: + return "POST"; + case HttpMethodType::Put: + return "PUT"; + case HttpMethodType::Delete: + return "DELETE"; + case HttpMethodType::Connect: + return "CONNECT"; + case HttpMethodType::Options: + return "OPTIONS"; + case HttpMethodType::Trace: + return "TRACE"; + } + } + } +} \ No newline at end of file diff --git a/include/ouc_server/http/http_method_type.hpp b/include/ouc_server/http/http_method_type.hpp new file mode 100644 index 0000000..5af9057 --- /dev/null +++ b/include/ouc_server/http/http_method_type.hpp @@ -0,0 +1,28 @@ +#ifndef INCLUDE_OUC_SERVER_HTTP_METHOD_TYPE +#define INCLUDE_OUC_SERVER_HTTP_METHOD_TYPE + +#include + +namespace ouc_server +{ + namespace http + { + enum class HttpMethodType + { + Get, + Head, + Post, + Put, + Delete, + Connect, + Options, + Trace + }; + + HttpMethodType str2type(const std::string &); + + std::string type2str(HttpMethodType); + } +} + +#endif // INCLUDE_OUC_SERVER_HTTP_METHOD_TYPE \ No newline at end of file diff --git a/include/ouc_server/http/http_request.cpp b/include/ouc_server/http/http_request.cpp new file mode 100644 index 0000000..afc441d --- /dev/null +++ b/include/ouc_server/http/http_request.cpp @@ -0,0 +1,91 @@ +#include + +namespace ouc_server +{ + namespace http + { + HttpRequest HttpRequest::from_string(const std::string &raw_str) + { + std::istringstream iss(raw_str); + HttpRequest req; + + parse_top_line(req, iss); + switch (req.method) + { + case HttpMethodType::Get: + default: + parse_get_request(req, iss); + break; + + case HttpMethodType::Head: + break; + case HttpMethodType::Post: + break; + case HttpMethodType::Put: + break; + case HttpMethodType::Delete: + break; + case HttpMethodType::Connect: + break; + case HttpMethodType::Options: + break; + case HttpMethodType::Trace: + break; + } + + return req; + } + + void HttpRequest::parse_top_line(HttpRequest &req, std::istringstream &iss) + { + std::string line, type_str; + std::getline(iss, line); + + while ((!line.empty()) && line.back() != '\r') + line.pop_back(); + + std::istringstream req_iss(line); + + req_iss >> type_str >> req.path >> req.version; + req.method = str2type(type_str); + } + + void HttpRequest::parse_get_request(HttpRequest &req, std::istringstream &iss) + { + std::string line; + + // parse top line + std::getline(iss, line); + while ((!line.empty()) && line.back() != '\r') + line.pop_back(); + std::istringstream req_iss(line); + std::string type_str; + req_iss >> type_str >> req.path >> req.version; + req.method = str2type(type_str); + + // parse headers + while (std::getline(iss, line) && line != "\r") + { + while ((!line.empty()) && line.back() == '\r') + line.pop_back(); + + auto pos = line.find(": "); + if (pos == std::string::npos) + pos = line.find(":"); + + if (pos != std::string::npos) + { + std::string key = line.substr(0, pos); + std::string val = line.substr(pos + 2); + req.headers[key] = val; + } + } + + // parse body (not finish) + std::string body; + while (std::getline(iss, line)) + body += line + "\n"; + req.body = body; + } + } +} \ No newline at end of file diff --git a/include/ouc_server/http/http_request.hpp b/include/ouc_server/http/http_request.hpp index f62ade4..a691888 100644 --- a/include/ouc_server/http/http_request.hpp +++ b/include/ouc_server/http/http_request.hpp @@ -2,28 +2,33 @@ #define INCLUDE_OUC_SERVER_HTTP_REQUEST #include +#include +#include + +#include namespace ouc_server { namespace http { - enum class HttpMethodType - { - Get, - Head, - Post, - Put, - Delete, - Connect, - Options, - Trace - }; struct HttpRequest { - HttpMethodType method; + HttpMethodType method = HttpMethodType::Get; + std::string path; + std::string version; + + std::unordered_map headers; + std::string body; + + static HttpRequest from_string(const std::string &); + + private: + static void parse_top_line(HttpRequest &, std::istringstream &); + + static void parse_get_request(HttpRequest &, std::istringstream &); }; } } -#endif \ No newline at end of file +#endif // INCLUDE_OUC_SERVER_HTTP_REQUEST \ No newline at end of file diff --git a/include/ouc_server/http/http_response.cpp b/include/ouc_server/http/http_response.cpp new file mode 100644 index 0000000..91a431c --- /dev/null +++ b/include/ouc_server/http/http_response.cpp @@ -0,0 +1,27 @@ +#include + +#include + +namespace ouc_server +{ + namespace http + { + std::string HttpResponse::to_string() const + { + std::ostringstream oss; + oss << version << " " + << stus_code << " " + << stus_msg << "\r\n"; + + for (auto &[k, v] : headers) + oss << k << ": " << v << "\r\n"; + + oss << "\r\n" + << body; + + return oss.str(); + } + + HttpResponseBuilder HttpResponse::create() { return {}; } + } +} diff --git a/include/ouc_server/http/http_response.hpp b/include/ouc_server/http/http_response.hpp new file mode 100644 index 0000000..1c8747e --- /dev/null +++ b/include/ouc_server/http/http_response.hpp @@ -0,0 +1,79 @@ +#ifndef INCLUDE_OUC_SERVER_HTTP_RESPONSE +#define INCLUDE_OUC_SERVER_HTTP_RESPONSE + +#include +#include + +#include + +namespace ouc_server +{ + namespace http + { + class HttpResponseBuilder; + + struct HttpResponse + { + std::string version = "HTTP/1.1"; + int stus_code = 200; + std::string stus_msg = "OK"; + + std::unordered_map headers; + std::string body; + + std::string to_string() const; + + static HttpResponseBuilder create(); + }; + + class HttpResponseBuilder + { + private: + HttpResponse res; + + public: + HttpResponseBuilder() = default; + ~HttpResponseBuilder() = default; + + HttpResponse build() { return res; } + + public: + HttpResponseBuilder &version(const std::string &p_version) + { + res.version = p_version; + return *this; + } + + HttpResponseBuilder &stus_code(int p_stus_code) + { + res.stus_code = p_stus_code; + return *this; + } + + HttpResponseBuilder &stus_msg(const std::string &p_stus_msg) + { + res.stus_msg = p_stus_msg; + return *this; + } + + HttpResponseBuilder &header(const std::pair p_header) + { + res.headers.insert(p_header); + return *this; + } + + HttpResponseBuilder &header(const std::string &p_key, const std::string &p_value) + { + return header({p_key, p_value}); + } + + HttpResponseBuilder &body(const std::string &p_body) + { + res.body = p_body; + return *this; + } + }; + } +} + +#endif // INCLUDE_OUC_SERVER_HTTP_RESPONSE \ No newline at end of file