]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph.git/commitdiff
librbd/migration: helper tool for parsing URLs into parts
authorJason Dillaman <dillaman@redhat.com>
Mon, 2 Nov 2020 15:22:47 +0000 (10:22 -0500)
committerJason Dillaman <dillaman@redhat.com>
Mon, 16 Nov 2020 23:16:24 +0000 (18:16 -0500)
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 <dillaman@redhat.com>
src/librbd/CMakeLists.txt
src/librbd/migration/Types.h [new file with mode: 0644]
src/librbd/migration/Utils.cc [new file with mode: 0644]
src/librbd/migration/Utils.h [new file with mode: 0644]
src/test/librbd/CMakeLists.txt
src/test/librbd/migration/test_mock_Utils.cc [new file with mode: 0644]

index ac4b196def1eb4229d2864cd95bee15a6ef83620..940a365134d99979ed696efbc16726177a8c24ff 100644 (file)
@@ -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 (file)
index 0000000..244dc28
--- /dev/null
@@ -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 <string>
+#include <utility>
+
+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 (file)
index 0000000..23b803d
--- /dev/null
@@ -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 <boost/lexical_cast.hpp>
+#include <regex>
+
+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<uint16_t>(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 (file)
index 0000000..a3e2fe0
--- /dev/null
@@ -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 <string>
+
+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
index 2c77d38b35dbd4f12fac3b50c3c098fe879bc945..5571712e23ad4946fab95955f573a7b732508637 100644 (file)
@@ -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 (file)
index 0000000..917c191
--- /dev/null
@@ -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