diff --git a/include/address.h b/include/address.h new file mode 100644 index 0000000..4d2f42a --- /dev/null +++ b/include/address.h @@ -0,0 +1,178 @@ +/** + * Address.h + * + * An AMQP address in the "amqp://user:password@hostname:port/vhost" notation + * + * @author Emiel Bruijntjes + * @copyright 2015 Copernica BV + */ + +/** + * Include guard + */ +#pragma once + +/** + * Set up namespace + */ +namespace AMQP { + +/** + * Class definition + */ +class Address +{ +private: + /** + * Login data (username + password) + * @var Login + */ + Login _login; + + /** + * The hostname + * @var std::string + */ + std::string _hostname; + + /** + * Port number + * @var uint16_t + */ + uint16_t _port = 5672; + + /** + * The vhost + * @var std::string + */ + std::string _vhost; + +public: + /** + * Constructor to parse an address string + * The address should be in "amqp:// + * @param address + * @throws std::runtime_error + */ + Address(const char *address) : _vhost("/") + { + // must start with amqp:// + if (strncmp(address, "amqp://", 7) != 0) throw std::runtime_error("AMQP address should start with \"amqp://\""); + + // begin of the string being parsed + const char *begin = address + 7; + + // do we have a '@' to split user-data and hostname? + const char *at = strchr(begin, '@'); + + // do we have one? + if (at != nullptr) + { + // colon could split username and password + const char *colon = (const char *)memchr(begin, ':', at - begin); + + // assign the login + _login = Login( + std::string(begin, colon ? colon - begin : at - begin), + std::string(colon ? colon + 1 : "", colon ? at - colon - 1 : 0) + ); + + // set begin to the start of the hostname + begin = at + 1; + } + + // find out where the vhost is set (starts with a slash) + const char *slash = strchr(begin, '/'); + + // was a vhost set? + if (slash != nullptr && slash[1]) _vhost = slash + 1; + + // the hostname is everything until the slash, check is portnumber was set + const char *colon = strchr(begin, ':'); + + // was a portnumber specified (colon must appear before the slash of the vhost) + if (colon && (!slash || colon < slash)) + { + // a portnumber was set to + _hostname.assign(begin, colon - begin); + _port = atoi(colon + 1); + } + else + { + // no portnumber was set + _hostname.assign(begin, slash ? slash - begin : strlen(begin)); + } + } + + /** + * Destructor + */ + virtual ~Address() {} + + /** + * Expose the login data + * @return Login + */ + const Login &login() const + { + return _login; + } + + /** + * Host name + * @return std::string + */ + const std::string &hostname() const + { + return _hostname; + } + + /** + * Port number + * @return uint16_t + */ + uint16_t port() const + { + return _port; + } + + /** + * The vhost to connect to + * @return std::string + */ + const std::string &vhost() const + { + return _vhost; + } + + /** + * Cast to a string + * @return std:string + */ + operator std::string () const + { + // result object + std::string str("amqp://"); + + // append login + str.append(_login.user()).append("@").append(_login.password()).append("@").append(_hostname); + + // do we need a special portnumber? + if (_port != 5672) str.append(":").append(std::to_string(_port)); + + // append default vhost + str.append("/"); + + // do we have a special vhost? + if (_vhost != "/") str.append(_vhost); + + // done + return str; + } +}; + +/** + * End of namespace + */ +} +