|
|
|||
File indexing completed on 2025-12-16 10:08:25
0001 /* Copyright (c) 2018-2024 Marcelo Zimbres Silva (mzimbres@gmail.com) 0002 * 0003 * Distributed under the Boost Software License, Version 1.0. (See 0004 * accompanying file LICENSE.txt) 0005 */ 0006 0007 #ifndef BOOST_REDIS_REQUEST_HPP 0008 #define BOOST_REDIS_REQUEST_HPP 0009 0010 #include <boost/redis/resp3/type.hpp> 0011 #include <boost/redis/resp3/serialization.hpp> 0012 0013 #include <string> 0014 #include <tuple> 0015 #include <algorithm> 0016 0017 // NOTE: For some commands like hset it would be a good idea to assert 0018 // the value type is a pair. 0019 0020 namespace boost::redis { 0021 0022 namespace detail{ 0023 auto has_response(std::string_view cmd) -> bool; 0024 } 0025 0026 /** \brief Creates Redis requests. 0027 * \ingroup high-level-api 0028 * 0029 * A request is composed of one or more Redis commands and is 0030 * referred to in the redis documentation as a pipeline, see 0031 * https://redis.io/topics/pipelining. For example 0032 * 0033 * @code 0034 * request r; 0035 * r.push("HELLO", 3); 0036 * r.push("FLUSHALL"); 0037 * r.push("PING"); 0038 * r.push("PING", "key"); 0039 * r.push("QUIT"); 0040 * @endcode 0041 * 0042 * \remarks 0043 * 0044 * Uses a std::string for internal storage. 0045 */ 0046 class request { 0047 public: 0048 /// Request configuration options. 0049 struct config { 0050 /** \brief If `true` calls to `connection::async_exec` will 0051 * complete with error if the connection is lost while the 0052 * request hasn't been sent yet. 0053 */ 0054 bool cancel_on_connection_lost = true; 0055 0056 /** \brief If `true` `connection::async_exec` will complete with 0057 * `boost::redis::error::not_connected` if the call happens 0058 * before the connection with Redis was established. 0059 */ 0060 bool cancel_if_not_connected = false; 0061 0062 /** \brief If `false` `connection::async_exec` will not 0063 * automatically cancel this request if the connection is lost. 0064 * Affects only requests that have been written to the socket 0065 * but remained unresponded when 0066 * `boost::redis::connection::async_run` completed. 0067 */ 0068 bool cancel_if_unresponded = true; 0069 0070 /** \brief If this request has a `HELLO` command and this flag 0071 * is `true`, the `boost::redis::connection` will move it to the 0072 * front of the queue of awaiting requests. This makes it 0073 * possible to send `HELLO` and authenticate before other 0074 * commands are sent. 0075 */ 0076 bool hello_with_priority = true; 0077 }; 0078 0079 /** \brief Constructor 0080 * 0081 * \param cfg Configuration options. 0082 */ 0083 explicit 0084 request(config cfg = config{true, false, true, true}) 0085 : cfg_{cfg} {} 0086 0087 //// Returns the number of responses expected for this request. 0088 [[nodiscard]] auto get_expected_responses() const noexcept -> std::size_t 0089 { return expected_responses_;}; 0090 0091 //// Returns the number of commands contained in this request. 0092 [[nodiscard]] auto get_commands() const noexcept -> std::size_t 0093 { return commands_;}; 0094 0095 [[nodiscard]] auto payload() const noexcept -> std::string_view 0096 { return payload_;} 0097 0098 [[nodiscard]] auto has_hello_priority() const noexcept -> auto const& 0099 { return has_hello_priority_;} 0100 0101 /// Clears the request preserving allocated memory. 0102 void clear() 0103 { 0104 payload_.clear(); 0105 commands_ = 0; 0106 expected_responses_ = 0; 0107 has_hello_priority_ = false; 0108 } 0109 0110 /// Calls std::string::reserve on the internal storage. 0111 void reserve(std::size_t new_cap = 0) 0112 { payload_.reserve(new_cap); } 0113 0114 /// Returns a const reference to the config object. 0115 [[nodiscard]] auto get_config() const noexcept -> auto const& {return cfg_; } 0116 0117 /// Returns a reference to the config object. 0118 [[nodiscard]] auto get_config() noexcept -> auto& {return cfg_; } 0119 0120 /** @brief Appends a new command to the end of the request. 0121 * 0122 * For example 0123 * 0124 * \code 0125 * request req; 0126 * req.push("SET", "key", "some string", "EX", "2"); 0127 * \endcode 0128 * 0129 * will add the `set` command with value "some string" and an 0130 * expiration of 2 seconds. 0131 * 0132 * \param cmd The command e.g redis or sentinel command. 0133 * \param args Command arguments. 0134 * \tparam Ts Non-string types will be converted to string by calling `boost_redis_to_bulk` on each argument. This function must be made available over ADL and must have the following signature 0135 * 0136 * @code 0137 * void boost_redis_to_bulk(std::string& to, T const& t); 0138 * { 0139 * boost::redis::resp3::boost_redis_to_bulk(to, serialize(t)); 0140 * } 0141 * @endcode 0142 * 0143 * See cpp20_serialization.cpp 0144 */ 0145 template <class... Ts> 0146 void push(std::string_view cmd, Ts const&... args) 0147 { 0148 auto constexpr pack_size = sizeof...(Ts); 0149 resp3::add_header(payload_, resp3::type::array, 1 + pack_size); 0150 resp3::add_bulk(payload_, cmd); 0151 resp3::add_bulk(payload_, std::tie(std::forward<Ts const&>(args)...)); 0152 0153 check_cmd(cmd); 0154 } 0155 0156 /** @brief Appends a new command to the end of the request. 0157 * 0158 * This overload is useful for commands that have a key and have a 0159 * dynamic range of arguments. For example 0160 * 0161 * @code 0162 * std::map<std::string, std::string> map 0163 * { {"key1", "value1"} 0164 * , {"key2", "value2"} 0165 * , {"key3", "value3"} 0166 * }; 0167 * 0168 * request req; 0169 * req.push_range("HSET", "key", std::cbegin(map), std::cend(map)); 0170 * @endcode 0171 * 0172 * \param cmd The command e.g. Redis or Sentinel command. 0173 * \param key The command key. 0174 * \param begin Iterator to the begin of the range. 0175 * \param end Iterator to the end of the range. 0176 * \tparam Ts Non-string types will be converted to string by calling `boost_redis_to_bulk` on each argument. This function must be made available over ADL and must have the following signature 0177 * 0178 * @code 0179 * void boost_redis_to_bulk(std::string& to, T const& t); 0180 * { 0181 * boost::redis::resp3::boost_redis_to_bulk(to, serialize(t)); 0182 * } 0183 * @endcode 0184 * 0185 * See cpp20_serialization.cpp 0186 */ 0187 template <class ForwardIterator> 0188 void 0189 push_range( 0190 std::string_view const& cmd, 0191 std::string_view const& key, 0192 ForwardIterator begin, 0193 ForwardIterator end, 0194 typename std::iterator_traits<ForwardIterator>::value_type * = nullptr) 0195 { 0196 using value_type = typename std::iterator_traits<ForwardIterator>::value_type; 0197 0198 if (begin == end) 0199 return; 0200 0201 auto constexpr size = resp3::bulk_counter<value_type>::size; 0202 auto const distance = std::distance(begin, end); 0203 resp3::add_header(payload_, resp3::type::array, 2 + size * distance); 0204 resp3::add_bulk(payload_, cmd); 0205 resp3::add_bulk(payload_, key); 0206 0207 for (; begin != end; ++begin) 0208 resp3::add_bulk(payload_, *begin); 0209 0210 check_cmd(cmd); 0211 } 0212 0213 /** @brief Appends a new command to the end of the request. 0214 * 0215 * This overload is useful for commands that have a dynamic number 0216 * of arguments and don't have a key. For example 0217 * 0218 * \code 0219 * std::set<std::string> channels 0220 * { "channel1" , "channel2" , "channel3" } 0221 * 0222 * request req; 0223 * req.push("SUBSCRIBE", std::cbegin(channels), std::cend(channels)); 0224 * \endcode 0225 * 0226 * \param cmd The Redis command 0227 * \param begin Iterator to the begin of the range. 0228 * \param end Iterator to the end of the range. 0229 * \tparam ForwardIterator If the value type is not a std::string it will be converted to a string by calling `boost_redis_to_bulk`. This function must be made available over ADL and must have the following signature 0230 * 0231 * @code 0232 * void boost_redis_to_bulk(std::string& to, T const& t); 0233 * { 0234 * boost::redis::resp3::boost_redis_to_bulk(to, serialize(t)); 0235 * } 0236 * @endcode 0237 * 0238 * See cpp20_serialization.cpp 0239 */ 0240 template <class ForwardIterator> 0241 void 0242 push_range( 0243 std::string_view const& cmd, 0244 ForwardIterator begin, 0245 ForwardIterator end, 0246 typename std::iterator_traits<ForwardIterator>::value_type * = nullptr) 0247 { 0248 using value_type = typename std::iterator_traits<ForwardIterator>::value_type; 0249 0250 if (begin == end) 0251 return; 0252 0253 auto constexpr size = resp3::bulk_counter<value_type>::size; 0254 auto const distance = std::distance(begin, end); 0255 resp3::add_header(payload_, resp3::type::array, 1 + size * distance); 0256 resp3::add_bulk(payload_, cmd); 0257 0258 for (; begin != end; ++begin) 0259 resp3::add_bulk(payload_, *begin); 0260 0261 check_cmd(cmd); 0262 } 0263 0264 /** @brief Appends a new command to the end of the request. 0265 * 0266 * Equivalent to the overload taking a range of begin and end 0267 * iterators. 0268 * 0269 * \param cmd Redis command. 0270 * \param key Redis key. 0271 * \param range Range to send e.g. `std::map`. 0272 * \tparam A type that can be passed to `std::cbegin()` and `std::cend()`. 0273 */ 0274 template <class Range> 0275 void 0276 push_range( 0277 std::string_view const& cmd, 0278 std::string_view const& key, 0279 Range const& range, 0280 decltype(std::begin(range)) * = nullptr) 0281 { 0282 using std::begin; 0283 using std::end; 0284 push_range(cmd, key, begin(range), end(range)); 0285 } 0286 0287 /** @brief Appends a new command to the end of the request. 0288 * 0289 * Equivalent to the overload taking a range of begin and end 0290 * iterators. 0291 * 0292 * \param cmd Redis command. 0293 * \param range Range to send e.g. `std::map`. 0294 * \tparam A type that can be passed to `std::cbegin()` and `std::cend()`. 0295 */ 0296 template <class Range> 0297 void 0298 push_range( 0299 std::string_view cmd, 0300 Range const& range, 0301 decltype(std::cbegin(range)) * = nullptr) 0302 { 0303 using std::cbegin; 0304 using std::cend; 0305 push_range(cmd, cbegin(range), cend(range)); 0306 } 0307 0308 private: 0309 void check_cmd(std::string_view cmd) 0310 { 0311 ++commands_; 0312 0313 if (!detail::has_response(cmd)) 0314 ++expected_responses_; 0315 0316 if (cmd == "HELLO") 0317 has_hello_priority_ = cfg_.hello_with_priority; 0318 } 0319 0320 config cfg_; 0321 std::string payload_; 0322 std::size_t commands_ = 0; 0323 std::size_t expected_responses_ = 0; 0324 bool has_hello_priority_ = false; 0325 }; 0326 0327 } // boost::redis::resp3 0328 0329 #endif // BOOST_REDIS_REQUEST_HPP
| [ Source navigation ] | [ Diff markup ] | [ Identifier search ] | [ general search ] |
|
This page was automatically generated by the 2.3.7 LXR engine. The LXR team |
|