From 18d4a2cd5640e7e892923511fd4b6f951ab1ebc7 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Mon, 28 Sep 2020 15:48:45 +0200 Subject: [PATCH 1/2] {auto} parameters in the address --- include/amqpcpp/address.h | 115 +++++++++++++++++++++++++++++++++++--- 1 file changed, 108 insertions(+), 7 deletions(-) diff --git a/include/amqpcpp/address.h b/include/amqpcpp/address.h index e9bab9a0..5f6ab628 100644 --- a/include/amqpcpp/address.h +++ b/include/amqpcpp/address.h @@ -12,6 +12,11 @@ */ #pragma once +/** + * Includes + */ +#include + /** * Set up namespace */ @@ -52,7 +57,12 @@ class Address * @var std::string */ std::string _vhost; - + + /** + * Extra provided options after the question mark /vhost?option=value + * @var std::map + */ + std::map _options; /** * The default port @@ -113,8 +123,32 @@ class Address // find out where the vhost is set (starts with a slash) const char *slash = (const char *)memchr(data, '/', last - data); + // if there is a slash, we search for the ? for extra options + const char *qm = static_cast(slash ? memchr(slash, '?', last - slash) : nullptr); + + // if there is a questionmark, we need to parse all options + if (qm != nullptr && last - qm > 1) + { + // we start at the question mark + const char *start = qm; + + do { + // find the next equals sign and start of the next parameter + const char *equals = (const char *)memchr(start + 1, '=', last - start - 1); + const char *next = (const char *)memchr(start + 1, '&', last - start - 1); + + // assign it to the options if we found an equals sign + if (equals) _options[std::string(start + 1, equals - start - 1)] = std::string(equals + 1, (next ? next - equals : last - equals) - 1); + + // we now have a new start + start = next; + + // keep iterating as long as there are more vars + } while (start); + } + // was a vhost set? - if (slash != nullptr && last - slash > 1) _vhost.assign(slash + 1, last - slash - 1); + if (slash != nullptr && last - slash > 1) _vhost.assign(slash + 1, (qm ? qm - slash : last - slash) - 1); // the hostname is everything until the slash, check is portnumber was set const char *colon = (const char *)memchr(data, ':', last - data); @@ -214,6 +248,15 @@ class Address return _vhost; } + /** + * Get access to the options + * @return std::map + */ + const std::map &options() const + { + return _options; + } + /** * Cast to a string * @return std::string @@ -233,7 +276,20 @@ class Address str.append("/"); // do we have a special vhost? - if (_vhost != "/") str.append(_vhost); + if (_vhost != "/" || !_options.empty()) str.append(_vhost); + + // iterate over all options, appending them + if (!_options.empty()) + { + // first append a question mark + str.push_back('?'); + + // iterate over all the options + for (const auto &kv : _options) str.append(kv.first).append("=").append(kv.second).append("&"); + + // remove the extra & + str.erase(str.size() - 1); + } // done return str; @@ -259,7 +315,10 @@ class Address if (_port != that._port) return false; // and finally the vhosts, they must match too - return _vhost == that._vhost; + if (_vhost == that._vhost) return false; + + // and the options as well + return _options == that._options; } /** @@ -296,7 +355,10 @@ class Address if (_port != that._port) return _port < that._port; // and finally compare the vhosts - return _vhost < that._vhost; + if (_vhost < that._vhost) return _vhost < that._vhost; + + // and finally lexicographically compare the options + return _options < that._options; } /** @@ -322,12 +384,51 @@ class Address // append default vhost stream << "/"; - // do we have a special vhost? - if (address._vhost != "/") stream << address._vhost; + // do we have a special vhost or options? + if (address._vhost != "/" || !address._options.empty()) stream << address._vhost; + + // iterate over all options, appending them + if (!address._options.empty()) + { + // first append a question mark + stream << '?'; + + // is this the first option? + bool first = true; + + // iterate over all the options + for (const auto &kv : address._options) + { + // write the pair to the stream + stream << (first ? "" : "&") << kv.first << "=" << kv.second; + + // no longer on first option + first = false; + } + } // done return stream; } + + /** + * Get an integer option + * @param name + * @param fallback + * @return T + */ + template ::value>::type* = nullptr> + T option(const char *name, T fallback) const + { + // find the option + auto iter = _options.find(name); + + // if not found, we return the default + if (iter == _options.end()) return fallback; + + // return the value in the map + return static_cast(atoll(iter->second.c_str())); + } }; /** From 66faa8a92518b266905e2c5944930390236a8196 Mon Sep 17 00:00:00 2001 From: Michael van der Werve Date: Tue, 29 Sep 2020 12:04:09 +0200 Subject: [PATCH 2/2] add const char * option and allow parameters to address without vhost --- include/amqpcpp/address.h | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/include/amqpcpp/address.h b/include/amqpcpp/address.h index 5f6ab628..796ac421 100644 --- a/include/amqpcpp/address.h +++ b/include/amqpcpp/address.h @@ -123,14 +123,18 @@ class Address // find out where the vhost is set (starts with a slash) const char *slash = (const char *)memchr(data, '/', last - data); - // if there is a slash, we search for the ? for extra options - const char *qm = static_cast(slash ? memchr(slash, '?', last - slash) : nullptr); + // where to start looking for the question mark, we also want to support urls where the + // hostname does not have a slash. + const char *start = slash ? slash : data; + + // we search for the ? for extra options + const char *qm = static_cast(memchr(start, '?', last - start)); // if there is a questionmark, we need to parse all options if (qm != nullptr && last - qm > 1) { - // we start at the question mark - const char *start = qm; + // we start at question mark now + start = qm; do { // find the next equals sign and start of the next parameter @@ -140,7 +144,7 @@ class Address // assign it to the options if we found an equals sign if (equals) _options[std::string(start + 1, equals - start - 1)] = std::string(equals + 1, (next ? next - equals : last - equals) - 1); - // we now have a new start + // we now have a new start, the next '&...' start = next; // keep iterating as long as there are more vars @@ -276,7 +280,7 @@ class Address str.append("/"); // do we have a special vhost? - if (_vhost != "/" || !_options.empty()) str.append(_vhost); + if (_vhost != "/") str.append(_vhost); // iterate over all options, appending them if (!_options.empty()) @@ -385,7 +389,7 @@ class Address stream << "/"; // do we have a special vhost or options? - if (address._vhost != "/" || !address._options.empty()) stream << address._vhost; + if (address._vhost != "/") stream << address._vhost; // iterate over all options, appending them if (!address._options.empty()) @@ -419,15 +423,28 @@ class Address */ template ::value>::type* = nullptr> T option(const char *name, T fallback) const + { + // find the const char* version of the option + const char *value = option(name); + + // if there is a value, convert it to integral, otherwise return the fallback + return value ? static_cast(atoll(value)) : fallback; + } + + /** + * Get a const char * option, returns nullptr if it does not exist. + * @return const char * + */ + const char *option(const char *name) const { // find the option auto iter = _options.find(name); // if not found, we return the default - if (iter == _options.end()) return fallback; + if (iter == _options.end()) return nullptr; // return the value in the map - return static_cast(atoll(iter->second.c_str())); + return iter->second.c_str(); } };