refactor: add benc::Handler::Context (#2531)

Prerequisite for using the new benc parser in tr_torrent_metainfo:
That code needs to know the span of the bencoded info dict.
This commit is contained in:
Charles Kerr 2022-01-28 16:46:14 -06:00 committed by GitHub
parent 7f60738cce
commit 9e264250d1
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 215 additions and 55 deletions

View File

@ -187,18 +187,18 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
{
}
bool StartDict() override
bool StartDict(Context const& context) override
{
BasicHandler::StartDict();
BasicHandler::StartDict(context);
pex_ = {};
return true;
}
bool EndDict() override
bool EndDict(Context const& context) override
{
BasicHandler::EndDict();
BasicHandler::EndDict(context);
if (tr_address_is_valid_for_peers(&pex_.addr, pex_.port))
{
@ -209,7 +209,7 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
return true;
}
bool Int64(int64_t value) override
bool Int64(int64_t value, Context const& context) override
{
auto const key = currentKey();
@ -237,11 +237,16 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
{
pex_.port = htons(uint16_t(value));
}
else if (!tr_error_is_set(context.error))
{
auto const msg = tr_strvJoin("unexpected int: key["sv, key, "] value["sv, std::to_string(value), "]"sv);
tr_error_set(context.error, EINVAL, msg);
}
return true;
}
bool String(std::string_view value) override
bool String(std::string_view value, Context const& context) override
{
auto const key = currentKey();
@ -269,6 +274,14 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
{
tr_address_from_string(&pex_.addr, value);
}
else if (key == "peer id")
{
// unused
}
else if (!tr_error_is_set(context.error))
{
tr_error_set(context.error, EINVAL, tr_strvJoin("unexpected str: key["sv, key, "] value["sv, value, "]"sv));
}
return true;
}
@ -276,7 +289,13 @@ void tr_announcerParseHttpAnnounceResponse(tr_announce_response& response, std::
auto stack = transmission::benc::ParserStack<MaxBencDepth>{};
auto handler = AnnounceHandler{ response };
transmission::benc::parse(benc, stack, handler);
tr_error* error = nullptr;
transmission::benc::parse(benc, stack, handler, nullptr, &error);
if (error != nullptr)
{
tr_logAddError("%s", error->message);
tr_error_clear(&error);
}
}
static void on_announce_done(
@ -376,9 +395,9 @@ void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::stri
{
}
bool Key(std::string_view value) override
bool Key(std::string_view value, Context const& context) override
{
BasicHandler::Key(value);
BasicHandler::Key(value, context);
auto needle = tr_sha1_digest_t{};
if (depth() == 2 && key(1) == "files"sv && std::size(value) == std::size(needle))
@ -402,30 +421,43 @@ void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::stri
return true;
}
bool Int64(int64_t value) override
bool Int64(int64_t value, Context const& context) override
{
if (row_ && currentKey() == "complete"sv)
auto const key = currentKey();
if (row_ && key == "complete"sv)
{
response_.rows[*row_].seeders = value;
}
else if (row_ && currentKey() == "downloaded"sv)
else if (row_ && key == "downloaded"sv)
{
response_.rows[*row_].downloads = value;
}
else if (row_ && currentKey() == "incomplete"sv)
else if (row_ && key == "incomplete"sv)
{
response_.rows[*row_].leechers = value;
}
else if (!tr_error_is_set(context.error))
{
auto const errmsg = tr_strvJoin("unexpected int: key["sv, key, "] value["sv, std::to_string(value), "]"sv);
tr_error_set(context.error, EINVAL, errmsg);
}
return true;
}
bool String(std::string_view value) override
bool String(std::string_view value, Context const& context) override
{
if (depth() == 1 && currentKey() == "failure reason"sv)
auto const key = currentKey();
if (depth() == 1 && key == "failure reason"sv)
{
response_.errmsg = value;
}
else if (!tr_error_is_set(context.error))
{
tr_error_set(context.error, EINVAL, tr_strvJoin("unexpected string: key["sv, key, "] value["sv, value, "]"sv));
}
return true;
}
@ -437,7 +469,7 @@ void tr_announcerParseHttpScrapeResponse(tr_scrape_response& response, std::stri
transmission::benc::parse(benc, stack, handler, nullptr, &error);
if (error != nullptr)
{
std::cerr << error->message << std::endl;
tr_logAddError("%s", error->message);
tr_error_clear(&error);
}
}

View File

@ -31,57 +31,86 @@ std::optional<std::string_view> ParseString(std::string_view* benc);
struct Handler
{
class Context
{
public:
Context(char const* stream_begin_in, tr_error** error_in)
: error{ error_in }
, stream_begin_{ stream_begin_in }
{
}
[[nodiscard]] std::pair<long, long> tokenSpan() const
{
return std::make_pair(token_begin_ - stream_begin_, token_end_ - stream_begin_);
}
[[nodiscard]] auto raw() const
{
return std::string_view{ token_begin_, size_t(token_end_ - token_begin_) };
}
void setTokenSpan(char const* a, size_t len)
{
token_begin_ = a;
token_end_ = token_begin_ + len;
}
tr_error** error = nullptr;
private:
char const* token_begin_ = nullptr;
char const* token_end_ = nullptr;
char const* const stream_begin_;
};
virtual ~Handler() = default;
virtual bool Int64(int64_t) = 0;
virtual bool String(std::string_view) = 0;
virtual bool Int64(int64_t, Context const& context) = 0;
virtual bool String(std::string_view, Context const& context) = 0;
virtual bool StartDict() = 0;
virtual bool Key(std::string_view) = 0;
virtual bool EndDict() = 0;
virtual bool StartDict(Context const& context) = 0;
virtual bool Key(std::string_view, Context const& context) = 0;
virtual bool EndDict(Context const& context) = 0;
virtual bool StartArray() = 0;
virtual bool EndArray() = 0;
virtual bool StartArray(Context const& context) = 0;
virtual bool EndArray(Context const& context) = 0;
};
template<std::size_t MaxDepth>
struct BasicHandler : public Handler
{
bool Int64(int64_t) override
bool Int64(int64_t /*value*/, Context const& /*context*/) override
{
return true;
}
bool String(std::string_view) override
bool String(std::string_view /*value*/, Context const& /*context*/) override
{
return true;
}
bool StartDict() override
bool StartDict(Context const& /*context*/) override
{
push();
return true;
}
bool Key(std::string_view key) override
bool Key(std::string_view key, Context const& /*context*/) override
{
keys_[depth_] = key;
return true;
}
bool EndDict() override
bool EndDict(Context const& /*context*/) override
{
pop();
return true;
}
bool StartArray() override
bool StartArray(Context const& /*context*/) override
{
push();
return true;
}
bool EndArray() override
bool EndArray(Context const& /*context*/) override
{
pop();
return true;
@ -210,6 +239,7 @@ bool parse(
tr_error** error = nullptr)
{
stack.clear();
auto context = Handler::Context(std::data(benc), error);
int err = 0;
for (;;)
@ -224,6 +254,7 @@ bool parse(
break;
}
auto const* const front = std::data(benc);
switch (benc.front())
{
case 'i': // int
@ -231,14 +262,18 @@ bool parse(
{
tr_error_set(error, err, "Malformed benc? Unable to parse integer");
}
else if (!handler.Int64(*value))
{
err = ECANCELED;
tr_error_set(error, err, "Handler indicated parser should stop");
}
else
{
stack.tokenWalked();
context.setTokenSpan(front, std::data(benc) - front);
if (!handler.Int64(*value, context))
{
err = ECANCELED;
}
else
{
stack.tokenWalked();
}
}
break;
@ -253,11 +288,11 @@ bool parse(
break;
}
ok = benc.front() == 'l' ? handler.StartArray() : handler.StartDict();
context.setTokenSpan(front, 1);
ok = benc.front() == 'l' ? handler.StartArray(context) : handler.StartDict(context);
if (!ok)
{
err = ECANCELED;
tr_error_set(error, err, "Handler indicated parser should stop");
break;
}
@ -274,13 +309,13 @@ bool parse(
else
{
stack.tokenWalked();
context.setTokenSpan(front, 1);
if (auto const ok = *parent_type == ParserStack<MaxDepth>::ParentType::Array ? handler.EndArray() :
handler.EndDict();
if (auto const ok = *parent_type == ParserStack<MaxDepth>::ParentType::Array ? handler.EndArray(context) :
handler.EndDict(context);
!ok)
{
err = ECANCELED;
tr_error_set(error, err, "Handler indicated parser should stop");
}
}
break;
@ -300,14 +335,17 @@ bool parse(
err = EILSEQ;
tr_error_set(error, err, "Malformed benc? Unable to parse string");
}
else if (bool const ok = stack.expectingDictKey() ? handler.Key(*sv) : handler.String(*sv); !ok)
{
err = ECANCELED;
tr_error_set(error, err, "Handler indicated parser should stop");
}
else
{
stack.tokenWalked();
context.setTokenSpan(front, std::data(benc) - front);
if (bool const ok = stack.expectingDictKey() ? handler.Key(*sv, context) : handler.String(*sv, context); !ok)
{
err = ECANCELED;
}
else
{
stack.tokenWalked();
}
}
break;

View File

@ -12,6 +12,11 @@
#include "tr-macros.h"
#include "utils.h"
bool tr_error_is_set(tr_error const* const* error)
{
return (error != nullptr) && (*error != nullptr);
}
void tr_error_free(tr_error* error)
{
if (error == nullptr)

View File

@ -23,6 +23,8 @@ struct tr_error
char* message;
};
bool tr_error_is_set(tr_error const* const* error);
/**
* @brief Free memory used by error object.
*

View File

@ -140,7 +140,7 @@ struct MyHandler : public transmission::benc::Handler
~MyHandler() override = default;
bool Int64(int64_t value) final
bool Int64(int64_t value, Context const& /*context*/) final
{
if (tr_variant* variant = get_node(); variant != nullptr)
{
@ -150,7 +150,7 @@ struct MyHandler : public transmission::benc::Handler
return true;
}
bool String(std::string_view sv) final
bool String(std::string_view sv, Context const& /*context*/) final
{
if (tr_variant* variant = get_node(); variant != nullptr)
{
@ -167,7 +167,7 @@ struct MyHandler : public transmission::benc::Handler
return true;
}
bool StartDict() final
bool StartDict(Context const& /*context*/) final
{
if (tr_variant* variant = get_node(); variant != nullptr)
{
@ -178,21 +178,21 @@ struct MyHandler : public transmission::benc::Handler
return true;
}
bool Key(std::string_view sv) final
bool Key(std::string_view sv, Context const& /*context*/) final
{
key_ = tr_quark_new(sv);
return true;
}
bool EndDict() final
bool EndDict(Context const& /*context*/) final
{
stack_.pop_back();
return true;
}
bool StartArray() final
bool StartArray(Context const& /*context*/) final
{
if (tr_variant* variant = get_node(); variant != nullptr)
{
@ -203,7 +203,7 @@ struct MyHandler : public transmission::benc::Handler
return true;
}
bool EndArray() final
bool EndArray(Context const& /*context*/) final
{
stack_.pop_back();

View File

@ -1,6 +1,7 @@
add_executable(libtransmission-test
announce-list-test.cc
announcer-test.cc
benc-test.cc
bitfield-test.cc
block-info-test.cc
blocklist-test.cc

View File

@ -199,7 +199,7 @@ TEST_F(AnnouncerTest, parseHttpScrapeResponseMulti)
EXPECT_EQ(9, response.rows[2].downloads);
}
TEST_F(AnnouncerTest, parseHttpScrapeResponseMultiWithExcess)
TEST_F(AnnouncerTest, parseHttpScrapeResponseMultiWithUnexpected)
{
// clang-format off
auto constexpr ResponseBenc =

View File

@ -0,0 +1,82 @@
// This file Copyright (C) 2022 Mnemosyne LLC.
// It may be used under GPLv2 (SPDX: GPL-2.0), GPLv3 (SPDX: GPL-3.0),
// or any future license endorsed by Mnemosyne LLC.
// License text can be found in the licenses/ folder.
#include "transmission.h"
#include "benc.h"
#include "utils.h"
#include "gtest/gtest.h"
using BencTest = ::testing::Test;
using namespace std::literals;
TEST_F(BencTest, ContextTokenIsCorrect)
{
// clang-format off
auto constexpr Benc =
"d"
"8:complete" "i3e"
"10:downloaded" "i2e"
"10:incomplete" "i0e"
"8:interval" "i1803e"
"12:min interval" "i1800e"
"5:peers" "0:"
"e"sv;
// clang-format on
auto constexpr MaxBencDepth = 32;
struct ContextHandler final : public transmission::benc::BasicHandler<MaxBencDepth>
{
using BasicHandler = transmission::benc::BasicHandler<MaxBencDepth>;
bool StartArray(Context const& context) override
{
BasicHandler::StartArray(context);
EXPECT_EQ("l"sv, context.raw());
return true;
}
bool EndArray(Context const& context) override
{
BasicHandler::EndArray(context);
EXPECT_EQ("e"sv, context.raw());
return true;
}
bool StartDict(Context const& context) override
{
BasicHandler::StartDict(context);
EXPECT_EQ("d"sv, context.raw());
return true;
}
bool EndDict(Context const& context) override
{
BasicHandler::EndDict(context);
EXPECT_EQ("e"sv, context.raw());
return true;
}
bool Int64(int64_t value, Context const& context) override
{
auto const expected = tr_strvJoin("i"sv, std::to_string(value), "e"sv);
EXPECT_EQ(expected, context.raw());
return true;
}
bool String(std::string_view value, Context const& context) override
{
auto const key = tr_strvJoin(std::to_string(std::size(value)), ":"sv, value);
EXPECT_EQ(key, context.raw());
return true;
}
};
auto stack = transmission::benc::ParserStack<MaxBencDepth>{};
auto handler = ContextHandler{};
tr_error* error = nullptr;
transmission::benc::parse(Benc, stack, handler, nullptr, &error);
}