safe register handler

This commit is contained in:
qicosmos
2025-09-01 17:15:45 +08:00
parent 02aee639c5
commit 85f07e20ec
6 changed files with 277 additions and 82 deletions
+5 -2
View File
@@ -124,9 +124,12 @@ int main() {
rpc_server server(9000, std::thread::hardware_concurrency(), 3600);
dummy d;
server.register_handler("add", &dummy::add, &d);
// safe register, same with `server.register_handler("dummy::add",
// &dummy::add, &d)`
server.register_handler<&dummy::add>(&d);
server.register_handler("get_dummy", get_dummy);
// safe register, same with `server.register_handler("get_dummy", get_dummy)`
server.register_handler<get_dummy>();
server.register_handler("translate", translate);
server.register_handler("hello", hello);
+2
View File
@@ -1,5 +1,7 @@
#pragma once
#include <cstdint>
#include <memory>
#include <string>
namespace rest_rpc {
+101
View File
@@ -0,0 +1,101 @@
#pragma once
#include <algorithm>
#include <array>
#include <cstddef>
#include <string_view>
#include <type_traits>
#include <utility>
namespace rest_rpc {
namespace detail {
template <auto Func>
constexpr std::string_view qualified_name_of_impl() noexcept {
#ifdef _MSC_VER
constexpr std::size_t suffix_size{16};
constexpr std::string_view keyword{
"rest_rpc::detail::qualified_name_of_impl<"};
constexpr std::string_view signature{__FUNCSIG__};
constexpr std::string_view anonymous_namespace{"`anonymous-namespace'::"};
#elif defined(__clang__)
constexpr std::size_t suffix_size{1};
constexpr std::string_view keyword{"[Func = "};
constexpr std::string_view signature{__PRETTY_FUNCTION__};
constexpr std::string_view anonymous_namespace{"(anonymous namespace)::"};
#elif defined(__GNUC__)
constexpr std::size_t suffix_size{1};
constexpr std::string_view keyword{"Func = "};
constexpr std::string_view signature{__PRETTY_FUNCTION__};
constexpr std::string_view anonymous_namespace{"{anonymous}::"};
#else
#error "Unsupported compiler."
#endif
// Skips the possible '&' token for GCC and Clang.
constexpr auto prefix_size = signature.find(keyword) + keyword.size();
constexpr auto additional_size = signature[prefix_size] == '&' ? 1 : 0;
constexpr auto intermediate = signature.substr(
prefix_size + additional_size,
signature.size() - prefix_size - additional_size - suffix_size);
constexpr std::string_view result = intermediate;
constexpr size_t rpos = result.rfind(anonymous_namespace);
if constexpr (rpos != std::string_view::npos) {
constexpr std::string_view str =
result.substr(rpos + anonymous_namespace.size());
constexpr size_t right = str.find('(');
if constexpr (right != std::string_view::npos) {
return str.substr(0, right);
} else {
return str;
}
} else {
constexpr size_t left = result.find("l ") + 2;
constexpr size_t right = result.find('(');
if constexpr (left != std::string_view::npos) {
if constexpr (right != std::string_view::npos) {
return result.substr(left, right - left);
} else {
return result;
}
} else {
return result;
}
}
}
} // namespace detail
template <auto Func> struct qualified_name_of {
static constexpr auto value = detail::qualified_name_of_impl<Func>();
};
template <auto Func>
inline constexpr auto &&qualified_name_of_v = qualified_name_of<Func>::value;
} // namespace rest_rpc
namespace rest_rpc {
template <size_t N>
constexpr std::string_view
string_view_array_has(const std::array<std::string_view, N> &array,
std::string_view value) {
for (const auto &v : array) {
if (value.find(v) == 0)
return v;
}
return std::string_view{""};
}
template <auto func> constexpr std::string_view get_func_name() {
constexpr std::array func_style_array{
std::string_view{"__cdecl "}, std::string_view{"__clrcall "},
std::string_view{"__stdcall "}, std::string_view{"__fastcall "},
std::string_view{"__thiscall "}, std::string_view{"__vectorcall "}};
constexpr auto qualified_name = std::string_view{qualified_name_of_v<func>};
constexpr auto func_style =
string_view_array_has(func_style_array, qualified_name);
if constexpr (func_style.length() > 0) {
return std::string_view{qualified_name.data() + func_style.length(),
qualified_name.length() - func_style.length()};
}
return qualified_name;
};
} // namespace rest_rpc
+132 -59
View File
@@ -1,64 +1,133 @@
#pragma once
#include <algorithm>
#include <array>
// #include <compare>
#include <cstddef>
#include <cstdint>
namespace rest_rpc {
namespace MD5 {
//////////////////////////////////////////////////////////////////////////////
// DATA STRUCTURES
// The data representation at each round is a 4-tuple of uint32_t.
struct IntermediateData {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
template <typename CharType, std::size_t Size> struct string_literal {
constexpr string_literal() = default;
constexpr string_literal(std::basic_string_view<CharType> str) : ar{} {
for (size_t i = 0; i < Size; ++i) {
ar[i] = str[i];
}
ar[Size] = '\0';
}
constexpr string_literal(const CharType (&value)[Size + 1]) : ar{} {
for (size_t i = 0; i <= Size; ++i) {
ar[i] = value[i];
}
}
template <std::size_t Size2>
constexpr bool
operator!=(const string_literal<CharType, Size2> &other) const {
if constexpr (Size == Size2) {
for (int i = 0; i < Size; ++i) {
if ((*this)[i] != other[i])
return true;
}
return false;
} else {
return true;
}
}
template <std::size_t Size2>
constexpr bool
operator==(const string_literal<CharType, Size2> &other) const {
return !(*this != other);
}
template <size_t Size2>
string_literal<CharType, Size + Size2> constexpr
operator+(string_literal<CharType, Size2> other) const {
string_literal<CharType, Size + Size2> ret{};
for (size_t i = 0; i < Size; ++i) {
ret[i] = (*this)[i];
}
for (size_t i = 0; i < Size2; ++i) {
ret[i + Size] = other[i];
}
return ret;
}
constexpr std::size_t size() const { return Size; }
constexpr std::size_t length() const { return Size; }
constexpr operator std::string_view() const noexcept {
return std::string_view{data(), Size};
}
constexpr bool empty() const { return !Size; }
constexpr CharType &operator[](std::size_t sz) { return ar[sz]; }
constexpr const char &operator[](std::size_t sz) const { return ar[sz]; }
constexpr const CharType *data() const { return &ar[0]; }
private:
CharType ar[Size + 1];
};
//////////////////////////////////////////////////////////////////////////////
// CONSTANTS
static constexpr std::array<uint32_t, 64> kConstants = {
{0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340,
0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}};
static constexpr std::array<uint32_t, 16> kShifts = {
{7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21}};
// The initial intermediate data.
static constexpr IntermediateData kInitialIntermediateData{
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
//////////////////////////////////////////////////////////////////////////////
template <typename CharType, std::size_t Size>
string_literal(const CharType (&value)[Size])
-> string_literal<CharType, Size - 1>;
namespace MD5 {
// The implementation here is based on the pseudocode provided by Wikipedia:
// https://en.wikipedia.org/wiki/MD5#Pseudocode
struct MD5CE {
//////////////////////////////////////////////////////////////////////////////
// DATA STRUCTURES
// The data representation at each round is a 4-tuple of uint32_t.
struct IntermediateData {
uint32_t a;
uint32_t b;
uint32_t c;
uint32_t d;
};
// The input data for a single round consists of 16 uint32_t (64 bytes).
using RoundData = std::array<uint32_t, 16>;
//////////////////////////////////////////////////////////////////////////////
// CONSTANTS
static constexpr std::array<uint32_t, 64> kConstants = {
{0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a,
0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,
0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340,
0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,
0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8,
0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,
0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa,
0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,
0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92,
0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,
0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}};
static constexpr std::array<uint32_t, 16> kShifts = {
{7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21}};
// The initial intermediate data.
static constexpr IntermediateData kInitialIntermediateData{
0x67452301, 0xefcdab89, 0x98badcfe, 0x10325476};
//////////////////////////////////////////////////////////////////////////////
// PADDED MESSAGE GENERATION / EXTRACTION
// Given the message length, calculates the padded message length. There has
// to be room for the 1-byte end-of-message marker, plus 8 bytes for the
// uint64_t encoded message length, all rounded up to a multiple of 64 bytes.
static uint32_t GetPaddedMessageLength(const uint32_t n) {
static constexpr uint32_t GetPaddedMessageLength(const uint32_t n) {
return (((n + 1 + 8) + 63) / 64) * 64;
}
// Extracts the |i|th byte of a uint64_t, where |i == 0| extracts the least
// significant byte. It is expected that 0 <= i < 8.
static uint8_t ExtractByte(const uint64_t value, const uint32_t i) {
static constexpr uint8_t ExtractByte(const uint64_t value, const uint32_t i) {
// DCHECK_LT(i, 8u);
return static_cast<uint8_t>((value >> (i * 8)) & 0xff);
}
// Extracts the |i|th byte of a message of length |n|.
static uint8_t GetPaddedMessageByte(const char *data, const uint32_t n,
const uint32_t m, const uint32_t i) {
static constexpr uint8_t GetPaddedMessageByte(const char *data,
const uint32_t n,
const uint32_t m,
const uint32_t i) {
// DCHECK_LT(i, m);
// DCHECK_LT(n, m);
// DCHECK_EQ(m % 64, 0u);
@@ -79,8 +148,10 @@ struct MD5CE {
// Extracts the uint32_t starting at position |i| from the padded message
// generate by the provided input |data| of length |n|. The bytes are treated
// in little endian order.
static uint32_t GetPaddedMessageWord(const char *data, const uint32_t n,
const uint32_t m, const uint32_t i) {
static constexpr uint32_t GetPaddedMessageWord(const char *data,
const uint32_t n,
const uint32_t m,
const uint32_t i) {
// DCHECK_EQ(i % 4, 0u);
// DCHECK_LT(i, m);
// DCHECK_LT(n, m);
@@ -95,8 +166,8 @@ struct MD5CE {
}
// Given an input buffer of length |n| bytes, extracts one round worth of data
// starting at offset |i|.
static RoundData GetRoundData(const char *data, const uint32_t n,
const uint32_t m, const uint32_t i) {
static constexpr RoundData GetRoundData(const char *data, const uint32_t n,
const uint32_t m, const uint32_t i) {
// DCHECK_EQ(i % 64, 0u);
// DCHECK_LT(i, m);
// DCHECK_LT(n, m);
@@ -121,8 +192,8 @@ struct MD5CE {
//////////////////////////////////////////////////////////////////////////////
// HASH IMPLEMENTATION
// Mixes elements |b|, |c| and |d| at round |i| of the calculation.
static uint32_t CalcF(const uint32_t i, const uint32_t b, const uint32_t c,
const uint32_t d) {
static constexpr uint32_t CalcF(const uint32_t i, const uint32_t b,
const uint32_t c, const uint32_t d) {
// DCHECK_LT(i, 64u);
if (i < 16) {
return d ^ (b & (c ^ d));
@@ -134,12 +205,12 @@ struct MD5CE {
return c ^ (b | (~d));
}
}
static uint32_t CalcF(const uint32_t i,
const IntermediateData &intermediate) {
static constexpr uint32_t CalcF(const uint32_t i,
const IntermediateData &intermediate) {
return CalcF(i, intermediate.b, intermediate.c, intermediate.d);
}
// Calculates the indexing function at round |i|.
static uint32_t CalcG(const uint32_t i) {
static constexpr uint32_t CalcG(const uint32_t i) {
// DCHECK_LT(i, 64u);
if (i < 16) {
return i;
@@ -152,18 +223,20 @@ struct MD5CE {
}
}
// Calculates the rotation to be applied at round |i|.
static uint32_t GetShift(const uint32_t i) {
static constexpr uint32_t GetShift(const uint32_t i) {
// DCHECK_LT(i, 64u);
return kShifts[(i / 16) * 4 + (i % 4)];
}
// Rotates to the left the given |value| by the given |bits|.
static uint32_t LeftRotate(const uint32_t value, const uint32_t bits) {
static constexpr uint32_t LeftRotate(const uint32_t value,
const uint32_t bits) {
// DCHECK_LT(bits, 32u);
return (value << bits) | (value >> (32 - bits));
}
// Applies the ith step of mixing.
static IntermediateData ApplyStep(const uint32_t i, const RoundData &data,
const IntermediateData &intermediate) {
static constexpr IntermediateData
ApplyStep(const uint32_t i, const RoundData &data,
const IntermediateData &intermediate) {
// DCHECK_LT(i, 64u);
const uint32_t g = CalcG(i);
// DCHECK_LT(g, 16u);
@@ -176,15 +249,15 @@ struct MD5CE {
/* d */ intermediate.c};
}
// Adds two IntermediateData together.
static IntermediateData Add(const IntermediateData &intermediate1,
const IntermediateData &intermediate2) {
static constexpr IntermediateData Add(const IntermediateData &intermediate1,
const IntermediateData &intermediate2) {
return IntermediateData{
intermediate1.a + intermediate2.a, intermediate1.b + intermediate2.b,
intermediate1.c + intermediate2.c, intermediate1.d + intermediate2.d};
}
// Processes an entire message.
static IntermediateData ProcessMessage(const char *message,
const uint32_t n) {
static constexpr IntermediateData ProcessMessage(const char *message,
const uint32_t n) {
const uint32_t m = GetPaddedMessageLength(n);
IntermediateData intermediate0 = kInitialIntermediateData;
for (uint32_t offset = 0; offset < m; offset += 64) {
@@ -198,7 +271,7 @@ struct MD5CE {
}
//////////////////////////////////////////////////////////////////////////////
// HELPER FUNCTIONS
static uint32_t StringLength(const char *string) {
static constexpr uint32_t StringLength(const char *string) {
const char *end = string;
while (*end != 0)
++end;
@@ -209,27 +282,27 @@ struct MD5CE {
// (end - string));
return static_cast<uint32_t>(end - string);
}
static uint32_t SwapEndian(uint32_t a) {
static constexpr uint32_t SwapEndian(uint32_t a) {
return ((a & 0xff) << 24) | (((a >> 8) & 0xff) << 16) |
(((a >> 16) & 0xff) << 8) | ((a >> 24) & 0xff);
}
//////////////////////////////////////////////////////////////////////////////
// WRAPPER FUNCTIONS
static uint64_t Hash64(const char *data, uint32_t n) {
static constexpr uint64_t Hash64(const char *data, uint32_t n) {
IntermediateData intermediate = ProcessMessage(data, n);
return (static_cast<uint64_t>(SwapEndian(intermediate.a)) << 32) |
static_cast<uint64_t>(SwapEndian(intermediate.b));
}
static uint32_t Hash32(const char *data, uint32_t n) {
static constexpr uint32_t Hash32(const char *data, uint32_t n) {
IntermediateData intermediate = ProcessMessage(data, n);
return SwapEndian(intermediate.a);
}
};
// https://chromium.googlesource.com/chromium/src/base/+/refs/heads/main/hash/md5__internal.h
inline uint32_t MD5Hash32(const char *string) {
// https://chromium.googlesource.com/chromium/src/base/+/refs/heads/main/hash/md5_constexpr_internal.h
constexpr uint32_t MD5Hash32(const char *string) {
return MD5CE::Hash32(string, MD5CE::StringLength(string));
}
inline uint32_t MD5Hash32(const char *string, uint32_t length) {
constexpr uint32_t MD5Hash32(const char *string, uint32_t length) {
return MD5CE::Hash32(string, length);
}
+32 -21
View File
@@ -2,6 +2,7 @@
#define REST_RPC_ROUTER_H_
#include "codec.h"
#include "function_name.h"
#include "md5.hpp"
#include "meta_util.hpp"
#include "string_view.hpp"
@@ -47,35 +48,30 @@ private:
class router : asio::noncopyable {
public:
template <bool is_pub = false, typename Function>
void register_handler(std::string const &name, Function f, bool pub = false) {
uint32_t key = MD5::MD5Hash32(name.data());
if (key2func_name_.find(key) != key2func_name_.end()) {
throw std::invalid_argument("duplicate registration key !");
} else {
key2func_name_.emplace(key, name);
return register_nonmember_func<is_pub>(key, std::move(f));
}
template <bool is_pub = false, typename Function, typename Self = void>
void register_handler(std::string_view name, const Function &f,
Self *self = nullptr) {
uint32_t key = MD5::MD5Hash32(name.data(), name.length());
register_handler_impl<is_pub>(key, name, f, self);
}
template <bool is_pub = false, typename Function, typename Self>
void register_handler(std::string const &name, const Function &f,
Self *self) {
uint32_t key = MD5::MD5Hash32(name.data());
if (key2func_name_.find(key) != key2func_name_.end()) {
throw std::invalid_argument("duplicate registration key !");
} else {
key2func_name_.emplace(key, name);
return register_member_func<is_pub>(key, f, self);
}
template <auto func, bool is_pub = false, typename Self = void>
void register_handler(Self *self = nullptr) {
constexpr auto name = get_func_name<func>();
return register_handler<is_pub>(name, func, self);
}
void remove_handler(std::string const &name) {
uint32_t key = MD5::MD5Hash32(name.data());
void remove_handler(std::string_view name) {
uint32_t key = MD5::MD5Hash32(name.data(), name.length());
this->map_invokers_.erase(key);
key2func_name_.erase(key);
}
template <auto func> void register_handler() {
constexpr std::string_view name = get_func_name<func>();
remove_handler(name);
}
std::string get_name_by_key(uint32_t key) {
auto it = key2func_name_.find(key);
if (it != key2func_name_.end()) {
@@ -125,6 +121,21 @@ private:
router(const router &) = delete;
router(router &&) = delete;
template <bool is_pub = false, typename Function, typename Self = void>
auto register_handler_impl(uint32_t key, std::string_view name,
const Function &f, Self *self = nullptr) {
if (key2func_name_.find(key) != key2func_name_.end()) {
throw std::invalid_argument("duplicate registration key !");
} else {
key2func_name_.emplace(key, name);
if constexpr (std::is_void_v<Self>) {
return register_nonmember_func<is_pub>(key, f);
} else {
return register_member_func<is_pub>(key, f, self);
}
}
}
template <typename F, size_t... I, typename... Args>
static std::invoke_result_t<F, std::weak_ptr<connection>, Args...>
call_helper(const F &f, const nonstd::index_sequence<I...> &,
+5
View File
@@ -50,6 +50,11 @@ public:
router_.register_handler<is_pub>(name, f);
}
template <auto func, bool is_pub = false, typename Self = void>
void register_handler(Self *self = nullptr) {
router_.register_handler<func, is_pub>(self);
}
template <bool is_pub = false, typename Function, typename Self>
void register_handler(std::string const &name, const Function &f,
Self *self) {