mirror of
https://github.com/transmission/transmission.git
synced 2026-02-14 23:19:34 +00:00
* test: `tr_urlPercentEncode()` * refactor: percent-encode URLs provided in announce list or webseeds * test: announce list and webseed
293 lines
10 KiB
C++
293 lines
10 KiB
C++
// This file Copyright (C) 2013-2022 Mnemosyne LLC.
|
|
// It may be used under GPLv2 (SPDX: GPL-2.0-only), GPLv3 (SPDX: GPL-3.0-only),
|
|
// or any future license endorsed by Mnemosyne LLC.
|
|
// License text can be found in the licenses/ folder.
|
|
|
|
#include <array>
|
|
#include <cstddef> // size_t
|
|
#include <string_view>
|
|
#include <utility>
|
|
#include <vector>
|
|
|
|
#ifdef _WIN32
|
|
#include <windows.h>
|
|
#define setenv(key, value, unused) SetEnvironmentVariableA(key, value)
|
|
#define unsetenv(key) SetEnvironmentVariableA(key, nullptr)
|
|
#endif
|
|
|
|
#include <libtransmission/crypto-utils.h>
|
|
#include <libtransmission/web-utils.h>
|
|
|
|
#include "test-fixtures.h"
|
|
|
|
using WebUtilsTest = ::tr::test::TransmissionTest;
|
|
using namespace std::literals;
|
|
|
|
TEST_F(WebUtilsTest, urlParse)
|
|
{
|
|
auto url = "http://1"sv;
|
|
auto parsed = tr_urlParse(url);
|
|
EXPECT_TRUE(parsed);
|
|
EXPECT_EQ("http"sv, parsed->scheme);
|
|
EXPECT_EQ("1"sv, parsed->host);
|
|
EXPECT_EQ("1"sv, parsed->sitename);
|
|
EXPECT_EQ(""sv, parsed->path);
|
|
EXPECT_EQ(""sv, parsed->query);
|
|
EXPECT_EQ(""sv, parsed->fragment);
|
|
EXPECT_EQ(80, parsed->port);
|
|
|
|
url = "http://www.some-tracker.org/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_TRUE(parsed);
|
|
EXPECT_EQ("http"sv, parsed->scheme);
|
|
EXPECT_EQ("www.some-tracker.org"sv, parsed->host);
|
|
EXPECT_EQ("some-tracker"sv, parsed->sitename);
|
|
EXPECT_EQ("/some/path"sv, parsed->path);
|
|
EXPECT_EQ(""sv, parsed->query);
|
|
EXPECT_EQ(""sv, parsed->fragment);
|
|
EXPECT_EQ(80, parsed->port);
|
|
|
|
url = "http://www.some-tracker.org:8080/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_TRUE(parsed);
|
|
EXPECT_EQ("http"sv, parsed->scheme);
|
|
EXPECT_EQ("www.some-tracker.org"sv, parsed->host);
|
|
EXPECT_EQ("some-tracker"sv, parsed->sitename);
|
|
EXPECT_EQ("/some/path"sv, parsed->path);
|
|
EXPECT_EQ(""sv, parsed->query);
|
|
EXPECT_EQ(""sv, parsed->fragment);
|
|
EXPECT_EQ(8080, parsed->port);
|
|
|
|
url = "http://www.some-tracker.org:8080/some/path?key=val&foo=bar#fragment"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_TRUE(parsed);
|
|
EXPECT_EQ("http"sv, parsed->scheme);
|
|
EXPECT_EQ("www.some-tracker.org"sv, parsed->host);
|
|
EXPECT_EQ("some-tracker"sv, parsed->sitename);
|
|
EXPECT_EQ("/some/path"sv, parsed->path);
|
|
EXPECT_EQ("key=val&foo=bar"sv, parsed->query);
|
|
EXPECT_EQ("fragment"sv, parsed->fragment);
|
|
EXPECT_EQ(8080, parsed->port);
|
|
|
|
url = "http://www.some-tracker.org:invalid/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_FALSE(parsed);
|
|
|
|
url = "http://www.some-tracker.org:/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_FALSE(parsed);
|
|
|
|
url = "http://www.some-tracker.org:0/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_FALSE(parsed);
|
|
|
|
url = "http://www.some-tracker.org:-1/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_FALSE(parsed);
|
|
|
|
url = "http://www.some-tracker.org:65536/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_FALSE(parsed);
|
|
|
|
url =
|
|
"magnet:"
|
|
"?xt=urn:btih:14ffe5dd23188fd5cb53a1d47f1289db70abf31e"
|
|
"&dn=ubuntu_12_04_1_desktop_32_bit"
|
|
"&tr=http%3A%2F%2Ftracker.publicbt.com%2Fannounce"
|
|
"&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80"
|
|
"&ws=http%3A%2F%2Ftransmissionbt.com"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_TRUE(parsed);
|
|
EXPECT_EQ("magnet"sv, parsed->scheme);
|
|
EXPECT_EQ(""sv, parsed->host);
|
|
EXPECT_EQ(""sv, parsed->sitename);
|
|
EXPECT_EQ(""sv, parsed->path);
|
|
EXPECT_EQ(
|
|
"xt=urn:btih:14ffe5dd23188fd5cb53a1d47f1289db70abf31e"
|
|
"&dn=ubuntu_12_04_1_desktop_32_bit"
|
|
"&tr=http%3A%2F%2Ftracker.publicbt.com%2Fannounce"
|
|
"&tr=udp%3A%2F%2Ftracker.publicbt.com%3A80"
|
|
"&ws=http%3A%2F%2Ftransmissionbt.com"sv,
|
|
parsed->query);
|
|
|
|
// test a host whose public suffix contains >1 dot
|
|
url = "https://www.example.co.uk:8080/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_TRUE(parsed);
|
|
EXPECT_EQ("https"sv, parsed->scheme);
|
|
EXPECT_EQ("example"sv, parsed->sitename);
|
|
EXPECT_EQ("www.example.co.uk"sv, parsed->host);
|
|
EXPECT_EQ("/some/path"sv, parsed->path);
|
|
EXPECT_EQ(8080, parsed->port);
|
|
|
|
// test a host that lacks a subdomain
|
|
url = "http://some-tracker.co.uk/some/other/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_TRUE(parsed);
|
|
EXPECT_EQ("http"sv, parsed->scheme);
|
|
EXPECT_EQ("some-tracker"sv, parsed->sitename);
|
|
EXPECT_EQ("some-tracker.co.uk"sv, parsed->host);
|
|
EXPECT_EQ("/some/other/path"sv, parsed->path);
|
|
EXPECT_EQ(80, parsed->port);
|
|
|
|
// test a host with an IPv4 address
|
|
url = "https://127.0.0.1:8080/some/path"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_TRUE(parsed);
|
|
EXPECT_EQ("https"sv, parsed->scheme);
|
|
EXPECT_EQ("127.0.0.1"sv, parsed->sitename);
|
|
EXPECT_EQ("127.0.0.1"sv, parsed->host);
|
|
EXPECT_EQ("/some/path"sv, parsed->path);
|
|
EXPECT_EQ(8080, parsed->port);
|
|
|
|
// test a host with a bracketed IPv6 address and explicit port
|
|
url = "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]:8080/announce"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_EQ("http"sv, parsed->scheme);
|
|
EXPECT_EQ("[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]"sv, parsed->sitename);
|
|
EXPECT_EQ("[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]"sv, parsed->host);
|
|
EXPECT_EQ("/announce"sv, parsed->path);
|
|
EXPECT_EQ(8080, parsed->port);
|
|
|
|
// test a host with a bracketed IPv6 address and implicit port
|
|
url = "http://[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]/announce"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_EQ("http"sv, parsed->scheme);
|
|
EXPECT_EQ("[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]"sv, parsed->sitename);
|
|
EXPECT_EQ("[2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d]"sv, parsed->host);
|
|
EXPECT_EQ("/announce"sv, parsed->path);
|
|
EXPECT_EQ(80, parsed->port);
|
|
|
|
// test a host with an unbracketed IPv6 address and implicit port
|
|
url = "http://2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d/announce"sv;
|
|
parsed = tr_urlParse(url);
|
|
EXPECT_EQ("http"sv, parsed->scheme);
|
|
EXPECT_EQ("2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d"sv, parsed->sitename);
|
|
EXPECT_EQ("2001:0db8:11a3:09d7:1f34:8a2e:07a0:765d"sv, parsed->host);
|
|
EXPECT_EQ("/announce"sv, parsed->path);
|
|
EXPECT_EQ(80, parsed->port);
|
|
}
|
|
|
|
TEST_F(WebUtilsTest, urlParseFuzz)
|
|
{
|
|
auto buf = std::vector<char>{};
|
|
|
|
for (size_t i = 0; i < 100000U; ++i)
|
|
{
|
|
buf.resize(tr_rand_int(1024U));
|
|
tr_rand_buffer(std::data(buf), std::size(buf));
|
|
(void)tr_urlParse({ std::data(buf), std::size(buf) });
|
|
}
|
|
}
|
|
|
|
TEST_F(WebUtilsTest, urlQueryEntries)
|
|
{
|
|
auto parsed = tr_url_parsed_t{};
|
|
parsed.query = "a=1&b=two&c=si&d_has_no_val&e=&f&g=gee"sv;
|
|
|
|
auto const keyvals = parsed.query_entries();
|
|
auto const end = std::cend(keyvals);
|
|
|
|
auto it = std::cbegin(keyvals);
|
|
EXPECT_NE(end, it);
|
|
EXPECT_EQ("a"sv, it->first);
|
|
EXPECT_EQ("1"sv, it->second);
|
|
|
|
++it;
|
|
EXPECT_NE(end, it);
|
|
EXPECT_EQ("b"sv, it->first);
|
|
EXPECT_EQ("two"sv, it->second);
|
|
|
|
++it;
|
|
EXPECT_NE(end, it);
|
|
EXPECT_EQ("c"sv, it->first);
|
|
EXPECT_EQ("si"sv, it->second);
|
|
|
|
++it;
|
|
EXPECT_NE(end, it);
|
|
EXPECT_EQ("d_has_no_val"sv, it->first);
|
|
EXPECT_EQ(""sv, it->second);
|
|
|
|
++it;
|
|
EXPECT_NE(end, it);
|
|
EXPECT_EQ("e"sv, it->first);
|
|
EXPECT_EQ(""sv, it->second);
|
|
|
|
++it;
|
|
EXPECT_NE(end, it);
|
|
EXPECT_EQ("f"sv, it->first);
|
|
EXPECT_EQ(""sv, it->second);
|
|
|
|
++it;
|
|
EXPECT_NE(end, it);
|
|
EXPECT_EQ("g"sv, it->first);
|
|
EXPECT_EQ("gee"sv, it->second);
|
|
|
|
++it;
|
|
EXPECT_EQ(end, it);
|
|
}
|
|
|
|
TEST_F(WebUtilsTest, urlIsValid)
|
|
{
|
|
EXPECT_FALSE(tr_urlIsValid("hello world"sv));
|
|
EXPECT_FALSE(tr_urlIsValid("http://www.💩.com/announce/"sv));
|
|
EXPECT_TRUE(tr_urlIsValid("http://www.example.com/announce/"sv));
|
|
EXPECT_FALSE(tr_urlIsValid(""sv));
|
|
EXPECT_FALSE(tr_urlIsValid("com"sv));
|
|
EXPECT_FALSE(tr_urlIsValid("www.example.com"sv));
|
|
EXPECT_FALSE(tr_urlIsValid("://www.example.com"sv));
|
|
EXPECT_FALSE(tr_urlIsValid("zzz://www.example.com"sv)); // syntactically valid, but unsupported scheme
|
|
EXPECT_TRUE(tr_urlIsValid("https://www.example.com"sv));
|
|
|
|
EXPECT_TRUE(tr_urlIsValid("sftp://www.example.com"sv));
|
|
EXPECT_FALSE(tr_urlIsValidTracker("sftp://www.example.com"sv)); // unsupported tracker scheme
|
|
}
|
|
|
|
TEST_F(WebUtilsTest, urlPercentDecode)
|
|
{
|
|
auto constexpr Tests = std::array<std::pair<std::string_view, std::string_view>, 13>{ {
|
|
{ "%-2"sv, "%-2"sv },
|
|
{ "%6 1"sv, "%6 1"sv },
|
|
{ "%6"sv, "%6"sv },
|
|
{ "%6%a"sv, "%6%a"sv },
|
|
{ "%61 "sv, "a "sv },
|
|
{ "%61"sv, "a"sv },
|
|
{ "%61a"sv, "aa"sv },
|
|
{ "%61b"sv, "ab"sv },
|
|
{ "%6a"sv, "j"sv },
|
|
{ "%FF"sv, "\xff"sv },
|
|
{ "%FF%00%ff"sv, "\xff\x00\xff"sv },
|
|
{ "%FG"sv, "%FG"sv },
|
|
{ "http%3A%2F%2Fwww.example.com%2F~user%2F%3Ftest%3D1%26test1%3D2"sv,
|
|
"http://www.example.com/~user/?test=1&test1=2"sv },
|
|
} };
|
|
|
|
for (auto const& [encoded, decoded] : Tests)
|
|
{
|
|
EXPECT_EQ(decoded, tr_urlPercentDecode(encoded));
|
|
}
|
|
}
|
|
|
|
TEST_F(WebUtilsTest, urlPercentEncode)
|
|
{
|
|
static auto constexpr Tests = std::array<std::tuple<std::string_view, std::string_view, bool>, 10U>{ {
|
|
{ "192.168.202.101"sv, "192.168.202.101"sv, true },
|
|
{ "8.8.8.8"sv, "8.8.8.8"sv, true },
|
|
{ "[2001:0:0eab:dead::a0:abcd:4e]"sv, "%5B2001%3A0%3A0eab%3Adead%3A%3Aa0%3Aabcd%3A4e%5D"sv, true },
|
|
{ "你好"sv, "%E4%BD%A0%E5%A5%BD"sv, true },
|
|
{ "Letöltések"sv, "Let%C3%B6lt%C3%A9sek"sv, true },
|
|
{ "Дыскаграфія"sv, "%D0%94%D1%8B%D1%81%D0%BA%D0%B0%D0%B3%D1%80%D0%B0%D1%84%D1%96%D1%8F"sv, true },
|
|
{ "https://example.com/Letöltések"sv, "https://example.com/Let%C3%B6lt%C3%A9sek"sv, false },
|
|
{ "https://example.com/Let%C3%B6lt%C3%A9sek"sv, "https://example.com/Let%C3%B6lt%C3%A9sek"sv, false },
|
|
{ "udp://你好.com/announce"sv, "udp://%E4%BD%A0%E5%A5%BD.com/announce"sv, false },
|
|
{ "udp://%E4%BD%A0%E5%A5%BD.com/announce"sv, "udp://%E4%BD%A0%E5%A5%BD.com/announce"sv, false },
|
|
} };
|
|
|
|
for (auto const& [decoded, encoded, escape_reserved] : Tests)
|
|
{
|
|
auto buf = tr_urlbuf{};
|
|
tr_urlPercentEncode(std::back_inserter(buf), decoded, escape_reserved);
|
|
EXPECT_EQ(encoded, buf);
|
|
}
|
|
}
|