summaryrefslogtreecommitdiff
path: root/qpid/cpp/src/qpid/Url.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'qpid/cpp/src/qpid/Url.cpp')
-rw-r--r--qpid/cpp/src/qpid/Url.cpp265
1 files changed, 265 insertions, 0 deletions
diff --git a/qpid/cpp/src/qpid/Url.cpp b/qpid/cpp/src/qpid/Url.cpp
new file mode 100644
index 0000000000..ab796f4642
--- /dev/null
+++ b/qpid/cpp/src/qpid/Url.cpp
@@ -0,0 +1,265 @@
+/*
+ *
+ * Copyright (c) 2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+#include "qpid/Url.h"
+#include "qpid/Exception.h"
+#include "qpid/Msg.h"
+#include "qpid/sys/SystemInfo.h"
+#include "qpid/sys/StrError.h"
+#include "qpid/client/Connector.h"
+#include "qpid/sys/Mutex.h"
+#include <boost/lexical_cast.hpp>
+
+#include <algorithm>
+#include <vector>
+#include <string>
+
+#include <string.h>
+
+using namespace std;
+using boost::lexical_cast;
+
+namespace qpid {
+
+class ProtocolTags {
+ public:
+ bool find(const string& tag) {
+ sys::Mutex::ScopedLock l(lock);
+ return std::find(tags.begin(), tags.end(), tag) != tags.end();
+ }
+
+ void add(const string& tag) {
+ sys::Mutex::ScopedLock l(lock);
+ if (std::find(tags.begin(), tags.end(), tag) == tags.end())
+ tags.push_back(tag);
+ }
+
+ static ProtocolTags& instance() {
+ /** First call must be made while program is still single threaded.
+ * This will be the case since tags are registered in static initializers.
+ */
+ static ProtocolTags tags;
+ return tags;
+ }
+
+ private:
+ sys::Mutex lock;
+ vector<string> tags;
+};
+
+Url::Invalid::Invalid(const string& s) : Exception(s) {}
+
+Url Url::getHostNameUrl(uint16_t port) {
+ Address address("tcp", std::string(), port);
+ if (!sys::SystemInfo::getLocalHostname(address))
+ throw Url::Invalid(QPID_MSG("Cannot get host name: " << qpid::sys::strError(errno)));
+ return Url(address);
+}
+
+Url Url::getIpAddressesUrl(uint16_t port) {
+ Url url;
+ sys::SystemInfo::getLocalIpAddresses(port, url);
+ return url;
+}
+
+string Url::str() const {
+ if (cache.empty() && !this->empty()) {
+ ostringstream os;
+ os << *this;
+ cache = os.str();
+ }
+ return cache;
+}
+
+ostream& operator<<(ostream& os, const Url& url) {
+ os << "amqp:";
+ if (!url.getUser().empty()) os << url.getUser();
+ if (!url.getPass().empty()) os << "/" << url.getPass();
+ if (!(url.getUser().empty() && url.getPass().empty())) os << "@";
+ Url::const_iterator i = url.begin();
+ if (i!=url.end()) {
+ os << *i++;
+ while (i != url.end())
+ os << "," << *i++;
+ }
+ return os;
+}
+
+static const std::string TCP = "tcp";
+
+/** Simple recursive-descent parser for this grammar:
+url = ["amqp:"][ user ["/" password] "@" ] protocol_addr *("," protocol_addr)
+protocol_addr = tcp_addr / rmda_addr / ssl_addr / .. others plug-in
+tcp_addr = ["tcp:"] host [":" port]
+rdma_addr = "rdma:" host [":" port]
+ssl_addr = "ssl:" host [":" port]
+*/
+class UrlParser {
+ public:
+ UrlParser(Url& u, const char* s) : url(u), text(s), end(s+strlen(s)), i(s) {}
+ bool parse() {
+ literal("amqp:"); // Optional
+ userPass(); // Optional
+ return list(&UrlParser::protocolAddr, &UrlParser::comma) && i == end;
+ }
+
+ private:
+ typedef bool (UrlParser::*Rule)();
+
+ bool userPass() {
+ const char* at = std::find(i, end, '@');
+ if (at == end) return false;
+ const char* slash = std::find(i, at, '/');
+ url.setUser(string(i, slash));
+ const char* pass = (slash == at) ? slash : slash+1;
+ url.setPass(string(pass, at));
+ i = at+1;
+ return true;
+ }
+
+ bool comma() { return literal(","); }
+
+ bool protocolAddr() {
+ Address addr(Address::TCP, "", Address::AMQP_PORT); // Set up defaults
+ protocolTag(addr.protocol); // Optional
+ bool ok = (host(addr.host) &&
+ (literal(":") ? port(addr.port) : true));
+ if (ok) url.push_back(addr);
+ return ok;
+ }
+
+ bool protocolTag(string& result) {
+ const char* j = std::find(i,end,':');
+ if (j != end) {
+ string tag(i,j);
+ if (ProtocolTags::instance().find(tag)) {
+ i = j+1;
+ result = tag;
+ return true;
+ }
+ }
+ return false;
+ }
+
+ // TODO aconway 2008-11-20: this does not fully 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) return false;//host is required
+ else h.assign(start, i);
+ return true;
+ }
+
+ bool unreserved() { return (::isalnum(*i) || ::strchr("-._~", *i)) && advance(); }
+
+ bool pctEncoded() { return literal("%") && hexDigit() && hexDigit(); }
+
+ bool hexDigit() { return i < end && ::strchr("01234567890abcdefABCDEF", *i) && advance(); }
+
+ bool port(uint16_t& p) { return decimalInt(p); }
+
+ 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) {
+ parseNoThrow(url);
+ if (empty())
+ throw Url::Invalid(QPID_MSG("Invalid URL: " << url));
+}
+
+void Url::parseNoThrow(const char* url) {
+ cache.clear();
+ if (!UrlParser(*this, url).parse())
+ clear();
+}
+
+void Url::throwIfEmpty() const {
+ if (empty())
+ throw Url::Invalid("URL contains no addresses");
+}
+
+std::string Url::getUser() const { return user; }
+std::string Url::getPass() const { return pass; }
+void Url::setUser(const std::string& s) { user = s; }
+void Url::setPass(const std::string& s) { pass = s; }
+
+std::istream& operator>>(std::istream& is, Url& url) {
+ std::string s;
+ is >> s;
+ url.parse(s);
+ return is;
+}
+
+void Url::addProtocol(const std::string& tag) { ProtocolTags::instance().add(tag); }
+
+} // namespace qpid