diff --git a/include/address.h b/include/address.h index 870f699..a033ac2 100644 --- a/include/address.h +++ b/include/address.h @@ -50,65 +50,82 @@ private: public: /** * Constructor to parse an address string - * The address should be in "amqp:// - * @param address + * The address should start with "amqp:// + * @param data + * @param size * @throws std::runtime_error */ - Address(const char *address) : _vhost("/") + Address(const char *data, size_t size) : _vhost("/") { + // position of the last byte + const char *last = data + size; + // must start with amqp:// - if (strncmp(address, "amqp://", 7) != 0) throw std::runtime_error("AMQP address should start with \"amqp://\""); + if (size < 7 || strncmp(data, "amqp://", 7) != 0) throw std::runtime_error("AMQP address should start with \"amqp://\""); - // begin of the string being parsed - const char *begin = address + 7; + // begin of the string was parsed + data += 7; // do we have a '@' to split user-data and hostname? - const char *at = strchr(begin, '@'); + const char *at = (const char *)memchr(data, '@', last - data); // do we have one? if (at != nullptr) { + // size of the user:password + size_t loginsize = at - data; + // colon could split username and password - const char *colon = (const char *)memchr(begin, ':', at - begin); + const char *colon = (const char *)memchr(data, ':', loginsize); // assign the login _login = Login( - std::string(begin, colon ? colon - begin : at - begin), + std::string(data, colon ? colon - data : loginsize), std::string(colon ? colon + 1 : "", colon ? at - colon - 1 : 0) ); - // set begin to the start of the hostname - begin = at + 1; + // set data to the start of the hostname + data = at + 1; } // find out where the vhost is set (starts with a slash) - const char *slash = strchr(begin, '/'); + const char *slash = (const char *)memchr(data, '/', last - data); // was a vhost set? - if (slash != nullptr && slash[1]) _vhost = slash + 1; + if (slash != nullptr && last - data > 1) _vhost.assign(slash + 1, last - data - 1); // the hostname is everything until the slash, check is portnumber was set - const char *colon = strchr(begin, ':'); + const char *colon = (const char *)memchr(data, ':', last - data); // 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); + _hostname.assign(data, colon - data); + + // calculate the port + _port = atoi(std::string(colon + 1, slash ? slash - colon - 1 : last - colon - 1).data()); } else { // no portnumber was set - _hostname.assign(begin, slash ? slash - begin : strlen(begin)); + _hostname.assign(data, slash ? slash - data : last - data); } } + + /** + * Constructor to parse an address string + * The address should start with "amqp:// + * @param data + * @throws std::runtime_error + */ + Address(const char *data) : Address(data, strlen(data)) {} /** * Constructor based on std::string * @param address */ - Address(const std::string &address) : Address(address.data()) {} + Address(const std::string &address) : Address(address.data(), address.size()) {} /** * Constructor based on already known properties @@ -117,14 +134,16 @@ public: * @param login * @param vhost */ - Address(std::string host, uint16_t port, Login login, std::string vhost) : _login(std::move(login)), - _hostname(std::move(host)), - _port(port), _vhost(std::move(vhost)) {} + Address(std::string host, uint16_t port, Login login, std::string vhost) : + _login(std::move(login)), + _hostname(std::move(host)), + _port(port), + _vhost(std::move(vhost)) {} /** * Destructor */ - virtual ~Address() {} + virtual ~Address() = default; /** * Expose the login data diff --git a/tests/address.cpp b/tests/address.cpp new file mode 100644 index 0000000..2145931 --- /dev/null +++ b/tests/address.cpp @@ -0,0 +1,39 @@ +/** + * Address.cpp + * + * Test program to parse address strings + * + * @author Emiel Bruijntjes + * @copyright 2017 Copernica BV + */ + +/** + * Dependencies + */ +#include + +/** + * Main procedure + * @param argc + * @param argv + * @return int + */ +int main(int argc, const char *argv[]) +{ + // iterate over the arguments + for (int i = 1; i < argc; ++i) + { + // parse address + AMQP::Address address(argv[i]); + + // output all properties + std::cout << "user: " << address.login().user() << std::endl; + std::cout << "password: " << address.login().password() << std::endl; + std::cout << "hostname: " << address.hostname() << std::endl; + std::cout << "port: " << address.port() << std::endl; + std::cout << "vhost: " << address.vhost() << std::endl; + } + + // done + return 0; +}