mirror of
https://github.com/ceph/ceph
synced 2025-02-23 11:07:35 +00:00
Merge pull request #15678 from cbodley/wip-rgw-string-join
rgw: add variadic string join for s3 signature generation Reviewed-by: Adam C. Emerson <aemerson@redhat.com> Reviewed-by: Radoslaw Zarzynski <rzarzynski@mirantis.com>
This commit is contained in:
commit
4c22420bdf
@ -646,21 +646,13 @@ get_v4_canon_req_hash(CephContext* cct,
|
||||
{
|
||||
ldout(cct, 10) << "payload request hash = " << request_payload_hash << dendl;
|
||||
|
||||
const size_t total_len = http_verb.length() + canonical_uri.length() + \
|
||||
canonical_qs.length() + canonical_hdrs.length() + signed_hdrs.length() + \
|
||||
request_payload_hash.length() + std::strlen("\n") * 5;
|
||||
const auto canonical_req = create_n_reserve<std::string>(total_len)
|
||||
.append(http_verb.data(), http_verb.length())
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(canonical_uri)
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(canonical_qs)
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(canonical_hdrs)
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(signed_hdrs.data(), signed_hdrs.length())
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(request_payload_hash.data(), request_payload_hash.length());
|
||||
const auto canonical_req = string_join_reserve("\n",
|
||||
http_verb,
|
||||
canonical_uri,
|
||||
canonical_qs,
|
||||
canonical_hdrs,
|
||||
signed_hdrs,
|
||||
request_payload_hash);
|
||||
|
||||
const auto canonical_req_hash = calc_hash_sha256(canonical_req);
|
||||
|
||||
@ -684,17 +676,14 @@ get_v4_string_to_sign(CephContext* const cct,
|
||||
const sha256_digest_t& canonreq_hash)
|
||||
{
|
||||
const auto hexed_cr_hash = buf_to_hex(canonreq_hash);
|
||||
const boost::string_view hexed_cr_hash_str(hexed_cr_hash.data(),
|
||||
hexed_cr_hash.size() - 1);
|
||||
|
||||
const size_t total_len = algorithm.length() + request_date.length() + \
|
||||
credential_scope.length() + hexed_cr_hash.size() - 1 + std::strlen("\n") * 3;
|
||||
const auto string_to_sign = create_n_reserve<std::string>(total_len)
|
||||
.append(algorithm.data(), algorithm.length())
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(request_date.data(), request_date.length())
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(credential_scope.data(), credential_scope.length())
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(hexed_cr_hash.data(), hexed_cr_hash.size() - 1);
|
||||
const auto string_to_sign = string_join_reserve("\n",
|
||||
algorithm,
|
||||
request_date,
|
||||
credential_scope,
|
||||
hexed_cr_hash_str);
|
||||
|
||||
ldout(cct, 10) << "string to sign = "
|
||||
<< rgw::crypt_sanitize::log_content{string_to_sign}
|
||||
@ -919,25 +908,13 @@ AWSv4ComplMulti::ChunkMeta::create_next(CephContext* const cct,
|
||||
std::string
|
||||
AWSv4ComplMulti::calc_chunk_signature(const std::string& payload_hash) const
|
||||
{
|
||||
const size_t algorithm_len = std::strlen(AWS4_HMAC_SHA256_STR);
|
||||
const size_t empty_hash_len = std::strlen(AWS4_EMPTY_PAYLOAD_HASH);
|
||||
|
||||
/* We want to avoid reallocations when concatenating the string_to_sign. */
|
||||
const size_t total_len = algorithm_len + date.length() + \
|
||||
credential_scope.length() + prev_chunk_signature.length() + \
|
||||
empty_hash_len + payload_hash.length() + std::strlen("\n") * 5;
|
||||
const auto string_to_sign = create_n_reserve<std::string>(total_len)
|
||||
.append(AWS4_HMAC_SHA256_STR, algorithm_len)
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(date.data(), date.length())
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(credential_scope.data(), credential_scope.length())
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(prev_chunk_signature)
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(AWS4_EMPTY_PAYLOAD_HASH, empty_hash_len)
|
||||
.append("\n", std::strlen("\n"))
|
||||
.append(payload_hash);
|
||||
const auto string_to_sign = string_join_reserve("\n",
|
||||
AWS4_HMAC_SHA256_STR,
|
||||
date,
|
||||
credential_scope,
|
||||
prev_chunk_signature,
|
||||
AWS4_EMPTY_PAYLOAD_HASH,
|
||||
payload_hash);
|
||||
|
||||
ldout(cct, 20) << "AWSv4ComplMulti: string_to_sign=\n" << string_to_sign
|
||||
<< dendl;
|
||||
|
@ -9,30 +9,31 @@
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/utility/string_view.hpp>
|
||||
|
||||
struct ltstr_nocase
|
||||
{
|
||||
bool operator()(const string& s1, const string& s2) const
|
||||
bool operator()(const std::string& s1, const std::string& s2) const
|
||||
{
|
||||
return strcasecmp(s1.c_str(), s2.c_str()) < 0;
|
||||
}
|
||||
};
|
||||
|
||||
static inline int stringcasecmp(const string& s1, const string& s2)
|
||||
static inline int stringcasecmp(const std::string& s1, const std::string& s2)
|
||||
{
|
||||
return strcasecmp(s1.c_str(), s2.c_str());
|
||||
}
|
||||
|
||||
static inline int stringcasecmp(const string& s1, const char *s2)
|
||||
static inline int stringcasecmp(const std::string& s1, const char *s2)
|
||||
{
|
||||
return strcasecmp(s1.c_str(), s2);
|
||||
}
|
||||
|
||||
static inline int stringcasecmp(const string& s1, int ofs, int size, const string& s2)
|
||||
static inline int stringcasecmp(const std::string& s1, int ofs, int size, const std::string& s2)
|
||||
{
|
||||
return strncasecmp(s1.c_str() + ofs, s2.c_str(), size);
|
||||
}
|
||||
|
||||
static inline int stringtoll(const string& s, int64_t *val)
|
||||
static inline int stringtoll(const std::string& s, int64_t *val)
|
||||
{
|
||||
char *end;
|
||||
|
||||
@ -48,7 +49,7 @@ static inline int stringtoll(const string& s, int64_t *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int stringtoull(const string& s, uint64_t *val)
|
||||
static inline int stringtoull(const std::string& s, uint64_t *val)
|
||||
{
|
||||
char *end;
|
||||
|
||||
@ -64,7 +65,7 @@ static inline int stringtoull(const string& s, uint64_t *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int stringtol(const string& s, int32_t *val)
|
||||
static inline int stringtol(const std::string& s, int32_t *val)
|
||||
{
|
||||
char *end;
|
||||
|
||||
@ -80,7 +81,7 @@ static inline int stringtol(const string& s, int32_t *val)
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline int stringtoul(const string& s, uint32_t *val)
|
||||
static inline int stringtoul(const std::string& s, uint32_t *val)
|
||||
{
|
||||
char *end;
|
||||
|
||||
@ -112,19 +113,6 @@ sview2cstr(const boost::string_view& sv)
|
||||
return cstr;
|
||||
}
|
||||
|
||||
/* We need this helper function because the interface of std::string::reserve
|
||||
* doesn't provide the chaining ability in the type append(). It's required
|
||||
* to concatenate string without reallocations in a way const-correct manner. */
|
||||
template <class StringT>
|
||||
static inline StringT create_n_reserve(const size_t reserve_len)
|
||||
{
|
||||
StringT ret;
|
||||
/* I would love to see reserve() returning "basic_string&" instead of "void"
|
||||
* in the standard library! */
|
||||
ret.reserve(reserve_len);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* std::strlen() isn't guaranteed to be computable at compile-time. Although
|
||||
* newer GCCs actually do that, Clang doesn't. Please be aware this function
|
||||
* IS NOT A DROP-IN REPLACEMENT FOR STRLEN -- it returns a different result
|
||||
@ -134,4 +122,98 @@ static inline constexpr size_t sarrlen(const char (&arr)[N]) {
|
||||
return N - 1;
|
||||
}
|
||||
|
||||
namespace detail {
|
||||
|
||||
// variadic sum() to add up string lengths for reserve()
|
||||
static inline constexpr size_t sum() { return 0; }
|
||||
template <typename... Args>
|
||||
constexpr size_t sum(size_t v, Args... args) { return v + sum(args...); }
|
||||
|
||||
// traits for string_size()
|
||||
template <typename T>
|
||||
struct string_traits {
|
||||
static constexpr size_t size(const T& s) { return s.size(); }
|
||||
};
|
||||
// specializations for char*/const char* use strlen()
|
||||
template <>
|
||||
struct string_traits<const char*> {
|
||||
static constexpr size_t size(const char* s) { return std::strlen(s); }
|
||||
};
|
||||
template <>
|
||||
struct string_traits<char*> : string_traits<const char*> {};
|
||||
// specializations for char[]/const char[] also use strlen()
|
||||
template <std::size_t N>
|
||||
struct string_traits<const char[N]> : string_traits<const char*> {};
|
||||
template <std::size_t N>
|
||||
struct string_traits<char[N]> : string_traits<const char*> {};
|
||||
|
||||
// helpers for string_cat_reserve()
|
||||
static inline void append_to(std::string& s) {}
|
||||
template <typename... Args>
|
||||
void append_to(std::string& s, const boost::string_view& v, const Args&... args)
|
||||
{
|
||||
s.append(v.begin(), v.end());
|
||||
append_to(s, args...);
|
||||
}
|
||||
|
||||
// helpers for string_join_reserve()
|
||||
static inline void join_next(std::string& s, const boost::string_view& d) {}
|
||||
template <typename... Args>
|
||||
void join_next(std::string& s, const boost::string_view& d,
|
||||
const boost::string_view& v, const Args&... args)
|
||||
{
|
||||
s.append(d.begin(), d.end());
|
||||
s.append(v.begin(), v.end());
|
||||
join_next(s, d, args...);
|
||||
}
|
||||
|
||||
static inline void join(std::string& s, const boost::string_view& d) {}
|
||||
template <typename... Args>
|
||||
void join(std::string& s, const boost::string_view& d,
|
||||
const boost::string_view& v, const Args&... args)
|
||||
{
|
||||
s.append(v.begin(), v.end());
|
||||
join_next(s, d, args...);
|
||||
}
|
||||
|
||||
} // namespace detail
|
||||
|
||||
/// return the length of a c string, string literal, or string type
|
||||
template <typename T>
|
||||
constexpr size_t string_size(const T& s)
|
||||
{
|
||||
return detail::string_traits<T>::size(s);
|
||||
}
|
||||
|
||||
/// concatenates the given string arguments, returning as a std::string that
|
||||
/// gets preallocated with reserve()
|
||||
template <typename... Args>
|
||||
std::string string_cat_reserve(const Args&... args)
|
||||
{
|
||||
size_t total_size = detail::sum(string_size(args)...);
|
||||
std::string result;
|
||||
result.reserve(total_size);
|
||||
detail::append_to(result, args...);
|
||||
return result;
|
||||
}
|
||||
|
||||
/// joins the given string arguments with a delimiter, returning as a
|
||||
/// std::string that gets preallocated with reserve()
|
||||
template <typename... Args>
|
||||
std::string string_join_reserve(const boost::string_view& delim,
|
||||
const Args&... args)
|
||||
{
|
||||
size_t delim_size = delim.size() * std::max<ssize_t>(0, sizeof...(args) - 1);
|
||||
size_t total_size = detail::sum(string_size(args)...) + delim_size;
|
||||
std::string result;
|
||||
result.reserve(total_size);
|
||||
detail::join(result, delim, args...);
|
||||
return result;
|
||||
}
|
||||
template <typename... Args>
|
||||
std::string string_join_reserve(char delim, const Args&... args)
|
||||
{
|
||||
return string_join_reserve(boost::string_view{&delim, 1}, args...);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -131,3 +131,7 @@ target_link_libraries(ceph_test_rgw_iam_policy
|
||||
${CRYPTO_LIBS}
|
||||
)
|
||||
set_target_properties(ceph_test_rgw_iam_policy PROPERTIES COMPILE_FLAGS ${UNITTEST_CXX_FLAGS})
|
||||
|
||||
# unitttest_rgw_string
|
||||
add_executable(unittest_rgw_string test_rgw_string.cc)
|
||||
add_ceph_unittest(unittest_rgw_string ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/unittest_rgw_string)
|
||||
|
71
src/test/rgw/test_rgw_string.cc
Normal file
71
src/test/rgw/test_rgw_string.cc
Normal file
@ -0,0 +1,71 @@
|
||||
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
|
||||
// vim: ts=8 sw=2 smarttab
|
||||
/*
|
||||
* Ceph - scalable distributed file system
|
||||
*
|
||||
* Copyright (C) 2017 Red Hat
|
||||
*
|
||||
* This is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public
|
||||
* License version 2.1, as published by the Free Software
|
||||
* Foundation. See file COPYING.
|
||||
*
|
||||
*/
|
||||
|
||||
#include "rgw/rgw_string.h"
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
const std::string abc{"abc"};
|
||||
const char *def{"def"}; // const char*
|
||||
char ghi_arr[] = {'g', 'h', 'i', '\0'};
|
||||
char *ghi{ghi_arr}; // char*
|
||||
constexpr boost::string_view jkl{"jkl", 3};
|
||||
#define mno "mno" // string literal (char[4])
|
||||
char pqr[] = {'p', 'q', 'r', '\0'};
|
||||
|
||||
TEST(string_size, types)
|
||||
{
|
||||
ASSERT_EQ(3u, string_size(abc));
|
||||
ASSERT_EQ(3u, string_size(def));
|
||||
ASSERT_EQ(3u, string_size(ghi));
|
||||
ASSERT_EQ(3u, string_size(jkl));
|
||||
ASSERT_EQ(3u, string_size(mno));
|
||||
ASSERT_EQ(3u, string_size(pqr));
|
||||
|
||||
constexpr auto compile_time_size = string_size(jkl);
|
||||
ASSERT_EQ(3u, compile_time_size);
|
||||
}
|
||||
|
||||
TEST(string_cat_reserve, types)
|
||||
{
|
||||
ASSERT_EQ("abcdefghijklmnopqr",
|
||||
string_cat_reserve(abc, def, ghi, jkl, mno, pqr));
|
||||
}
|
||||
|
||||
TEST(string_cat_reserve, count)
|
||||
{
|
||||
ASSERT_EQ("", string_cat_reserve());
|
||||
ASSERT_EQ("abc", string_cat_reserve(abc));
|
||||
ASSERT_EQ("abcdef", string_cat_reserve(abc, def));
|
||||
}
|
||||
|
||||
TEST(string_join_reserve, types)
|
||||
{
|
||||
ASSERT_EQ("abc, def, ghi, jkl, mno, pqr",
|
||||
string_join_reserve(", ", abc, def, ghi, jkl, mno, pqr));
|
||||
}
|
||||
|
||||
TEST(string_join_reserve, count)
|
||||
{
|
||||
ASSERT_EQ("", string_join_reserve(", "));
|
||||
ASSERT_EQ("abc", string_join_reserve(", ", abc));
|
||||
ASSERT_EQ("abc, def", string_join_reserve(", ", abc, def));
|
||||
}
|
||||
|
||||
TEST(string_join_reserve, delim)
|
||||
{
|
||||
ASSERT_EQ("abcdef", string_join_reserve("", abc, def));
|
||||
ASSERT_EQ("abc def", string_join_reserve(' ', abc, def));
|
||||
ASSERT_EQ("abc\ndef", string_join_reserve('\n', abc, def));
|
||||
ASSERT_EQ("abcfoodef", string_join_reserve(std::string{"foo"}, abc, def));
|
||||
}
|
Loading…
Reference in New Issue
Block a user