diff --git a/CMakeLists.txt b/CMakeLists.txt index c210e6c..70c95c8 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -36,7 +36,7 @@ add_executable(terminal-web-server src/server/resource.h ) -find_package(Boost 1.53.0 COMPONENTS system thread filesystem url log log_setup REQUIRED) +find_package(Boost 1.53.0 COMPONENTS system thread filesystem log log_setup REQUIRED) find_package(OpenSSL REQUIRED) target_link_libraries(terminal-web-server ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES}) target_include_directories(terminal-web-server PRIVATE ${Boost_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}) diff --git a/src/server/connection.cpp b/src/server/connection.cpp index 328d0cd..4178014 100644 --- a/src/server/connection.cpp +++ b/src/server/connection.cpp @@ -13,7 +13,7 @@ namespace http::server { } void Connection::start() { - do_read(); + doRead(); } void Connection::stop() { @@ -22,11 +22,14 @@ namespace http::server { Connection::~Connection() = default; - void Connection::do_read() { + void Connection::doRead() { request_parser_.reset(); request_.headers.clear(); request_.method.clear(); - request_.uri.clear(); + request_.queryUri.clear(); + if (request_.url != nullptr) { + request_.url.reset(nullptr); + } auto self(shared_from_this()); socket_.async_read_some(boost::asio::buffer(buffer_), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) { @@ -37,12 +40,12 @@ namespace http::server { if (result == RequestParser::good) { request_handler_(request_, reply_); - do_write(); + doWrite(); } else if (result == RequestParser::bad) { stockReply(bad_request, reply_); - do_write(); + doWrite(); } else { - do_read(); + doRead(); } } else { connection_manager_.stop(shared_from_this()); @@ -50,7 +53,7 @@ namespace http::server { }); } - void Connection::do_write() { + void Connection::doWrite() { reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE}); if (!reply_.content.empty()) { reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())}); @@ -59,13 +62,13 @@ namespace http::server { reply_.headers.push_back({.name = "Connection", .value = "keep-alive"}); } - BOOST_LOG_TRIVIAL(info) << "HTTP query " << reply_.status << " " << request_.method << " " << request_.uri; + BOOST_LOG_TRIVIAL(info) << "HTTP query " << reply_.status << " " << request_.method << " " << request_.queryUri; auto self(shared_from_this()); async_write(socket_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) { if (!ec) { // keep alive Connection - do_read(); + doRead(); } else { connection_manager_.stop(shared_from_this()); } @@ -97,7 +100,7 @@ namespace http::server { request_parser_.reset(); request_.headers.clear(); request_.method.clear(); - request_.uri.clear(); + request_.queryUri.clear(); auto self(shared_from_this()); stream_.async_read_some(boost::asio::buffer(buffer_), [this, self](boost::system::error_code ec, std::size_t bytes_transferred) { @@ -130,7 +133,7 @@ namespace http::server { reply_.headers.push_back({.name = "Connection", .value = "keep-alive"}); } - BOOST_LOG_TRIVIAL(info) << "HTTP query " << reply_.status << " " << request_.method << " " << request_.uri; + BOOST_LOG_TRIVIAL(info) << "HTTPS query " << reply_.status << " " << request_.method << " " << request_.queryUri; auto self(shared_from_this()); async_write(stream_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) { diff --git a/src/server/connection.hpp b/src/server/connection.hpp index 4dd786a..eecda3f 100644 --- a/src/server/connection.hpp +++ b/src/server/connection.hpp @@ -47,10 +47,10 @@ namespace http::server { ~Connection() override; private: /// Perform an asynchronous read operation. - void do_read(); + void doRead(); /// Perform an asynchronous write operation. - void do_write(); + void doWrite(); /// Socket for the Connection. boost::asio::ip::tcp::socket socket_; diff --git a/src/server/request.hpp b/src/server/request.hpp index 0236c72..425adf3 100644 --- a/src/server/request.hpp +++ b/src/server/request.hpp @@ -3,14 +3,26 @@ #include #include +#include #include "header.hpp" namespace http::server { + class Url { + public: + explicit Url(const std::string& url); + + std::string path; + std::map params; + + ~Url(); + }; + /// A request received from a client. struct Request { std::string method; - std::string uri; + std::string queryUri; + std::unique_ptr url; bool is_keep_alive; int http_version_major; int http_version_minor; diff --git a/src/server/request_parser.cpp b/src/server/request_parser.cpp index dcc6cbf..fa90f85 100644 --- a/src/server/request_parser.cpp +++ b/src/server/request_parser.cpp @@ -1,8 +1,38 @@ #include "request_parser.hpp" + +#include + #include "request.hpp" namespace http::server { + static void parseParams(Url& u, const std::string& query) { + std::istringstream iss(query); + std::string param; + while (std::getline(iss, param, '&')) { + size_t equal_pos = param.find('='); + if (equal_pos != std::string::npos) { + const std::string key = param.substr(0, equal_pos); + const std::string value = param.substr(equal_pos + 1); + u.params[key] = value; + } + } + } + + Url::Url(const std::string &url) { + size_t question_mark_pos = url.find('?'); + if (question_mark_pos != std::string::npos) { + path = url.substr(0, question_mark_pos); + const std::string query = url.substr(question_mark_pos + 1); + parseParams(*this, query); + } else { + path = url; + } + } + + Url::~Url() = default; + + RequestParser::RequestParser() : state_(method_start) { } @@ -38,7 +68,7 @@ namespace http::server { } else if (is_ctl(input)) { return bad; } else { - req.uri.push_back(input); + req.queryUri.push_back(input); return indeterminate; } case http_version_h: @@ -131,7 +161,7 @@ namespace http::server { } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { return bad; } else { - req.headers.push_back(header()); + req.headers.emplace_back(); req.headers.back().name.push_back(input); state_ = header_name; return indeterminate; @@ -184,7 +214,11 @@ namespace http::server { return bad; } case expecting_newline_3: - return (input == '\n') ? good : bad; + if (input == '\n') { + req.url = std::make_unique(req.queryUri); + return good; + } + return bad; default: return bad; } diff --git a/src/server/server.cpp b/src/server/server.cpp index 2a65fbe..4148466 100644 --- a/src/server/server.cpp +++ b/src/server/server.cpp @@ -1,7 +1,6 @@ #include "server.hpp" #include #include -#include namespace http::server { @@ -113,12 +112,8 @@ namespace http::server { } void Server::requestHandler(const Request &req, reply &rep) { - boost::urls::url_view url(req.uri); - - const auto path = url.path(); - // Request path must be absolute and not contain "..". - if (path.empty() || path[0] != '/' || path.find("..") != std::string::npos) { + if (req.url->path.empty() || req.url->path[0] != '/' || req.url->path.find("..") != std::string::npos) { stockReply(bad_request, rep); return; } @@ -128,7 +123,7 @@ namespace http::server { rep.content.clear(); for (auto& res: resources) { - if (res->path != path) { + if (res->path != req.url->path) { continue; } res->handle(req, rep);