diff --git a/cmake/third_party.cmake b/cmake/third_party.cmake index 6bc47ee8..d7567241 100644 --- a/cmake/third_party.cmake +++ b/cmake/third_party.cmake @@ -360,7 +360,7 @@ add_third_party( add_third_party( pugixml - URL https://github.com/zeux/pugixml/archive/refs/tags/v1.13.tar.gz + URL https://github.com/zeux/pugixml/archive/refs/tags/v1.14.tar.gz ) if (WITH_AWS) diff --git a/examples/gcs_demo.cc b/examples/gcs_demo.cc index ce53423c..616b40d9 100644 --- a/examples/gcs_demo.cc +++ b/examples/gcs_demo.cc @@ -134,7 +134,7 @@ int main(int argc, char** argv) { pp->GetNextProactor()->Await([&] { error_code ec = provider.Init(); CHECK(!ec) << "Could not load credentials " << ec.message(); - provider.List(); + provider.ListContainers([](std::string_view item) { CONSOLE_INFO << item << endl; }); }); } else { pp->GetNextProactor()->Await([ctx] { Run(ctx); }); diff --git a/util/cloud/azure/CMakeLists.txt b/util/cloud/azure/CMakeLists.txt index 185617f9..e47571e1 100644 --- a/util/cloud/azure/CMakeLists.txt +++ b/util/cloud/azure/CMakeLists.txt @@ -1,2 +1,2 @@ add_library(azure_lib azure.cc) -cxx_link(azure_lib http_client_lib strings_lib) +cxx_link(azure_lib http_client_lib strings_lib TRDP::pugixml) diff --git a/util/cloud/azure/azure.cc b/util/cloud/azure/azure.cc index 1cac3cb1..0020ddb4 100644 --- a/util/cloud/azure/azure.cc +++ b/util/cloud/azure/azure.cc @@ -1,9 +1,13 @@ // Copyright 2024, Roman Gershman. All rights reserved. // See LICENSE for licensing terms. +#include #include #include +#include +#include + #include "base/logging.h" #include "util/cloud/azure/creds_provider.h" #include "util/cloud/utils.h" @@ -31,6 +35,10 @@ error_code CredsProvider::Init() { return {}; } +auto UnexpectedError(errc code) { + return nonstd::make_unexpected(make_error_code(code)); +} + void HMAC(absl::string_view key, absl::string_view msg, uint8_t dest[32]) { // HMAC_xxx are deprecated since openssl 3.0 // Ubuntu 20.04 uses openssl 1.1. @@ -80,8 +88,38 @@ string Sign(string_view account, const boost::beast::http::header& req_hea return signature; } -void CredsProvider::List() { +io::Result> ParseXmlListBuckets(string_view xml_resp) { + pugi::xml_document doc; + pugi::xml_parse_result result = doc.load_buffer(xml_resp.data(), xml_resp.size()); + + if (!result) { + LOG(ERROR) << "Could not parse xml response " << result.description(); + return UnexpectedError(errc::bad_message); + } + + pugi::xml_node root = doc.child("EnumerationResults"); + if (root.type() != pugi::node_element) { + LOG(ERROR) << "Could not find root node " << xml_resp; + return UnexpectedError(errc::bad_message); + } + + pugi::xml_node buckets = root.child("Containers"); + if (buckets.type() != pugi::node_element) { + LOG(ERROR) << "Could not find buckets node " << xml_resp; + return UnexpectedError(errc::bad_message); + } + + vector res; + for (pugi::xml_node bucket = buckets.child("Container"); bucket; bucket = bucket.next_sibling()) { + res.push_back(bucket.child_value("Name")); + } + return res; +} + +error_code CredsProvider::ListContainers(function cb) { SSL_CTX* ctx = util::http::TlsClient::CreateSslContext(); + absl::Cleanup cleanup([ctx] { util::http::TlsClient::FreeContext(ctx); }); + fb2::ProactorBase* pb = fb2::ProactorBase::me(); CHECK(pb); string endpoint = account_name_ + ".blob.core.windows.net"; @@ -110,8 +148,20 @@ void CredsProvider::List() { ec = client->get()->ReadHeader(&parser); CHECK(!ec) << ec; DVLOG(1) << "Response: " << parser.get().base(); + h2::response_parser resp(std::move(parser)); + client->get()->Recv(&resp); + + auto msg = resp.release(); + DVLOG(1) << "Body: " << msg.body(); + auto res = ParseXmlListBuckets(msg.body()); + if (!res) { + return res.error(); + } - util::http::TlsClient::FreeContext(ctx); + for (const auto& b : *res) { + cb(b); + } + return {}; } } // namespace cloud::azure diff --git a/util/cloud/azure/creds_provider.h b/util/cloud/azure/creds_provider.h index d8e900a9..711fc2c9 100644 --- a/util/cloud/azure/creds_provider.h +++ b/util/cloud/azure/creds_provider.h @@ -16,7 +16,8 @@ class CredsProvider { const std::string& account_name() const { return account_name_; } const std::string& account_key() const { return account_key_; } - void List(); + using ContainerItem = std::string_view; + std::error_code ListContainers(std::function); private: std::string account_name_;