выкинул зависимость boost::url

This commit is contained in:
Vladislav Ostapov 2024-10-30 11:05:27 +03:00
parent 5c348ace87
commit 839198820e
6 changed files with 69 additions and 25 deletions

View File

@ -36,7 +36,7 @@ add_executable(terminal-web-server
src/server/resource.h 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) find_package(OpenSSL REQUIRED)
target_link_libraries(terminal-web-server ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES}) target_link_libraries(terminal-web-server ${Boost_LIBRARIES} ${OPENSSL_LIBRARIES})
target_include_directories(terminal-web-server PRIVATE ${Boost_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR}) target_include_directories(terminal-web-server PRIVATE ${Boost_INCLUDE_DIR} ${OPENSSL_INCLUDE_DIR})

View File

@ -13,7 +13,7 @@ namespace http::server {
} }
void Connection::start() { void Connection::start() {
do_read(); doRead();
} }
void Connection::stop() { void Connection::stop() {
@ -22,11 +22,14 @@ namespace http::server {
Connection::~Connection() = default; Connection::~Connection() = default;
void Connection::do_read() { void Connection::doRead() {
request_parser_.reset(); request_parser_.reset();
request_.headers.clear(); request_.headers.clear();
request_.method.clear(); request_.method.clear();
request_.uri.clear(); request_.queryUri.clear();
if (request_.url != nullptr) {
request_.url.reset(nullptr);
}
auto self(shared_from_this()); 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) { 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) { if (result == RequestParser::good) {
request_handler_(request_, reply_); request_handler_(request_, reply_);
do_write(); doWrite();
} else if (result == RequestParser::bad) { } else if (result == RequestParser::bad) {
stockReply(bad_request, reply_); stockReply(bad_request, reply_);
do_write(); doWrite();
} else { } else {
do_read(); doRead();
} }
} else { } else {
connection_manager_.stop(shared_from_this()); 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}); reply_.headers.push_back({.name = "Server", .value = SERVER_HEADER_VALUE});
if (!reply_.content.empty()) { if (!reply_.content.empty()) {
reply_.headers.push_back({.name = "Content-Length", .value = std::to_string(reply_.content.size())}); 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"}); 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()); auto self(shared_from_this());
async_write(socket_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) { async_write(socket_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) {
if (!ec) { if (!ec) {
// keep alive Connection // keep alive Connection
do_read(); doRead();
} else { } else {
connection_manager_.stop(shared_from_this()); connection_manager_.stop(shared_from_this());
} }
@ -97,7 +100,7 @@ namespace http::server {
request_parser_.reset(); request_parser_.reset();
request_.headers.clear(); request_.headers.clear();
request_.method.clear(); request_.method.clear();
request_.uri.clear(); request_.queryUri.clear();
auto self(shared_from_this()); 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) { 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"}); 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()); auto self(shared_from_this());
async_write(stream_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) { async_write(stream_, reply_.to_buffers(), [this, self](boost::system::error_code ec, std::size_t) {

View File

@ -47,10 +47,10 @@ namespace http::server {
~Connection() override; ~Connection() override;
private: private:
/// Perform an asynchronous read operation. /// Perform an asynchronous read operation.
void do_read(); void doRead();
/// Perform an asynchronous write operation. /// Perform an asynchronous write operation.
void do_write(); void doWrite();
/// Socket for the Connection. /// Socket for the Connection.
boost::asio::ip::tcp::socket socket_; boost::asio::ip::tcp::socket socket_;

View File

@ -3,14 +3,26 @@
#include <string> #include <string>
#include <vector> #include <vector>
#include <map>
#include "header.hpp" #include "header.hpp"
namespace http::server { namespace http::server {
class Url {
public:
explicit Url(const std::string& url);
std::string path;
std::map<std::string, std::string> params;
~Url();
};
/// A request received from a client. /// A request received from a client.
struct Request { struct Request {
std::string method; std::string method;
std::string uri; std::string queryUri;
std::unique_ptr<Url> url;
bool is_keep_alive; bool is_keep_alive;
int http_version_major; int http_version_major;
int http_version_minor; int http_version_minor;

View File

@ -1,8 +1,38 @@
#include "request_parser.hpp" #include "request_parser.hpp"
#include <sstream>
#include "request.hpp" #include "request.hpp"
namespace http::server { 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() RequestParser::RequestParser()
: state_(method_start) { : state_(method_start) {
} }
@ -38,7 +68,7 @@ namespace http::server {
} else if (is_ctl(input)) { } else if (is_ctl(input)) {
return bad; return bad;
} else { } else {
req.uri.push_back(input); req.queryUri.push_back(input);
return indeterminate; return indeterminate;
} }
case http_version_h: case http_version_h:
@ -131,7 +161,7 @@ namespace http::server {
} else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) { } else if (!is_char(input) || is_ctl(input) || is_tspecial(input)) {
return bad; return bad;
} else { } else {
req.headers.push_back(header()); req.headers.emplace_back();
req.headers.back().name.push_back(input); req.headers.back().name.push_back(input);
state_ = header_name; state_ = header_name;
return indeterminate; return indeterminate;
@ -184,7 +214,11 @@ namespace http::server {
return bad; return bad;
} }
case expecting_newline_3: case expecting_newline_3:
return (input == '\n') ? good : bad; if (input == '\n') {
req.url = std::make_unique<Url>(req.queryUri);
return good;
}
return bad;
default: default:
return bad; return bad;
} }

View File

@ -1,7 +1,6 @@
#include "server.hpp" #include "server.hpp"
#include <utility> #include <utility>
#include <boost/beast/core/basic_stream.hpp> #include <boost/beast/core/basic_stream.hpp>
#include <boost/url/url_view.hpp>
namespace http::server { namespace http::server {
@ -113,12 +112,8 @@ namespace http::server {
} }
void Server::requestHandler(const Request &req, reply &rep) { 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 "..". // 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); stockReply(bad_request, rep);
return; return;
} }
@ -128,7 +123,7 @@ namespace http::server {
rep.content.clear(); rep.content.clear();
for (auto& res: resources) { for (auto& res: resources) {
if (res->path != path) { if (res->path != req.url->path) {
continue; continue;
} }
res->handle(req, rep); res->handle(req, rep);