From 46c0275ddfdeebd95d4e393791db1259df87b136 Mon Sep 17 00:00:00 2001 From: Jason Dillaman Date: Mon, 2 Nov 2020 10:22:47 -0500 Subject: [PATCH] librbd/migration: helper tool for parsing URLs into parts The boost::beast framework doesn't include a URL parser but the HTTP client requires the host, port, and path to be specified seperately. Additionally, if https is utilized the http stream needs to be wrapped with an SSL stream. Signed-off-by: Jason Dillaman --- src/librbd/CMakeLists.txt | 1 + src/librbd/migration/Types.h | 42 +++++++++++++ src/librbd/migration/Utils.cc | 65 ++++++++++++++++++++ src/librbd/migration/Utils.h | 21 +++++++ src/test/librbd/CMakeLists.txt | 1 + src/test/librbd/migration/test_mock_Utils.cc | 47 ++++++++++++++ 6 files changed, 177 insertions(+) create mode 100644 src/librbd/migration/Types.h create mode 100644 src/librbd/migration/Utils.cc create mode 100644 src/librbd/migration/Utils.h create mode 100644 src/test/librbd/migration/test_mock_Utils.cc diff --git a/src/librbd/CMakeLists.txt b/src/librbd/CMakeLists.txt index ac4b196def1..940a365134d 100644 --- a/src/librbd/CMakeLists.txt +++ b/src/librbd/CMakeLists.txt @@ -131,6 +131,7 @@ set(librbd_internal_srcs migration/OpenSourceImageRequest.cc migration/RawFormat.cc migration/SourceSpecBuilder.cc + migration/Utils.cc mirror/DemoteRequest.cc mirror/DisableRequest.cc mirror/EnableRequest.cc diff --git a/src/librbd/migration/Types.h b/src/librbd/migration/Types.h new file mode 100644 index 00000000000..244dc28b774 --- /dev/null +++ b/src/librbd/migration/Types.h @@ -0,0 +1,42 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIGRATION_TYPES_H +#define CEPH_LIBRBD_MIGRATION_TYPES_H + +#include +#include + +namespace librbd { +namespace migration { + +enum UrlScheme { + URL_SCHEME_HTTP, + URL_SCHEME_HTTPS, +}; + +struct UrlSpec { + UrlSpec() {} + UrlSpec(UrlScheme scheme, const std::string& host, const std::string& port, + const std::string& path) + : scheme(scheme), host(host), port(port), path(path) { + } + + UrlScheme scheme = URL_SCHEME_HTTP; + std::string host; + std::string port = "80"; + std::string path = "/"; + +}; + +inline bool operator==(const UrlSpec& lhs, const UrlSpec& rhs) { + return (lhs.scheme == rhs.scheme && + lhs.host == rhs.host && + lhs.port == rhs.port && + lhs.path == rhs.path); +} + +} // namespace migration +} // namespace librbd + +#endif // CEPH_LIBRBD_MIGRATION_TYPES_H diff --git a/src/librbd/migration/Utils.cc b/src/librbd/migration/Utils.cc new file mode 100644 index 00000000000..23b803d2762 --- /dev/null +++ b/src/librbd/migration/Utils.cc @@ -0,0 +1,65 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "librbd/migration/Utils.h" +#include "common/dout.h" +#include "common/errno.h" +#include +#include + +namespace librbd { +namespace migration { +namespace util { + +#define dout_subsys ceph_subsys_rbd +#undef dout_prefix +#define dout_prefix *_dout << "librbd::migration::util::" << __func__ << ": " + +int parse_url(CephContext* cct, const std::string& url, UrlSpec* url_spec) { + ldout(cct, 10) << "url=" << url << dendl; + *url_spec = UrlSpec{}; + + // parse the provided URL (scheme, user, password, host, port, path, + // parameters, query, and fragment) + std::regex url_regex( + R"(^(?:([^:/]*)://)?(?:(\w+)(?::(\w+))?@)?([^/;\?:#]+)(?::([^/;\?#]+))?)" + R"((?:/([^;\?#]*))?(?:;([^\?#]+))?(?:\?([^#]+))?(?:#(\w+))?$)"); + std::smatch match; + if(!std::regex_match(url, match, url_regex)) { + lderr(cct) << "invalid url: '" << url << "'" << dendl; + return -EINVAL; + } + + auto& scheme = match[1]; + if (scheme == "http" || scheme == "") { + url_spec->scheme = URL_SCHEME_HTTP; + } else if (scheme == "https") { + url_spec->scheme = URL_SCHEME_HTTPS; + url_spec->port = "443"; + } else { + lderr(cct) << "invalid url scheme: '" << url << "'" << dendl; + return -EINVAL; + } + + url_spec->host = match[4]; + auto& port = match[5]; + if (port.matched) { + try { + boost::lexical_cast(port); + } catch (boost::bad_lexical_cast&) { + lderr(cct) << "invalid url port: '" << url << "'" << dendl; + return -EINVAL; + } + url_spec->port = port; + } + + auto& path = match[6]; + if (path.matched) { + url_spec->path += path; + } + return 0; +} + +} // namespace util +} // namespace migration +} // namespace librbd diff --git a/src/librbd/migration/Utils.h b/src/librbd/migration/Utils.h new file mode 100644 index 00000000000..a3e2fe0132c --- /dev/null +++ b/src/librbd/migration/Utils.h @@ -0,0 +1,21 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#ifndef CEPH_LIBRBD_MIGRATION_UTILS_H +#define CEPH_LIBRBD_MIGRATION_UTILS_H + +#include "include/common_fwd.h" +#include "librbd/migration/Types.h" +#include + +namespace librbd { +namespace migration { +namespace util { + +int parse_url(CephContext* cct, const std::string& url, UrlSpec* url_spec); + +} // namespace util +} // namespace migration +} // namespace librbd + +#endif // CEPH_LIBRBD_MIGRATION_UTILS_H diff --git a/src/test/librbd/CMakeLists.txt b/src/test/librbd/CMakeLists.txt index 2c77d38b35d..5571712e23a 100644 --- a/src/test/librbd/CMakeLists.txt +++ b/src/test/librbd/CMakeLists.txt @@ -91,6 +91,7 @@ set(unittest_librbd_srcs managed_lock/test_mock_ReleaseRequest.cc migration/test_mock_FileStream.cc migration/test_mock_RawFormat.cc + migration/test_mock_Utils.cc mirror/snapshot/test_mock_CreateNonPrimaryRequest.cc mirror/snapshot/test_mock_CreatePrimaryRequest.cc mirror/snapshot/test_mock_ImageMeta.cc diff --git a/src/test/librbd/migration/test_mock_Utils.cc b/src/test/librbd/migration/test_mock_Utils.cc new file mode 100644 index 00000000000..917c191dde6 --- /dev/null +++ b/src/test/librbd/migration/test_mock_Utils.cc @@ -0,0 +1,47 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab + +#include "test/librbd/test_mock_fixture.h" +#include "test/librbd/test_support.h" +#include "librbd/migration/Utils.h" +#include "gtest/gtest.h" +#include "gmock/gmock.h" + +namespace librbd { +namespace migration { +namespace util { + +class TestMockMigrationUtils : public TestMockFixture { +public: +}; + +TEST_F(TestMockMigrationUtils, ParseUrl) { + UrlSpec url_spec; + ASSERT_EQ(-EINVAL, parse_url(g_ceph_context, "", &url_spec)); + ASSERT_EQ(-EINVAL, parse_url(g_ceph_context, "jttp://google.com/path", + &url_spec)); + ASSERT_EQ(-EINVAL, parse_url(g_ceph_context, "http://google.com:absd/path", + &url_spec)); + + ASSERT_EQ(0, parse_url(g_ceph_context, "ceph.io/path", &url_spec)); + ASSERT_EQ(UrlSpec(URL_SCHEME_HTTP, "ceph.io", "80", "/path"), url_spec); + + ASSERT_EQ(0, parse_url(g_ceph_context, "http://google.com/path", &url_spec)); + ASSERT_EQ(UrlSpec(URL_SCHEME_HTTP, "google.com", "80", "/path"), url_spec); + + ASSERT_EQ(0, parse_url(g_ceph_context, "https://ceph.io/", &url_spec)); + ASSERT_EQ(UrlSpec(URL_SCHEME_HTTPS, "ceph.io", "443", "/"), url_spec); + + ASSERT_EQ(0, parse_url(g_ceph_context, + "http://google.com:1234/some/other/path", &url_spec)); + ASSERT_EQ(UrlSpec(URL_SCHEME_HTTP, "google.com", "1234", "/some/other/path"), + url_spec); + + ASSERT_EQ(0, parse_url(g_ceph_context, + "http://1.2.3.4/", &url_spec)); + ASSERT_EQ(UrlSpec(URL_SCHEME_HTTP, "1.2.3.4", "80", "/"), url_spec); +} + +} // namespace util +} // namespace migration +} // namespace librbd -- 2.47.3