PROTON-1935: [cpp] connection configuration and default connect() docs/connect-config.md: describes connection configuration JSON format.
container::connect() connects using the default configuration file Additional API in proton::connect_config allows the user to parse configuration and apply to a connection_options object for more flexible use. Project: http://git-wip-us.apache.org/repos/asf/qpid-proton/repo Commit: http://git-wip-us.apache.org/repos/asf/qpid-proton/commit/b164d99c Tree: http://git-wip-us.apache.org/repos/asf/qpid-proton/tree/b164d99c Diff: http://git-wip-us.apache.org/repos/asf/qpid-proton/diff/b164d99c Branch: refs/heads/go1 Commit: b164d99c80129a2a24ae7203846579569c9cf3b5 Parents: 348e9da Author: Alan Conway <[email protected]> Authored: Tue Sep 18 09:31:47 2018 -0400 Committer: Alan Conway <[email protected]> Committed: Tue Sep 18 11:03:25 2018 -0400 ---------------------------------------------------------------------- INSTALL.md | 2 + c/include/proton/cproton.i | 1 + c/include/proton/version.h.in | 2 + cpp/CMakeLists.txt | 20 ++- cpp/docs/CMakeLists.txt | 2 +- cpp/docs/user.doxygen.in | 3 +- cpp/include/proton/connect_config.hpp | 49 +++++++ cpp/include/proton/container.hpp | 6 + cpp/src/connect_config.cpp | 219 +++++++++++++++++++++++++++++ cpp/src/connect_config_dummy.cpp | 31 ++++ cpp/src/connect_config_test.cpp | 142 +++++++++++++++++++ cpp/src/connection_options.cpp | 1 + cpp/src/container.cpp | 4 + cpp/src/proactor_container_impl.cpp | 7 + cpp/src/proactor_container_impl.hpp | 1 + cpp/src/test_bits.hpp | 8 ++ docs/connect_config.md | 42 ++++++ tools/cmake/Modules/FindJsonCpp.cmake | 70 +++++++++ 18 files changed, 607 insertions(+), 3 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/INSTALL.md ---------------------------------------------------------------------- diff --git a/INSTALL.md b/INSTALL.md index 430d648..76e6af2 100644 --- a/INSTALL.md +++ b/INSTALL.md @@ -20,6 +20,7 @@ Linux dependencies - GCC 4.4+ - Cyrus SASL 2.1+ (for SASL support) - OpenSSL 1.0+ (for SSL support) + - JsonCpp 1.8+ for C++ connection configuration file support Windows dependencies @@ -46,6 +47,7 @@ language. $ yum install swig # Required for all bindings $ yum install python-devel # Python $ yum install ruby-devel rubygem-minitest # Ruby + $ yum install jsoncpp-devel # C++ optional config file # Dependencies needed to generate documentation $ yum install epydoc # Python http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/c/include/proton/cproton.i ---------------------------------------------------------------------- diff --git a/c/include/proton/cproton.i b/c/include/proton/cproton.i index 430deca..c642438 100644 --- a/c/include/proton/cproton.i +++ b/c/include/proton/cproton.i @@ -37,6 +37,7 @@ typedef unsigned long int uintptr_t; %include "proton/import_export.h" %ignore _PROTON_VERSION_H; +%ignore PN_INSTALL_PREFIX; %include "proton/version.h" /* We cannot safely just wrap pn_bytes_t but each language binding must have a typemap for it - presumably to a string type */ http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/c/include/proton/version.h.in ---------------------------------------------------------------------- diff --git a/c/include/proton/version.h.in b/c/include/proton/version.h.in index 133b0bb..13ee411 100644 --- a/c/include/proton/version.h.in +++ b/c/include/proton/version.h.in @@ -26,4 +26,6 @@ #define PN_VERSION_MINOR @PN_VERSION_MINOR@ #define PN_VERSION_POINT @PN_VERSION_POINT@ +#define PN_INSTALL_PREFIX "@CMAKE_INSTALL_PREFIX@" + #endif /* version.h */ http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/cpp/CMakeLists.txt b/cpp/CMakeLists.txt index d0b3cfb..35f5478 100644 --- a/cpp/CMakeLists.txt +++ b/cpp/CMakeLists.txt @@ -27,6 +27,16 @@ include(versions.cmake) set (BUILD_CPP_03 OFF CACHE BOOL "Compile the C++ binding as C++03 even when C++11 is available") +# Check for JSON-CPP support for connection configuration +find_package(JsonCpp) +option(ENABLE_JSONCPP "Use jsoncpp parser for connection configuration" ${JsonCpp_FOUND}) +if (ENABLE_JSONCPP) + set(CONNECT_CONFIG_SRC src/connect_config.cpp) + set(CONNECT_CONFIG_LIBS ${JsonCpp_LIBRARY}) +else() + set(CONNECT_CONFIG_SRC src/connect_config_dummy.cpp) +endif() + # This effectively checks for cmake version 3.1 or later if (DEFINED CMAKE_CXX_COMPILE_FEATURES) if (BUILD_CPP_03) @@ -150,6 +160,7 @@ set(qpid-proton-cpp-source src/uuid.cpp src/value.cpp src/work_queue.cpp + ${CONNECT_CONFIG_SRC} ) set_source_files_properties ( @@ -167,7 +178,7 @@ if(BUILD_STATIC_LIBS) add_library(qpid-proton-cpp-static STATIC ${qpid-proton-cpp-source}) endif(BUILD_STATIC_LIBS) -target_link_libraries (qpid-proton-cpp LINK_PRIVATE ${PLATFORM_LIBS} qpid-proton-core qpid-proton-proactor) +target_link_libraries (qpid-proton-cpp LINK_PRIVATE ${PLATFORM_LIBS} qpid-proton-core qpid-proton-proactor ${CONNECT_CONFIG_LIBS}) set_target_properties ( qpid-proton-cpp @@ -258,3 +269,10 @@ add_cpp_test(container_test) add_cpp_test(url_test) add_cpp_test(reconnect_test) add_cpp_test(link_test) +if (ENABLE_JSONCPP) + # Directories needed by connect_config tests + file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/testdata/.config/messaging") + add_cpp_test(connect_config_test) + set_tests_properties(cpp-connect_config_test PROPERTIES + WORKING_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}") +endif() http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/docs/CMakeLists.txt ---------------------------------------------------------------------- diff --git a/cpp/docs/CMakeLists.txt b/cpp/docs/CMakeLists.txt index d512d15..690230d 100644 --- a/cpp/docs/CMakeLists.txt +++ b/cpp/docs/CMakeLists.txt @@ -24,7 +24,7 @@ if (DOXYGEN_FOUND) ${CMAKE_CURRENT_SOURCE_DIR}/user.doxygen.in ${CMAKE_CURRENT_BINARY_DIR}/user.doxygen) - file(GLOB_RECURSE headers ../include/proton/*.hpp) + file(GLOB_RECURSE sources ../include/proton/*.hpp ../../connect_config.md) add_custom_target (docs-cpp COMMAND ${CMAKE_COMMAND} -E remove_directory html # get rid of old files COMMAND ${DOXYGEN_EXECUTABLE} user.doxygen http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/docs/user.doxygen.in ---------------------------------------------------------------------- diff --git a/cpp/docs/user.doxygen.in b/cpp/docs/user.doxygen.in index 6bcd1bd..84375d8 100644 --- a/cpp/docs/user.doxygen.in +++ b/cpp/docs/user.doxygen.in @@ -55,7 +55,8 @@ WARNINGS = YES INPUT = @CMAKE_SOURCE_DIR@/cpp/include \ @CMAKE_SOURCE_DIR@/cpp/docs \ - @CMAKE_SOURCE_DIR@/cpp/examples + @CMAKE_SOURCE_DIR@/cpp/examples \ + @CMAKE_SOURCE_DIR@/docs/connect_config.md FILE_PATTERNS = *.hpp *.md *.dox EXCLUDE_PATTERNS = @CMAKE_SOURCE_DIR@/cpp/examples/*.?pp \ @CMAKE_SOURCE_DIR@/cpp/include/proton/internal/*.hpp http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/include/proton/connect_config.hpp ---------------------------------------------------------------------- diff --git a/cpp/include/proton/connect_config.hpp b/cpp/include/proton/connect_config.hpp new file mode 100644 index 0000000..be5c7ac --- /dev/null +++ b/cpp/include/proton/connect_config.hpp @@ -0,0 +1,49 @@ +#ifndef CONNECT_CONFIG_HPP +#define CONNECT_CONFIG_HPP + +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <proton/connection_options.hpp> + +namespace proton { + +class connection_options; + +/// *Unsettled API* +namespace connect_config { + +/// @return name of the default connection configuration file +/// @throw proton::error if no default file is found +PN_CPP_EXTERN std::string default_file(); + +/// Parse configuration from @p is and update @p opts +/// @param is input stream for configuration file/string +/// @param opts [out] connection options to update +/// @return address suitable for container::connect() from configuration +PN_CPP_EXTERN std::string parse(std::istream& is, connection_options& opts); + +/// Parse configuration from default_file() and update @p opts +/// @param opts [out] connection options to update +/// @return address suitable for container::connect() from configuration +PN_CPP_EXTERN std::string parse_default(connection_options& opts); + +}} // namespace proton::connect_config + +#endif // CONNECT_CONFIG_HPP http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/include/proton/container.hpp ---------------------------------------------------------------------- diff --git a/cpp/include/proton/container.hpp b/cpp/include/proton/container.hpp index 27ac498..362dba0 100644 --- a/cpp/include/proton/container.hpp +++ b/cpp/include/proton/container.hpp @@ -113,6 +113,12 @@ class PN_CPP_CLASS_EXTERN container { /// @copydetails returned PN_CPP_EXTERN returned<connection> connect(const std::string& conn_url); + /// Connect using the default @ref connect_config + /// FIXME aconway 2018-08-07: cmake - copy connect_config.md into C++ doc + /// + /// @copydetails returned + PN_CPP_EXTERN returned<connection> connect(); + /// Listen for new connections on `listen_url`. /// /// If the listener opens successfully, listen_handler::on_open() is called. http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/src/connect_config.cpp ---------------------------------------------------------------------- diff --git a/cpp/src/connect_config.cpp b/cpp/src/connect_config.cpp new file mode 100644 index 0000000..0dc577a --- /dev/null +++ b/cpp/src/connect_config.cpp @@ -0,0 +1,219 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "msg.hpp" + +#include <proton/connect_config.hpp> +#include <proton/error.hpp> +#include <proton/ssl.hpp> + +#include <proton/version.h> + +#include <json/value.h> +#include <json/reader.h> + +#include <cstdlib> +#include <fstream> + +using namespace Json; +using std::string; + +namespace { +const char *type_name(ValueType t) { + switch (t) { + case nullValue: return "null"; + case intValue: return "int"; + case uintValue: return "uint"; + case realValue: return "real"; + case stringValue: return "string"; + case booleanValue: return "boolean"; + case arrayValue: return "array"; + case objectValue: return "object"; + default: return "unknown"; + } +} +} // namespace + +namespace std { +ostream& operator<<(ostream& o, ValueType t) { return o << type_name(t); } +} + +namespace proton { +namespace connect_config { + +namespace { + +void raise(const string& message) { + throw proton::error("connection configuration: " + message); +} + +Value validate(ValueType t, const Value& v, const string& name) { + if (v.type() != t) + raise(msg() << " '" << name << "' expected " << t << ", found " << v.type()); + return v; +} + +Value get(ValueType t, const Value& obj, const char *key, const Value& dflt=Value()) { + Value v = obj ? obj.get(key, dflt) : dflt; + return validate(t, v, key); +} + +bool get_bool(const Value& obj, const char *key, bool dflt) { + return get(booleanValue, obj, key, dflt).asBool(); +} + +string get_string(const Value& obj, const char *key, const string& dflt) { + return get(stringValue, obj, key, dflt).asString(); +} + +static const string HOME("HOME"); +static const string ENV_VAR("MESSAGING_CONNECT_FILE"); +static const string FILE_NAME("connect.json"); +static const string HOME_FILE_NAME("/.config/messaging/" + FILE_NAME); +static const string ETC_FILE_NAME("/etc/messaging/" + FILE_NAME); + +bool exists(const string& name) { return std::ifstream(name.c_str()).good(); } + +void parse_sasl(Value root, connection_options& opts) { + Value sasl = root.get("sasl", Value()); + opts.sasl_enabled(get_bool(sasl, "enable", true)); + if (sasl) { + validate(objectValue, sasl, "sasl"); + opts.sasl_allow_insecure_mechs(get_bool(sasl, "allow_insecure", false)); + Value mechs = sasl.get("mechanisms", Value()); + switch (mechs.type()) { + case nullValue: + break; + case stringValue: + opts.sasl_allowed_mechs(mechs.asString()); + break; + case arrayValue: { + std::ostringstream s; + for (ArrayIndex i= 0; i < mechs.size(); ++i) { + Value v = mechs.get(i, Value()); + if (v.type() != stringValue) { + raise(msg() << "'sasl/mechanisms' expect string elements, found " << v.type()); + } + if (i > 0) s << " "; + s << v.asString(); + } + opts.sasl_allowed_mechs(s.str().c_str()); + break; + } + default: + raise(msg() << "'mechanisms' expected string or array, found " << mechs.type()); + } + } +} + +void parse_tls(const string& scheme, Value root, connection_options& opts) { + Value tls = root.get("tls", Value()); + if (tls) { + validate(objectValue, tls, "tls"); + if (scheme != "amqps") { + raise(msg() << "'tls' object is not allowed unless scheme is \"amqps\""); + } + string ca = get_string(tls, "ca", ""); + bool verify = get_bool(tls, "verify", true); + Value cert = get(stringValue, tls, "cert"); + ssl::verify_mode mode = verify ? ssl::VERIFY_PEER_NAME : ssl::ANONYMOUS_PEER; + if (cert) { + Value key = get(stringValue, tls, "key"); + ssl_certificate cert2 = key ? + ssl_certificate(cert.asString(), key.asString()) : + ssl_certificate(cert.asString()); + opts.ssl_client_options(ssl_client_options(cert2, ca, mode)); + } else { + ssl_client_options(ssl_client_options(ca, mode)); + } + } +} + +} // namespace + +std::string parse(std::istream& is, connection_options& opts) { + Value root; + is >> root; + + string scheme = get_string(root, "scheme", "amqps"); + if (scheme != "amqp" && scheme != "amqps") { + raise(msg() << "'scheme' must be \"amqp\" or \"amqps\""); + } + + string host = get_string(root, "host", ""); + opts.virtual_host(host.c_str()); + + Value port = root.get("port", scheme); + if (!port.isIntegral() && !port.isString()) { + raise(msg() << "'port' expected string or integer, found " << port.type()); + } + + Value user = root.get("user", Value()); + if (user) opts.user(validate(stringValue, user, "user").asString()); + Value password = root.get("password", Value()); + if (password) opts.password(validate(stringValue, password, "password").asString()); + + parse_sasl(root, opts); + parse_tls(scheme, root, opts); + return host + ":" + port.asString(); +} + +string default_file() { + /* Use environment variable if set */ + const char *env_path = getenv(ENV_VAR.c_str()); + if (env_path) return env_path; + /* current directory */ + if (exists(FILE_NAME)) return FILE_NAME; + /* $HOME/.config/messaging/FILE_NAME */ + const char *home = getenv(HOME.c_str()); + if (home) { + string path = home + HOME_FILE_NAME; + if (exists(path)) return path; + } + /* INSTALL_PREFIX/etc/messaging/FILE_NAME */ + if (PN_INSTALL_PREFIX && *PN_INSTALL_PREFIX) { + string path = PN_INSTALL_PREFIX + ETC_FILE_NAME; + if (exists(path)) return path; + } + /* /etc/messaging/FILE_NAME */ + if (exists(ETC_FILE_NAME)) return ETC_FILE_NAME; + raise("no default configuration"); + return ""; // Never get here, keep compiler happy +} + +string parse_default(connection_options& opts) { + string name = default_file(); + std::ifstream f; + try { + f.exceptions(~std::ifstream::goodbit); + f.open(name); + } catch (const std::exception& e) { + raise(msg() << "error opening '" << name << "': " << e.what()); + } + try { + return parse(f, opts); + } catch (const std::exception& e) { + raise(msg() << "error parsing '" << name << "': " << e.what()); + } catch (...) { + raise(msg() << "error parsing '" << name); + } + return ""; // Never get here, keep compiler happy +} + +}} // namespace proton::connect_config http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/src/connect_config_dummy.cpp ---------------------------------------------------------------------- diff --git a/cpp/src/connect_config_dummy.cpp b/cpp/src/connect_config_dummy.cpp new file mode 100644 index 0000000..4d46726 --- /dev/null +++ b/cpp/src/connect_config_dummy.cpp @@ -0,0 +1,31 @@ +/* +* Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 <proton/connect_config.hpp> +#include <proton/error.hpp> + +namespace proton { +namespace connect_config { +namespace { const error nope("connection configuration is not supported"); } + +std::string default_file() { throw nope; } +std::string parse(std::istream& is, connection_options& opts) { throw nope; } +std::string parse_default(proton::connection_options&) { throw nope; } + +}} // namespace proton::connect_config http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/src/connect_config_test.cpp ---------------------------------------------------------------------- diff --git a/cpp/src/connect_config_test.cpp b/cpp/src/connect_config_test.cpp new file mode 100644 index 0000000..17f0c4b --- /dev/null +++ b/cpp/src/connect_config_test.cpp @@ -0,0 +1,142 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you 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 "test_bits.hpp" + +#include "proton/connect_config.hpp" +#include "proton/connection.hpp" +#include "proton/connection_options.hpp" +#include "proton/container.hpp" +#include "proton/error_condition.hpp" +#include "proton/listener.hpp" +#include "proton/messaging_handler.hpp" +#include "proton/transport.hpp" + +#include <sstream> +#include <fstream> +#include <cstdio> + +#include <stdlib.h> + +namespace { + +using namespace std; +using namespace proton; +using proton::error_condition; + +string configure(connection_options& opts, const string& config) { + istringstream is(config); + return connect_config::parse(is, opts); +} + +void test_default_file() { + // Default file locations in order of preference. + ::setenv("MESSAGING_CONNECT_FILE", "environment", 1); + ofstream("connect.json") << "{ \"host\": \"current\" }" << endl; + ::setenv("HOME", "testdata", 1); + ofstream("testdata/.config/messaging/connect.json") << "{ \"host\": \".config\" }" << endl; + ASSERT_EQUAL("environment", connect_config::default_file()); + ::unsetenv("MESSAGING_CONNECT_FILE"); + ASSERT_EQUAL("connect.json", connect_config::default_file()); + remove("connect.json"); + ASSERT_EQUAL("testdata/.config/messaging/connect.json", connect_config::default_file()); + remove("testdata/.config/messaging/connect.json"); + + // We can't fully test prefix and /etc locations, we have no control. + try { + ASSERT_SUBSTRING("/etc/messaging", connect_config::default_file()); + } catch (...) {} // OK if not there +} + +void test_addr() { + connection_options opts; + ASSERT_EQUAL("foo:bar", configure(opts, "{ \"host\":\"foo\", \"port\":\"bar\" }")); + ASSERT_EQUAL("foo:1234", configure(opts, "{ \"host\":\"foo\", \"port\":\"1234\" }")); + ASSERT_EQUAL(":amqps", configure(opts, "{}")); + ASSERT_EQUAL(":amqp", configure(opts, "{\"scheme\":\"amqp\"}")); + ASSERT_EQUAL("foo:bar", configure(opts, "{ \"host\":\"foo\", /* inline comment */\"port\":\"bar\" // end of line comment\n}")); + + ASSERT_THROWS_MSG(error, "'scheme' must be", configure(opts, "{\"scheme\":\"bad\"}")); + ASSERT_THROWS_MSG(error, "'scheme' expected string, found boolean", configure(opts, "{\"scheme\":true}")); + ASSERT_THROWS_MSG(error, "'port' expected string or integer, found boolean", configure(opts, "{\"port\":true}")); + ASSERT_THROWS_MSG(error, "'host' expected string, found boolean", configure(opts, "{\"host\":true}")); +} + +class test_handler : public messaging_handler { + protected: + string config_; + listener listener_; + bool opened_; + proton::error_condition error_; + + public: + + void on_container_start(container& c) PN_CPP_OVERRIDE { + listener_ = c.listen("//:0"); + } + + virtual void check_connection(connection& c) {} + + void on_connection_open(connection& c) PN_CPP_OVERRIDE { + if (!c.active()) { // Server side + opened_ = true; + check_connection(c); + listener_.stop(); + c.close(); + } + } + + void on_error(const error_condition& e) PN_CPP_OVERRIDE { + FAIL("unexpected error " << e); + } + + void run(const string& config) { + config_ = config; + container(*this).run(); + } +}; + +class test_default_connect : public test_handler { + public: + + void on_container_start(container& c) PN_CPP_OVERRIDE { + test_handler::on_container_start(c); + ofstream os("connect.json"); + ASSERT(os << "{ \"port\": " << listener_.port() << "}" << endl); + os.close(); + c.connect(); + } + + void run() { + container(*this).run(); + ASSERT(opened_); + ASSERT(!error_); + } +}; + +} // namespace + + +int main(int argc, char** argv) { + int failed = 0; + RUN_ARGV_TEST(failed, test_default_file()); + RUN_ARGV_TEST(failed, test_addr()); + RUN_ARGV_TEST(failed, test_default_connect().run()); + return failed; +} http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/src/connection_options.cpp ---------------------------------------------------------------------- diff --git a/cpp/src/connection_options.cpp b/cpp/src/connection_options.cpp index 1903536..fdc770e 100644 --- a/cpp/src/connection_options.cpp +++ b/cpp/src/connection_options.cpp @@ -217,4 +217,5 @@ void connection_options::apply_unbound_client(pn_transport_t *t) const { impl_-> void connection_options::apply_unbound_server(pn_transport_t *t) const { impl_->apply_sasl(t); impl_->apply_ssl(t, false); impl_->apply_transport(t); } messaging_handler* connection_options::handler() const { return impl_->handler.value; } + } // namespace proton http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/src/container.cpp ---------------------------------------------------------------------- diff --git a/cpp/src/container.cpp b/cpp/src/container.cpp index cfc0196..013fb4b 100644 --- a/cpp/src/container.cpp +++ b/cpp/src/container.cpp @@ -83,6 +83,10 @@ returned<connection> container::connect(const std::string& url, const connection return impl_->connect(url, opts); } +returned<connection> container::connect() { + return impl_->connect(); +} + listener container::listen(const std::string& url, listen_handler& l) { return impl_->listen(url, l); } void container::run() { impl_->run(1); } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/src/proactor_container_impl.cpp ---------------------------------------------------------------------- diff --git a/cpp/src/proactor_container_impl.cpp b/cpp/src/proactor_container_impl.cpp index 01aea77..e0057cd 100644 --- a/cpp/src/proactor_container_impl.cpp +++ b/cpp/src/proactor_container_impl.cpp @@ -20,6 +20,7 @@ #include "proactor_container_impl.hpp" #include "proactor_work_queue_impl.hpp" +#include "proton/connect_config.hpp" #include "proton/error_condition.hpp" #include "proton/listener.hpp" #include "proton/listen_handler.hpp" @@ -350,6 +351,12 @@ returned<connection> container::impl::connect( return make_returned<proton::connection>(pnc); } +returned<connection> container::impl::connect() { + connection_options opts; + std::string addr = connect_config::parse_default(opts); + return connect(addr, opts); +} + returned<sender> container::impl::open_sender(const std::string &urlstr, const proton::sender_options &o1, const connection_options &o2) { proton::url url(urlstr); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/src/proactor_container_impl.hpp ---------------------------------------------------------------------- diff --git a/cpp/src/proactor_container_impl.hpp b/cpp/src/proactor_container_impl.hpp index dcc9381..912a036 100644 --- a/cpp/src/proactor_container_impl.hpp +++ b/cpp/src/proactor_container_impl.hpp @@ -71,6 +71,7 @@ class container::impl { impl(container& c, const std::string& id, messaging_handler* = 0); ~impl(); std::string id() const { return id_; } + returned<connection> connect(); returned<connection> connect(const std::string&, const connection_options&); returned<sender> open_sender( const std::string&, const proton::sender_options &, const connection_options &); http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/cpp/src/test_bits.hpp ---------------------------------------------------------------------- diff --git a/cpp/src/test_bits.hpp b/cpp/src/test_bits.hpp index 28271f0..50ea2ee 100644 --- a/cpp/src/test_bits.hpp +++ b/cpp/src/test_bits.hpp @@ -52,6 +52,11 @@ inline void assert_equalish(T want, T got, T delta, const std::string& what) throw fail(MSG(what << " " << want << " !=~ " << got)); } +void assert_substring(const std::string& want, const std::string& got, const std::string& what) { + if (got.find(want) == std::string::npos) + throw fail(MSG(what << " '" << want << "' not found in '" << got << "'")); +} + #define FAIL_MSG(WHAT) (MSG(__FILE__ << ":" << __LINE__ << ": " << WHAT).str()) #define FAIL(WHAT) throw test::fail(FAIL_MSG(WHAT)) #define ASSERT(TEST) do { if (!(TEST)) FAIL("failed ASSERT(" #TEST ")"); } while(false) @@ -59,7 +64,10 @@ inline void assert_equalish(T want, T got, T delta, const std::string& what) test::assert_equal((WANT), (GOT), FAIL_MSG("failed ASSERT_EQUAL(" #WANT ", " #GOT ")")) #define ASSERT_EQUALISH(WANT, GOT, DELTA) \ test::assert_equalish((WANT), (GOT), (DELTA), FAIL_MSG("failed ASSERT_EQUALISH(" #WANT ", " #GOT ")")) +#define ASSERT_SUBSTRING(WANT, GOT) \ + test::assert_substring((WANT), (GOT), FAIL_MSG("failed ASSERT_SUBSTRING(" #WANT ", " #GOT ")")) #define ASSERT_THROWS(WANT, EXPR) do { try { EXPR; FAIL("Expected " #WANT); } catch(const WANT&) {} } while(0) +#define ASSERT_THROWS_MSG(CLASS, MSG, EXPR) do { try { EXPR; FAIL("Expected " #CLASS); } catch(const CLASS& e) { ASSERT_SUBSTRING((MSG), e.what()); } } while(0) #define RUN_TEST(BAD_COUNT, TEST) \ do { \ http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/docs/connect_config.md ---------------------------------------------------------------------- diff --git a/docs/connect_config.md b/docs/connect_config.md new file mode 100644 index 0000000..f0419cc --- /dev/null +++ b/docs/connect_config.md @@ -0,0 +1,42 @@ +# Connection Configuration + +Proton clients can read default connection information from a +configuration file. + +If the environment variable `MESSAGING_CONNECT_FILE` is set, it is the +path to the file. Otherwise the client looks for a file named +`connect.json` in the following locations, using the first one found: + +* Current working directory of client process. +* `$HOME/.config/messaging/` - $HOME is user's home directory. +* `$PREFIX/etc/messaging/` - $PREFIX is the prefix where the proton library is installed +* `/etc/messaging/` + +The configuration file is in JSON object format. Comments are allowed, +as defined by the [JavaScript Minifier](https://www.crockford.com/javascript/jsmin.html) + +The file format is as follows. Properties are shown with their default +values, all properties are optional. + + { + "scheme": "amqps", // [string] "amqp" (no TLS) or "amqps" + "host": "", // [string] DNS or IP address for connection. Defaults to local host. + "port": "amqps", // [string] "amqp", "amqps" or port number. Defaults to value of 'scheme'. + "user": null, // [string] Authentication user name + "password": null, // [string] Authentication password + + "sasl": { + "enable": true, // [bool] Enable or disable SASL + "mechanisms": null, // [list] List of allowed SASL mechanism names. + // If null the library determines the default list. + "allow_insecure": false // [boolean] Allow mechanisms that send clear-text passwords + }, + + // Note: it is an error to have a "tls" object unless scheme="amqps" + "tls": { + "cert": null, // [string] name of client certificate or database + "key": null // [string] private key or identity for client certificate + "ca": null, // [string] name of CA certificate or database + "verify": true, // [bool] if true, require a valid cert with matching host name + } + } http://git-wip-us.apache.org/repos/asf/qpid-proton/blob/b164d99c/tools/cmake/Modules/FindJsonCpp.cmake ---------------------------------------------------------------------- diff --git a/tools/cmake/Modules/FindJsonCpp.cmake b/tools/cmake/Modules/FindJsonCpp.cmake new file mode 100644 index 0000000..083d3fc --- /dev/null +++ b/tools/cmake/Modules/FindJsonCpp.cmake @@ -0,0 +1,70 @@ +# +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# + +#.rst +# FindJsonCpp +#---------- +# +# Find jsoncpp include directories and libraries. +# +# Sets the following variables: +# +# JsonCpp_FOUND - True if headers and requested libraries were found +# JsonCpp_INCLUDE_DIRS - JsonCpp include directories +# JsonCpp_LIBRARIES - Link these to use jsoncpp. +# +# This module reads hints about search locations from variables:: +# JSONCPP_ROOT - Preferred installation prefix +# JSONCPP_INCLUDEDIR - Preferred include directory e.g. <prefix>/include +# JSONCPP_LIBRARYDIR - Preferred library directory e.g. <prefix>/lib + +find_package (PkgConfig) +pkg_check_modules (PC_JsonCpp QUIET jsoncpp) + +find_library(JsonCpp_LIBRARY NAMES jsoncpp libjsoncpp + HINTS ${JSONCPP_LIBRARYDIR} ${JSONCPP_ROOT}/lib ${CMAKE_INSTALL_PREFIX}/lib + PATHS ${PC_JsonCpp_LIBRARY_DIRS}) + +find_path(JsonCpp_INCLUDE_DIR NAMES json/json.h json/value.h + HINTS ${JSONCPP_INCLUDEDIR} ${JSONCPP_ROOT}/include ${CMAKE_INSTALL_PREFIX}/include + PATHS /usr/include ${PC_JsonCpp_INCLUDE_DIRS}) + +set(JsonCpp_VERSION ${PC_JsonCpp_VERSION}) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(JsonCpp + REQUIRED_VARS JsonCpp_LIBRARY JsonCpp_INCLUDE_DIR + VERSION_VAR JsonCpp_VERSION) + +if (JsonCpp_FOUND) + set(JsonCpp_INCLUDE_DIRS ${JsonCpp_INCLUDE_DIR}) + set(JsonCpp_LIBRARIES ${JsonCpp_LIBRARY}) + + if (NOT TARGET JsonCpp::JsonCpp) + add_library(JsonCpp::JsonCpp UNKNOWN IMPORTED) + set_target_properties(JsonCpp::JsonCpp + PROPERTIES + IMPORTED_LOCATION "${JsonCpp_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${JsonCpp_INCLUDE_DIR}" + ) + endif () + +endif () + +mark_as_advanced (JsonCpp_LIBRARY JsonCpp_INCLUDE_DIR) --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
