summaryrefslogtreecommitdiff
path: root/cpp/src/qpid/Url.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'cpp/src/qpid/Url.cpp')
-rw-r--r--cpp/src/qpid/Url.cpp215
1 files changed, 125 insertions, 90 deletions
diff --git a/cpp/src/qpid/Url.cpp b/cpp/src/qpid/Url.cpp
index 95d6a34136..f831167dd8 100644
--- a/cpp/src/qpid/Url.cpp
+++ b/cpp/src/qpid/Url.cpp
@@ -19,57 +19,32 @@
#include "qpid/Url.h"
#include "qpid/Exception.h"
#include "qpid/Msg.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/StrError.h"
-#include <limits.h> // NB: must be before boost/spirit headers.
-#include <boost/spirit.hpp>
-#include <boost/spirit/actor.hpp>
+#include <boost/lexical_cast.hpp>
-#include <sstream>
+#include <algorithm>
-#include <sys/ioctl.h>
-#include <net/if.h>
-#include <unistd.h>
-#include <arpa/inet.h>
-#include <stdio.h>
-#include <errno.h>
+#include <string.h>
-using namespace boost::spirit;
using namespace std;
+using boost::lexical_cast;
namespace qpid {
-std::ostream& operator<<(std::ostream& os, const TcpAddress& a) {
- return os << "tcp:" << a.host << ":" << a.port;
-}
-
-std::istream& operator>>(std::istream&, const TcpAddress&);
+Url::Invalid::Invalid(const string& s) : Exception(s) {}
Url Url::getHostNameUrl(uint16_t port) {
- char name[HOST_NAME_MAX];
- if (::gethostname(name, sizeof(name)) != 0)
- throw InvalidUrl(QPID_MSG("Cannot get host name: " << qpid::sys::strError(errno)));
- return Url(TcpAddress(name, port));
+ TcpAddress address(std::string(), port);
+ if (!sys::SystemInfo::getLocalHostname(address))
+ throw Url::Invalid(QPID_MSG("Cannot get host name: " << qpid::sys::strError(errno)));
+ return Url(address);
}
-static const string LOCALHOST("127.0.0.1");
-
Url Url::getIpAddressesUrl(uint16_t port) {
Url url;
- int s = socket (PF_INET, SOCK_STREAM, 0);
- for (int i=1;;i++) {
- struct ifreq ifr;
- ifr.ifr_ifindex = i;
- if (::ioctl (s, SIOCGIFNAME, &ifr) < 0)
- break;
- /* now ifr.ifr_name is set */
- if (::ioctl (s, SIOCGIFADDR, &ifr) < 0)
- continue;
- struct sockaddr_in *sin = (struct sockaddr_in *) &ifr.ifr_addr;
- string addr(inet_ntoa(sin->sin_addr));
- if (addr != LOCALHOST)
- url.push_back(TcpAddress(addr, port));
- }
- close (s);
+ sys::SystemInfo::getLocalIpAddresses(port, url);
return url;
}
@@ -93,78 +68,138 @@ ostream& operator<<(ostream& os, const Url& url) {
return os;
}
-// Addition to boost::spirit parsers: accept any character from a
-// string. Vastly more compile-time-efficient than long rules of the
-// form: ch_p('x') | ch_p('y') |...
-//
-struct ch_in : public char_parser<ch_in> {
- ch_in(const string& chars_) : chars(chars_) {}
- bool test(char ch_) const {
- return chars.find(ch_) != string::npos;
- }
- string chars;
-};
-inline ch_in ch_in_p(const string& chars) {
- return ch_in(chars);
-}
+/** Simple recursive-descent parser for url grammar in AMQP 0-10 spec:
+
+ amqp_url = "amqp:" prot_addr_list
+ prot_addr_list = [prot_addr ","]* prot_addr
+ prot_addr = tcp_prot_addr | tls_prot_addr
+
+ tcp_prot_addr = tcp_id tcp_addr
+ tcp_id = "tcp:" | ""
+ tcp_addr = [host [":" port] ]
+ host = <as per http://www.ietf.org/rfc/rfc3986.txt>
+ port = number]]>
+*/
+class UrlParser {
+ public:
+ UrlParser(Url& u, const char* s) : url(u), text(s), end(s+strlen(s)), i(s) {}
+ bool parse() { return literal("amqp:") && list(&UrlParser::protAddr, &UrlParser::comma) && i == end; }
-/** Grammar for AMQP URLs. */
-struct UrlGrammar : public grammar<UrlGrammar>
-{
- Url& addr;
+ private:
+ typedef bool (UrlParser::*Rule)();
+
+ bool comma() { return literal(","); }
+
+ // NOTE: tcpAddr must be last since it is allowed to omit it's tcp: tag.
+ bool protAddr() { return exampleAddr() || tcpAddr(); }
+
+ bool tcpAddr() {
+ TcpAddress addr;
+ literal("tcp:"); // Don't check result, allowed to be absent.
+ return addIf(host(addr.host) && (literal(":") ? port(addr.port) : true), addr);
+ }
- UrlGrammar(Url& addr_) : addr(addr_) {}
-
- template <class ScannerT>
- struct definition {
- TcpAddress tcp;
-
- definition(const UrlGrammar& self)
- {
- first = eps_p[clear_a(self.addr)] >> amqp_url;
- amqp_url = str_p("amqp:") >> prot_addr_list >>
- !(str_p("/") >> !parameters);
- prot_addr_list = prot_addr % ',';
- prot_addr = tcp_prot_addr; // Extend for TLS etc.
-
- // TCP addresses
- tcp_prot_addr = tcp_id >> tcp_addr[push_back_a(self.addr, tcp)];
- tcp_id = !str_p("tcp:");
- tcp_addr = !(host[assign_a(tcp.host)] >> !(':' >> port));
-
- // See http://www.apps.ietf.org/rfc/rfc3986.html#sec-A
- // for real host grammar. Shortcut:
- port = uint_parser<uint16_t>()[assign_a(tcp.port)];
- host = *( unreserved | pct_encoded );
- unreserved = alnum_p | ch_in_p("-._~");
- pct_encoded = "%" >> xdigit_p >> xdigit_p;
- parameters = *anychar_p >> end_p; // Ignore, not used yet.
+ // Placeholder address type till we have multiple address types. Address is a single char.
+ bool exampleAddr () {
+ if (literal("example:") && i < end) {
+ ExampleAddress ex(*i++);
+ url.push_back(ex);
+ return true;
}
+ return false;
+ }
+
+ // FIXME aconway 2008-11-20: this does not implement http://www.ietf.org/rfc/rfc3986.txt.
+ // Works for DNS names and ipv4 literals but won't handle ipv6.
+ bool host(string& h) {
+ const char* start=i;
+ while (unreserved() || pctEncoded())
+ ;
+ if (start == i) h = LOCALHOST; // Default
+ else h.assign(start, i);
+ return true;
+ }
+
+ bool unreserved() { return (::isalnum(*i) || ::strchr("-._~", *i)) && advance(); }
- const rule<ScannerT>& start() const { return first; }
+ bool pctEncoded() { return literal("%") && hexDigit() && hexDigit(); }
- rule<ScannerT> first, amqp_url, prot_addr_list, prot_addr,
- tcp_prot_addr, tcp_id, tcp_addr, host, port,
- unreserved, pct_encoded, parameters;
+ bool hexDigit() { return i < end && ::strchr("01234567890abcdefABCDEF", *i) && advance(); }
+
+ bool port(uint16_t& p) { return decimalInt(p); }
+
+ template <class AddrType> bool addIf(bool ok, const AddrType& addr) { if (ok) url.push_back(addr); return ok; }
+
+ template <class IntType> bool decimalInt(IntType& n) {
+ const char* start = i;
+ while (decDigit())
+ ;
+ try {
+ n = lexical_cast<IntType>(string(start, i));
+ return true;
+ } catch(...) { return false; }
+ }
+
+ bool decDigit() { return i < end && ::isdigit(*i) && advance(); }
+
+ bool literal(const char* s) {
+ int n = ::strlen(s);
+ if (n <= end-i && equal(s, s+n, i)) return advance(n);
+ return false;
};
+
+ bool noop() { return true; }
+
+ /** List of item, separated by separator, with min & max bounds. */
+ bool list(Rule item, Rule separator, size_t min=0, size_t max=UNLIMITED) {
+ assert(max > 0);
+ assert(max >= min);
+ if (!(this->*item)()) return min == 0; // Empty list.
+ size_t n = 1;
+ while (n < max && i < end) {
+ if (!(this->*separator)()) break;
+ if (i == end || !(this->*item)()) return false; // Separator with no item.
+ ++n;
+ }
+ return n >= min;
+ }
+
+ /** List of items with no separator */
+ bool list(Rule item, size_t min=0, size_t max=UNLIMITED) { return list(item, &UrlParser::noop, min, max); }
+
+ bool advance(size_t n=1) {
+ if (i+n > end) return false;
+ i += n;
+ return true;
+ }
+
+ static const size_t UNLIMITED = size_t(~1);
+ static const std::string LOCALHOST;
+
+ Url& url;
+ const char* text;
+ const char* end;
+ const char* i;
};
+const string UrlParser::LOCALHOST("127.0.0.1");
+
void Url::parse(const char* url) {
- cache.clear();
- if (!boost::spirit::parse(url, UrlGrammar(*this)).full)
- throw InvalidUrl(string("Invalid AMQP url: ")+url);
+ parseNoThrow(url);
+ if (empty())
+ throw Url::Invalid(QPID_MSG("Invalid URL: " << url));
}
void Url::parseNoThrow(const char* url) {
cache.clear();
- if (!boost::spirit::parse(url, UrlGrammar(*this)).full)
+ if (!UrlParser(*this, url).parse())
clear();
}
void Url::throwIfEmpty() const {
if (empty())
- throw InvalidUrl("URL contains no addresses");
+ throw Url::Invalid("URL contains no addresses");
}
std::istream& operator>>(std::istream& is, Url& url) {