Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Use Boost.Beast to parse HTTP request #6294

Merged
merged 1 commit into from
Aug 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Unreleased
- Changes from 5.26.0
- API:
- FIXED: Use Boost.Beast to parse HTTP request. [#6294](https://github.com/Project-OSRM/osrm-backend/pull/6294)
- FIXED: Fix inefficient osrm-routed connection handling [#6113](https://github.com/Project-OSRM/osrm-backend/pull/6113)
- Misc:
- FIXED: Fix bug with reading Set values from Lua scripts. [#6285](https://github.com/Project-OSRM/osrm-backend/pull/6285)
Expand Down
3 changes: 1 addition & 2 deletions fuzz/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,7 @@ if (ENABLE_FUZZING)
"table_parameters"
"tile_parameters"
"trip_parameters"
"url_parser"
"request_parser")
"url_parser")

foreach (target ${ServerTargets})
add_fuzz_target(${target})
Expand Down
28 changes: 0 additions & 28 deletions fuzz/request_parser.cc

This file was deleted.

10 changes: 7 additions & 3 deletions include/server/connection.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,15 @@
#include "server/http/compression_type.hpp"
#include "server/http/reply.hpp"
#include "server/http/request.hpp"
#include "server/request_parser.hpp"

#include <boost/array.hpp>
#include <boost/asio.hpp>
#include <boost/beast/core.hpp>
#include <boost/beast/http.hpp>
#include <boost/config.hpp>
#include <boost/version.hpp>

#include <memory>
#include <optional>
#include <vector>

// workaround for incomplete std::shared_ptr compatibility in old boost versions
Expand Down Expand Up @@ -47,6 +48,7 @@ class Connection : public std::enable_shared_from_this<Connection>
void start();

private:
using RequestParser = boost::beast::http::request_parser<boost::beast::http::string_body>;
void handle_read(const boost::system::error_code &e, std::size_t bytes_transferred);

/// Handle completion of a write operation.
Expand All @@ -60,11 +62,13 @@ class Connection : public std::enable_shared_from_this<Connection>
std::vector<char> compress_buffers(const std::vector<char> &uncompressed_data,
const http::compression_type compression_type);

void fill_request(const RequestParser::value_type &httpMessage, http::request &request);

boost::asio::strand<boost::asio::io_context::executor_type> strand;
boost::asio::ip::tcp::socket TCP_socket;
boost::asio::deadline_timer timer;
RequestHandler &request_handler;
RequestParser request_parser;
std::optional<RequestParser> http_request_parser;
boost::array<char, 8192> incoming_data_buffer;
http::request current_request;
http::reply current_reply;
Expand Down
75 changes: 0 additions & 75 deletions include/server/request_parser.hpp

This file was deleted.

96 changes: 65 additions & 31 deletions src/server/connection.cpp
Original file line number Diff line number Diff line change
@@ -1,12 +1,10 @@
#include "server/connection.hpp"
#include "server/request_handler.hpp"
#include "server/request_parser.hpp"

#include <boost/algorithm/string/predicate.hpp>
#include <boost/bind.hpp>
#include <boost/iostreams/filter/gzip.hpp>
#include <boost/iostreams/filtering_stream.hpp>

#include <vector>

namespace osrm
Expand All @@ -16,12 +14,32 @@ namespace server

Connection::Connection(boost::asio::io_context &io_context, RequestHandler &handler)
: strand(boost::asio::make_strand(io_context)), TCP_socket(strand), timer(strand),
request_handler(handler)
request_handler(handler), http_request_parser(std::make_optional<RequestParser>())
{
}

boost::asio::ip::tcp::socket &Connection::socket() { return TCP_socket; }

namespace
{

http::compression_type select_compression(const boost::beast::http::fields &fields)
{
const auto header_value = fields[boost::beast::http::field::accept_encoding];
/* giving gzip precedence over deflate */
if (boost::icontains(header_value, "deflate"))
{
return http::deflate_rfc1951;
}
if (boost::icontains(header_value, "gzip"))
{
return http::gzip_rfc1952;
}
return http::no_compression;
}

} // namespace

/// Start the first asynchronous operation for the connection.
void Connection::start()
{
Expand Down Expand Up @@ -60,20 +78,45 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t
timer.expires_from_now(boost::posix_time::seconds(0));
}

boost::beast::error_code ec;
http_request_parser->put(boost::asio::buffer(incoming_data_buffer, bytes_transferred), ec);
// no error detected, let's parse the request
http::compression_type compression_type(http::no_compression);
RequestParser::RequestStatus result;
std::tie(result, compression_type) =
request_parser.parse(current_request,
incoming_data_buffer.data(),
incoming_data_buffer.data() + bytes_transferred);

// the request has been parsed
if (result == RequestParser::RequestStatus::valid)

if (ec)
{
if (ec == boost::beast::http::error::need_more)
{
// we don't have a result yet, so continue reading
TCP_socket.async_read_some(boost::asio::buffer(incoming_data_buffer),
boost::bind(&Connection::handle_read,
this->shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
else
{
// request is not parseable
current_reply = http::reply::stock_reply(http::reply::bad_request);

boost::asio::async_write(TCP_socket,
current_reply.to_buffers(),
boost::bind(&Connection::handle_write,
this->shared_from_this(),
boost::asio::placeholders::error));
}
}
else
{
// the request has been parsed
const auto &message = http_request_parser->get();
compression_type = select_compression(message);

fill_request(message, current_request);

boost::system::error_code ec;
current_request.endpoint = TCP_socket.remote_endpoint(ec).address();

if (ec)
{
util::Log(logDEBUG) << "Socket remote endpoint error: " << ec.message();
Expand Down Expand Up @@ -127,25 +170,6 @@ void Connection::handle_read(const boost::system::error_code &error, std::size_t
this->shared_from_this(),
boost::asio::placeholders::error));
}
else if (result == RequestParser::RequestStatus::invalid)
{ // request is not parseable
current_reply = http::reply::stock_reply(http::reply::bad_request);

boost::asio::async_write(TCP_socket,
current_reply.to_buffers(),
boost::bind(&Connection::handle_write,
this->shared_from_this(),
boost::asio::placeholders::error));
}
else
{
// we don't have a result yet, so continue reading
TCP_socket.async_read_some(boost::asio::buffer(incoming_data_buffer),
boost::bind(&Connection::handle_read,
this->shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
}

/// Handle completion of a write operation.
Expand All @@ -158,7 +182,7 @@ void Connection::handle_write(const boost::system::error_code &error)
--processed_requests;
current_request = http::request();
current_reply = http::reply();
request_parser = RequestParser();
http_request_parser.emplace();
incoming_data_buffer = boost::array<char, 8192>();
output_buffer.clear();
this->start();
Expand Down Expand Up @@ -220,5 +244,15 @@ std::vector<char> Connection::compress_buffers(const std::vector<char> &uncompre

return compressed_data;
}

void Connection::fill_request(const RequestParser::value_type &http_message,
http::request &current_request)
{
current_request.uri = http_message.target().to_string();
current_request.agent = http_message[boost::beast::http::field::user_agent].to_string();
current_request.referrer = http_message[boost::beast::http::field::referer].to_string();
current_request.connection = http_message[boost::beast::http::field::connection].to_string();
}

} // namespace server
} // namespace osrm
Loading