├── .gitmodules ├── ext ├── debian │ ├── compat │ ├── lintian-overrides │ ├── docs │ ├── source │ │ └── format │ ├── cthun-client.install │ ├── changelog.erb │ ├── control │ ├── copyright │ └── rules ├── cpp-pcp-client.cmake ├── project_data.yaml ├── build_defaults.yaml └── redhat │ └── cpp-pcp-client.spec.erb ├── lib ├── tests │ ├── resources │ │ ├── ssl-new │ │ │ ├── inventory.old │ │ │ ├── ca.srl │ │ │ ├── inventory.attr │ │ │ ├── inventory │ │ │ ├── v3.ext │ │ │ ├── crl_empty.pem │ │ │ ├── crl_bad_revoked.pem │ │ │ ├── bad_cert.csr │ │ │ ├── good_cert.csr │ │ │ ├── ca.pem │ │ │ ├── bad_cert.pem │ │ │ ├── good_cert.pem │ │ │ ├── gen.sh │ │ │ ├── bad_key.pem │ │ │ ├── good_key.pem │ │ │ └── ca_key.pem │ │ ├── not_a_cert │ │ ├── protected_key.pem │ │ └── mismatched_key.pem │ ├── unit │ │ ├── protocol │ │ │ └── v1 │ │ │ │ └── schemas_test.cc │ │ ├── connector │ │ │ ├── certs.hpp │ │ │ ├── connector_utils.hpp │ │ │ ├── mock_server.hpp │ │ │ ├── certs.cc │ │ │ ├── client_metadata_test.cc │ │ │ └── v1 │ │ │ │ └── connector_test.cc │ │ └── validator │ │ │ └── validator_test.cc │ ├── test.hpp │ ├── main.cc │ └── CMakeLists.txt ├── inc │ └── cpp-pcp-client │ │ ├── protocol │ │ ├── chunks.hpp │ │ ├── errors.hpp │ │ ├── message.hpp │ │ ├── schemas.hpp │ │ ├── serialization.hpp │ │ ├── v2 │ │ │ ├── schemas.hpp │ │ │ └── message.hpp │ │ ├── v1 │ │ │ ├── errors.hpp │ │ │ ├── chunks.hpp │ │ │ ├── schemas.hpp │ │ │ ├── message.hpp │ │ │ └── serialization.hpp │ │ └── parsed_chunks.hpp │ │ ├── connector │ │ ├── connector.hpp │ │ ├── session_association.hpp │ │ ├── v1 │ │ │ └── session_association.hpp │ │ ├── errors.hpp │ │ ├── client_metadata.hpp │ │ ├── timings.hpp │ │ └── v2 │ │ │ └── connector.hpp │ │ ├── ws_config.hpp │ │ ├── util │ │ ├── chrono.hpp │ │ ├── logging.hpp │ │ └── thread.hpp │ │ └── validator │ │ ├── validator.hpp │ │ └── schema.hpp ├── src │ ├── protocol │ │ ├── v1 │ │ │ ├── serialization.cc │ │ │ ├── chunks.cc │ │ │ └── schemas.cc │ │ ├── v2 │ │ │ ├── schemas.cc │ │ │ └── message.cc │ │ └── parsed_chunks.cc │ ├── connector │ │ └── v1 │ │ │ └── session_association.cc │ ├── validator │ │ ├── validator.cc │ │ └── schema.cc │ └── util │ │ └── logging.cc └── CMakeLists.txt ├── CODEOWNERS ├── vendor ├── valijson-600aeb9.zip ├── websocketpp-0.8.2.zip ├── websocketpp.cmake ├── valijson.cmake └── FindDependency.cmake ├── .gitignore ├── templates └── root_path.hpp ├── Rakefile ├── CONTRIBUTING.md ├── tutorial ├── 10 │ └── common.h ├── 06 │ ├── common.h │ ├── controller │ │ └── main.cpp │ └── agent │ │ └── main.cpp ├── 01 │ └── agent │ │ └── main.cpp ├── 07 │ ├── common.h │ └── controller │ │ └── main.cpp ├── 08 │ └── common.h ├── 09 │ └── common.h ├── 02 │ └── agent │ │ └── main.cpp ├── resources │ ├── agent_certs │ │ ├── crt.pem │ │ ├── ca.pem │ │ └── key.pem │ └── controller_certs │ │ ├── crt.pem │ │ ├── ca.pem │ │ └── key.pem ├── 03 │ └── agent │ │ └── main.cpp ├── 05 │ ├── controller │ │ └── main.cpp │ └── agent │ │ └── main.cpp ├── README.md └── 04 │ └── agent │ └── main.cpp ├── .travis.yml ├── appveyor.yml ├── scripts └── FindDependency.cmake └── CMakeLists.txt /.gitmodules: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ext/debian/compat: -------------------------------------------------------------------------------- 1 | 7 2 | 3 | -------------------------------------------------------------------------------- /ext/debian/lintian-overrides: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /ext/debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | LICENSE 3 | -------------------------------------------------------------------------------- /ext/debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/inventory.old: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/ca.srl: -------------------------------------------------------------------------------- 1 | C1A9A0777CCBC339 2 | -------------------------------------------------------------------------------- /CODEOWNERS: -------------------------------------------------------------------------------- 1 | # default to skeletor 2 | * @puppetlabs/skeletor 3 | -------------------------------------------------------------------------------- /lib/tests/resources/not_a_cert: -------------------------------------------------------------------------------- 1 | # this is not a certificate file! -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/inventory.attr: -------------------------------------------------------------------------------- 1 | unique_subject = yes 2 | -------------------------------------------------------------------------------- /ext/debian/cthun-client.install: -------------------------------------------------------------------------------- 1 | usr/lib/ 2 | usr/include/cpp-pcp-client 3 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/inventory: -------------------------------------------------------------------------------- 1 | R 230109164235Z 200415164235Z C1A9A0777CCBC339 unknown /CN=bad 2 | -------------------------------------------------------------------------------- /vendor/valijson-600aeb9.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puppetlabs/cpp-pcp-client/main/vendor/valijson-600aeb9.zip -------------------------------------------------------------------------------- /vendor/websocketpp-0.8.2.zip: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/puppetlabs/cpp-pcp-client/main/vendor/websocketpp-0.8.2.zip -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | /build/ 3 | /bin/ 4 | /test-resources/ 5 | /ext/packaging 6 | .DS_Store 7 | export.h 8 | /dev-resources/ 9 | 10 | #Generated files 11 | /.idea/ -------------------------------------------------------------------------------- /templates/root_path.hpp: -------------------------------------------------------------------------------- 1 | #ifndef TEMPLATES_CPP_PCP_CLIENT_ROOT_PATH_HPP_ 2 | #define TEMPLATES_CPP_PCP_CLIENT_ROOT_PATH_HPP_ 3 | 4 | #define CPP_PCP_CLIENT_ROOT_PATH "@ROOT_PATH@" 5 | 6 | #endif // TEMPLATES_CPP_PCP_CLIENT_ROOT_PATH_HPP_ 7 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/v3.ext: -------------------------------------------------------------------------------- 1 | authorityKeyIdentifier=keyid,issuer 2 | basicConstraints=CA:FALSE 3 | keyUsage = digitalSignature, keyEncipherment, dataEncipherment, keyAgreement 4 | extendedKeyUsage = serverAuth, clientAuth 5 | subjectAltName = @alt_names 6 | 7 | [alt_names] 8 | IP.1 = 127.0.0.1 9 | DNS.1 = localhost -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/chunks.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a shim to maintain compatibility with pxp-agent during development. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace PCPClient { 9 | 10 | using namespace v1; 11 | 12 | } // namespace PCPClient 13 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/errors.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a shim to maintain compatibility with pxp-agent during development. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace PCPClient { 9 | 10 | using namespace v1; 11 | 12 | } // namespace PCPClient 13 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/message.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a shim to maintain compatibility with pxp-agent during development. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace PCPClient { 9 | 10 | using namespace v1; 11 | 12 | } // namespace PCPClient 13 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/schemas.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a shim to maintain compatibility with pxp-agent during development. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace PCPClient { 9 | 10 | using namespace v1; 11 | 12 | } // namespace PCPClient 13 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/connector/connector.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a shim to maintain compatibility with pxp-agent during development. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace PCPClient { 9 | 10 | using namespace v1; 11 | 12 | } // namespace PCPClient 13 | -------------------------------------------------------------------------------- /lib/tests/unit/protocol/v1/schemas_test.cc: -------------------------------------------------------------------------------- 1 | #include "tests/test.hpp" 2 | 3 | #include 4 | 5 | using namespace PCPClient; 6 | using namespace v1; 7 | 8 | TEST_CASE("AssociateResponseSchema", "[message]") { 9 | REQUIRE_NOTHROW(Protocol::AssociateResponseSchema()); 10 | } 11 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/serialization.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a shim to maintain compatibility with pxp-agent during development. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace PCPClient { 9 | 10 | using namespace v1; 11 | 12 | } // namespace PCPClient 13 | -------------------------------------------------------------------------------- /lib/tests/test.hpp: -------------------------------------------------------------------------------- 1 | #ifndef PCP_CLIENT_TEST_TEST_HPP_ 2 | #define PCP_CLIENT_TEST_TEST_HPP_ 3 | 4 | // To enable log messages: 5 | // #define ENABLE_PCP_LOGGING 6 | // #define ENABLE_MOCKSERVER_WEBSOCKETPP_LOGGING 7 | 8 | #include "root_path.hpp" 9 | 10 | #include 11 | 12 | #endif // PCP_CLIENT_TEST_TEST_HPP_ 13 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/connector/session_association.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a shim to maintain compatibility with pxp-agent during development. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | 8 | namespace PCPClient { 9 | 10 | using namespace v1; 11 | 12 | } // namespace PCPClient 13 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/ws_config.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | namespace PCPClient { 6 | struct ws_config : public websocketpp::config::asio_tls_client { 7 | static const websocketpp::log::level elog_level = 8 | websocketpp::log::elevel::all; 9 | static const websocketpp::log::level alog_level = 10 | websocketpp::log::alevel::all; 11 | }; 12 | } 13 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'packaging' 2 | Pkg::Util::RakeUtils.load_packaging_tasks 3 | 4 | namespace :package do 5 | # desc "Bootstrap packaging automation, e.g. clone into packaging repo" 6 | task :bootstrap do 7 | puts "package:bootstrap is no longer needed, using packaging as a gem." 8 | end 9 | # desc "Remove all cloned packaging automation" 10 | task :implode do 11 | puts "package:implode is no longer needed, using packaging as a gem." 12 | end 13 | end -------------------------------------------------------------------------------- /ext/debian/changelog.erb: -------------------------------------------------------------------------------- 1 | cpp-pcp-client (<%= @debversion %>) wheezy; urgency=low 2 | 3 | * Update to version <% @debversion %> 4 | 5 | -- Puppet Labs Release <%= Time.now.strftime("%a, %d %b %Y %H:%M:%S %z")%> 6 | 7 | cpp-pcp-client (0.0.1-1puppetlabs1) wheezy; urgency=low 8 | 9 | * Initial packaging of libcpp-pcp-client 10 | 11 | -- Pieter Loubser <%= Time.now.strftime("%a, %d %b %Y %H:%M:%S %z")%> 12 | -------------------------------------------------------------------------------- /lib/tests/unit/connector/certs.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | std::string getCertname(); 4 | std::string getCaPath(); 5 | std::string getCertPath(); 6 | std::string getKeyPath(); 7 | std::string getMismatchedKeyPath(); 8 | std::string getProtectedKeyPath(); 9 | std::string getNotACertPath(); 10 | std::string getNotExistentFilePath(); 11 | std::string getBadCertPath(); 12 | std::string getBadKeyPath(); 13 | std::string getEmptyCrlPath(); 14 | std::string getRevokedCrlPath(); 15 | -------------------------------------------------------------------------------- /vendor/websocketpp.cmake: -------------------------------------------------------------------------------- 1 | INCLUDE(ExternalProject) 2 | 3 | # Add an external project to build websocketpp 4 | EXTERNALPROJECT_ADD( 5 | websocketpp 6 | PREFIX "${PROJECT_BINARY_DIR}" 7 | URL "file://${VENDOR_DIRECTORY}/websocketpp-0.8.2.zip" 8 | URL_MD5 "87d06d8ce62767602cb339d46b6664a1" 9 | CONFIGURE_COMMAND "" 10 | BUILD_COMMAND "" 11 | BUILD_IN_SOURCE 1 12 | INSTALL_COMMAND "" 13 | ) 14 | 15 | EXTERNALPROJECT_GET_PROPERTY(websocketpp SOURCE_DIR) 16 | SET(WEBSOCKETPP_INCLUDE_DIRS "${SOURCE_DIR}") 17 | -------------------------------------------------------------------------------- /ext/debian/control: -------------------------------------------------------------------------------- 1 | Source: cpp-pcp-client 2 | Section: devel 3 | Priority: optional 4 | Maintainer: Puppet Labs 5 | Build-Depends: debhelper (>> 7), pl-gcc (>= 4.8.2-1puppetlabs3), pl-cmake (>= 2.8.12-1puppetlabs6), pl-libboost-devel (>= 1.55.0-1puppetlabs3), pl-libboost-static (>= 1.55.0-1puppetlabs3), libssl-dev 6 | Standards-Version: 3.9.1 7 | Homepage: http://www.puppetlabs.com 8 | 9 | Package: cpp-pcp-client 10 | Architecture: any 11 | Depends: ${shlibs:Depends}, ${misc:Depends}, libssl1.0.0 | libssl0.9.8 12 | Description: TBD 13 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # How to contribute 2 | 3 | Third-party patches are essential for keeping puppet open-source projects 4 | great. We want to keep it as easy as possible to contribute changes that 5 | allow you to get the most out of our projects. There are a few guidelines 6 | that we need contributors to follow so that we can have a chance of keeping on 7 | top of things. For more info, see our canonical guide to contributing: 8 | 9 | [https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md](https://github.com/puppetlabs/puppet/blob/master/CONTRIBUTING.md) 10 | -------------------------------------------------------------------------------- /ext/debian/copyright: -------------------------------------------------------------------------------- 1 | This package was debianized by Pieter Loubser on 2 | Mon, 23 Mar 2015 15:08:35 +0000 3 | 4 | It was downloaded from https://github.com/puppetlabs/cpp-pcp-client 5 | 6 | Upstream Author: 7 | 8 | Puppet Labs 9 | 10 | Copyright: 11 | 12 | Copyright 2010-2011 Puppet Labs 13 | 14 | License: ASL-2 15 | http://www.apache.org/licenses/LICENSE-2.0 16 | 17 | On a Debian system, the license can be found at 18 | /usr/share/common-licenses/Apache-2.0 . 19 | 20 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/crl_empty.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN X509 CRL----- 2 | MIIBUjA8MA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNVBAMMAmNhFw0yMDA0MTUxNjQy 3 | MzVaFw0yMzAxMTAxNjQyMzVaMA0GCSqGSIb3DQEBCwUAA4IBAQCCtMGuiG9o7F/j 4 | z+TbKQVN0w9H+xwnLg5i5Ri9BuBNPhRE/W4me2VDY2fqEbl/t/feBmoOk+9fEBsn 5 | KG1TbiDC0mXlpmeb3mmw8BBw1NO4ev/d0PK/IQCGHDMJFeM509Gc8DzhLoY5AlqL 6 | GJWv/cd+eZj902oSweCIz5KYje/4zUdwaXmk0cLOMlR4U3rX4kMYiLzM5cYXvAHR 7 | Ma9BstlXOKrnWXWv7aF3R+W/deOhpr13+03ctC8OoyMknkmA1ykZzLbNet3htsz4 8 | aF0bsFve3MpnCJX3u6YcVAjyL3rM0SdhcF3p6/rGhoCqitOj+FM98uythIWLIwJp 9 | 6hyn9BLy 10 | -----END X509 CRL----- 11 | -------------------------------------------------------------------------------- /ext/cpp-pcp-client.cmake: -------------------------------------------------------------------------------- 1 | include(ExternalProject) 2 | 3 | # Add an external project to build cpp-pcp-client 4 | externalproject_add( 5 | cpp-pcp-client 6 | PREFIX "${PROJECT_BINARY_DIR}" 7 | SOURCE_DIR "${VENDOR_DIRECTORY}/cpp-pcp-client" 8 | DOWNLOAD_COMMAND "" 9 | URL "" 10 | URL_MD5 "" 11 | CONFIGURE_COMMAND "" 12 | BUILD_COMMAND "make" 13 | BUILD_IN_SOURCE 1 14 | INSTALL_COMMAND "" 15 | ) 16 | externalproject_get_property(cpp-pcp-client SOURCE_DIR) 17 | set(CPP_PCP_CLIENT_INCLUDE_DIRS "${SOURCE_DIR}/src") 18 | set(CPP_PCP_CLIENT_LIB "${SOURCE_DIR}/lib/libcpp-pcp-client.so") 19 | -------------------------------------------------------------------------------- /lib/src/protocol/v1/serialization.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #if _WIN32 4 | #include 5 | #else 6 | #include // endianess functions: htonl, ntohl 7 | #endif 8 | 9 | namespace PCPClient { 10 | namespace v1 { 11 | 12 | #ifdef BOOST_ENDIAN_LITTLE_BYTE 13 | 14 | uint32_t getNetworkNumber(const uint32_t& number) 15 | { 16 | return htonl(number); 17 | } 18 | 19 | uint32_t getHostNumber(const uint32_t& number) 20 | { 21 | return ntohl(number); 22 | } 23 | 24 | #endif // BOOST_ENDIAN_LITTLE_BYTE 25 | 26 | } // namespace v1 27 | } // namespace PCPClient 28 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/crl_bad_revoked.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN X509 CRL----- 2 | MIIBcDBaMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNVBAMMAmNhFw0yMDA0MTUxNjQy 3 | MzVaFw0yMzAxMTAxNjQyMzVaMBwwGgIJAMGpoHd8y8M5Fw0yMDA0MTUxNjQyMzVa 4 | MA0GCSqGSIb3DQEBCwUAA4IBAQCyEMXwaSsx37HVCigFjjckJ/PLG/H0v3I6201E 5 | 5f8Fodw5is9HDHnGAkTxFcbBcrKBoCKR6pawiFoYejR6ih2QoMkS0Dvcf2UOfv3D 6 | zLlYfqGe6VwRmoOsHJy2Wbj7A6TQKT0EgUGw70meLJJYD+VYZ/Z1JNxSG0pDo4DD 7 | 0hy/tNl/l3KAhRMkrCQL4YXvBeqHkcARJ1P9LRlKsaeVTJYjsEhFk+xQBP52cdV1 8 | yO6SOksOXT9iByxywQS5+pN3eKW0SQIAqFHz3wk0ffiTA50B0LDBUomyPi8Iyy1W 9 | qM8PIq7mKEau6XtifYo/xV7+Tt2XaJDEafMpN2eG+Q+t+4cx 10 | -----END X509 CRL----- 11 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/util/chrono.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_PCP_CLIENT_SRC_UTIL_CHRONO_HPP_ 2 | #define CPP_PCP_CLIENT_SRC_UTIL_CHRONO_HPP_ 3 | 4 | #include 5 | 6 | /* This header encapsulates our use of chrono. This exists to support the changes 7 | that had to be made during PCP-53 where we were forced to switch from using 8 | std::thread to boost::thread. This encapsulation means that we can swtich back 9 | by changing boost::chrono to std::chrono. 10 | */ 11 | 12 | namespace PCPClient { 13 | namespace Util { 14 | 15 | namespace chrono = boost::chrono; 16 | 17 | } // namespace Util 18 | } // namespace PCPClient 19 | 20 | #endif // CPP_PCP_CLIENT_SRC_UTIL_CHRONO_HPP_ 21 | -------------------------------------------------------------------------------- /vendor/valijson.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8.12) 2 | include(ExternalProject) 3 | 4 | 5 | # zip sourced from https://github.com/tristanpenman/valijson/archive/master.zip 6 | 7 | # Add an external project to unpack valijson 8 | externalproject_add( 9 | valijson 10 | PREFIX "${PROJECT_BINARY_DIR}" 11 | URL "file://${VENDOR_DIRECTORY}/valijson-600aeb9.zip" 12 | URL_MD5 "ce53330afc51281e39d6613041448133" 13 | CONFIGURE_COMMAND "" 14 | BUILD_COMMAND "" 15 | BUILD_IN_SOURCE 1 16 | INSTALL_COMMAND "" 17 | ) 18 | 19 | # Set some useful variables based on the source directory 20 | externalproject_get_property(valijson SOURCE_DIR) 21 | set(VALIJSON_INCLUDE_DIRS "${SOURCE_DIR}/include") 22 | -------------------------------------------------------------------------------- /lib/src/connector/v1/session_association.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace PCPClient { 4 | namespace v1 { 5 | 6 | SessionAssociation::SessionAssociation(uint32_t assoc_timeout_s) 7 | : success { false }, 8 | in_progress { false }, 9 | got_messaging_failure { false }, 10 | request_id {}, 11 | error {}, 12 | mtx {}, 13 | cond_var {}, 14 | association_timeout_s { assoc_timeout_s } 15 | { 16 | } 17 | 18 | void SessionAssociation::reset() 19 | { 20 | success = false; 21 | in_progress = false; 22 | got_messaging_failure = false; 23 | request_id.clear(); 24 | error.clear(); 25 | } 26 | 27 | } // namespace v1 28 | } // namespace PCPClient 29 | -------------------------------------------------------------------------------- /lib/tests/unit/connector/connector_utils.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace PCPClient { 8 | 9 | static constexpr int WS_TIMEOUT_MS { 5000 }; 10 | static constexpr uint32_t ASSOCIATION_TIMEOUT_S { 15 }; 11 | static constexpr uint32_t ASSOCIATION_REQUEST_TTL_S { 10 }; 12 | static constexpr uint32_t PONG_TIMEOUTS_BEFORE_RETRY { 3 }; 13 | static constexpr uint32_t PONG_TIMEOUT { 900 }; 14 | 15 | inline void wait_for(std::function func, uint16_t pause_s = 2) 16 | { 17 | leatherman::util::Timer timer {}; 18 | 19 | while (!func() && timer.elapsed_seconds() < pause_s) 20 | Util::this_thread::sleep_for(Util::chrono::milliseconds(1)); 21 | } 22 | 23 | } // namespace PCPClient 24 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/bad_cert.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICUzCCATsCAQAwDjEMMAoGA1UEAwwDYmFkMIIBIjANBgkqhkiG9w0BAQEFAAOC 3 | AQ8AMIIBCgKCAQEAoKNTzXSxmhmUPDNZRwU2LNUuYJCWnel8JhY8b/maiGFOYCvk 4 | B0vmjBAgqreclo71z4bbwF3cBtfTL2J23NMQR2Hh0KglxqLavZIwqgv87hU3sYqy 5 | j6xausISLtJr7D6kadeUhgqUJ/nDR6joury8c2XosoOqdyT9kABiLcLmLDcrlyM5 6 | EDF2yjj6JUZCsapb8R08wzc1a+KoI/pNcEbGN3a5PhikcY14ZjiC0+KKM/LrHmLe 7 | hIJDNSdXMHyb9/hcCQpIUGsr00UsaeGLvb8RtdXsqUP+Gt9HSOkIQSy5Fanwgg0l 8 | SciOzcQ0bgvtOgNYPpXBvkm3Kji9jCQnkXpxHwIDAQABoAAwDQYJKoZIhvcNAQEL 9 | BQADggEBAC023jJ1B9//A4pYo16bjrypnlLFXrVJEx0GnN4zgoAd0Tw/lmpFujS/ 10 | qElvELIrIb1sS3a3uvNa2/gZVGuP2DkN8bWWaBTtcxRM5xH1sNhJpX+W2lQuCn7p 11 | /dpMKNNtttJYPMf8tv6ZsEWnvMFdoPgd6fWokK+4t/2F5LJ5sl/thu1RoZq/V2VM 12 | pwD9CbN41+EcTs5wAvP/r3Q50zJ4ea81iKV7jdppfIU6uRbNmnEx6Z1tREB8AGff 13 | ov5rAFj5ph06VY7cQOTVgTZPHuQZWEgtI7KP8E0xreyxIXDbtp2yddiaK+OtBArs 14 | STAYD9GDo1BmtmG+XsA0TkZqCXsjgHw= 15 | -----END CERTIFICATE REQUEST----- 16 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/good_cert.csr: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE REQUEST----- 2 | MIICVDCCATwCAQAwDzENMAsGA1UEAwwEZ29vZDCCASIwDQYJKoZIhvcNAQEBBQAD 3 | ggEPADCCAQoCggEBAKYQ8TlA/Ii9YaDMdP/Nk/NmFBDVR7o3PhZWEzilS9eNXxfb 4 | L0vtdrSKLvp+3eY1eVMSaYipomBtNJT5zexeaawLqNwoaTIPYpY3xFlsIRMtk55M 5 | 6QBq9F4BcLs08qlKXdqoULQnUyKmLGpTjjmvirN17DezoD7EYpPnKYyprCC3ITJJ 6 | ykXcEVYdpbV0ct4qm9wIkboWf2eXZyCdw07U0eLOnIMMpHM51KZEqCFpi3+r6E03 7 | 4AjbmYi3+0p+qdRrqIK+WNa9NLOhjmSurqmts0MSuWQsjh5OzOpIX51zRbbmNhzn 8 | cMho4zeVn31zB8b2J+u/EQNdIRhLjn3begxRyfMCAwEAAaAAMA0GCSqGSIb3DQEB 9 | CwUAA4IBAQAbY9hEY41RuPnDEtbc/oekwLyA0oMU3liG+xcLa0RYZpqcdVXij6nD 10 | dPFl3iaGefjs+IeSLenPn76nbys/Pz4yNL3eljQmhkV+cm6u8anmUX+AljsbdLb3 11 | 3mod2OafrXQzoTZFu4R2kMTIWKC0WXJ0trVvXdgQ1IZ4Kz3yt0o2E9QuoSh3FyQi 12 | 6PFRQ9DwbgCKgVU1QqEh8aeVwCHxHttGThudkVruzHQ6gzgqpj0F4AVIZfUV98Mo 13 | FVwwMReqd5uuYaIR0SkobmzVy+vd9zCBZ+mLdYMSueyBit9jS27I7BRIduPm8ktD 14 | 2mJNogwfQ7+EOALZDehp/eCOuvpoZ48x 15 | -----END CERTIFICATE REQUEST----- 16 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/v2/schemas.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace PCPClient { 7 | namespace v2 { 8 | namespace Protocol { 9 | 10 | // 11 | // envelope 12 | // 13 | 14 | static const std::string ENVELOPE_SCHEMA_NAME { "envelope_schema" }; 15 | LIBCPP_PCP_CLIENT_EXPORT Schema EnvelopeSchema(); 16 | 17 | // 18 | // data 19 | // 20 | 21 | // inventory 22 | static const std::string INVENTORY_REQ_TYPE { "http://puppetlabs.com/inventory_request" }; 23 | static const std::string INVENTORY_RESP_TYPE { "http://puppetlabs.com/inventory_response" }; 24 | LIBCPP_PCP_CLIENT_EXPORT Schema InventoryRequestSchema(); 25 | LIBCPP_PCP_CLIENT_EXPORT Schema InventoryResponseSchema(); 26 | 27 | // error 28 | static const std::string ERROR_MSG_TYPE { "http://puppetlabs.com/error_message" }; 29 | LIBCPP_PCP_CLIENT_EXPORT Schema ErrorMessageSchema(); 30 | 31 | } // namespace Protocol 32 | } // namespace v2 33 | } // namespace PCPClient 34 | -------------------------------------------------------------------------------- /tutorial/06/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include // Schema, ContentType 4 | 5 | namespace Tutorial { 6 | 7 | static const std::string AGENT_CLIENT_TYPE { "tutorial-agent" }; 8 | static const std::string CONTROLLER_CLIENT_TYPE { "tutorial-controller" }; 9 | 10 | static const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 11 | 12 | static const std::string REQUEST_SCHEMA_NAME { "http://puppetlabs.com/tutorial_request_schema" }; 13 | 14 | static const int MSG_TIMEOUT_S { 10 }; 15 | 16 | static PCPClient::Schema getRequestMessageSchema() { 17 | PCPClient::Schema schema { REQUEST_SCHEMA_NAME, 18 | PCPClient::ContentType::Json }; 19 | // Add constraints to the request message schema 20 | using T_C = PCPClient::TypeConstraint; 21 | schema.addConstraint("request", T_C::Object, true); // mandatory 22 | schema.addConstraint("details", T_C::String, false); // optional 23 | return schema; 24 | } 25 | 26 | } // namespace Tutorial 27 | -------------------------------------------------------------------------------- /ext/debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | export DH_VERBOSE=1 4 | export VERBOSE=1 5 | 6 | PREFIX :=/usr 7 | BUILDPREFIX :=/opt/pl-build-tools 8 | NPROC :=$(shell nproc) 9 | CPPFLAGS :=$(shell dpkg-buildflags --get CPPFLAGS) 10 | CFLAGS :=$(shell dpkg-buildflags --get CFLAGS) 11 | CXXFLAGS :=$(shell dpkg-buildflags --get CXXFLAGS) 12 | LDFLAGS :=$(shell dpkg-buildflags --get LDFLAGS) 13 | NUMPROC :=$(shell expr $$(nproc) + 1) 14 | 15 | export CPPFLAGS CFLAGS CXXFLAGS LDFLAGS 16 | 17 | clean: 18 | 19 | override_dh_auto_configure: 20 | $(BUILDPREFIX)/bin/cmake \ 21 | -DCMAKE_TOOLCHAIN_FILE=$(BUILDPREFIX)/pl-build-toolchain.cmake \ 22 | -DCMAKE_VERBOSE_MAKEFILE=ON \ 23 | -DCMAKE_INSTALL_PREFIX=$(PREFIX) \ 24 | -DBOOST_STATIC=ON \ 25 | -DYAMLCPP_STATIC=ON \ 26 | . 27 | 28 | override_dh_auto_build: 29 | $(MAKE) -j$(NUMPROC) DESTDIR=$(CURDIR)/debian/tmp 30 | 31 | override_dh_auto_install: 32 | $(MAKE) -j$(NUMPROC) install DESTDIR=$(CURDIR)/debian/tmp 33 | 34 | override_dh_auto_test: 35 | $(BUILDPREFIX)/bin/ctest -V 36 | 37 | %: 38 | dh $@ --parallel=$(NUMPROC) 39 | -------------------------------------------------------------------------------- /ext/project_data.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | project: 'cpp-pcp-client' 3 | author: 'Puppet Labs' 4 | email: 'info@puppetlabs.com' 5 | homepage: 'https://github.com/puppetlabs/cpp-pcp-client' 6 | summary: 'C++ library for interacting with a PCP broker' 7 | description: 'C++ library for interacting with a PCP broker' 8 | # files to be packaged into a tarball and released with deb/rpm 9 | files: '[A-Z]* bin lib vendor cmake' 10 | # space separated list of files to *exclude* from the tarball 11 | # note that each listing in files, above, is recursively copied into the tarball, so 12 | # 'tar\_excludes' only needs to include any undesired subdirectories/files of the 'files' 13 | # list to exclude 14 | tar_excludes: 'ext/packaging' 15 | # Array of templates or globs of templates to evaluate. Note that without this key, the packaging will 16 | # default to searching for any files in `ext` with the extension '.erb' and evaluate them. When this 17 | # key is supplied, its values override the defaults, and all desired erb files must be specified with a path or glob. 18 | templates: 19 | - ext/redhat/cpp-pcp-client.spec.erb 20 | - ext/debian/changelog.erb 21 | -------------------------------------------------------------------------------- /tutorial/01/agent/main.cpp: -------------------------------------------------------------------------------- 1 | #include // Connector 2 | #include // connection_config_error 3 | 4 | #include 5 | #include 6 | 7 | namespace Tutorial { 8 | 9 | const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 10 | 11 | const std::string AGENT_CLIENT_TYPE { "tutorial_agent" }; 12 | 13 | const std::string CA { "../../resources/agent_certs/ca.pem" }; 14 | const std::string CERT { "../../resources/agent_certs/crt.pem" }; 15 | const std::string KEY { "../../resources/agent_certs/key.pem" }; 16 | 17 | int main(int argc, char *argv[]) { 18 | // Connector constructor 19 | 20 | PCPClient::Connector connector { BROKER_URL, 21 | AGENT_CLIENT_TYPE, 22 | CA, 23 | CERT, 24 | KEY }; 25 | 26 | std::cout << "Configured the connector\n"; 27 | 28 | return 0; 29 | } 30 | 31 | } // namespace Tutorial 32 | 33 | int main(int argc, char** argv) { 34 | return Tutorial::main(argc, argv); 35 | } 36 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIC7TCCAdWgAwIBAgIJALqjGtGnTdbJMA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNV 3 | BAMMAmNhMB4XDTIwMDQxNTE2NDIzNVoXDTIzMDExMDE2NDIzNVowDTELMAkGA1UE 4 | AwwCY2EwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCywPUhmk9VoKfz 5 | lEDSgCiSnA9BZFTaUCNE2hzEIj7qmewtKGq1t88fMLIa+zvFpofQTw3NMNp9egA9 6 | 1bDw2Zfub74F59q0d5VarVzURN0133vUOodbawyGR1/TMjllJA8Y2im06nlA2X/o 7 | m7Bf4aca8WHhReI436PNLqhL+dPYHCOW4JEhs+3R58QLw1f1dttKdNxiZp/gcKyD 8 | hPIob/5dHy/nURRpmPIwXTSun5inUG5lh2lVAS+CnIjXoOuW9LZMgq7GhO2FBP5c 9 | HJEULSjGlgdKRW/8oH+2Vsy3qLnLM8t8LHDwa6JLpyz7cVF4DZPJbZPRjQjOzAyS 10 | GykfDMuLAgMBAAGjUDBOMB0GA1UdDgQWBBQN21v4CNVhDe8UdHYf3W4D2mgC7DAf 11 | BgNVHSMEGDAWgBQN21v4CNVhDe8UdHYf3W4D2mgC7DAMBgNVHRMEBTADAQH/MA0G 12 | CSqGSIb3DQEBCwUAA4IBAQAOxbybZmE/58+ND6EfsFF8nz4TwFypIznOeta/jxSk 13 | BlM7ITuImukLVHdGGpjWBe6QdvhS7PJdi8aaOZL8Nz04RekgV4mQkserMKXyxqQg 14 | 4aBySZB29+uxtAYVYtEKaesh6VpTAJl8qFJ1kj1RZyZBusw2lmpn8hy5S6tC9j05 15 | 87s1wHrTPRxnfYdRahuH2qZOfO67VWqyklrgascR/gkLA3DjbcyRRWhKGDRNLgtS 16 | Zh41nX3pMYU3EkQG3OqKisuxBuweFUpY96Wox7dgG+zIPSHe1tZPdvWkqiF3Wt+n 17 | d8GisTqGUUNDpP2/3NojclsAMwzsAUzBXP9Kn3cg3FUW 18 | -----END CERTIFICATE----- 19 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/connector/v1/session_association.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_PCP_CLIENT_SRC_CONNECTOR_SESSION_ASSOCIATION_HPP_ 2 | #define CPP_PCP_CLIENT_SRC_CONNECTOR_SESSION_ASSOCIATION_HPP_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace PCPClient { 11 | namespace v1 { 12 | 13 | // Encapsulates concurrency things for managing the state of the 14 | // Associate Session process; consumer code may want to hold a lock 15 | // over the instance mutex, depending on the usage 16 | 17 | struct LIBCPP_PCP_CLIENT_EXPORT SessionAssociation { 18 | std::atomic success; 19 | std::atomic in_progress; 20 | std::atomic got_messaging_failure; 21 | std::string request_id; 22 | std::string error; 23 | Util::mutex mtx; 24 | Util::condition_variable cond_var; 25 | uint32_t association_timeout_s; 26 | 27 | SessionAssociation(uint32_t assoc_timeout_s); 28 | 29 | // Does not lock mtx 30 | void reset(); 31 | }; 32 | 33 | } // namespace v1 34 | } // namespace PCPClient 35 | 36 | #endif // CPP_PCP_CLIENT_SRC_CONNECTOR_SESSION_ASSOCIATION_HPP_ 37 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/bad_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDFDCCAfygAwIBAgIJAMGpoHd8y8M5MA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNV 3 | BAMMAmNhMB4XDTIwMDQxNTE2NDIzNVoXDTIzMDEwOTE2NDIzNVowDjEMMAoGA1UE 4 | AwwDYmFkMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoKNTzXSxmhmU 5 | PDNZRwU2LNUuYJCWnel8JhY8b/maiGFOYCvkB0vmjBAgqreclo71z4bbwF3cBtfT 6 | L2J23NMQR2Hh0KglxqLavZIwqgv87hU3sYqyj6xausISLtJr7D6kadeUhgqUJ/nD 7 | R6joury8c2XosoOqdyT9kABiLcLmLDcrlyM5EDF2yjj6JUZCsapb8R08wzc1a+Ko 8 | I/pNcEbGN3a5PhikcY14ZjiC0+KKM/LrHmLehIJDNSdXMHyb9/hcCQpIUGsr00Us 9 | aeGLvb8RtdXsqUP+Gt9HSOkIQSy5Fanwgg0lSciOzcQ0bgvtOgNYPpXBvkm3Kji9 10 | jCQnkXpxHwIDAQABo3YwdDAfBgNVHSMEGDAWgBQN21v4CNVhDe8UdHYf3W4D2mgC 11 | 7DAJBgNVHRMEAjAAMAsGA1UdDwQEAwIDuDAdBgNVHSUEFjAUBggrBgEFBQcDAQYI 12 | KwYBBQUHAwIwGgYDVR0RBBMwEYcEfwAAAYIJbG9jYWxob3N0MA0GCSqGSIb3DQEB 13 | CwUAA4IBAQAOD5F38uz6pZHD8BAaqMK/OC9XY6tzoq7wxkkNeDIKUgBSYHwn+REI 14 | BEAGU+oH34sCO31kQYBK5afHd/NdwEJ2aZY9ARa0kVcJ8x5U5dJinSHcbIqhSUcQ 15 | ChLhZOVfCvPla/akcc/3JKqRA+yD/6XZTMjATexqVDEg5DAeu6I5AC3nmSNDRpZz 16 | 2LNsBcUKdGWHLrZrYdhr6d4wyEPBRglYljw58ZuKLWWZir6F7nWXvWz24dAq3eOH 17 | COcYtlccgxvIsob8kEDWsoDwSEs2pW7xga0AFAigZzgvsTXNASnFgd1P25jgw7aC 18 | cU9J9oidz79ZGxeBrkRmr6Gl48zhvIyj 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/good_cert.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIDFTCCAf2gAwIBAgIJAMGpoHd8y8M4MA0GCSqGSIb3DQEBCwUAMA0xCzAJBgNV 3 | BAMMAmNhMB4XDTIwMDQxNTE2NDIzNVoXDTIzMDEwOTE2NDIzNVowDzENMAsGA1UE 4 | AwwEZ29vZDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKYQ8TlA/Ii9 5 | YaDMdP/Nk/NmFBDVR7o3PhZWEzilS9eNXxfbL0vtdrSKLvp+3eY1eVMSaYipomBt 6 | NJT5zexeaawLqNwoaTIPYpY3xFlsIRMtk55M6QBq9F4BcLs08qlKXdqoULQnUyKm 7 | LGpTjjmvirN17DezoD7EYpPnKYyprCC3ITJJykXcEVYdpbV0ct4qm9wIkboWf2eX 8 | ZyCdw07U0eLOnIMMpHM51KZEqCFpi3+r6E034AjbmYi3+0p+qdRrqIK+WNa9NLOh 9 | jmSurqmts0MSuWQsjh5OzOpIX51zRbbmNhzncMho4zeVn31zB8b2J+u/EQNdIRhL 10 | jn3begxRyfMCAwEAAaN2MHQwHwYDVR0jBBgwFoAUDdtb+AjVYQ3vFHR2H91uA9po 11 | AuwwCQYDVR0TBAIwADALBgNVHQ8EBAMCA7gwHQYDVR0lBBYwFAYIKwYBBQUHAwEG 12 | CCsGAQUFBwMCMBoGA1UdEQQTMBGHBH8AAAGCCWxvY2FsaG9zdDANBgkqhkiG9w0B 13 | AQsFAAOCAQEAIpeCbTRf3syrso6N6h+NytZHKR+3a9Izv9+tDnk7QhNJfUux7w2c 14 | qSBo0rV+3Gt8isiYKDe5rOS8GNJFZ/X9OkNc4bQmCwR1v/5eaOE6OQstx5bHeqEs 15 | b8CPoeT1cTt3fBsg5jH1T2IhkpajkXa18oK0piPzYqYgP9FOVDkCIxY/LJjPEVQq 16 | 7mgDMy62es/3kiQ8F7BzpLwqxMk0Ga2ML+Q4tQEglB/WEcAu7CUxNWYZ70Wkl13y 17 | jvKzgh0aqP0l68kHArJfWmCFXFiGMTv++vUXMTRKGxCQfwZQj6+ikoO34b+rfHxZ 18 | folX5RLykltZ3wRXWVycYEKRyl8WfVaS+w== 19 | -----END CERTIFICATE----- 20 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/v1/errors.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | namespace PCPClient { 8 | namespace v1 { 9 | 10 | /// Base error class. 11 | class LIBCPP_PCP_CLIENT_EXPORT message_error : public std::runtime_error { 12 | public: 13 | explicit message_error(std::string const& msg) 14 | : std::runtime_error(msg) {} 15 | }; 16 | 17 | /// Serialization error 18 | class LIBCPP_PCP_CLIENT_EXPORT message_serialization_error : public message_error { 19 | public: 20 | explicit message_serialization_error(std::string const& msg) 21 | : message_error(msg) {} 22 | }; 23 | 24 | /// Unsupported version error 25 | class LIBCPP_PCP_CLIENT_EXPORT unsupported_version_error : public message_error { 26 | public: 27 | explicit unsupported_version_error(std::string const& msg) 28 | : message_error(msg) {} 29 | }; 30 | 31 | /// Invalid chunk error 32 | class LIBCPP_PCP_CLIENT_EXPORT invalid_chunk_error : public message_error { 33 | public: 34 | explicit invalid_chunk_error(std::string const& msg) 35 | : message_error(msg) {} 36 | }; 37 | 38 | } // namespace v1 39 | } // namespace PCPClient 40 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/util/logging.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | #include 7 | 8 | // Forward declaration for leatherman::logging::log_level 9 | namespace leatherman { 10 | namespace logging { 11 | enum class log_level; 12 | } // namespace leatherman 13 | } // namespace logging 14 | 15 | /* This header provides a utility to setup logging for the cpp-pcp-client library. 16 | When Boost.Log is statically linked, logging configuration has to be done from 17 | within the cpp-pcp-client library for logging to work. 18 | */ 19 | 20 | #define LOG_ACCESS(message) PCPClient::Util::logAccess(message); 21 | 22 | namespace PCPClient { 23 | namespace Util { 24 | 25 | void logAccess(std::string const& message); 26 | 27 | LIBCPP_PCP_CLIENT_EXPORT 28 | void setupLogging(std::ostream &stream, 29 | bool force_colorization, 30 | std::string const& loglevel_label, 31 | std::shared_ptr access_stream = nullptr); 32 | 33 | LIBCPP_PCP_CLIENT_EXPORT 34 | void setupLogging(std::ostream &log_stream, 35 | bool force_colorization, 36 | leatherman::logging::log_level const& lvl, 37 | std::shared_ptr access_stream = nullptr); 38 | 39 | } // namespace Util 40 | } // namespace PCPClient 41 | -------------------------------------------------------------------------------- /tutorial/07/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include // Schema, ContentType 4 | 5 | namespace Tutorial { 6 | 7 | static const std::string AGENT_CLIENT_TYPE { "tutorial-agent" }; 8 | static const std::string CONTROLLER_CLIENT_TYPE { "tutorial-controller" }; 9 | 10 | static const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 11 | 12 | static const std::string REQUEST_SCHEMA_NAME { "http://puppetlabs.com/tutorial_request_schema" }; 13 | static const std::string RESPONSE_SCHEMA_NAME { "http://puppetlabs.com/tutorial_response_schema" }; 14 | 15 | static const int MSG_TIMEOUT_S { 10 }; 16 | 17 | using T_C = PCPClient::TypeConstraint; 18 | 19 | static PCPClient::Schema getRequestMessageSchema() { 20 | PCPClient::Schema schema { REQUEST_SCHEMA_NAME, 21 | PCPClient::ContentType::Json }; 22 | // Add constraints to the request message schema 23 | schema.addConstraint("request", T_C::Object, true); // mandatory 24 | schema.addConstraint("details", T_C::String, false); // optional 25 | return schema; 26 | } 27 | 28 | static PCPClient::Schema getResponseMessageSchema() { 29 | PCPClient::Schema schema { RESPONSE_SCHEMA_NAME, 30 | PCPClient::ContentType::Json }; 31 | schema.addConstraint("response", T_C::String, true); // mandatory 32 | return schema; 33 | } 34 | 35 | } // namespace Tutorial 36 | -------------------------------------------------------------------------------- /tutorial/08/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include // Schema, ContentType 4 | 5 | namespace Tutorial { 6 | 7 | static const std::string AGENT_CLIENT_TYPE { "tutorial-agent" }; 8 | static const std::string CONTROLLER_CLIENT_TYPE { "tutorial-controller" }; 9 | 10 | static const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 11 | 12 | static const std::string REQUEST_SCHEMA_NAME { "http://puppetlabs.com/tutorial_request_schema" }; 13 | static const std::string RESPONSE_SCHEMA_NAME { "http://puppetlabs.com/tutorial_response_schema" }; 14 | 15 | static const int MSG_TIMEOUT_S { 10 }; 16 | 17 | using T_C = PCPClient::TypeConstraint; 18 | 19 | static PCPClient::Schema getRequestMessageSchema() { 20 | PCPClient::Schema schema { REQUEST_SCHEMA_NAME, 21 | PCPClient::ContentType::Json }; 22 | // Add constraints to the request message schema 23 | schema.addConstraint("request", T_C::Object, true); // mandatory 24 | schema.addConstraint("details", T_C::String, false); // optional 25 | return schema; 26 | } 27 | 28 | static PCPClient::Schema getResponseMessageSchema() { 29 | PCPClient::Schema schema { RESPONSE_SCHEMA_NAME, 30 | PCPClient::ContentType::Json }; 31 | schema.addConstraint("response", T_C::String, true); // mandatory 32 | return schema; 33 | } 34 | 35 | } // namespace Tutorial 36 | -------------------------------------------------------------------------------- /tutorial/09/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include // Schema, ContentType 4 | 5 | namespace Tutorial { 6 | 7 | static const std::string AGENT_CLIENT_TYPE { "tutorial-agent" }; 8 | static const std::string CONTROLLER_CLIENT_TYPE { "tutorial-controller" }; 9 | 10 | static const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 11 | 12 | static const std::string REQUEST_SCHEMA_NAME { "http://puppetlabs.com/tutorial_request_schema" }; 13 | static const std::string RESPONSE_SCHEMA_NAME { "http://puppetlabs.com/tutorial_response_schema" }; 14 | 15 | static const int MSG_TIMEOUT_S { 10 }; 16 | 17 | using T_C = PCPClient::TypeConstraint; 18 | 19 | static PCPClient::Schema getRequestMessageSchema() { 20 | PCPClient::Schema schema { REQUEST_SCHEMA_NAME, 21 | PCPClient::ContentType::Json }; 22 | // Add constraints to the request message schema 23 | schema.addConstraint("request", T_C::Object, true); // mandatory 24 | schema.addConstraint("details", T_C::String, false); // optional 25 | return schema; 26 | } 27 | 28 | static PCPClient::Schema getResponseMessageSchema() { 29 | PCPClient::Schema schema { RESPONSE_SCHEMA_NAME, 30 | PCPClient::ContentType::Json }; 31 | schema.addConstraint("response", T_C::String, true); // mandatory 32 | return schema; 33 | } 34 | 35 | } // namespace Tutorial 36 | -------------------------------------------------------------------------------- /tutorial/10/common.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include // Schema, ContentType 4 | 5 | namespace Tutorial { 6 | 7 | static const std::string AGENT_CLIENT_TYPE { "tutorial-agent" }; 8 | static const std::string CONTROLLER_CLIENT_TYPE { "tutorial-controller" }; 9 | 10 | static const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 11 | static const std::string FAILOVER_URL { "wss://127.0.0.1:8143/pcp/" }; 12 | 13 | static const std::string REQUEST_SCHEMA_NAME { "http://puppetlabs.com/tutorial_request_schema" }; 14 | static const std::string RESPONSE_SCHEMA_NAME { "http://puppetlabs.com/tutorial_response_schema" }; 15 | 16 | static const int MSG_TIMEOUT_S { 10 }; 17 | 18 | using T_C = PCPClient::TypeConstraint; 19 | 20 | static PCPClient::Schema getRequestMessageSchema() { 21 | PCPClient::Schema schema { REQUEST_SCHEMA_NAME, 22 | PCPClient::ContentType::Json }; 23 | // Add constraints to the request message schema 24 | schema.addConstraint("request", T_C::Object, true); // mandatory 25 | schema.addConstraint("details", T_C::String, false); // optional 26 | return schema; 27 | } 28 | 29 | static PCPClient::Schema getResponseMessageSchema() { 30 | PCPClient::Schema schema { RESPONSE_SCHEMA_NAME, 31 | PCPClient::ContentType::Json }; 32 | schema.addConstraint("response", T_C::String, true); // mandatory 33 | return schema; 34 | } 35 | 36 | } // namespace Tutorial 37 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/util/thread.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_PCP_CLIENT_SRC_UTIL_THREAD_HPP_ 2 | #define CPP_PCP_CLIENT_SRC_UTIL_THREAD_HPP_ 3 | 4 | #pragma GCC diagnostic push 5 | #pragma GCC diagnostic ignored "-Wstrict-aliasing" 6 | #pragma GCC diagnostic ignored "-Wunused-variable" 7 | #include 8 | #include 9 | #include 10 | #include 11 | #pragma GCC diagnostic pop 12 | 13 | /* This header encapsulates our use of threads and locking structures. 14 | During PCP-53 we were forced to switch from using std::thread to boost::thread. 15 | This encapsulation means that we can swtich back by changing boost::thread, etc 16 | to std::thread, etc here and leave the rest of the code untouched. 17 | 18 | PCP-582 modifies this further to include exception_ptr, which libstdc++ doesn't 19 | support on all architectures. 20 | */ 21 | 22 | namespace PCPClient { 23 | namespace Util { 24 | 25 | using thread = boost::thread; 26 | using mutex = boost::mutex; 27 | using condition_variable = boost::condition_variable; 28 | const boost::defer_lock_t defer_lock {}; 29 | 30 | template 31 | using lock_guard = boost::lock_guard; 32 | 33 | template 34 | using unique_lock = boost::unique_lock; 35 | 36 | namespace this_thread = boost::this_thread; 37 | 38 | using exception_ptr = boost::exception_ptr; 39 | 40 | } // namespace Util 41 | } // namespace PCPClient 42 | 43 | #endif // CPP_PCP_CLIENT_SRC_UTIL_THREAD_HPP_ 44 | -------------------------------------------------------------------------------- /lib/src/protocol/v1/chunks.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace PCPClient { 4 | namespace v1 { 5 | 6 | namespace lth_jc = leatherman::json_container; 7 | 8 | // 9 | // MessageChunk 10 | // 11 | 12 | MessageChunk::MessageChunk() : descriptor { 0 }, 13 | size { 0 }, 14 | content {} { 15 | } 16 | 17 | MessageChunk::MessageChunk(uint8_t _descriptor, 18 | uint32_t _size, 19 | std::string _content) 20 | : descriptor { _descriptor }, 21 | size { _size }, 22 | content { _content } { 23 | } 24 | 25 | MessageChunk::MessageChunk(uint8_t _descriptor, 26 | std::string _content) 27 | : MessageChunk(_descriptor, _content.size(), _content) { 28 | } 29 | 30 | bool MessageChunk::operator==(const MessageChunk& other_msg_chunk) const { 31 | return descriptor == other_msg_chunk.descriptor 32 | && size == other_msg_chunk.size 33 | && content == other_msg_chunk.content; 34 | } 35 | 36 | void MessageChunk::serializeOn(SerializedMessage& buffer) const { 37 | serialize(descriptor, 1, buffer); 38 | serialize(size, 4, buffer); 39 | serialize(content, size, buffer); 40 | } 41 | 42 | std::string MessageChunk::toString() const { 43 | return "size: " + std::to_string(size) + " bytes - content: " + content; 44 | } 45 | 46 | } // namespace v1 47 | } // namespace PCPClient 48 | -------------------------------------------------------------------------------- /lib/tests/main.cc: -------------------------------------------------------------------------------- 1 | // Refer to https://github.com/philsquared/Catch/blob/master/docs/own-main.md 2 | // for providing our own main function to Catch 3 | #define CATCH_CONFIG_RUNNER 4 | 5 | #include "test.hpp" 6 | 7 | #include 8 | 9 | // NB: ENABLE_PCP_LOGGING could be defined in test.hpp 10 | #ifdef ENABLE_PCP_LOGGING 11 | #define LEATHERMAN_LOGGING_NAMESPACE "puppetlabs.cpp_pcp_client.test" 12 | #include 13 | #include 14 | #endif 15 | 16 | int main(int argc, char** argv) { 17 | #ifdef ENABLE_PCP_LOGGING 18 | leatherman::logging::setup_logging(boost::nowide::cout); 19 | leatherman::logging::set_level(leatherman::logging::log_level::debug); 20 | #endif 21 | 22 | // Create the Catch session, pass CL args, and start it 23 | Catch::Session test_session {}; 24 | 25 | // NOTE(ale): to list the reporters use: 26 | // test_session.configData().listReporters = true; 27 | 28 | // NOTE(ale): out of the box, Reporters are "xml", "junit", "console", 29 | // and "compact" (single line); "console" is the default 30 | // test_session.configData().reporterNames = 31 | // std::vector { "xml" }; 32 | 33 | // ShowDurations::Always, ::Never, ::DefaultForReporter 34 | test_session.configData().showDurations = Catch::ShowDurations::Always; 35 | 36 | // NOTE(ale): enforcing ConfigData::useColour == UseColour::No 37 | // on Windows is not necessary; the default ::Auto works fine 38 | 39 | return test_session.run(argc, argv); 40 | } 41 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/gen.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | set -ex 4 | # Clean up any left over data from before 5 | # This script needs v3.ext 6 | rm *pem inventory* *csr *srl 7 | # Setup min required files 8 | touch "inventory" 9 | ## Min config 10 | # printf "%s\n" "database = inventory" "default_md = md5" > "conf.cnf" 11 | 12 | openssl genrsa -aes128 -out ca_key.pem -passout pass:my_password 2048 13 | 14 | openssl req -new -key ca_key.pem -x509 -days 1000 -out ca.pem -subj /CN=ca -passin pass:my_password 15 | 16 | openssl ca -name ca -config <(echo database = inventory) -keyfile ca_key.pem -passin pass:my_password -cert ca.pem -md sha256 -gencrl -crldays 1000 -out crl_empty.pem 17 | 18 | openssl req -new -sha256 -nodes -out good_cert.csr -newkey rsa:2048 -keyout good_key.pem -subj /CN=good 19 | 20 | openssl req -new -sha256 -nodes -out bad_cert.csr -newkey rsa:2048 -keyout bad_key.pem -subj /CN=bad 21 | 22 | openssl x509 -req -in good_cert.csr -CA ca.pem -CAkey ca_key.pem -CAcreateserial -out good_cert.pem -passin pass:my_password -days 999 -sha256 -extfile v3.ext 23 | 24 | openssl x509 -req -in bad_cert.csr -CA ca.pem -CAkey ca_key.pem -CAcreateserial -out bad_cert.pem -passin pass:my_password -days 999 -sha256 -extfile v3.ext 25 | 26 | openssl ca -name ca -config <(echo database = inventory) -keyfile ca_key.pem -passin pass:my_password -cert ca.pem -md sha256 -revoke bad_cert.pem 27 | 28 | openssl ca -name ca -config <(echo database = inventory) -keyfile ca_key.pem -passin pass:my_password -cert ca.pem -md sha256 -gencrl -crldays 1000 -out crl_bad_revoked.pem -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/v1/chunks.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | #include 10 | #include // uint8_t 11 | 12 | namespace PCPClient { 13 | namespace v1 { 14 | 15 | // 16 | // ChunkDescriptor 17 | // 18 | 19 | namespace ChunkDescriptor { 20 | // Filter the chunk type bits (envelope, data, debug) 21 | static const uint8_t TYPE_MASK { 0x0F }; 22 | 23 | static const uint8_t ENVELOPE { 0x01 }; 24 | static const uint8_t DATA { 0x02 }; 25 | static const uint8_t DEBUG { 0x03 }; 26 | 27 | static std::map names { 28 | { ENVELOPE, "envelope" }, 29 | { DATA, "data" }, 30 | { DEBUG, "debug" } 31 | }; 32 | 33 | } // namespace ChunkDescriptor 34 | 35 | // 36 | // MessageChunk 37 | // 38 | 39 | struct LIBCPP_PCP_CLIENT_EXPORT MessageChunk { 40 | uint8_t descriptor; 41 | uint32_t size; // [byte] 42 | std::string content; 43 | 44 | MessageChunk(); 45 | 46 | MessageChunk(uint8_t _descriptor, uint32_t _size, std::string _content); 47 | 48 | MessageChunk(uint8_t _descriptor, std::string _content); 49 | 50 | bool operator==(const MessageChunk& other_msg_chunk) const; 51 | 52 | void serializeOn(SerializedMessage& buffer) const; 53 | 54 | std::string toString() const; 55 | }; 56 | 57 | using ParsedChunks = PCPClient::ParsedChunks; 58 | 59 | } // namespace v1 60 | } // namespace PCPClient 61 | -------------------------------------------------------------------------------- /lib/src/protocol/v2/schemas.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace PCPClient { 4 | namespace v2 { 5 | namespace Protocol { 6 | 7 | // HERE(ale): this must be kept up to date with 8 | // https://github.com/puppetlabs/pcp-specifications 9 | 10 | Schema EnvelopeSchema() { 11 | Schema schema { ENVELOPE_SCHEMA_NAME, ContentType::Json }; 12 | schema.addConstraint("id", TypeConstraint::String, true); 13 | schema.addConstraint("message_type", TypeConstraint::String, true); 14 | schema.addConstraint("target", TypeConstraint::String, false); 15 | schema.addConstraint("sender", TypeConstraint::String, false); 16 | schema.addConstraint("in_reply_to", TypeConstraint::String, false); 17 | schema.addConstraint("data", TypeConstraint::Any, false); 18 | return schema; 19 | } 20 | 21 | Schema InventoryRequestSchema() { 22 | Schema schema { INVENTORY_REQ_TYPE, ContentType::Json }; 23 | // TODO(ale): add array item constraint once implemented in Schema 24 | schema.addConstraint("query", TypeConstraint::Array, true); 25 | schema.addConstraint("subscribe", TypeConstraint::Bool, false); 26 | return schema; 27 | } 28 | 29 | Schema InventoryResponseSchema() { 30 | Schema schema { INVENTORY_RESP_TYPE, ContentType::Json }; 31 | // TODO(ale): add array item constraint once implemented in Schema 32 | schema.addConstraint("uris", TypeConstraint::Array, true); 33 | return schema; 34 | } 35 | 36 | Schema ErrorMessageSchema() { 37 | Schema schema { ERROR_MSG_TYPE, ContentType::Json, TypeConstraint::String }; 38 | return schema; 39 | } 40 | 41 | } // namespace Protocol 42 | } // namespace v2 43 | } // namespace PCPClient 44 | -------------------------------------------------------------------------------- /tutorial/02/agent/main.cpp: -------------------------------------------------------------------------------- 1 | #include // Connector 2 | #include // connection_config_error 3 | 4 | #include 5 | #include 6 | 7 | namespace Tutorial { 8 | 9 | const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 10 | 11 | const std::string AGENT_CLIENT_TYPE { "tutorial_agent" }; 12 | 13 | const std::string CA { "../../resources/agent_certs/ca.pem" }; 14 | const std::string CERT { "../../resources/agent_certs/crt.pem" }; 15 | const std::string KEY { "../../resources/agent_certs/key.pem" }; 16 | 17 | int main(int argc, char *argv[]) { 18 | // Connector constructor 19 | 20 | PCPClient::Connector connector { BROKER_URL, 21 | AGENT_CLIENT_TYPE, 22 | CA, 23 | CERT, 24 | KEY }; 25 | 26 | std::cout << "Configured the connector\n"; 27 | 28 | // Connector::connect() 29 | 30 | int num_connect_attempts { 4 }; 31 | 32 | connector.connect(num_connect_attempts); 33 | 34 | // Connector::isConnected() 35 | 36 | if (connector.isConnected()) { 37 | std::cout << "Successfully connected to " << BROKER_URL << "\n"; 38 | } else { 39 | std::cout << "The connection has dropped; the monitoring task " 40 | "will take care of re-establishing it\n"; 41 | } 42 | 43 | // Conneection::monitorConnection() 44 | 45 | connector.monitorConnection(num_connect_attempts); 46 | 47 | return 0; 48 | } 49 | 50 | } // namespace Tutorial 51 | 52 | int main(int argc, char** argv) { 53 | return Tutorial::main(argc, argv); 54 | } 55 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/bad_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvQIBADANBgkqhkiG9w0BAQEFAASCBKcwggSjAgEAAoIBAQCgo1PNdLGaGZQ8 3 | M1lHBTYs1S5gkJad6XwmFjxv+ZqIYU5gK+QHS+aMECCqt5yWjvXPhtvAXdwG19Mv 4 | Ynbc0xBHYeHQqCXGotq9kjCqC/zuFTexirKPrFq6whIu0mvsPqRp15SGCpQn+cNH 5 | qOi6vLxzZeiyg6p3JP2QAGItwuYsNyuXIzkQMXbKOPolRkKxqlvxHTzDNzVr4qgj 6 | +k1wRsY3drk+GKRxjXhmOILT4ooz8useYt6EgkM1J1cwfJv3+FwJCkhQayvTRSxp 7 | 4Yu9vxG11eypQ/4a30dI6QhBLLkVqfCCDSVJyI7NxDRuC+06A1g+lcG+SbcqOL2M 8 | JCeRenEfAgMBAAECggEAAjX9Q0il4R8VATcbtXSG6FsOxll3GTlLciLANjJTRClC 9 | sOQpwUf0Jl1adb8NMq1JCNAsWhtwmc+CopZSWHu3L6RTPVHzIya+p/lJHi4IDybP 10 | m2vzRTL0FLnzEh3nSlsaXDKRYr7mhx5S57uVaGyMk3IFn99cC0PBEahPsZ2LGXSr 11 | Wbkp13xLRqRSow6/1IuISc21KUpGEMgstzcw4uCJF1S049coQlBH4kxEtXGpV3HU 12 | 9Cu5PGd97f5ZhIzH0oFz5FZai3GiE1i70kcN0XGqZaJ7R5Vw4wfpd6RDcp8qAWnR 13 | g+bM74yyIhZMv6GGLs45IgdqIrj+n0pQwR3E5WeHcQKBgQDN6u0NwjecWs+JB/2o 14 | oVx5LSEJ7BsqYGSWZs2fg9CKRmsD+C5pjtQF80OOV9dQ3GnK+QE4IWyqmIQE+w9X 15 | xTrkHRdM9q9SxaU5mIYNS/kVgR/PmqNQmI3BgFxDlkuB90G5uMHNGUZszUcpY8jk 16 | IlkeVR0awkiq2iPB5KPgKJpjdQKBgQDHtSXU102lYVXHkdJIho05O8F1iYFDgg70 17 | 9ogJ3CouWz/d/K13NE8+oXWFT6nvzdY1txHIaI3WoRmGviPv0wRPHLIFoBGyLS6o 18 | vT3zO/7LBVlUqoVMT5KoiQ1PfCkgP/Jzmhdz52OSK4FY0x/lPQNcdzkIAn6uAXu1 19 | /1W0D6MTwwKBgBJr7O8pK95yBVUGAPxgCyzQC7+efsOowfbulYCqwzJLoHFHd6d7 20 | 3p31qFqIozuZrGE7K4/A9b7BTDZB25qHi4Ay1g3Nl9u40NI5QezYmh0BYz4oYGnq 21 | a+wetqJZRMIIw7mAvN3DYBiFuNMMGUHHjN94z9TAAs4+G6rR8JMyLrWtAoGAa4jO 22 | Qypf4/JidyC9A1J4Tt4vULPnB8vLc/ofzHdhuy5lWIoLecmP9iCIoG0CC19hvfDC 23 | wZAS+AQtM5HE1glhI5xG+6JyhOexXSjxZ7zheL57mIxtVGbwiJGruiYYGwZz7e2G 24 | PWHWCsokTwj3/r74xVxpODfnsJiZ3akMo/VbkXMCgYEAhbKU4ySzBLSULx1yhhAo 25 | 5qVIlGUqL8WOnm2CVHfrta2gDm/FqyLP5scwI0C8yXU1flC/XtSLR4NhUFsJbRmi 26 | KistdtXBlipJTVbIkTT4Xe2iZSPjHXdIiGPgGHG2FxHlUlg7nTyTDGDQ9dl5y0TV 27 | wKQRJotmYesj1iLAxyZGprw= 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/good_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN PRIVATE KEY----- 2 | MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCmEPE5QPyIvWGg 3 | zHT/zZPzZhQQ1Ue6Nz4WVhM4pUvXjV8X2y9L7Xa0ii76ft3mNXlTEmmIqaJgbTSU 4 | +c3sXmmsC6jcKGkyD2KWN8RZbCETLZOeTOkAavReAXC7NPKpSl3aqFC0J1Mipixq 5 | U445r4qzdew3s6A+xGKT5ymMqawgtyEyScpF3BFWHaW1dHLeKpvcCJG6Fn9nl2cg 6 | ncNO1NHizpyDDKRzOdSmRKghaYt/q+hNN+AI25mIt/tKfqnUa6iCvljWvTSzoY5k 7 | rq6prbNDErlkLI4eTszqSF+dc0W25jYc53DIaOM3lZ99cwfG9ifrvxEDXSEYS459 8 | 23oMUcnzAgMBAAECggEAf++Dd0FKjCmiIdgBtuRwu84DXWiCRsKyUVDko/osMJaY 9 | EO/qQwthLPtb7913JBhELKx9oyxUjQb0wGSmsReXP1UINPvo9wkFQYca/G0iGf09 10 | wXGs+4ZqVlWzvHWxEJzbK04KRpAZKxNsGknewzAqodESN2Np65EuBJXM27IHinds 11 | zITH77otjVIVpPktDHhwYUOCkFvEKhn1mwYC+46xmERjBAgwbVhmYlFxry5DbFhO 12 | hRFH1ZGsZrF7Ij6DkdaAoJN1BPQ3SFBPiMpNbPy7Mg0EKHxJC+qQfgIs1O920neT 13 | +voEnO9WXDsvILKt4IokOgaiH80EVsJAPQaQQIS8eQKBgQDYRgG125SsvbT+A9qZ 14 | prZfva4mZkMvIU0K3eL7Y80cXhUXCyuw1Qoa/n1eXLhcXBGoZgE7x4xVsFm7ILBF 15 | kICnmtRYV54KE850QVjPBX2c3JO2HHy/g2cH7xsZDxcDPYNjwz8Sk5RXXX49YFBc 16 | HuUXZPyBkV5q3MCIU2y1BNABBwKBgQDEkf/8gCG7zH+9pfm1p5A9TnoezjK51g0c 17 | fskW8AQWh+TPpCW+oSDN6w22CenkxKz4Y2Dxzs1M5ipUm80N1AYWWnWVEAcgYSA/ 18 | ylclKlW9TJSH1ANhRAacVsKj/jo1QDmpsqs0VcxfDUy7eGN7kU+AtqaXV8E/Y2s6 19 | QLAPV0JwtQKBgDAVqI2xa2XYq+QKIVEmMsgomQ6qj+drpQB0guHXt6PF+zRrZGRl 20 | iWClEmSngsnuInLpcv7CiB4JHnovc2GUFjatgua9XELrUmyglsk4dEwq8qB7xvSh 21 | l4e5DrJMm5Y51/uXTm+n5kRiYg3tWxCQQekop8d69Z4WxKvKuJGqp0CDAoGBALis 22 | vgX4TzkDmGORQnfSlCW0y/CTIn8LRcgmqT++gOi2BVa6zac58/rhwUDsMWz3BdZa 23 | b2LeOmzrtItG0LAUKR/pHpzLsRZPiZ/Etni5PFIbV1QA8Kf55AgPTtAJcaBD1ajD 24 | rir+DIKkmRlrsnMOAtZXQ03eaTTnb38L9RyEi2wdAoGBANMGOwCpVSo3E45v48mh 25 | rtyoLRIM4Xk6Ux5LuZsjVH1RiZqoDccRMFpftYwSSmrxz4++/3Av2vAcD0E40R5N 26 | OgZpH6TcBlAjbQv9VbNM2AIDqOSLD7ZIrmwp+2jJJrzRWgNyA3Cv5xLr+4NABpjU 27 | momSss0rxafqhnj7dIbJHIGo 28 | -----END PRIVATE KEY----- 29 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/parsed_chunks.hpp: -------------------------------------------------------------------------------- 1 | /** 2 | * This is a shim to maintain compatibility with pxp-agent during development. 3 | */ 4 | #pragma once 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | 11 | namespace PCPClient { 12 | 13 | // 14 | // ParsedChunks 15 | // 16 | namespace lth_jc = leatherman::json_container; 17 | 18 | struct LIBCPP_PCP_CLIENT_EXPORT ParsedChunks { 19 | // Envelope 20 | lth_jc::JsonContainer envelope; 21 | 22 | // Data 23 | bool has_data; 24 | bool invalid_data; 25 | ContentType data_type; 26 | lth_jc::JsonContainer data; 27 | std::string binary_data; 28 | 29 | // Debug 30 | std::vector debug; 31 | unsigned int num_invalid_debug; 32 | 33 | ParsedChunks(); 34 | 35 | ParsedChunks(lth_jc::JsonContainer _envelope, 36 | std::vector _debug, 37 | unsigned int _num_invalid_debug); 38 | 39 | ParsedChunks(lth_jc::JsonContainer _envelope, 40 | bool _invalid_data, // invalid data 41 | std::vector _debug, 42 | unsigned int _num_invalid_debug); 43 | 44 | ParsedChunks(lth_jc::JsonContainer _envelope, 45 | lth_jc::JsonContainer _data, // JSON data 46 | std::vector _debug, 47 | unsigned int _num_invalid_debug); 48 | 49 | ParsedChunks(lth_jc::JsonContainer _envelope, 50 | std::string _binary_data, // binary data 51 | std::vector _debug, 52 | unsigned int _num_invalid_debug); 53 | 54 | std::string toString() const; 55 | }; 56 | 57 | } // namespace PCPClient 58 | -------------------------------------------------------------------------------- /lib/tests/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # allow include directives with absolute paths for cpp-pcp-client sources 2 | set(BASEPATH "${CMAKE_CURRENT_LIST_DIR}/..") 3 | include_directories("${BASEPATH}") 4 | SET(SOURCE_DIR "${BASEPATH}/src") 5 | include_directories("${SOURCE_DIR}") 6 | 7 | set(SOURCES 8 | main.cc 9 | unit/connector/certs.cc 10 | unit/connector/client_metadata_test.cc 11 | unit/connector/connection_test.cc 12 | unit/connector/connector_base_test.cc 13 | unit/connector/mock_server.cc 14 | unit/connector/v1/connector_test.cc 15 | unit/connector/v2/connector_test.cc 16 | unit/protocol/v1/serialization_test.cc 17 | unit/protocol/v1/message_test.cc 18 | unit/protocol/v1/schemas_test.cc 19 | unit/protocol/v2/message_test.cc 20 | unit/validator/schema_test.cc 21 | unit/validator/validator_test.cc 22 | ) 23 | 24 | include_directories( 25 | ${LEATHERMAN_CATCH_INCLUDE} 26 | ${VALIJSON_INCLUDE_DIRS} 27 | ) 28 | 29 | if (WIN32) 30 | list(APPEND PLATFORM_LIBS Mswsock) 31 | elseif (CMAKE_SYSTEM_NAME MATCHES "Linux" OR CMAKE_SYSTEM_NAME MATCHES "SunOS") 32 | list(APPEND PLATFORM_LIBS rt) 33 | endif() 34 | 35 | set(test_BIN cpp-pcp-client-unittests) 36 | add_executable(${test_BIN} ${SOURCES}) 37 | # As a warning, don't put Boost.Log before libcpp-pcp-client. Boost.Log uses 38 | # Boost.Asio in its syslog backend, and putting it before libcpp-pcp-client in 39 | # linking causes segfaults as it picks up the wrong Asio symbols when using 40 | # Boost.Asio directly in the test, and Boost.Log and libcpp-pcp-client are 41 | # both dynamic libraries. 42 | target_link_libraries(${test_BIN} libcpp-pcp-client ${LIBS} ${PLATFORM_LIBS}) 43 | 44 | add_custom_target(check 45 | "${EXECUTABLE_OUTPUT_PATH}/${test_BIN}" 46 | DEPENDS ${test_BIN} 47 | COMMENT "Executing unit tests..." 48 | VERBATIM 49 | SOURCES ${SOURCES} 50 | ) 51 | -------------------------------------------------------------------------------- /lib/tests/resources/protected_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: AES-128-CBC,7BB1DF602C6B5DB75DAFA39BCCE20F12 4 | 5 | wcuPuIcMJJem6m9HCN2JgypqZrPHSesqOysCdZXDMvGDu1DImedor3ydDYfh4C+G 6 | YMEd4jQekNbLhH7W9T+lqkRMgWbm4Zz09trPCbt4BvCqW36Blfs1vLgneuXKodvu 7 | 1qA/pUIp7MCSkBR+gTDjfbyViQXO1fMCI9KVCvu3L0iTbb9DmDuOEM61WRMFgWU3 8 | Is3H6jq9RoMtdP08485R6O9qOK1jVDGFgmqcJGDAEzObR2q1I4k3L34gy7pxA778 9 | Dgj+PQhofeRuJNQL+itd7vBlzm0PWtPvxKBfzdfV3eat9qhPOYMipGlX0iftqOBz 10 | oBPsTu5Jr+CNwVgj6xxciy86kb49IfJygH9jSuZ4HidUh5O6y2yBjCKkBfADbNUl 11 | XwSqae7KQXhu0XFUOJoYhBXtzDZ8f9/XOq4/AgBA0xpMGyjKznsAuKKlo6UhAJPf 12 | NFDKwDEGBxZgSkGq8vpgPCmNF6f9D0ZIWyAuYsDC+Wcw6EEFOrSFEQleeLt/fcAs 13 | yTUaalu73+3jSWMquV+b8LNsznLl3/2d4IxrAbL/gDjjF1NJubup1Q9NipAcrhfq 14 | PnAcDA+6DYZ5dv3teaCvHgPCvX3rBWhFxcO1e8maGzOa7gqmTJxpYp6hh3HxdFWS 15 | 8sMMQjOjuHCO+F5tYUyg85jNp+poMqegClzgR670fkJIgne8dXXpjw0VDOkPvs8N 16 | x8gYCnL3E8l0oE7tfJhvK1DjZjGWDpSzoE/okn3HSe8/L73hLAwy24QXrg1+NhzO 17 | Cnh5ZXfN1f09gvuAaXYt5wPWRxDxUxEWHt5Z+a/+MRZOC1SQnjMoR/IvN/AafQuN 18 | pH/5MPbFdq5FaXL1GynPgNIUqSkCuslK+DUXGTK00yvtAptbtgLxYlc1vsPx4Nvz 19 | vACTavm3lKf2E61xnrj5qw6JU6jQpSgB7A150gnLDem+dBfxqKv90/kVVxKe8g0i 20 | AxfdKqY+kZLtB8cV3eRVIYq3yvAupdyG5ZHpkB+7GBLyR507EMVTbbotmlwKXAlI 21 | nNVChMQIYHRHXUYPbgnYW2mqRL5ivtj97J7rhmfbz+EDeR3moDURAf4bSMR3f5Md 22 | 27hT1Bvuq3P0/ynq+dz6FOvHWm8UVjYoucv4X6aN8kgTreOYk6OAVSmqh3WY+aDZ 23 | mEH/rg7fRHx0XK+Yfxqxh7utVofgvFHNyLFHfxphRyTfkjQog8F096i/HR+Wn7rH 24 | St3HCE96T/Ucof46+YHch3NxdvTFzIzFIXZRZQjH+umcuFyJISftZSQ7qATleggs 25 | tdkAUQHzNaEZQp0uEBSdIve0CGofwOiA/mzk81unQS3RJ8m6T/Cvj3cfa1aNANMd 26 | 4EK1Xf/QAJwmcyngGKBkIzfow4PzDaCjtwL/fGsYI7n3iNXVVXphPM3fb/r2i+b7 27 | Kk061yVQtoSepVXg4fd5Pqc7OkYYDM9m8mUjVFKft+bjB573EUEGUQXJmml4IFpj 28 | OICBaiMU0cKFcLGyL7fhCNzKNf2QM+oqC4KtsP+IYx0WyhGbH/24iTAOIEwq6Ob/ 29 | vlg3Pzr1OlhGzYX3CZ8eOUcEMNNu0f/JSuYO736dOvSm6JNEmxSKMvjjuGo76rQz 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /lib/tests/resources/ssl-new/ca_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | Proc-Type: 4,ENCRYPTED 3 | DEK-Info: AES-128-CBC,AD1C389F4DD4174AEA0785F603A4F411 4 | 5 | GMOw8IMOZ1bsg58XqUcnyxf5M7Dk5w4M8ga6RQ074cW0oMv97+zvEEeABCSX0Mpb 6 | FU3k0xt+HG8m1gygQNFkSJWEYxBdO/6LRhJGwo9BCHh8G0A62+FUM7RDDJj+L/pb 7 | UD99wlsT4huQOn16w9t/RWvVGwdfu1RB4XAGgw7fWpfYyRe3yII8kwbURzkSO9nS 8 | 2QIXjexsrgwt34c8NFdaP4+UpAhbA7AZ0VSQ4S4sdmQVV5ociuhHanvNR9XLPg3P 9 | /Sc1mPndU0ZvIb5vQmmtWDyG3GOecIzFhm/DXLRSHwzZcNHnLw6hLNJEY9hHgs4n 10 | pG7z0zIKTzOPiIs79l/F/GWyE9gCGDfn2ZR1dondFceKnKpFfYdshWTdYlk0bByf 11 | SilBDM8iuR1owpJ43nDg3fC59YsAJDBAdi9rPd/lXX4vH6psM3WxwP859oQIfolY 12 | 75E5wzwKySIzednavYJfxfRWoLn3gWI2dxREJvoOEeN/5iA5kS0gupsD8oBT9kc9 13 | j54h3MGyBs2fVWZmhxO22tQFiMEbdUbvpXKTnpS7S8nF99RgSX4yd+0VKfNU1HN5 14 | qpsrk9JmX4Wxv7UtMyKe96+/QPOf/mV/pwnFCop965jleT/nzChHJqPVbhNE79gi 15 | NAmB+C6Ci4iDI4G3e8BQo3QGKbCpYkx4V/3AwHh3vP/PlxEpTzjJ7JpCQ9q/7/Yl 16 | pjSKZ1YaRXpUCnOnA91Id4Y+GRawWT5UUACUpTQK4FqYgga0kM2lpOjoTpoxvyKr 17 | JQWXgzlARuwYpQaa1ayhoEdZQbFemy0loF2ngB0OvgHq+m8Y3zf2Vg3+DhuhsJLL 18 | HuLbCNOUbciWUuYTN1dk8AUHTyqVIiQRAWj2eNCWv8+RbLHrVNqAUleTu8GOQwcK 19 | HXn4DLY8bhtHkFejEeHzDO+pFwtA0u8seBjIxBRasGtjO4zmBXhAvX1d9b0HGEO0 20 | M+JGBhj3xVGPbSKA6J68hUeO6rrEB0wFyPvHC8vJsJvf6QGBeH8RLMJKrme4dq+/ 21 | GETR8ZolvpS1hhDNACjf7FKI4jkQc6EcEbHmL481hFoNZjJkh18jKPWaZY/L3FxM 22 | y6sxDXkLaZ6nOqZJqX6HxsafB1YZRZZUDKpvlKLOrrRbAfcQgoURiyoaarPT++zo 23 | CmFo/Q1EU93vcxgyQAmvsduDUEK1txiYM/qzEjtsMV7//nve7812B7pIHqQKhSBA 24 | 79ypa2+kjQLTyT6OlcLxwsNhcXC9T3a2SiKVgmhoXkpJAiKxjU+yiTNZtlnGH//V 25 | +qTjR2ccLwIpBJWNMdH0VEhRs7dT2kb3jPdIqgq/BF4Z5KHlY5CajtQ/vGmYm2MA 26 | DVQLg6VKtSAgF8qwjPaK5tRQKN1Te3nlAiRMt7vGDsvzHQQX/iOFZiEf4HCbLL2I 27 | u8BlRK4A0j9Ub/LJ0cnwt8GjalDd4CpjqOqBOLO0AHqkDfvIPVrtyZdhihl1XrWT 28 | uOz9LQutE/aGKG5vTQrXxu3oNG2MAauHTNGg0JkHPhF7Yj/mtnXrgxQnad2ko6p8 29 | 6+wYvSO358hQJKHVQFh+498cTDHLCchG9woLxNbIIhPn+kGusgGDx9V/5oCiGH6V 30 | -----END RSA PRIVATE KEY----- 31 | -------------------------------------------------------------------------------- /tutorial/resources/agent_certs/crt.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFhTCCA22gAwIBAgIBBDANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNQdXBw 3 | ZXQgQ0E6IGphbWVzcy1tYWNib29rLXByby5sb2NhbDAeFw0xNjAxMDYxMzU2MTZa 4 | Fw0yMTAxMDUxMzU2MTZaMB8xHTAbBgNVBAMMFGNsaWVudDAxLmV4YW1wbGUuY29t 5 | MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEAyYOdOuaWP7YiByn6/O5O 6 | X86uNX5rZ7I+vj4KBD5B9bdcjxo3b3XxnarhTnzreBP0mop7y0DNTJExebuuXoF/ 7 | 9zZeiKrM9nYr5pZ6GTxJuWCw6C6S9dksMibVzByDi3d4Sz2U1QX0lXhHj/wdjGmP 8 | LWf+Q6QCrJAtSwhFvgVab7uyaRB6LN3tepu8LXLQbncqIWcapUyy74ogLdmJWoQ2 9 | J2v8LReiBMlfaqwXCDhon9XUdmAY1B1I+7CTU76Bpi62sgtVXflW8932rn/OSlCa 10 | pyJ10/gepw5+xL4r9fITF3/YHRuWZqNBvLkY3EFoL2KtYe0gJhahtJjEfKShbZrM 11 | daLcnj0//D5KeN37R9w8SKQ6u3LuxjOUYsQH69OqiG5DRBOsZ4/3wc/5AD+mxic4 12 | 6IRvQ9Dxzj6dlUejk+M7a7dM85r0t2tqJ6p0G3HyI9CYPI8R7OHM8MNVfSY5aLcF 13 | 2FwtK5kW/u/6k9dwvlfDB+trpJaLfc68hbV+x8IFSpjiHL6bRTnaMNbbtlEZCHm2 14 | 6mFqk6AOWUBMnS/FpZSCuq1Ksi4XpVFwjnall+QcFvA9d1upiXS43y96o+qp9ADO 15 | FMTCDLBr6hN6Y3XYEczTv2WzwL4hpN1Dc3/9VyD1Sy0aLKRpuAqajp6nC3zZAFOi 16 | +CUfP825JsCnzhmAwek4zgcCAwEAAaOBvDCBuTA3BglghkgBhvhCAQ0EKgwoUHVw 17 | cGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5hbCBDZXJ0aWZpY2F0ZTAOBgNVHQ8BAf8E 18 | BAMCBaAwIAYDVR0lAQH/BBYwFAYIKwYBBQUHAwEGCCsGAQUFBwMCMAwGA1UdEwEB 19 | /wQCMAAwHQYDVR0OBBYEFBqT3pHdK/sp2iEQCM+ZLBvBDHj3MB8GA1UdIwQYMBaA 20 | FPxMZnmA/cR1uDl9+tNXk0JH/wPGMA0GCSqGSIb3DQEBCwUAA4ICAQCo7zejfOhk 21 | v027H8VXhvheeg0CR0JZ4lmZ/nfFEWYwzyb597JvT0ybUYe+qCH4nsE3ZZu3Twgv 22 | fo82syDFz85yf6coMoPYFi7ft1Sy0OKa2pfB6z7WQmD6hUWpCki65u6ldKJOyLI6 23 | Ej5sgfV7MAc4mrALrOTnw6ErOVcvJZ9Ivr6jLx8hrKXM8PYcpEP9GlAR93Q8F9Jo 24 | OChAhdRyJmDpf5mwXSR2rmfojGLcUkeMfpJytMyEQgN2L1+ylrnFEF5lVocHj1rE 25 | ZPUdQM9DXAoo3xAJN6JKUEQExVj01E9rg6KHQRV8b6Ywn35+J/ek+lRTBUc9QL+h 26 | yVbiVr1g5DNM2FhXs5Jjhcr9LQKvNi/n/6p9EQq7jejVp1x5vb8QRgZkaw5Jg+Ty 27 | aPd5MkCSGBlNHLmGjj13+1NyePLZVz7q7nwHvCQOn2Hdd3oklWCeI98SHLdKbTv6 28 | +PrmdPIhd2drb6PHdp2vTq6rvw5Hva2Zuv1PoQRmqaKzXk6hai//tSU/Yfof1JGx 29 | 3gFzINO3ugt6qk6dcz9jKHh2Z6wKF5MZQ/ILCLFdvFKUtXQYokQTigrmG2IW8cMq 30 | OQH7sJ09S/N2RvUBT8sZHte2LNome+2yZaZCR6EtIrbuODB8NURY70sae/7fAdzE 31 | 2iQI3bgQRNbkP6PudH2Kdlrf032pgWnNDA== 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /tutorial/resources/controller_certs/crt.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFiTCCA3GgAwIBAgIBDjANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNQdXBw 3 | ZXQgQ0E6IGphbWVzcy1tYWNib29rLXByby5sb2NhbDAeFw0xNjAxMDYxNDA2NDVa 4 | Fw0yMTAxMDUxNDA2NDVaMCMxITAfBgNVBAMMGGNvbnRyb2xsZXIwMS5leGFtcGxl 5 | LmNvbTCCAiIwDQYJKoZIhvcNAQEBBQADggIPADCCAgoCggIBAO0Kt5Tv6COQlsEC 6 | xr/O1cg9fSeiBR8YVcR5W8iflocKXMETjHIX2HhS1KmpLZRgvzhta4fUwSWiZPHs 7 | x3Xo8ChyjldGeiKhw/ObZ0JAXefFshO2zACW4sJeG1mtViavKbks71bCjqBXS7pZ 8 | JfKXYyeaFDCwKuqhYKC2QhaH/dXMcN2eu0ZxZFYrE1rHN6N6Ns5visilUUnzkwKR 9 | TGbSFn8XPS+vcTKBVGr8DZuMKzaEKO815kosuSazJdV9HAHvSy2Tc+NbMPpGapDV 10 | oBCtC4p6Klcjp2NwmN+/63Xn17x2QD/D2rETn8OTytnZo3NZOulMhX50qPCU34XO 11 | LklPYQ/ydLAlmn1jpsRyD0pXqLPbWA1F2Gu+rqIyhpOBuBsKIzVIof+mZfs2c+0E 12 | OHfWMqcWJTN9zq5ZRxnPcKyFNbhG4Dq7WIbKFBHH8VwssiAfxgvgKta1W43bc8ZX 13 | syxN2KptUlTSWVPhzFrUOPbS4YLhi77MjlNq47Yjg9Bp2GxvenFSys/fWBRlWZum 14 | JK2M155wRoqOE358VqgbNXhWxKjdYo5JwpbrouQN2bpdQMCjsPPT3XUtlc+VClK0 15 | yQ1+ooVL2hGFptN3ig+8vSywoNuK2T6nJnfeAgqMF/RuvCtPuUcGXsxwy7yCiK0v 16 | ArB5iJI4ArRve2WOocRt0UAuTC0lAgMBAAGjgbwwgbkwNwYJYIZIAYb4QgENBCoM 17 | KFB1cHBldCBSdWJ5L09wZW5TU0wgSW50ZXJuYWwgQ2VydGlmaWNhdGUwDgYDVR0P 18 | AQH/BAQDAgWgMCAGA1UdJQEB/wQWMBQGCCsGAQUFBwMBBggrBgEFBQcDAjAMBgNV 19 | HRMBAf8EAjAAMB0GA1UdDgQWBBQ9FOlyZh85IQH7o2j3S5D0ZDC3ODAfBgNVHSME 20 | GDAWgBT8TGZ5gP3Edbg5ffrTV5NCR/8DxjANBgkqhkiG9w0BAQsFAAOCAgEAba0L 21 | Ji2nEUmu66t+PB+afuBArc2CHsd2KGClEDtwjCc1uPsLWtdQMJb9OOpecyK/6KO/ 22 | 6eE7q6CyNCfe7cMwd4SsJlu7kkRXswsPnVk8osKkSwFfbCpvlD+ZnFuT7U3R44Ce 23 | XT0r/riGAq79yYcznWEGWAOrfbaU+D/PrErPLPSz0NL/1zqvVjj3cgev6JF9QtwN 24 | AreWfeBbznjgW69APDVjm0o2NiBh8BZ1Mf/kbGpoea4vlDW/J+U1IgMKh2+Ueax3 25 | BkDNW8OHs8FawVnbPCCZ/0m8k5CBphDZWTKx44j3KfAu1nH3MWE7QYImz+4nlZtn 26 | nkR7ulo/r2y/T2f/O2BXz0/Xb8S7cfH8uI5w/MuDS+SvfA39G9wudrGX5entJbn8 27 | A0bahfvv+XTD8As9llCEmc7cU/rpneqDY2v7i9P2S1opvB+8yAZG7XUheHbh6o6o 28 | DOqklGgza9oEJ9efNjHocvLKRJwC3CibLw/8xwQcSx+b0Z/trOpW4OfZbUHTFd1B 29 | Z8OTYVkPs3mUh1EfhaGOetPwe34+5yacxqOVhykZyI8Sia7MkESjxiRVO6CVaefn 30 | E2EYe8yX26BjDSNdAXd+h3OAszLLzHJu3v7+VTb+9VtVvZg78hKx9J2fPlKdwqc4 31 | 6U/0C+bwudwr/wiNAiFUreUvO+/D9h1XjXW56lI= 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/connector/errors.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_PCP_CLIENT_SRC_CONNECTOR_ERRORS_H_ 2 | #define CPP_PCP_CLIENT_SRC_CONNECTOR_ERRORS_H_ 3 | 4 | #include 5 | #include 6 | 7 | namespace PCPClient { 8 | 9 | /// Base error class. 10 | class connection_error : public std::runtime_error { 11 | public: 12 | explicit connection_error(std::string const& msg) 13 | : std::runtime_error(msg) {} 14 | }; 15 | 16 | /// Connection fatal error. 17 | class connection_fatal_error : public connection_error { 18 | public: 19 | explicit connection_fatal_error(std::string const& msg) 20 | : connection_error(msg) {} 21 | }; 22 | 23 | /// Connection configuration error. 24 | class connection_config_error : public connection_error { 25 | public: 26 | explicit connection_config_error(std::string const& msg) 27 | : connection_error(msg) {} 28 | }; 29 | 30 | /// Connection processing error. 31 | class connection_processing_error : public connection_error { 32 | public: 33 | explicit connection_processing_error(std::string const& msg) 34 | : connection_error(msg) {} 35 | }; 36 | 37 | /// Connection not initialized error. 38 | class connection_not_init_error : public connection_error { 39 | public: 40 | explicit connection_not_init_error(std::string const& msg) 41 | : connection_error(msg) {} 42 | }; 43 | 44 | /// Connection error due to a timeout or a corrupted message during a 45 | /// session association exchange. 46 | class connection_association_error : public connection_error { 47 | public: 48 | explicit connection_association_error(std::string const& msg) 49 | : connection_error(msg) {} 50 | }; 51 | 52 | /// Connection error due to a Session Association response message 53 | /// that reports a failure. 54 | class connection_association_response_failure : public connection_error { 55 | public: 56 | explicit connection_association_response_failure(std::string const& msg) 57 | : connection_error(msg) {} 58 | }; 59 | 60 | } // namespace PCPClient 61 | 62 | #endif // CPP_PCP_CLIENT_SRC_CONNECTOR_ERRORS_H_ 63 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/v2/message.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | namespace PCPClient { 14 | namespace v2 { 15 | 16 | // 17 | // Message 18 | // 19 | 20 | class LIBCPP_PCP_CLIENT_EXPORT Message { 21 | public: 22 | // The default ctor is deleted since, for the PCP protocol, a 23 | // valid message must have an envelope. 24 | Message() = delete; 25 | 26 | // Create a new message with a given envelope. 27 | explicit Message(lth_jc::JsonContainer envelope); 28 | 29 | // Construct a Message by parsing the payload delivered 30 | // by the transport layer as a std::string. 31 | // 32 | // Throw a data_parse_error in case the envelope content contains 33 | // invalid JSON text. 34 | explicit Message(const std::string& transport_payload); 35 | 36 | // Parse the content of all message chunks, validate, and return 37 | // them as a ParsedChunks instance. The data chunk will be 38 | // validated with the schema indicated in the envelope. 39 | // 40 | // Throw a data_parse_error in case the envelope content contains 41 | // invalid JSON text. 42 | // Throw a validation_error in case the envelope content does 43 | // not match the envelope schema (as in pcp-specifications). 44 | // Throw a schema_not_found_error in case the envelope schema 45 | // was not registered. 46 | // 47 | // Note that bad debug/data chunks are reported in the returned 48 | // ParsedChunks objects; no error will will be propagated. 49 | ParsedChunks getParsedChunks(const Validator& validator) const; 50 | 51 | // Getter 52 | lth_jc::JsonContainer const& getEnvelope() const; 53 | 54 | // Return a string representation of the message. 55 | std::string toString() const; 56 | 57 | private: 58 | lth_jc::JsonContainer envelope_; 59 | }; 60 | 61 | } // namespace v2 62 | } // namespace PCPClient 63 | -------------------------------------------------------------------------------- /tutorial/resources/agent_certs/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFljCCA36gAwIBAgIBATANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNQdXBw 3 | ZXQgQ0E6IGphbWVzcy1tYWNib29rLXByby5sb2NhbDAeFw0xNjAxMDYxMjQ1MjFa 4 | Fw0yMTAxMDUxMjQ1MjFaMC4xLDAqBgNVBAMMI1B1cHBldCBDQTogamFtZXNzLW1h 5 | Y2Jvb2stcHJvLmxvY2FsMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA 6 | qzGn4GB3MKROmStur/ayiE7uxzRP38TUbuIctEMLm5gWKXaE2QvcsSAhF+sZpGm4 7 | ku6kggfl5jF9vkCKN7cCiwuOm3lhx5sx013Vym3KPvoBlHALsbvx3Qkvyzzpn76j 8 | 1Kx+dYD8xZdRb/Yvqh4PJW4pgU+ZgYaoxknU3KvoF38UWza/AizgBKpgASgWB1ql 9 | 20Xbf1qExCkPlVIDijQy4sFCzPZ5c/c2ugHtjkJq4dOz+upeIe6PiVhI2NPka2xU 10 | Pe6HR8JAb7wjXV5GgAKSaiMvPJBnSTFMz62/3eA3NYrUCm/My7d6Pbs/RygQP3jp 11 | 7tpyFgnmttuE+41O2ubiztKYLolNqCIyD2u1iL6JVR8bsIkjKPxgTiAtZNGwpOGw 12 | YoUO4zWXxgccLbDdIB0ZJi9vVwEbQcB4a6ZH91B4v/jiHy79t5Pcqv1cFWbvaMJM 13 | yfSUHk8GrtdQa+BIM3Kz8yGqQAnd/EJDzjgUad76Gc8STY1jKEzjJUtBEud6l96Y 14 | W3VuRsc3yC6WpZZ7z4IMu61PhSPLVn94XvEyoFHz137zxGkpTLFqYxwQtOaMJoPf 15 | hXfux9+AGV8S+O3E8oQqOAmgoCcz3v6EToYPPC4rFrGND9zDXVrqPbYAVu73AYlP 16 | IVegZD2M5F/X10zyz3ypEMFTETKJYNGBtAYveN+9/x8CAwEAAaOBvjCBuzA3Bglg 17 | hkgBhvhCAQ0EKgwoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5hbCBDZXJ0aWZp 18 | Y2F0ZTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU 19 | /ExmeYD9xHW4OX3601eTQkf/A8YwQAYDVR0jBDkwN6EypDAwLjEsMCoGA1UEAwwj 20 | UHVwcGV0IENBOiBqYW1lc3MtbWFjYm9vay1wcm8ubG9jYWyCAQEwDQYJKoZIhvcN 21 | AQELBQADggIBADjYwVByEnLN6zoqrH2YW99aXKUXrpabB+oniE1DJamknRZbeowl 22 | bf7nOeEaeEgNfCC0DczMFqSrX/TZ62U/I3NzsCc+gr6hvIGimoCgiMVkbwu2XMYy 23 | Ch4Jx+dsgQLfqMHpadl2ODOLHyIyLFe9JwbFj6KsUwzYbi37ROne3PtjOzwJSrpd 24 | yCHCTXuHnuhD3EuHB6W2rTeO7N1jqkOWIW8ZGX9nAePFEbkAV4u5e7iRA3XnANge 25 | A+w8bsoMw6+hih8+GheL4E9Twe5cnX/e32B2ebwEpDT6U3e+N3mnPqGcQCHbFvcE 26 | wL2lp7rCcw4Bw/IdTC+rCfSmBl2yKCTakujLEjIjZXi6vKNTJ5PsTnrIuFgoxoTt 27 | bHewhZtFfTlithPyUgiQ8HDRSIsGewTwP14px25lq9Szos6bDRjiglhbDK2OLd3z 28 | 4oQqgtNMNigQ0H8PydETi3hhi8Cafqzhnb4bSkh4vA263Xu4Gz5N+fFT7nmmb10N 29 | H/TJYEh16qihpYZcnC/avtCH/shIOWgcE0VInx2pkV1OO4xwoxYUAaa9MqO69R3L 30 | pDpI7kbPWGJpT7gGAPKlP87aattaqFzzWScIqdhj0ddAzyzOX+d6uUrxn1VsilAq 31 | J7kaC/I8qsAJV0H+0VGgzrqUH9kI33wsi3bOmqAwFTUbGuSvqT+/KqKC 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | 5 | before_install: 6 | - docker pull gcr.io/cpp-projects/cpp-ci:1 7 | script: 8 | - > 9 | docker run -v `pwd`:/cpp-pcp-client gcr.io/cpp-projects/cpp-ci:1 /bin/bash -c " 10 | wget https://github.com/puppetlabs/leatherman/releases/download/${LEATHERMAN_VERSION}/leatherman.tar.gz && 11 | tar xzvf leatherman.tar.gz --strip 1 -C / && 12 | cd /cpp-pcp-client && 13 | cmake $EXTRA_VARS . && 14 | mkdir dest && 15 | make $TARGET DESTDIR=/cpp-pcp-client/dest VERBOSE=1 -j2 && 16 | { [[ '$COVERALLS' != 'ON' ]] || coveralls --gcov-options '\-lp' -r . -b . -e src -e vendor >/dev/null || true; } 17 | " 18 | - if [[ $DO_RELEASE = true ]]; then tar czvf cpp-pcp-client.tar.gz `find dest -type f -print`; fi 19 | 20 | env: 21 | global: 22 | - LEATHERMAN_VERSION=1.4.0 23 | matrix: 24 | - TARGET=cpplint 25 | - TARGET=cppcheck 26 | - TARGET="all test install ARGS=-V" DO_RELEASE=true EXTRA_VARS="-DBOOST_STATIC=ON -DBUILD_SHARED_LIBS=OFF" 27 | - TARGET="all test install ARGS=-V" EXTRA_VARS="-DBOOST_STATIC=ON -DCMAKE_BUILD_TYPE=Debug -DCOVERALLS=ON -DBUILD_SHARED_LIBS=OFF" COVERALLS=ON 28 | 29 | deploy: 30 | provider: releases 31 | api_key: 32 | secure: "RvRaZyPvBa3Ll/DrJGbCk41KS1mNBO+OaDNgJCJuqUdUT6PCuXd957oM2c2ZTHoINtyK96AWomza4P9OythlF1pNLIlT5xvv+PBkkoyVwPdu7pnPhPvauHdVXvLwpYf7GQCOcVDH0Cg8tP+CP7amqpCNgOxlQmn1nqHbTd+kKTUNmPZtLoN+noPIM4HvGAAOyx5zFQN+61yQkhQ8VQzP4j361Qud4fqgCkbr26JJvOAakj8XCer4AcqsWAEL32WVmpR59vLm6zo+R4BCsUVHmtiCKLOA2O/blxmS5qsRbRJdrA2aWOwbm+W6U5Qx7vLYn57a7m6C5c4MsH1+UOxytMETrzXKt0+AM1uHEbXkg2pGgj9I6bLFVFXHdt0bcl7e+jNR5YPU+YgguwqttTVOOadLi2qhB0UaW65QVILABMj6PMcPmqfYrXW0wAkIytJiGJuVkM7MuNzu8gWQkYU6W6sLVLJwRw0JejGhXdefGlVV5bTIxKxZBsmoQaIHkXMbCEmo+EFOkDbOw8o6Vo72FbS3LtnR9/MdnpXrBi0wzl+dyRG86WDva3XDloStELaDqyNpQg/WrAfZE6XRyuvtiFrZycsgIAShrDBghrCUDs5hY97jslM0t9imlzarPGH+ML4LQW3QiMmjR35XhsNvicdGZpKq7dOjBh1sUlGopG0=" 33 | file: cpp-pcp-client.tar.gz 34 | skip_cleanup: true 35 | on: 36 | repo: puppetlabs/cpp-pcp-client 37 | tags: true 38 | condition: $DO_RELEASE = true 39 | 40 | notifications: 41 | email: false 42 | -------------------------------------------------------------------------------- /tutorial/resources/controller_certs/ca.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN CERTIFICATE----- 2 | MIIFljCCA36gAwIBAgIBATANBgkqhkiG9w0BAQsFADAuMSwwKgYDVQQDDCNQdXBw 3 | ZXQgQ0E6IGphbWVzcy1tYWNib29rLXByby5sb2NhbDAeFw0xNjAxMDYxMjQ1MjFa 4 | Fw0yMTAxMDUxMjQ1MjFaMC4xLDAqBgNVBAMMI1B1cHBldCBDQTogamFtZXNzLW1h 5 | Y2Jvb2stcHJvLmxvY2FsMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA 6 | qzGn4GB3MKROmStur/ayiE7uxzRP38TUbuIctEMLm5gWKXaE2QvcsSAhF+sZpGm4 7 | ku6kggfl5jF9vkCKN7cCiwuOm3lhx5sx013Vym3KPvoBlHALsbvx3Qkvyzzpn76j 8 | 1Kx+dYD8xZdRb/Yvqh4PJW4pgU+ZgYaoxknU3KvoF38UWza/AizgBKpgASgWB1ql 9 | 20Xbf1qExCkPlVIDijQy4sFCzPZ5c/c2ugHtjkJq4dOz+upeIe6PiVhI2NPka2xU 10 | Pe6HR8JAb7wjXV5GgAKSaiMvPJBnSTFMz62/3eA3NYrUCm/My7d6Pbs/RygQP3jp 11 | 7tpyFgnmttuE+41O2ubiztKYLolNqCIyD2u1iL6JVR8bsIkjKPxgTiAtZNGwpOGw 12 | YoUO4zWXxgccLbDdIB0ZJi9vVwEbQcB4a6ZH91B4v/jiHy79t5Pcqv1cFWbvaMJM 13 | yfSUHk8GrtdQa+BIM3Kz8yGqQAnd/EJDzjgUad76Gc8STY1jKEzjJUtBEud6l96Y 14 | W3VuRsc3yC6WpZZ7z4IMu61PhSPLVn94XvEyoFHz137zxGkpTLFqYxwQtOaMJoPf 15 | hXfux9+AGV8S+O3E8oQqOAmgoCcz3v6EToYPPC4rFrGND9zDXVrqPbYAVu73AYlP 16 | IVegZD2M5F/X10zyz3ypEMFTETKJYNGBtAYveN+9/x8CAwEAAaOBvjCBuzA3Bglg 17 | hkgBhvhCAQ0EKgwoUHVwcGV0IFJ1YnkvT3BlblNTTCBJbnRlcm5hbCBDZXJ0aWZp 18 | Y2F0ZTAOBgNVHQ8BAf8EBAMCAQYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQU 19 | /ExmeYD9xHW4OX3601eTQkf/A8YwQAYDVR0jBDkwN6EypDAwLjEsMCoGA1UEAwwj 20 | UHVwcGV0IENBOiBqYW1lc3MtbWFjYm9vay1wcm8ubG9jYWyCAQEwDQYJKoZIhvcN 21 | AQELBQADggIBADjYwVByEnLN6zoqrH2YW99aXKUXrpabB+oniE1DJamknRZbeowl 22 | bf7nOeEaeEgNfCC0DczMFqSrX/TZ62U/I3NzsCc+gr6hvIGimoCgiMVkbwu2XMYy 23 | Ch4Jx+dsgQLfqMHpadl2ODOLHyIyLFe9JwbFj6KsUwzYbi37ROne3PtjOzwJSrpd 24 | yCHCTXuHnuhD3EuHB6W2rTeO7N1jqkOWIW8ZGX9nAePFEbkAV4u5e7iRA3XnANge 25 | A+w8bsoMw6+hih8+GheL4E9Twe5cnX/e32B2ebwEpDT6U3e+N3mnPqGcQCHbFvcE 26 | wL2lp7rCcw4Bw/IdTC+rCfSmBl2yKCTakujLEjIjZXi6vKNTJ5PsTnrIuFgoxoTt 27 | bHewhZtFfTlithPyUgiQ8HDRSIsGewTwP14px25lq9Szos6bDRjiglhbDK2OLd3z 28 | 4oQqgtNMNigQ0H8PydETi3hhi8Cafqzhnb4bSkh4vA263Xu4Gz5N+fFT7nmmb10N 29 | H/TJYEh16qihpYZcnC/avtCH/shIOWgcE0VInx2pkV1OO4xwoxYUAaa9MqO69R3L 30 | pDpI7kbPWGJpT7gGAPKlP87aattaqFzzWScIqdhj0ddAzyzOX+d6uUrxn1VsilAq 31 | J7kaC/I8qsAJV0H+0VGgzrqUH9kI33wsi3bOmqAwFTUbGuSvqT+/KqKC 32 | -----END CERTIFICATE----- 33 | -------------------------------------------------------------------------------- /ext/build_defaults.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | deb_build_mirrors: 3 | - deb http://pl-build-tools.delivery.puppetlabs.net/debian __DIST__ main 4 | default_cow: 'base-wheezy-i386.cow' 5 | # Which debian distributions to build for. Noarch packages only need one arch of each cow. 6 | cows: 'base-lucid-amd64.cow base-lucid-i386.cow base-precise-amd64.cow base-precise-i386.cow base-squeeze-amd64.cow base-squeeze-i386.cow base-stable-amd64.cow base-stable-i386.cow base-trusty-amd64.cow base-trusty-i386.cow base-wheezy-amd64.cow base-wheezy-i386.cow' 7 | # The pbuilder configuration file to use 8 | pbuild_conf: '/etc/pbuilderrc' 9 | # Who is packaging. Turns up in various packaging artifacts 10 | packager: 'puppetlabs' 11 | # Who is signing packages 12 | gpg_name: 'info@puppetlabs.com' 13 | # GPG key ID of the signer 14 | gpg_key: '4BD6EC30' 15 | # Whether to require tarball signing as a prerequisite of other package building 16 | sign_tar: FALSE 17 | # a space separated list of mock configs. These are the rpm distributions to package for. If a noarch package, only one arch of each is needed. 18 | final_mocks: 'pl-el-5-x86_64 pl-el-5-i386 pl-el-6-x86_64 pl-el-6-i386 pl-el-7-x86_64 pl-fedora-20-i386 pl-fedora-20-x86_64 pl-fedora-21-i386 pl-fedora-21-x86_64' 19 | # The host that contains the yum repository to ship to 20 | yum_host: 'yum.puppetlabs.com' 21 | # The remote path the repository on the yum\_host 22 | yum_repo_path: '/opt/repository/yum/' 23 | # The host that contains the apt repository to ship to 24 | apt_host: 'apt.puppetlabs.com' 25 | # The URL to use for the apt dependencies in cow building 26 | apt_repo_url: 'http://apt.puppetlabs.com' 27 | # The path on the remote apt host that debs should ship to 28 | apt_repo_path: '/opt/repository/incoming' 29 | # The host that stores the tarballs for downloading 30 | tar_host: 'downloads.puppetlabs.com' 31 | # Whether to present the gem and apple tasks 32 | build_gem: FALSE 33 | build_dmg: FALSE 34 | # Whether to execute the rdoc rake tasks prior to composing the tarball 35 | build_doc: FALSE 36 | # Whether to present the Solaris 11 IPS packaging tasks 37 | # This requires suitable IPS packaging artifacts in the project in ext/ips 38 | build_ips: FALSE 39 | # Whether this project is a PE project or not 40 | build_pe: FALSE 41 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/v1/schemas.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | namespace PCPClient { 7 | namespace v1 { 8 | namespace Protocol { 9 | 10 | // 11 | // envelope 12 | // 13 | 14 | static const std::string ENVELOPE_SCHEMA_NAME { "envelope_schema" }; 15 | LIBCPP_PCP_CLIENT_EXPORT Schema EnvelopeSchema(); 16 | 17 | // 18 | // data 19 | // 20 | 21 | // associate session 22 | static const std::string ASSOCIATE_REQ_TYPE { "http://puppetlabs.com/associate_request" }; 23 | static const std::string ASSOCIATE_RESP_TYPE { "http://puppetlabs.com/associate_response" }; 24 | // NB: associate requests don't have a data chunk 25 | LIBCPP_PCP_CLIENT_EXPORT Schema AssociateResponseSchema(); 26 | 27 | // inventory 28 | static const std::string INVENTORY_REQ_TYPE { "http://puppetlabs.com/inventory_request" }; 29 | static const std::string INVENTORY_RESP_TYPE { "http://puppetlabs.com/inventory_response" }; 30 | LIBCPP_PCP_CLIENT_EXPORT Schema InventoryRequestSchema(); 31 | LIBCPP_PCP_CLIENT_EXPORT Schema InventoryResponseSchema(); 32 | 33 | // error 34 | static const std::string ERROR_MSG_TYPE { "http://puppetlabs.com/error_message" }; 35 | LIBCPP_PCP_CLIENT_EXPORT Schema ErrorMessageSchema(); 36 | 37 | // destination report 38 | static const std::string DESTINATION_REPORT_TYPE { "http://puppetlabs.com/destination_report" }; 39 | LIBCPP_PCP_CLIENT_EXPORT Schema DestinationReportSchema(); 40 | 41 | // ttl expired 42 | static const std::string TTL_EXPIRED_TYPE { "http://puppetlabs.com/ttl_expired" }; 43 | LIBCPP_PCP_CLIENT_EXPORT Schema TTLExpiredSchema(); 44 | 45 | // version error 46 | static const std::string VERSION_ERROR_TYPE { "http://puppetlabs.com/version_error" }; 47 | LIBCPP_PCP_CLIENT_EXPORT Schema VersionErrorSchema(); 48 | 49 | // 50 | // debug 51 | // 52 | 53 | static const std::string DEBUG_SCHEMA_NAME { "debug_schema" }; 54 | LIBCPP_PCP_CLIENT_EXPORT Schema DebugSchema(); 55 | // TODO(ale): remove this once we implement array item constraints 56 | static const std::string DEBUG_ITEM_SCHEMA_NAME { "debug_item_schema" }; 57 | LIBCPP_PCP_CLIENT_EXPORT Schema DebugItemSchema(); 58 | 59 | } // namespace Protocol 60 | } // namespace v1 61 | } // namespace PCPClient 62 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/validator/validator.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_PCP_CLIENT_SRC_VALIDATOR_VALIDATOR_H_ 2 | #define CPP_PCP_CLIENT_SRC_VALIDATOR_VALIDATOR_H_ 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | 10 | namespace PCPClient { 11 | 12 | // 13 | // Errors 14 | // 15 | 16 | /// General validator error 17 | class LIBCPP_PCP_CLIENT_EXPORT validator_error : public std::runtime_error { 18 | public: 19 | explicit validator_error(std::string const& msg) 20 | : std::runtime_error(msg) {} 21 | }; 22 | 23 | class LIBCPP_PCP_CLIENT_EXPORT schema_redefinition_error : public validator_error { 24 | public: 25 | explicit schema_redefinition_error(std::string const& msg) 26 | : validator_error(msg) {} 27 | }; 28 | 29 | class LIBCPP_PCP_CLIENT_EXPORT schema_not_found_error : public validator_error { 30 | public: 31 | explicit schema_not_found_error(std::string const& msg) 32 | : validator_error(msg) {} 33 | }; 34 | 35 | class LIBCPP_PCP_CLIENT_EXPORT validation_error : public validator_error { 36 | public: 37 | explicit validation_error(std::string const& msg) 38 | : validator_error(msg) {} 39 | }; 40 | 41 | // 42 | // Validator 43 | // 44 | 45 | namespace lth_jc = leatherman::json_container; 46 | 47 | class LIBCPP_PCP_CLIENT_EXPORT Validator { 48 | public: 49 | Validator(); 50 | 51 | // NB: Validator is thread-safe; it employs a mutex for that. As a 52 | // consequence, Validator instances are not copyable. 53 | Validator(Validator&& other_validator); 54 | 55 | void registerSchema(const Schema& schema); 56 | 57 | // Validates data with the specified schema. 58 | // Throw a schema_not_found error in case the specified schema 59 | // was not registered. 60 | // Throw a validation_error in case the data does not match the 61 | // specified schema. 62 | void validate(const lth_jc::JsonContainer& data, std::string schema_name) const; 63 | 64 | bool includesSchema(std::string schema_name) const; 65 | ContentType getSchemaContentType(std::string schema_name) const; 66 | 67 | private: 68 | std::map schema_map_; 69 | mutable Util::mutex lookup_mutex_; 70 | }; 71 | 72 | } // namespace PCPClient 73 | 74 | #endif // CPP_PCP_CLIENT_SRC_VALIDATOR_VALIDATOR_H_ 75 | -------------------------------------------------------------------------------- /lib/tests/unit/connector/mock_server.hpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "certs.hpp" 6 | 7 | namespace boost { 8 | class thread; 9 | } 10 | 11 | namespace websocketpp { 12 | template 13 | class server; 14 | 15 | namespace config { 16 | struct asio_tls; 17 | } 18 | 19 | namespace message_buffer { 20 | namespace alloc { 21 | template 22 | class con_msg_manager; 23 | } 24 | 25 | template class con_msg_manager> 26 | class message; 27 | } 28 | 29 | using connection_hdl = std::weak_ptr; 30 | } 31 | 32 | namespace PCPClient { 33 | 34 | class MockServer 35 | { 36 | public: 37 | enum class Version { v1, v2 }; 38 | MockServer(uint16_t port = 0, 39 | std::string certPath = getCertPath(), 40 | std::string keyPath = getKeyPath(), 41 | Version v = Version::v1); 42 | ~MockServer(); 43 | void go(); 44 | uint16_t port() const; 45 | std::string connection_path(websocketpp::connection_hdl) const; 46 | 47 | void set_tcp_pre_init_handler(std::function func); 48 | 49 | // NOTE(ale): pausing more than 15 ms in the `validate` handler 50 | // may end up invalidating the received message 51 | void set_validate_handler(std::function func); 52 | 53 | void set_open_handler(std::function func); 54 | 55 | void set_ping_handler(std::function func); 57 | 58 | private: 59 | std::string certPath_, keyPath_; 60 | std::unique_ptr bt_; 61 | std::unique_ptr> server_; 62 | 63 | void association_request_handler( 64 | websocketpp::connection_hdl hdl, 65 | std::shared_ptr< 66 | class websocketpp::message_buffer::message< 67 | websocketpp::message_buffer::alloc::con_msg_manager>> req); 68 | 69 | void message_handler( 70 | websocketpp::connection_hdl hdl, 71 | std::shared_ptr< 72 | class websocketpp::message_buffer::message< 73 | websocketpp::message_buffer::alloc::con_msg_manager>> req); 74 | }; 75 | 76 | } 77 | -------------------------------------------------------------------------------- /lib/src/protocol/v2/message.cc: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define LEATHERMAN_LOGGING_NAMESPACE CPP_PCP_CLIENT_LOGGING_PREFIX".message" 5 | 6 | #include 7 | 8 | // TODO(ale): disable assert() once we're confident with the code... 9 | // To disable assert() 10 | // #define NDEBUG 11 | #include 12 | 13 | namespace PCPClient { 14 | namespace v2 { 15 | 16 | namespace lth_jc = leatherman::json_container; 17 | 18 | // 19 | // Message 20 | // 21 | 22 | // Constructors 23 | 24 | Message::Message(lth_jc::JsonContainer envelope) : envelope_(envelope) 25 | { } 26 | 27 | Message::Message(const std::string& transport_msg) : 28 | Message(lth_jc::JsonContainer(transport_msg)) 29 | { } 30 | 31 | static bool validate_data(lth_jc::JsonContainer const& envelope, Validator const& validator) 32 | { 33 | auto message_type = envelope.get("message_type"); 34 | auto data = envelope.get("data"); 35 | 36 | try { 37 | validator.validate(data, message_type); 38 | return true; 39 | } catch (leatherman::json_container::data_type_error& e) { 40 | LOG_DEBUG("Invalid data in message {1}: {2}", envelope.get("id"), e.what()); 41 | } catch (validator_error& e) { 42 | LOG_DEBUG("Invalid data in message {1}: {2}", envelope.get("id"), e.what()); 43 | } 44 | return false; 45 | } 46 | 47 | ParsedChunks Message::getParsedChunks(const Validator& validator) const { 48 | validator.validate(envelope_, Protocol::ENVELOPE_SCHEMA_NAME); 49 | 50 | if (envelope_.includes("data")) { 51 | if (validate_data(envelope_, validator)) { 52 | return ParsedChunks(envelope_, envelope_.get("data"), 53 | std::vector{}, 0); 54 | } else { 55 | return ParsedChunks(envelope_, true, std::vector{}, 0); 56 | } 57 | } else { 58 | return ParsedChunks(envelope_, std::vector{}, 0); 59 | } 60 | } 61 | 62 | // Getter 63 | 64 | lth_jc::JsonContainer const& Message::getEnvelope() const { 65 | return envelope_; 66 | } 67 | 68 | // toString 69 | 70 | std::string Message::toString() const { 71 | return envelope_.toString(); 72 | } 73 | 74 | } // namespace v2 75 | } // namespace PCPClient 76 | -------------------------------------------------------------------------------- /lib/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | include_directories( 2 | inc 3 | ${Boost_INCLUDE_DIRS} 4 | ${VALIJSON_INCLUDE_DIRS} 5 | ${WEBSOCKETPP_INCLUDE_DIRS} 6 | ${LEATHERMAN_INCLUDE_DIRS} 7 | ${OPENSSL_INCLUDE_DIR} 8 | ) 9 | 10 | set(SOURCES 11 | src/connector/client_metadata.cc 12 | src/connector/connection.cc 13 | src/connector/connector_base.cc 14 | src/connector/timings.cc 15 | src/connector/v1/connector.cc 16 | src/connector/v1/session_association.cc 17 | src/connector/v2/connector.cc 18 | src/protocol/parsed_chunks.cc 19 | src/protocol/v1/chunks.cc 20 | src/protocol/v1/message.cc 21 | src/protocol/v1/schemas.cc 22 | src/protocol/v1/serialization.cc 23 | src/protocol/v2/message.cc 24 | src/protocol/v2/schemas.cc 25 | src/util/logging.cc 26 | src/validator/schema.cc 27 | src/validator/validator.cc 28 | ) 29 | 30 | # Static libraries should come before shared libraries, or you can end up 31 | # with some really annoying link errors. However, we can configure whether 32 | # Boost and Leatherman are linked statically or dynamically. 33 | if (LEATHERMAN_SHARED) 34 | # If Leatherman is shared, Boost should come first because 35 | # it's static, or the order doesn't matter. 36 | list(APPEND LIBS ${Boost_LIBRARIES} ${LEATHERMAN_LIBRARIES}) 37 | else() 38 | # If Leatherman is static, it should come first as it depends 39 | # on Boost. 40 | list(APPEND LIBS ${LEATHERMAN_LIBRARIES} ${Boost_LIBRARIES}) 41 | endif() 42 | 43 | list(APPEND LIBS 44 | ${OPENSSL_SSL_LIBRARY} 45 | ${OPENSSL_CRYPTO_LIBRARY} 46 | ${CMAKE_THREAD_LIBS_INIT} 47 | ) 48 | 49 | if (WIN32) 50 | set(PLATFORM_LIBS Ws2_32) 51 | elseif ("${CMAKE_SYSTEM_NAME}" MATCHES "SunOS") 52 | set(PLATFORM_LIBS socket nsl) 53 | endif() 54 | 55 | add_library(libcpp-pcp-client ${SOURCES}) 56 | set_target_properties(libcpp-pcp-client PROPERTIES VERSION ${PROJECT_VERSION}) 57 | 58 | # Explicit dependency on external projects to ensure they get 59 | # extracted when building with multiple jobs 60 | add_dependencies(libcpp-pcp-client websocketpp valijson) 61 | 62 | target_link_libraries(libcpp-pcp-client PRIVATE ${LIBS} ${PLATFORM_LIBS}) 63 | 64 | # Generate the export header 65 | symbol_exports(libcpp-pcp-client "${CMAKE_CURRENT_LIST_DIR}/inc/cpp-pcp-client/export.h") 66 | 67 | leatherman_install(libcpp-pcp-client) 68 | install(DIRECTORY inc/cpp-pcp-client DESTINATION include) 69 | 70 | add_subdirectory(tests) 71 | -------------------------------------------------------------------------------- /lib/tests/unit/validator/validator_test.cc: -------------------------------------------------------------------------------- 1 | #include "tests/test.hpp" 2 | 3 | #include 4 | #include 5 | 6 | using namespace PCPClient; 7 | 8 | TEST_CASE("Validator::registerSchema", "[validation]") { 9 | Validator validator {}; 10 | Schema schema { "spam" }; 11 | 12 | SECTION("it can register a schema") { 13 | validator.registerSchema(schema); 14 | } 15 | 16 | SECTION("it cannot register a schema name more than once") { 17 | validator.registerSchema(schema); 18 | REQUIRE_THROWS_AS(validator.registerSchema(schema), 19 | schema_redefinition_error); 20 | } 21 | } 22 | 23 | TEST_CASE("Validator::validate", "[validation]") { 24 | lth_jc::JsonContainer data {}; 25 | Schema schema { "test-schema" }; 26 | Validator validator {}; 27 | 28 | SECTION("it throws a schema_not_found_error if the requested schema was " 29 | "not registered") { 30 | REQUIRE_THROWS_AS(validator.validate(data, "test-schema"), 31 | schema_not_found_error); 32 | } 33 | 34 | SECTION("it throws a validation_error when validation fails") { 35 | data.set("key", "value"); 36 | schema.addConstraint("key", TypeConstraint::Int); 37 | validator.registerSchema(schema); 38 | REQUIRE_THROWS_AS(validator.validate(data, "test-schema"), 39 | validation_error); 40 | } 41 | 42 | SECTION("it doesn't throw when validation succeeds") { 43 | data.set("key", "value"); 44 | schema.addConstraint("key", TypeConstraint::String); 45 | validator.registerSchema(schema); 46 | REQUIRE_NOTHROW(validator.validate(data, "test-schema")); 47 | } 48 | 49 | // TODO(ale): move old SECTION("default schemas") to Connector test 50 | } 51 | 52 | TEST_CASE("Validator::getSchemaContentType", "[validation]") { 53 | Schema schema { "foo", ContentType::Binary }; 54 | Validator validator {}; 55 | 56 | SECTION("it can return the schema's content type") { 57 | validator.registerSchema(schema); 58 | REQUIRE(validator.getSchemaContentType("foo") == ContentType::Binary); 59 | } 60 | 61 | SECTION("it throws when the schema is undefined") { 62 | REQUIRE_THROWS_AS(validator.getSchemaContentType("foo"), 63 | schema_not_found_error); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /lib/tests/unit/connector/certs.cc: -------------------------------------------------------------------------------- 1 | #include "tests/test.hpp" 2 | #include "tests/unit/connector/certs.hpp" 3 | 4 | std::string getCertname() { 5 | static const std::string certname { "good" }; 6 | return certname; 7 | } 8 | 9 | std::string getCaPath() { 10 | static const std::string ca { 11 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/ssl-new/ca.pem" }; 12 | return ca; 13 | } 14 | 15 | std::string getCertPath() { 16 | static const std::string cert { 17 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/ssl-new/good_cert.pem" }; 18 | return cert; 19 | } 20 | 21 | std::string getKeyPath() { 22 | static const std::string key { 23 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/ssl-new/good_key.pem" }; 24 | return key; 25 | } 26 | 27 | std::string getMismatchedKeyPath() { 28 | static const std::string key { 29 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/mismatched_key.pem" }; 30 | return key; 31 | } 32 | 33 | std::string getProtectedKeyPath() { 34 | static const std::string key { 35 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/protected_key.pem" }; 36 | return key; 37 | } 38 | 39 | std::string getNotACertPath() { 40 | static const std::string tmp { 41 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/not_a_cert" }; 42 | return tmp; 43 | } 44 | 45 | std::string getNotExistentFilePath() { 46 | static const std::string tmp { 47 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/nothing.here" }; 48 | return tmp; 49 | } 50 | 51 | std::string getBadCertPath() { 52 | static const std::string cert { 53 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/ssl-new/bad_cert.pem" }; 54 | return cert; 55 | } 56 | 57 | std::string getBadKeyPath() { 58 | static const std::string key { 59 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/ssl-new/bad_key.pem" }; 60 | return key; 61 | } 62 | 63 | std::string getEmptyCrlPath() { 64 | static const std::string crl { 65 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/ssl-new/crl_empty.pem" }; 66 | return crl; 67 | } 68 | 69 | std::string getRevokedCrlPath() { 70 | static const std::string crl { 71 | std::string { CPP_PCP_CLIENT_ROOT_PATH } + "/lib/tests/resources/ssl-new/crl_bad_revoked.pem" }; 72 | return crl; 73 | } 74 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | environment: 2 | LEATHERMAN_VERSION: 1.4.0 3 | matrix: 4 | - shared: OFF 5 | - shared: ON 6 | 7 | init: 8 | - | 9 | choco install -y mingw-w64 -Version 5.2.0 -source https://www.myget.org/F/puppetlabs 10 | choco install -y cmake -Version 3.2.2 -source https://www.myget.org/F/puppetlabs 11 | choco install -y gettext -Version 0.19.6 -source https://www.myget.org/F/puppetlabs 12 | choco install -y pl-toolchain-x64 -Version 2015.12.01.1 -source https://www.myget.org/F/puppetlabs 13 | choco install -y pl-boost-x64 -Version 1.58.0.2 -source https://www.myget.org/F/puppetlabs 14 | choco install -y pl-openssl-x64 -Version 1.0.24.1 -source https://www.myget.org/F/puppetlabs 15 | choco install -y pl-curl-x64 -Version 7.46.0.1 -source https://www.myget.org/F/puppetlabs 16 | 17 | install: 18 | - ps: | 19 | wget "https://github.com/puppetlabs/leatherman/releases/download/$env:LEATHERMAN_VERSION/leatherman.7z" -OutFile "$pwd\leatherman.7z" 20 | 7z.exe x leatherman.7z -oC:\tools | FIND /V "ing " 21 | 22 | # Minimize environment polution; previously we were linking against the wrong OpenSSL DLLs. 23 | - SET PATH=C:\tools\pl-build-tools\bin;C:\tools\mingw64\bin;C:\ProgramData\chocolatey\bin;C:\Program Files\7-Zip;C:\Windows\system32;C:\Windows 24 | - ps: rm -r C:\OpenSSL-Win64 25 | 26 | build_script: 27 | - ps: | 28 | cmake -G "MinGW Makefiles" -DCMAKE_TOOLCHAIN_FILE="C:\tools\pl-build-tools\pl-build-toolchain.cmake" -DCMAKE_PREFIX_PATH="C:\tools\leatherman" -DCMAKE_INSTALL_PREFIX=C:\tools\cpp-pcp-client -DBOOST_STATIC=ON -DBUILD_SHARED_LIBS="$env:shared" . 29 | mingw32-make -j2 30 | 31 | test_script: 32 | # DLLs in C:\Windows\system32 get picked up first, despite PATH. Make local copies to override that behavior. 33 | - ps: | 34 | cp C:\Tools\pl-build-tools\bin\libeay32.dll .\bin 35 | cp C:\Tools\pl-build-tools\bin\ssleay32.dll .\bin 36 | ctest -V 2>&1 | %{ if ($_ -is [System.Management.Automation.ErrorRecord]) { $_ | c++filt } else { $_ } } 37 | mingw32-make install 38 | 7z.exe a -t7z cpp-pcp-client.7z C:\tools\cpp-pcp-client\ 39 | 40 | artifacts: 41 | - path: cpp-pcp-client.7z 42 | name: cpp-pcp-client.7z 43 | 44 | deploy: 45 | description: cpp-pcp-client build from AppVeyor 46 | provider: GitHub 47 | auth_token: 48 | secure: RlVaba6k+N1/p56mjknNAwKrXQwQpKRn0SIr+AZ8TuVszFntRxl0qFPHcq4bOMui 49 | artifact: cpp-pcp-client.7z 50 | on: 51 | appveyor_repo_tag: true 52 | shared: OFF 53 | -------------------------------------------------------------------------------- /scripts/FindDependency.cmake: -------------------------------------------------------------------------------- 1 | # A function for finding dependencies 2 | function(find_dependency) 3 | include(CMakeParseArguments) 4 | cmake_parse_arguments(FIND_DEPENDENCY "" "DISPLAY" "HEADERS;LIBRARIES" ${ARGN}) 5 | 6 | set(FIND_DEPENDENCY_NAME ${ARGV0}) 7 | 8 | # Setup the include path hint 9 | if (${FIND_DEPENDENCY_NAME}_INCLUDEDIR) 10 | set(INCLUDE_HINTS "${${FIND_DEPENDENCY_NAME}_INCLUDEDIR}") 11 | elseif (${FIND_DEPENDENCY_NAME}_ROOT) 12 | set(INCLUDE_HINTS "${${FIND_DEPENDENCY_NAME}_ROOT}/include" "${${FIND_DEPENDENCY_NAME}_ROOT}") 13 | endif() 14 | 15 | # Setup the library path hint 16 | if (${FIND_DEPENDENCY_NAME}_LIBRARYDIR) 17 | set(LIBRARY_HINTS "${${FIND_DEPENDENCY_NAME}_LIBRARYDIR}") 18 | elseif (${FIND_DEPENDENCY_NAME}_ROOT) 19 | set(LIBRARY_HINTS "${${FIND_DEPENDENCY_NAME}_ROOT}/lib") 20 | endif() 21 | 22 | # Find headers and libraries 23 | find_path(${FIND_DEPENDENCY_NAME}_INCLUDE_DIR NAMES ${FIND_DEPENDENCY_HEADERS} HINTS ${INCLUDE_HINTS}) 24 | find_library(${FIND_DEPENDENCY_NAME}_LIBRARY NAMES ${FIND_DEPENDENCY_LIBRARIES} HINTS ${LIBRARY_HINTS}) 25 | 26 | # Handle the find_package arguments 27 | include(FindPackageHandleStandardArgs) 28 | find_package_handle_standard_args(${FIND_DEPENDENCY_NAME} "${FIND_DEPENDENCY_DISPLAY} was not found." ${FIND_DEPENDENCY_NAME}_LIBRARY ${FIND_DEPENDENCY_NAME}_INCLUDE_DIR) 29 | 30 | # Set the output variables in the parent's scope 31 | if (${FIND_DEPENDENCY_NAME}_FOUND) 32 | set(${FIND_DEPENDENCY_NAME}_FOUND ${${FIND_DEPENDENCY_NAME}_FOUND} PARENT_SCOPE) 33 | 34 | # Include dirs 35 | set(${FIND_DEPENDENCY_NAME}_INCLUDE_DIRS ${${FIND_DEPENDENCY_NAME}_INCLUDE_DIR} PARENT_SCOPE) 36 | 37 | # Libraries 38 | if (${FIND_DEPENDENCY_NAME}_LIBRARY) 39 | set(${FIND_DEPENDENCY_NAME}_LIBRARIES ${${FIND_DEPENDENCY_NAME}_LIBRARY} PARENT_SCOPE) 40 | else() 41 | set(${FIND_DEPENDENCY_NAME}_LIBRARIES "" PARENT_SCOPE) 42 | endif() 43 | 44 | # Get the library name 45 | get_filename_component(${FIND_DEPENDENCY_NAME}_LIBRARY_DIRS ${${FIND_DEPENDENCY_NAME}_LIBRARY} PATH) 46 | set(${FIND_DEPENDENCY_NAME_LIBRARY} ${${FIND_DEPENDENCY_NAME_LIBRARY}} PARENT_SCOPE) 47 | 48 | # Add a define for the found package 49 | add_definitions(-DUSE_${FIND_DEPENDENCY_NAME}) 50 | endif() 51 | 52 | # Advanced options for not cluttering the cmake UIs 53 | mark_as_advanced(${FIND_DEPENDENCY_NAME}_INCLUDE_DIR ${FIND_DEPENDENCY_NAME}_LIBRARY) 54 | endfunction() 55 | -------------------------------------------------------------------------------- /vendor/FindDependency.cmake: -------------------------------------------------------------------------------- 1 | # A function for finding dependencies 2 | function(find_dependency) 3 | include(CMakeParseArguments) 4 | cmake_parse_arguments(FIND_DEPENDENCY "" "DISPLAY" "HEADERS;LIBRARIES" ${ARGN}) 5 | 6 | set(FIND_DEPENDENCY_NAME ${ARGV0}) 7 | 8 | # Setup the include path hint 9 | if (${FIND_DEPENDENCY_NAME}_INCLUDEDIR) 10 | set(INCLUDE_HINTS "${${FIND_DEPENDENCY_NAME}_INCLUDEDIR}") 11 | elseif (${FIND_DEPENDENCY_NAME}_ROOT) 12 | set(INCLUDE_HINTS "${${FIND_DEPENDENCY_NAME}_ROOT}/include" "${${FIND_DEPENDENCY_NAME}_ROOT}") 13 | endif() 14 | 15 | # Setup the library path hint 16 | if (${FIND_DEPENDENCY_NAME}_LIBRARYDIR) 17 | set(LIBRARY_HINTS "${${FIND_DEPENDENCY_NAME}_LIBRARYDIR}") 18 | elseif (${FIND_DEPENDENCY_NAME}_ROOT) 19 | set(LIBRARY_HINTS "${${FIND_DEPENDENCY_NAME}_ROOT}/lib") 20 | endif() 21 | 22 | # Find headers and libraries 23 | find_path(${FIND_DEPENDENCY_NAME}_INCLUDE_DIR NAMES ${FIND_DEPENDENCY_HEADERS} HINTS ${INCLUDE_HINTS}) 24 | find_library(${FIND_DEPENDENCY_NAME}_LIBRARY NAMES ${FIND_DEPENDENCY_LIBRARIES} HINTS ${LIBRARY_HINTS}) 25 | 26 | # Handle the find_package arguments 27 | include(FindPackageHandleStandardArgs) 28 | find_package_handle_standard_args(${FIND_DEPENDENCY_NAME} "${FIND_DEPENDENCY_DISPLAY} was not found." ${FIND_DEPENDENCY_NAME}_LIBRARY ${FIND_DEPENDENCY_NAME}_INCLUDE_DIR) 29 | 30 | # Set the output variables in the parent's scope 31 | if (${FIND_DEPENDENCY_NAME}_FOUND) 32 | set(${FIND_DEPENDENCY_NAME}_FOUND ${${FIND_DEPENDENCY_NAME}_FOUND} PARENT_SCOPE) 33 | 34 | # Include dirs 35 | set(${FIND_DEPENDENCY_NAME}_INCLUDE_DIRS ${${FIND_DEPENDENCY_NAME}_INCLUDE_DIR} PARENT_SCOPE) 36 | 37 | # Libraries 38 | if (${FIND_DEPENDENCY_NAME}_LIBRARY) 39 | set(${FIND_DEPENDENCY_NAME}_LIBRARIES ${${FIND_DEPENDENCY_NAME}_LIBRARY} PARENT_SCOPE) 40 | else() 41 | set(${FIND_DEPENDENCY_NAME}_LIBRARIES "" PARENT_SCOPE) 42 | endif() 43 | 44 | # Get the library name 45 | get_filename_component(${FIND_DEPENDENCY_NAME}_LIBRARY_DIRS ${${FIND_DEPENDENCY_NAME}_LIBRARY} PATH) 46 | set(${FIND_DEPENDENCY_NAME_LIBRARY} ${${FIND_DEPENDENCY_NAME_LIBRARY}} PARENT_SCOPE) 47 | 48 | # Add a define for the found package 49 | add_definitions(-DUSE_${FIND_DEPENDENCY_NAME}) 50 | endif() 51 | 52 | # Advanced options for not cluttering the cmake UIs 53 | mark_as_advanced(${FIND_DEPENDENCY_NAME}_INCLUDE_DIR ${FIND_DEPENDENCY_NAME}_LIBRARY) 54 | endfunction() 55 | -------------------------------------------------------------------------------- /tutorial/03/agent/main.cpp: -------------------------------------------------------------------------------- 1 | #include // Connector 2 | #include // connection_config_error 3 | 4 | #include 5 | #include 6 | #include // unique_ptr 7 | 8 | namespace Tutorial { 9 | 10 | const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 11 | 12 | const std::string AGENT_CLIENT_TYPE { "tutorial_agent" }; 13 | 14 | const std::string CA { "../../resources/agent_certs/ca.pem" }; 15 | const std::string CERT { "../../resources/agent_certs/crt.pem" }; 16 | const std::string KEY { "../../resources/agent_certs/key.pem" }; 17 | 18 | int main(int argc, char *argv[]) { 19 | std::unique_ptr connector_ptr; 20 | 21 | // Connector constructor 22 | 23 | try { 24 | connector_ptr.reset(new PCPClient::Connector { BROKER_URL, 25 | AGENT_CLIENT_TYPE, 26 | CA, 27 | CERT, 28 | KEY }); 29 | std::cout << "Configured the connector\n"; 30 | } catch (PCPClient::connection_config_error& e) { 31 | std::cout << "Failed to configure the PCP connector: " 32 | << e.what() << "\n"; 33 | return 1; 34 | } 35 | 36 | // Connector::connect() 37 | 38 | int num_connect_attempts { 4 }; 39 | 40 | try { 41 | connector_ptr->connect(num_connect_attempts); 42 | } catch (PCPClient::connection_config_error& e) { 43 | std::cout << "Failed to configure WebSocket: " << e.what() << "\n"; 44 | return 2; 45 | } catch (PCPClient::connection_fatal_error& e) { 46 | std::cout << "Failed to connect to " << BROKER_URL << " after " 47 | << num_connect_attempts << " attempts: " << e.what() << "\n"; 48 | return 2; 49 | } 50 | 51 | // Connector::isConnected() 52 | 53 | if (connector_ptr->isConnected()) { 54 | std::cout << "Successfully connected to " << BROKER_URL << "\n"; 55 | } else { 56 | std::cout << "The connection has dropped; the monitoring task " 57 | "will take care of re-establishing it\n"; 58 | } 59 | 60 | // Conneection::monitorConnection() 61 | 62 | try { 63 | connector_ptr->monitorConnection(num_connect_attempts); 64 | } catch (PCPClient::connection_fatal_error& e) { 65 | std::cout << "Failed to reconnect to " << BROKER_URL << " after " 66 | << num_connect_attempts << " attempts: " << e.what() << "\n"; 67 | return 2; 68 | } 69 | 70 | return 0; 71 | } 72 | 73 | } // namespace Tutorial 74 | 75 | int main(int argc, char** argv) { 76 | return Tutorial::main(argc, argv); 77 | } 78 | -------------------------------------------------------------------------------- /ext/redhat/cpp-pcp-client.spec.erb: -------------------------------------------------------------------------------- 1 | # Only >=el7 and fedora have a sufficient version of ruby available 2 | %if 0%{?rhel} >= 7 || 0%{?fedora} 3 | %{!?vendor_ruby: %global vendor_ruby %(ruby -rrbconfig -e "puts RbConfig::CONFIG['vendordir']")} 4 | %endif 5 | 6 | # Building debuginfo is pointless, as this has no symbols. 7 | %global debug_package %{nil} 8 | 9 | # VERSION is subbed out during rake srpm process 10 | %global realversion <%= @version %> 11 | %global rpmversion <%= @rpmversion %> 12 | 13 | %global build_prefix /opt/pl-build-tools 14 | %global _prefix /usr 15 | %global _libdir %{_prefix}/lib 16 | 17 | Name: <%= @project %> 18 | Summary: summary goes here 19 | Version: %{rpmversion} 20 | Release: <%= @rpmrelease -%>%{?dist} 21 | Vendor: %{?_host_vendor} 22 | License: ASL 2.0 23 | Group: System Environment/Base 24 | URL: http://www.puppetlabs.com/puppet/related-projects/%{name} 25 | # Note this URL will only be valid at official tags from Puppet Labs 26 | Source0: http://puppetlabs.com/downloads/%{name}/%{name}-%{realversion}.tar.gz 27 | 28 | BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 29 | 30 | BuildRequires: pl-gcc >= 4.8.2-4 31 | BuildRequires: pl-cmake >= 2.8.12-6 32 | BuildRequires: pl-libboost-devel >= 1.55.0-7 33 | BuildRequires: pl-libboost-static >= 1.55.0-7 34 | BuildRequires: openssl-devel 35 | 36 | Requires: glibc 37 | # If we're linking against openssl, we need to ensure those libraries are available 38 | # during runtime. On EL7, those dependency libraries are available via the openssl-libs 39 | # package, but we don't have that package on earlier versions. We want to install as 40 | # few extra packages as possible, which is why we're installing openssl-libs when available. 41 | %if 0%{?rhel} >= 7 || 0%{?fedora} 42 | Requires: openssl-libs 43 | %endif 44 | %if 0%{?rhel} == 6 45 | Requires: openssl 46 | %endif 47 | 48 | AutoReq: 0 49 | AutoProv: 0 50 | 51 | %description 52 | A description goes here 53 | 54 | %prep 55 | %setup -q -n %{name}-%{realversion} 56 | 57 | %build 58 | rm -rf %{buildroot} 59 | %{build_prefix}/bin/cmake \ 60 | -DCMAKE_TOOLCHAIN_FILE=%{build_prefix}/pl-build-toolchain.cmake \ 61 | -DCMAKE_VERBOSE_MAKEFILE=ON \ 62 | -DCMAKE_INSTALL_PREFIX=%{_prefix} \ 63 | -DBOOST_STATIC=ON \ 64 | . 65 | make %{?_smp_mflags} DESTDIR=%{buildroot} 66 | 67 | %install 68 | make %{?_smp_mflags} install DESTDIR=%{buildroot} 69 | 70 | %clean 71 | rm -rf %{buildroot} 72 | 73 | %check 74 | %{build_prefix}/bin/ctest -V 75 | 76 | %files 77 | %defattr(-,root,root,-) 78 | %{_libdir}/libcpp-pcp-client.so* 79 | %{_includedir}/cpp-pcp-client 80 | %doc LICENSE README.md 81 | 82 | 83 | %changelog 84 | * <%= Time.now.strftime("%a %b %d %Y") %> Puppet Labs Release - <%= @rpmversion %>-<%= @rpmrelease %> 85 | - Build for <%= @version %> 86 | 87 | * Thu Jun 05 2014 Pieter Loubser - 1.7.2-1 88 | - Initial build of cthun-client 89 | -------------------------------------------------------------------------------- /lib/tests/unit/connector/client_metadata_test.cc: -------------------------------------------------------------------------------- 1 | #include "tests/test.hpp" 2 | #include "certs.hpp" 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | 9 | using namespace PCPClient; 10 | 11 | TEST_CASE("validatePrivateKeyCertPair", "[connector]") { 12 | SECTION("validates a matched key cert pair") { 13 | REQUIRE_NOTHROW(validatePrivateKeyCertPair(getKeyPath(), getCertPath())); 14 | } 15 | 16 | SECTION("does not validate a mismatched key cert pair") { 17 | REQUIRE_THROWS_AS(validatePrivateKeyCertPair(getMismatchedKeyPath(), 18 | getCertPath()), 19 | connection_config_error); 20 | } 21 | 22 | SECTION("raises a connection_config_error with no CL prompt in case of " 23 | "password protected key") { 24 | REQUIRE_THROWS_AS(validatePrivateKeyCertPair(getProtectedKeyPath(), 25 | getCertPath()), 26 | connection_config_error); 27 | } 28 | } 29 | 30 | static constexpr int WS_TIMEOUT { 5000 }; 31 | static constexpr uint32_t ASSOCIATION_TIMEOUT_S { 15 }; 32 | static constexpr uint32_t ASSOCIATION_REQUEST_TTL_S { 10 }; 33 | static constexpr uint32_t PONG_TIMEOUTS_BEFORE_RETRY { 3 }; 34 | static constexpr uint32_t PONG_TIMEOUT_MS { 10000 }; 35 | 36 | TEST_CASE("ClientMetadata::ClientMetadata", "[connector]") { 37 | SECTION("retrieves correctly the client common name from the certificate (call with proxy, crl constructor)") { 38 | std::string type { "test" }; 39 | ClientMetadata c_m { type, getCaPath(), getCertPath(), getKeyPath(), getEmptyCrlPath(), "", 40 | WS_TIMEOUT, PONG_TIMEOUTS_BEFORE_RETRY, PONG_TIMEOUT_MS }; 41 | 42 | REQUIRE(c_m.common_name == getCertname()); 43 | } 44 | 45 | SECTION("determines correctly the client URI") { 46 | std::string type { "test" }; 47 | ClientMetadata c_m { type, getCaPath(), getCertPath(), getKeyPath(), 48 | WS_TIMEOUT, PONG_TIMEOUTS_BEFORE_RETRY, PONG_TIMEOUT_MS }; 49 | std::string expected_uri { "pcp://" + getCertname() + "/" + type }; 50 | 51 | REQUIRE(c_m.uri == expected_uri); 52 | } 53 | 54 | SECTION("throws a connection_config_error if the provided certificate " 55 | "file does not exist") { 56 | REQUIRE_THROWS_AS(ClientMetadata("test", getCaPath(), 57 | getNotExistentFilePath(), getKeyPath(), 58 | WS_TIMEOUT, PONG_TIMEOUTS_BEFORE_RETRY, PONG_TIMEOUT_MS), 59 | connection_config_error); 60 | } 61 | 62 | SECTION("throws a connection_config_error if the provided certificate " 63 | "is invalid") { 64 | REQUIRE_THROWS_AS(ClientMetadata("test", getCaPath(), getNotACertPath(), 65 | getKeyPath(), 66 | WS_TIMEOUT, PONG_TIMEOUTS_BEFORE_RETRY, PONG_TIMEOUT_MS), 67 | connection_config_error); 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/connector/client_metadata.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_PCP_CLIENT_SRC_CONNECTOR_CLIENT_METADATA_H_ 2 | #define CPP_PCP_CLIENT_SRC_CONNECTOR_CLIENT_METADATA_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | 10 | namespace PCPClient { 11 | 12 | LIBCPP_PCP_CLIENT_EXPORT void validatePrivateKeyCertPair(const std::string& key, const std::string& crt); 13 | 14 | LIBCPP_PCP_CLIENT_EXPORT std::string getCommonNameFromCert(const std::string& crt); 15 | 16 | class LIBCPP_PCP_CLIENT_EXPORT ClientMetadata { 17 | public: 18 | std::string ca; 19 | std::string crt; 20 | std::string key; 21 | std::string crl; 22 | std::string client_type; 23 | std::string common_name; 24 | std::string uri; 25 | std::string proxy; 26 | long ws_connection_timeout_ms; 27 | uint32_t pong_timeouts_before_retry; 28 | long pong_timeout_ms; 29 | leatherman::logging::log_level loglevel{}; 30 | std::ostream* logstream; 31 | 32 | /// Throws a connection_config_error in case: the client 33 | /// certificate file does not exist or is invalid; it fails to 34 | /// retrieve the client identity from the file; the client 35 | /// certificate and private key are not paired 36 | 37 | // legacy constructor: pre proxy 38 | ClientMetadata(std::string _client_type, 39 | std::string _ca, 40 | std::string _crt, 41 | std::string _key, 42 | long _ws_connection_timeout_ms, 43 | uint32_t _pong_timeouts_before_retry, 44 | long _pong_timeout_ms); 45 | 46 | // constructor proxy addition 47 | ClientMetadata(std::string _client_type, 48 | std::string _ca, 49 | std::string _crt, 50 | std::string _key, 51 | std::string proxy, 52 | long _ws_connection_timeout_ms, 53 | uint32_t _pong_timeouts_before_retry, 54 | long _pong_timeout_ms); 55 | 56 | // constructor crl addition 57 | ClientMetadata(std::string _client_type, 58 | std::string _ca, 59 | std::string _crt, 60 | std::string _key, 61 | std::string _crl, 62 | std::string proxy, 63 | long _ws_connection_timeout_ms, 64 | uint32_t _pong_timeouts_before_retry, 65 | long _pong_timeout_ms); 66 | 67 | // constructor logging addition 68 | ClientMetadata(std::string _client_type, 69 | std::string _ca, 70 | std::string _crt, 71 | std::string _key, 72 | std::string _crl, 73 | std::string proxy, 74 | leatherman::logging::log_level loglevel, 75 | std::ostream* logstream, 76 | long _ws_connection_timeout_ms, 77 | uint32_t _pong_timeouts_before_retry, 78 | long _pong_timeout_ms); 79 | }; 80 | 81 | } // namespace PCPClient 82 | 83 | #endif // CPP_PCP_CLIENT_SRC_CONNECTOR_CLIENT_METADATA_H_ 84 | -------------------------------------------------------------------------------- /lib/tests/resources/mismatched_key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /tutorial/resources/agent_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /tutorial/resources/controller_certs/key.pem: -------------------------------------------------------------------------------- 1 | -----BEGIN RSA PRIVATE KEY----- 2 | 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 51 | -----END RSA PRIVATE KEY----- 52 | -------------------------------------------------------------------------------- /lib/src/protocol/parsed_chunks.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace PCPClient { 4 | 5 | // 6 | // ParsedChunks 7 | // 8 | 9 | // Default ctor 10 | ParsedChunks::ParsedChunks() 11 | : envelope {}, 12 | has_data { false }, 13 | invalid_data { false }, 14 | data_type { ContentType::Json }, 15 | data {}, 16 | binary_data { "" }, 17 | debug {}, 18 | num_invalid_debug { 0 } { 19 | } 20 | 21 | // No data ctor 22 | ParsedChunks::ParsedChunks(lth_jc::JsonContainer _envelope, 23 | std::vector _debug, 24 | unsigned int _num_invalid_debug) 25 | : envelope { _envelope }, 26 | has_data { false }, 27 | invalid_data { false }, 28 | data_type { ContentType::Json }, 29 | data {}, 30 | binary_data { "" }, 31 | debug { _debug }, 32 | num_invalid_debug { _num_invalid_debug } { 33 | } 34 | 35 | // Invalid data ctor 36 | ParsedChunks::ParsedChunks(lth_jc::JsonContainer _envelope, 37 | bool _invalid_data, 38 | std::vector _debug, 39 | unsigned int _num_invalid_debug) 40 | : envelope { _envelope }, 41 | has_data { _invalid_data }, 42 | invalid_data { _invalid_data }, 43 | data_type { ContentType::Json }, 44 | data {}, 45 | binary_data { "" }, 46 | debug { _debug }, 47 | num_invalid_debug { _num_invalid_debug } { 48 | } 49 | 50 | // JSON data ctor 51 | ParsedChunks::ParsedChunks(lth_jc::JsonContainer _envelope, 52 | lth_jc::JsonContainer _data, 53 | std::vector _debug, 54 | unsigned int _num_invalid_debug) 55 | : envelope { _envelope }, 56 | has_data { true }, 57 | invalid_data { false }, 58 | data_type { ContentType::Json }, 59 | data { _data }, 60 | binary_data { "" }, 61 | debug { _debug }, 62 | num_invalid_debug { _num_invalid_debug } { 63 | } 64 | 65 | // Binary data ctor 66 | ParsedChunks::ParsedChunks(lth_jc::JsonContainer _envelope, 67 | std::string _binary_data, 68 | std::vector _debug, 69 | unsigned int _num_invalid_debug) 70 | : envelope { _envelope }, 71 | has_data { true }, 72 | invalid_data { false }, 73 | data_type { ContentType::Binary }, 74 | data {}, 75 | binary_data { _binary_data }, 76 | debug { _debug }, 77 | num_invalid_debug { _num_invalid_debug } { 78 | } 79 | 80 | std::string ParsedChunks::toString() const { 81 | auto s = "ENVELOPE: " + envelope.toString(); 82 | 83 | if (has_data) { 84 | s += "\nDATA: "; 85 | if (invalid_data) { 86 | s += "INVALID"; 87 | } else if (data_type == ContentType::Json) { 88 | s += data.toString(); 89 | } else { 90 | s += binary_data; 91 | } 92 | } 93 | 94 | for (auto& d : debug) { 95 | s += ("\nDEBUG: " + d.toString()); 96 | } 97 | 98 | return s; 99 | } 100 | 101 | } // namespace PCPClient 102 | -------------------------------------------------------------------------------- /tutorial/05/controller/main.cpp: -------------------------------------------------------------------------------- 1 | #include // Connector 2 | #include // connection_config_error 3 | 4 | #include // JsonContainer 5 | 6 | #include 7 | #include 8 | #include // unique_ptr 9 | 10 | namespace Tutorial { 11 | 12 | const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 13 | 14 | const std::string AGENT_CLIENT_TYPE { "tutorial_agent" }; 15 | const std::string CONTROLLER_CLIENT_TYPE { "tutorial_controller" }; 16 | 17 | const std::string CA { "../../resources/controller_certs/ca.pem" }; 18 | const std::string CERT { "../../resources/controller_certs/crt.pem" }; 19 | const std::string KEY { "../../resources/controller_certs/key.pem" }; 20 | 21 | const std::string REQUEST_SCHEMA_NAME { "tutorial_request_schema" }; 22 | const int MSG_TIMEOUT_S { 10 }; 23 | 24 | int main(int argc, char *argv[]) { 25 | std::unique_ptr connector_ptr; 26 | 27 | // Connector constructor 28 | 29 | try { 30 | connector_ptr.reset(new PCPClient::Connector { BROKER_URL, 31 | CONTROLLER_CLIENT_TYPE, 32 | CA, 33 | CERT, 34 | KEY }); 35 | std::cout << "Configured the connector\n"; 36 | } catch (PCPClient::connection_config_error& e) { 37 | std::cout << "Failed to configure the PCP Connector: " 38 | << e.what() << "\n"; 39 | return 1; 40 | } 41 | 42 | // Connector::connect() 43 | 44 | int num_connect_attempts { 4 }; 45 | 46 | try { 47 | connector_ptr->connect(num_connect_attempts); 48 | } catch (PCPClient::connection_config_error& e) { 49 | std::cout << "Failed to configure WebSocket: " << e.what() << "\n"; 50 | return 2; 51 | } catch (PCPClient::connection_fatal_error& e) { 52 | std::cout << "Failed to connect to " << BROKER_URL << " after " 53 | << num_connect_attempts << " attempts: " << e.what() << "\n"; 54 | return 2; 55 | } 56 | 57 | // Connector::isConnected() 58 | 59 | if (connector_ptr->isConnected()) { 60 | std::cout << "Successfully connected to " << BROKER_URL << "\n"; 61 | } else { 62 | std::cout << "The connection has dropped; we can't send anything\n"; 63 | return 2; 64 | } 65 | 66 | // Connector::send() - specify message fields 67 | 68 | leatherman::json_container::JsonContainer track { 69 | "{\"artist\" : \"Captain Beefheart\"}" }; 70 | leatherman::json_container::JsonContainer data_entries {}; 71 | data_entries.set("request", track); 72 | data_entries.set("details", "please send some good music"); 73 | 74 | std::vector endpoints { "pcp://*/" + AGENT_CLIENT_TYPE }; 75 | 76 | try { 77 | connector_ptr->send(endpoints, 78 | REQUEST_SCHEMA_NAME, 79 | MSG_TIMEOUT_S, 80 | data_entries); 81 | std::cout << "Request message sent\n"; 82 | } catch (PCPClient::connection_processing_error& e) { 83 | std::cout << "Failed to send the request message: " << e.what() << "\n"; 84 | return 2; 85 | } 86 | 87 | return 0; 88 | } 89 | 90 | } // namespace Tutorial 91 | 92 | int main(int argc, char** argv) { 93 | return Tutorial::main(argc, argv); 94 | } 95 | -------------------------------------------------------------------------------- /lib/src/protocol/v1/schemas.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | namespace PCPClient { 4 | namespace v1 { 5 | namespace Protocol { 6 | 7 | // HERE(ale): this must be kept up to date with 8 | // https://github.com/puppetlabs/pcp-specifications 9 | 10 | Schema EnvelopeSchema() { 11 | Schema schema { ENVELOPE_SCHEMA_NAME, ContentType::Json }; 12 | schema.addConstraint("id", TypeConstraint::String, true); 13 | schema.addConstraint("message_type", TypeConstraint::String, true); 14 | schema.addConstraint("expires", TypeConstraint::String, true); 15 | // TODO(ale): add array item constraint once implemented in Schema 16 | schema.addConstraint("targets", TypeConstraint::Array, true); 17 | schema.addConstraint("sender", TypeConstraint::String, true); 18 | schema.addConstraint("destination_report", TypeConstraint::Bool, false); 19 | schema.addConstraint("in-reply-to", TypeConstraint::String, false); 20 | return schema; 21 | } 22 | 23 | Schema AssociateResponseSchema() { 24 | Schema schema { ASSOCIATE_RESP_TYPE, ContentType::Json }; 25 | schema.addConstraint("id", TypeConstraint::String, true); 26 | schema.addConstraint("success", TypeConstraint::Bool, true); 27 | schema.addConstraint("reason", TypeConstraint::String, false); 28 | return schema; 29 | } 30 | 31 | Schema InventoryRequestSchema() { 32 | Schema schema { INVENTORY_REQ_TYPE, ContentType::Json }; 33 | schema.addConstraint("query", TypeConstraint::String, true); 34 | return schema; 35 | } 36 | 37 | Schema InventoryResponseSchema() { 38 | Schema schema { INVENTORY_RESP_TYPE, ContentType::Json }; 39 | // TODO(ale): add array item constraint once implemented in Schema 40 | schema.addConstraint("uris", TypeConstraint::Array, true); 41 | return schema; 42 | } 43 | 44 | Schema ErrorMessageSchema() { 45 | Schema schema { ERROR_MSG_TYPE, ContentType::Json }; 46 | schema.addConstraint("description", TypeConstraint::String, true); 47 | schema.addConstraint("id", TypeConstraint::String, false); 48 | return schema; 49 | } 50 | 51 | Schema DestinationReportSchema() { 52 | Schema schema { DESTINATION_REPORT_TYPE, ContentType::Json }; 53 | schema.addConstraint("id", TypeConstraint::String, true); 54 | // TODO(ale): add array item constraint once implemented in Schema 55 | schema.addConstraint("targets", TypeConstraint::Array, true); 56 | return schema; 57 | } 58 | 59 | Schema TTLExpiredSchema() { 60 | Schema schema { TTL_EXPIRED_TYPE, ContentType::Json }; 61 | // NB: additionalProperties = false 62 | schema.addConstraint("id", TypeConstraint::String, true); 63 | return schema; 64 | } 65 | 66 | Schema VersionErrorSchema() { 67 | Schema schema { VERSION_ERROR_TYPE, ContentType::Json }; 68 | // NB: additionalProperties = false 69 | schema.addConstraint("id", TypeConstraint::String, true); 70 | schema.addConstraint("target", TypeConstraint::String, true); 71 | schema.addConstraint("reason", TypeConstraint::String, true); 72 | return schema; 73 | } 74 | 75 | Schema DebugSchema() { 76 | Schema schema { DEBUG_SCHEMA_NAME, ContentType::Json }; 77 | schema.addConstraint("hops", TypeConstraint::Array, true); 78 | return schema; 79 | } 80 | 81 | Schema DebugItemSchema() { 82 | Schema schema { DEBUG_ITEM_SCHEMA_NAME, ContentType::Json }; 83 | schema.addConstraint("server", TypeConstraint::String, true); 84 | schema.addConstraint("time", TypeConstraint::String, true); 85 | schema.addConstraint("stage", TypeConstraint::String, false); 86 | return schema; 87 | } 88 | 89 | } // namespace Protocol 90 | } // namespace v1 91 | } // namespace PCPClient 92 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/v1/message.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include // uint8_t 14 | 15 | namespace PCPClient { 16 | namespace v1 { 17 | 18 | // 19 | // Message 20 | // 21 | 22 | class LIBCPP_PCP_CLIENT_EXPORT Message { 23 | public: 24 | // The default ctor is deleted since, for the PCP protocol, a 25 | // valid message must have an envelope chunk (invariant) 26 | Message() = delete; 27 | 28 | // Construct a Message by parsing the payload delivered 29 | // by the transport layer as a std::string. 30 | // Throw an unsupported_version_error in case the indicated 31 | // message format version is not supported. 32 | // Throw a message_serialization_error in case of invalid message. 33 | explicit Message(const std::string& transport_payload); 34 | 35 | // Create a new message with a given envelope. 36 | // Throw a invalid_chunk_error in case of invalid chunk 37 | // (unknown descriptor or wrong size). 38 | explicit Message(MessageChunk envelope); 39 | 40 | // ... and a data chunk; throw a invalid_chunk_error as above 41 | Message(MessageChunk envelope, 42 | MessageChunk data_chunk); 43 | 44 | // ... and a debug chunk; throw a invalid_chunk_error as above 45 | Message(MessageChunk envelope, 46 | MessageChunk data_chunk, 47 | MessageChunk debug_chunk); 48 | 49 | // Add optional chunks after validating it. 50 | // Throw a invalid_chunk_error in case of invalid chunk (unknown 51 | // descriptor or wrong size). 52 | void setDataChunk(MessageChunk data_chunk); 53 | void addDebugChunk(MessageChunk debug_chunk); 54 | 55 | // Getters 56 | uint8_t getVersion() const; 57 | MessageChunk getEnvelopeChunk() const; 58 | MessageChunk getDataChunk() const; 59 | std::vector getDebugChunks() const; 60 | 61 | // Inspectors 62 | bool hasData() const; 63 | bool hasDebug() const; 64 | 65 | // Return the buffer containing the bytes of the serialized 66 | // message. 67 | // Throw a message_processing_error in case it fails to allocate 68 | // memory for the buffer. 69 | SerializedMessage getSerialized() const; 70 | 71 | // Parse the content of all message chunks, validate, and return 72 | // them as a ParsedChunks instance. The data chunk will be 73 | // validated with the schema indicated in the envelope. 74 | // 75 | // Throw a data_parse_error in case the envelope content contains 76 | // invalid JSON text. 77 | // Throw a validation_error in case the envelope content does 78 | // not match the envelope schema (as in pcp-specifications). 79 | // Throw a schema_not_found_error in case the envelope schema 80 | // was not registered. 81 | // 82 | // Note that bad debug/data chunks are reported in the returned 83 | // ParsedChunks objects; no error will will be propagated. 84 | ParsedChunks getParsedChunks(const Validator& validator) const; 85 | 86 | // Return a string representation of all message fields. 87 | std::string toString() const; 88 | 89 | private: 90 | uint8_t version_; 91 | MessageChunk envelope_chunk_; 92 | MessageChunk data_chunk_; 93 | std::vector debug_chunks_; 94 | 95 | void parseMessage(const std::string& transport_msg); 96 | 97 | void validateVersion(const uint8_t& version) const; 98 | void validateChunk(const MessageChunk& chunk) const; 99 | }; 100 | 101 | } // namespace v1 102 | } // namespace PCPClient 103 | -------------------------------------------------------------------------------- /lib/src/validator/validator.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LEATHERMAN_LOGGING_NAMESPACE CPP_PCP_CLIENT_LOGGING_PREFIX".validator" 4 | #include 5 | 6 | #include 7 | 8 | #pragma GCC diagnostic push 9 | #pragma GCC diagnostic ignored "-Wextra" 10 | #pragma GCC diagnostic ignored "-Wignored-qualifiers" 11 | #include 12 | #include 13 | #include 14 | #include 15 | #pragma GCC diagnostic pop 16 | 17 | namespace PCPClient { 18 | 19 | namespace lth_jc = leatherman::json_container; 20 | namespace lth_loc = leatherman::locale; 21 | 22 | /// 23 | /// Auxiliary functions 24 | /// 25 | 26 | std::string getValidationError(valijson::ValidationResults& validation_results) { 27 | std::string err_msg {}; 28 | valijson::ValidationResults::Error error; 29 | unsigned int err_idx { 0 }; 30 | static std::string err_label { lth_loc::translate("ERROR") }; 31 | 32 | while (validation_results.popError(error)) { 33 | if (!err_msg.empty()) { 34 | err_msg += " - "; 35 | } 36 | err_idx++; 37 | err_msg += err_label + std::to_string(err_idx) + ":"; 38 | for (const auto& context_element : error.context) { 39 | err_msg += " " + context_element; 40 | } 41 | } 42 | 43 | return err_msg; 44 | } 45 | 46 | bool validateJsonContainer(const lth_jc::JsonContainer& data, const Schema& schema) { 47 | valijson::Validator validator { schema.getRaw() }; 48 | valijson::adapters::RapidJsonAdapter adapted_document { data.getRaw() }; 49 | valijson::ValidationResults validation_results; 50 | 51 | auto success = validator.validate(adapted_document, &validation_results); 52 | 53 | if (!success) { 54 | auto err_msg = getValidationError(validation_results); 55 | LOG_DEBUG("Schema validation failure: {1}", err_msg); 56 | } 57 | 58 | return success; 59 | } 60 | 61 | /// 62 | /// Public API 63 | /// 64 | 65 | Validator::Validator() 66 | : schema_map_ {}, 67 | lookup_mutex_ {} { 68 | } 69 | 70 | Validator::Validator(Validator&& other_validator) 71 | : schema_map_ { other_validator.schema_map_ }, 72 | lookup_mutex_ {} { 73 | } 74 | 75 | void Validator::registerSchema(const Schema& schema) { 76 | Util::lock_guard lock(lookup_mutex_); 77 | auto schema_name = schema.getName(); 78 | if (includesSchema(schema_name)) { 79 | throw schema_redefinition_error { 80 | lth_loc::format("schema '{1}' already defined", schema_name) }; 81 | } 82 | 83 | auto p = std::pair(schema_name, schema); 84 | schema_map_.insert(p); 85 | } 86 | 87 | void Validator::validate(const lth_jc::JsonContainer& data, 88 | std::string schema_name) const { 89 | Util::unique_lock lock(lookup_mutex_); 90 | if (!includesSchema(schema_name)) { 91 | throw schema_not_found_error { 92 | lth_loc::format("'{1}' is not a registered schema", schema_name) }; 93 | } 94 | lock.unlock(); 95 | 96 | // we can freely unlock. When a schema has been set it cannot be modified 97 | 98 | if (!validateJsonContainer(data, schema_map_.at(schema_name))) { 99 | throw validation_error { 100 | lth_loc::format("does not match schema: '{1}'", schema_name) }; 101 | } 102 | } 103 | 104 | bool Validator::includesSchema(std::string schema_name) const { 105 | return schema_map_.find(schema_name) != schema_map_.end(); 106 | } 107 | 108 | ContentType Validator::getSchemaContentType(std::string schema_name) const { 109 | Util::unique_lock lock(lookup_mutex_); 110 | if (!includesSchema(schema_name)) { 111 | throw schema_not_found_error { 112 | lth_loc::format("'{1}' is not a registered schema", schema_name) }; 113 | } 114 | lock.unlock(); 115 | 116 | return schema_map_.at(schema_name).getContentType(); 117 | } 118 | 119 | } // namespace PCPClient 120 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/validator/schema.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_PCP_CLIENT_SRC_VALIDATOR_SCHEMA_H_ 2 | #define CPP_PCP_CLIENT_SRC_VALIDATOR_SCHEMA_H_ 3 | 4 | #include 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | 12 | // boost forward declarations used in valijson forward declarations 13 | namespace boost { 14 | template 15 | class ptr_map; 16 | struct heap_clone_allocator; 17 | } 18 | 19 | // valijson forward declarations 20 | namespace valijson { 21 | class Schema; 22 | 23 | namespace constraints { 24 | struct TypeConstraint; 25 | typedef boost::ptr_map, 27 | boost::heap_clone_allocator, 28 | std::allocator< std::pair>> PropertySchemaMap; 30 | typedef std::set RequiredProperties; 31 | } 32 | } 33 | 34 | namespace PCPClient { 35 | 36 | namespace V_C = valijson::constraints; 37 | namespace lth_jc = leatherman::json_container; 38 | 39 | enum class TypeConstraint { Object, Array, String, Int, Bool, Double, Null, Any }; 40 | enum class ContentType { Json, Binary }; 41 | 42 | class LIBCPP_PCP_CLIENT_EXPORT schema_error : public std::runtime_error { 43 | public: 44 | explicit schema_error(std::string const& msg) 45 | : std::runtime_error(msg) {} 46 | }; 47 | 48 | class LIBCPP_PCP_CLIENT_EXPORT Schema { 49 | public: 50 | Schema() = delete; 51 | 52 | // The following constructors instantiate an empty Schema with no 53 | // constraint 54 | 55 | Schema(std::string name, 56 | ContentType content_type, 57 | TypeConstraint type); 58 | 59 | Schema(std::string name, 60 | ContentType content_type); 61 | 62 | Schema(std::string name, 63 | TypeConstraint type); 64 | 65 | Schema(std::string name); 66 | 67 | Schema(const Schema& schema); 68 | 69 | // Instantiate a Schema of type ContentType::Json by parsing the 70 | // JSON schema passed as a JsonContainer object. 71 | // It won't be possible to add further constraints to such schema 72 | // Throw a schema_error in case of parsing failure. 73 | Schema(std::string name, const lth_jc::JsonContainer& json_schema); 74 | 75 | ~Schema(); 76 | 77 | // Add constraints to a JSON object schema. 78 | // Throw a schema_error in case the Schema instance is not of 79 | // TypeConstraint::Object type or if it was constructed by parsing 80 | // a JSON schema. 81 | void addConstraint(std::string field, TypeConstraint type, bool required = false); 82 | void addConstraint(std::string field, Schema sub_schema, bool required = false); 83 | 84 | const std::string getName() const; 85 | ContentType getContentType() const; 86 | const valijson::Schema getRaw() const; 87 | 88 | private: 89 | std::string name_; 90 | 91 | // To distinguish between binary and JSON data 92 | const ContentType content_type_; 93 | 94 | // To add a global type constraint; default to Object (see ctors) 95 | const TypeConstraint type_; 96 | 97 | // Stores the schema parsed by the parsing ctor 98 | const std::unique_ptr parsed_json_schema_; 99 | 100 | // Flag; set in case the used ctor was the parsing one 101 | const bool parsed_; 102 | 103 | // Store constraints for validjson 104 | std::unique_ptr properties_; 105 | std::unique_ptr pattern_properties_; 106 | std::unique_ptr required_properties_; 107 | 108 | // Convert PCPClient::TypeConstraint to the validjson ones 109 | V_C::TypeConstraint getConstraint(TypeConstraint type) const; 110 | 111 | // Check if it's possible to add constraints 112 | void checkAddConstraint(); 113 | }; 114 | 115 | } // namespace PCPClient 116 | 117 | #endif // CPP_PCP_CLIENT_SRC_VALIDATOR_SCHEMA_H_ 118 | -------------------------------------------------------------------------------- /tutorial/06/controller/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | 3 | #include // Connector 4 | #include // connection_config_error 5 | 6 | #include // JsonContainer 7 | 8 | #include 9 | #include 10 | #include // unique_ptr 11 | 12 | namespace Tutorial { 13 | 14 | const std::string CA { "../../resources/controller_certs/ca.pem" }; 15 | const std::string CERT { "../../resources/controller_certs/crt.pem" }; 16 | const std::string KEY { "../../resources/controller_certs/key.pem" }; 17 | 18 | // 19 | // controller_error 20 | // 21 | 22 | class controller_error : public std::runtime_error { 23 | public: 24 | explicit controller_error(std::string const& msg) : std::runtime_error(msg) {} 25 | }; 26 | 27 | // 28 | // Controller 29 | // 30 | 31 | class Controller { 32 | public: 33 | Controller(); 34 | void sendRequest(); 35 | 36 | private: 37 | int num_connect_attempts_; 38 | std::unique_ptr connector_ptr_; 39 | }; 40 | 41 | Controller::Controller() 42 | try 43 | : num_connect_attempts_ { 4 }, 44 | connector_ptr_ { new PCPClient::Connector { BROKER_URL, 45 | CONTROLLER_CLIENT_TYPE, 46 | CA, 47 | CERT, 48 | KEY } } { 49 | } catch (PCPClient::connection_config_error& e) { 50 | std::string err_msg { "failed to configure the PCP Connector: " }; 51 | throw controller_error { err_msg + e.what() }; 52 | } 53 | 54 | void Controller::sendRequest() { 55 | // Connector::connect() 56 | 57 | try { 58 | connector_ptr_->connect(num_connect_attempts_); 59 | } catch (PCPClient::connection_config_error& e) { 60 | std::string err_msg { "failed to configure WebSocket: " }; 61 | throw controller_error { err_msg + e.what() }; 62 | } catch (PCPClient::connection_fatal_error& e) { 63 | std::string err_msg { "failed to connect to " + BROKER_URL + " after " 64 | + std::to_string(num_connect_attempts_) 65 | + " attempts: " }; 66 | throw controller_error { err_msg + e.what() }; 67 | } 68 | 69 | // Connector::isConnected() 70 | 71 | if (connector_ptr_->isConnected()) { 72 | std::cout << "Successfully connected to " << BROKER_URL << "\n"; 73 | } else { 74 | // The connection has dropped; we can't send anything 75 | throw controller_error { "connection dropped" }; 76 | } 77 | 78 | // Connector::send() - specify message fields; use JsonContainer 79 | 80 | leatherman::json_container::JsonContainer track { 81 | "{\"artist\" : \"Captain Beefheart\"}" }; 82 | leatherman::json_container::JsonContainer data_entries {}; 83 | data_entries.set("request", track); 84 | data_entries.set("details", "please send some good music"); 85 | 86 | std::vector endpoints { "pcp://*/" + AGENT_CLIENT_TYPE }; 87 | 88 | try { 89 | connector_ptr_->send(endpoints, 90 | REQUEST_SCHEMA_NAME, 91 | MSG_TIMEOUT_S, 92 | data_entries); 93 | std::cout << "Request message sent\n"; 94 | } catch (PCPClient::connection_processing_error& e) { 95 | std::string err_msg { "failed to send the request message: " }; 96 | throw controller_error { err_msg + e.what() }; 97 | } 98 | } 99 | 100 | // 101 | // main 102 | // 103 | 104 | int main(int argc, char *argv[]) { 105 | try { 106 | Controller controller {}; 107 | 108 | try { 109 | controller.sendRequest(); 110 | } catch (controller_error& e) { 111 | std::cout << "Failed to process requests: " << e.what() << std::endl; 112 | return 2; 113 | } 114 | } catch (controller_error& e) { 115 | std::cout << "Failed to initialize the controller: " << e.what() 116 | << std::endl; 117 | return 1; 118 | } 119 | 120 | return 0; 121 | } 122 | 123 | } // namespace Tutorial 124 | 125 | int main(int argc, char** argv) { 126 | return Tutorial::main(argc, argv); 127 | } 128 | -------------------------------------------------------------------------------- /tutorial/README.md: -------------------------------------------------------------------------------- 1 | ## Tutorial 2 | 3 | *TODO(ale):* update urls 4 | 5 | This tutorial shows how to create a PCP agent / controller pair with 6 | cpp-pcp-client. 7 | 8 | To build on OS X: 9 | ``` 10 | g++ -std=c++11 -o agent -L /usr/local/lib -lcpp-pcp-client \ 11 | -lboost_system -I ../../../lib/inc main.cpp 12 | ``` 13 | 14 | You need to install PCP and [leatherman][1] before that: `make` then 15 | `sudo make install`; `libcpp-pcp-client.so` and `libleatherman_json_container.a` 16 | should be then in /usr/local/lib, otherwise you must modify the above -L option 17 | argument appropriately. 18 | 19 | To test the agent / controller pair you will need a running PCP broker. 20 | Please refer to the [PCP broker repo][2] for setting the required SSL 21 | certificates infrastructure; the certificates located in the tutorial/resources 22 | directory will not work out of the box. 23 | 24 | ### Step 1 25 | 26 | Create an instance of PCPClient::Connector that will manage the connection. 27 | 28 | Note that the paths to the certificate files are hardcoded. 29 | 30 | ### Step 2 31 | 32 | - Connect to the specified broker; the broker WebSocket URI is hardcoded. 33 | The Connector::connect() method will establish the underlying transport 34 | connection (WebSockets) and login into the PCP broker. 35 | - Ensure that the connection is up by calling Connector::isConnected(); 36 | - Enable the connection persistence by starting the monitoring task. 37 | Connector::monitorConnection() will periodically check the connection and, 38 | depending on its state, send a keepalive message to the broker or reconnect. 39 | 40 | Note that the connector will attempt to connect or reconnect 4 times at most. 41 | 42 | ### Step 3 43 | 44 | Add exception handling. 45 | 46 | ### Step 4 47 | 48 | - Refactor the connection logic to the new Agent class. 49 | - Define the request message schema with PCPClient::Schema. 50 | - Register a callback (processRequest) that will be executed when request 51 | messages are received; the callback will simply log a message event. The 52 | registration is done by calling Connector::registerMessageCallback(). 53 | 54 | More in detail, calling Connector::registerMessageCallback() ensures that all 55 | received messages with a `data_schema` entry equals to `"tutorial_request_schema"` 56 | (which is the name we gave to the request Schema object) are processed by 57 | Agent::processRequest; `data_schema` is required entry of envelope chunks 58 | (refer to the [PCP specs][3]. 59 | 60 | ### Step 5 61 | 62 | - Create a controller, similar to the agent. At this point, two different 63 | executables should be built. 64 | - The Controller 1) connects to broker, 2) sends a request to the Agent, and 65 | 3) logs the received responses messages. The request is sent by simply 66 | specifying the message entries; the connector will then take care of creating 67 | message chunks. 68 | 69 | Note that Connector::send has different overloads. 70 | 71 | ### Step 6 72 | 73 | Create a Controller class and refactor common code. 74 | 75 | ### Step 7 76 | 77 | - Define the response message schema. 78 | - For each incoming request message, the Agent::processRequest callback will 79 | send a response message back to the requester endpoint. 80 | - Register a Controller::processResponse callback that will notify received 81 | responses. 82 | 83 | Note that the controller will simply wait for a predefined time interval; no 84 | concurrency logic will be employed to manage the asynchronous callbacks. 85 | 86 | ### Step 8 87 | 88 | - Use the error message schema defined in protocol/schemas. 89 | - For each incoming request message, the Agent::processRequest callback will 90 | validate the content of the request by using PCPClient::Validator. 91 | - In case the request content is invalid, the agent will respond to the 92 | controller with an error message. 93 | - Add a callback to the Controller to process error messages. 94 | 95 | ### Step 9 96 | 97 | The controller retrieves the list of the agent nodes connected to the message 98 | fabric (inventory) by relying on the associate response sent by the broker. This 99 | is done by 1) registering an associate response callback for processing and 100 | displaying the inventory list and 2) sending an inventory request to the broker. 101 | 102 | ### Step 10 103 | 104 | Register a failover broker in case the original broker becomes unavailable. 105 | 106 | [1]: https://github.com/puppetlabs/leatherman 107 | [2]: https://github.com/puppetlabs/pcp-broker 108 | [3]: https://github.com/puppetlabs/pcp-specifications 109 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.2.2) 2 | project(cpp-pcp-client VERSION 1.7.8) 3 | 4 | # Project paths 5 | 6 | set(EXECUTABLE_OUTPUT_PATH ${PROJECT_BINARY_DIR}/bin) 7 | set(LIBRARY_OUTPUT_PATH ${PROJECT_BINARY_DIR}/lib) 8 | set(VENDOR_DIRECTORY ${PROJECT_SOURCE_DIR}/vendor) 9 | list(APPEND CMAKE_MODULE_PATH ${VENDOR_DIRECTORY}) 10 | 11 | if ("${CMAKE_PROJECT_NAME}" STREQUAL "${PROJECT_NAME}") 12 | set(CPP_PCP_CLIENT_TOPLEVEL TRUE) 13 | else () 14 | set(CPP_PCP_CLIENT_TOPLEVEL FALSE) 15 | endif () 16 | 17 | if (NOT CMAKE_BUILD_TYPE) 18 | message(STATUS "Defaulting to a release build.") 19 | set(CMAKE_BUILD_TYPE "Release" CACHE STRING "Choose the type of build, options are: None Debug Release RelWithDebInfo MinSizeRel." FORCE) 20 | endif() 21 | 22 | option(BUILD_SHARED_LIBS "Build shared library for cpp-pcp-client if on, otherwise build static libs" ON) 23 | 24 | # Set the macro for leatherman's logging namespace 25 | add_definitions(-DCPP_PCP_CLIENT_LOGGING_PREFIX="puppetlabs.cpp_pcp_client") 26 | 27 | # Disable SSLv2 and SSLv3 (in Boost.Asio) for reals. Needed for some builds of OpenSSL. 28 | add_definitions(-DOPENSSL_NO_SSL2 -DOPENSSL_NO_SSL3) 29 | 30 | # Defined further options 31 | option(DEV_LOG_RAW_MESSAGE "Enable logging serialized messages (development setting - avoid this on Windows)" OFF) 32 | 33 | # Set the root path macro and expand related template 34 | set(ROOT_PATH ${PROJECT_SOURCE_DIR}) 35 | configure_file(templates/root_path.hpp ${CMAKE_BINARY_DIR}/generated/root_path.hpp) 36 | include_directories(${CMAKE_BINARY_DIR}/generated) 37 | 38 | # Set RPATH if not installing to a system library directory 39 | list(FIND CMAKE_PLATFORM_IMPLICIT_LINK_DIRECTORIES "${CMAKE_INSTALL_PREFIX}/lib" INSTALL_IS_SYSTEM_DIR) 40 | if ("${INSTALL_IS_SYSTEM_DIR}" STREQUAL "-1") 41 | set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/lib") 42 | endif() 43 | 44 | # Find libraries 45 | find_package(Leatherman REQUIRED 46 | COMPONENTS locale nowide catch logging rapidjson json_container util) 47 | find_package(Boost 1.54 REQUIRED 48 | COMPONENTS filesystem chrono system date_time thread log regex random) 49 | find_package(OpenSSL REQUIRED) 50 | # Needed for linking on AIX 51 | find_package(Threads) 52 | 53 | # Leatherman it up 54 | include(options) 55 | include(cflags) 56 | leatherman_logging_line_numbers() 57 | 58 | # Set compiler flags 59 | set(CPP_PCP_CLIENT_FLAGS "-Wno-deprecated-declarations -Wno-reorder -Wno-sign-compare") 60 | set(CMAKE_CXX_FLAGS "${LEATHERMAN_CXX_FLAGS} ${CPP_PCP_CLIENT_FLAGS}") 61 | add_definitions(${LEATHERMAN_DEFINITIONS}) 62 | 63 | if (WIN32) 64 | link_libraries("-Wl,--nxcompat -Wl,--dynamicbase") 65 | endif() 66 | 67 | if(DEV_LOG_RAW_MESSAGE) 68 | add_definitions(-DDEV_LOG_RAW_MESSAGE) 69 | endif() 70 | 71 | # TODO(ale): enable i18n with add_definitions(-DLEATHERMAN_I18N) - PCP-257 72 | # TODO(ale): enable translation with set(LEATHERMAN_LOCALES "...;...") and 73 | # gettext_compile(${CMAKE_CURRENT_SOURCE_DIR}/locales share/locale) 74 | 75 | # Configure i18n 76 | file(GLOB_RECURSE PCP_CLIENT_SOURCES */src/*.cc */inc/*.hpp) 77 | gettext_templates(${CMAKE_CURRENT_SOURCE_DIR}/locales ${PCP_CLIENT_SOURCES}) 78 | 79 | # Prefer openssl from ports 80 | if ("${CMAKE_SYSTEM_NAME}" MATCHES "Darwin") 81 | set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /opt/local/lib) 82 | set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /opt/local/include) 83 | else() 84 | set(CMAKE_LIBRARY_PATH ${CMAKE_LIBRARY_PATH} /usr/lib) 85 | set(CMAKE_INCLUDE_PATH ${CMAKE_INCLUDE_PATH} /usr/include) 86 | endif() 87 | 88 | # Include vendor libraries and directories 89 | include(${VENDOR_DIRECTORY}/websocketpp.cmake) 90 | include(${VENDOR_DIRECTORY}/valijson.cmake) 91 | 92 | # Display a summary of the features 93 | include(FeatureSummary) 94 | feature_summary(WHAT ALL) 95 | 96 | link_directories( 97 | ${Boost_LIBRARY_DIRS} 98 | ${OPENSSL_INCLUDE_DIR} 99 | ) 100 | 101 | # Pull in helper macros for working with leatherman libraries 102 | include(leatherman) 103 | 104 | # Add src subdirectory 105 | add_subdirectory(lib) 106 | 107 | # Add the test suite 108 | if (CPP_PCP_CLIENT_TOPLEVEL) 109 | enable_testing() 110 | 111 | add_test( 112 | NAME "cpp-pcp-client\\ library\\ tests" 113 | COMMAND "${EXECUTABLE_OUTPUT_PATH}/cpp-pcp-client-unittests" 114 | ) 115 | 116 | # Add cpplint target 117 | FILE (GLOB_RECURSE ALL_SOURCES lib/*.cc lib/*.hpp) 118 | add_cpplint_files(${ALL_SOURCES}) 119 | enable_cpplint() 120 | 121 | add_cppcheck_dirs("${PROJECT_SOURCE_DIR}/lib") 122 | enable_cppcheck() 123 | endif() 124 | -------------------------------------------------------------------------------- /tutorial/06/agent/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | 3 | #include // Connector 4 | #include // connection_config_error 5 | 6 | #include // ParsedChunk 7 | 8 | #include // Schema, ContentType 9 | 10 | #include 11 | #include 12 | #include // unique_ptr 13 | #include 14 | #include 15 | 16 | namespace Tutorial { 17 | 18 | const std::string CA { "../../resources/agent_certs/ca.pem" }; 19 | const std::string CERT { "../../resources/agent_certs/crt.pem" }; 20 | const std::string KEY { "../../resources/agent_certs/key.pem" }; 21 | 22 | // 23 | // agent_error 24 | // 25 | 26 | class agent_error : public std::runtime_error { 27 | public: 28 | explicit agent_error(std::string const& msg) : std::runtime_error(msg) {} 29 | }; 30 | 31 | // 32 | // Agent 33 | // 34 | 35 | class Agent { 36 | public: 37 | Agent(); 38 | void start(); 39 | 40 | private: 41 | int num_connect_attempts_; 42 | PCPClient::Schema request_schema_; 43 | std::unique_ptr connector_ptr_; 44 | 45 | void processRequest(const PCPClient::ParsedChunks& parsed_chunks); 46 | }; 47 | 48 | Agent::Agent() 49 | try 50 | : num_connect_attempts_ { 4 }, 51 | request_schema_ { getRequestMessageSchema() }, 52 | connector_ptr_ { new PCPClient::Connector { BROKER_URL, 53 | AGENT_CLIENT_TYPE, 54 | CA, 55 | CERT, 56 | KEY } } { 57 | } catch (PCPClient::connection_config_error& e) { 58 | std::string err_msg { "failed to configure the PCP Connector: " }; 59 | throw agent_error { err_msg + e.what() }; 60 | } 61 | 62 | void Agent::start() { 63 | // Connector::registerMessageCallback() 64 | 65 | connector_ptr_->registerMessageCallback( 66 | request_schema_, 67 | [this](const PCPClient::ParsedChunks& parsed_chunks) { 68 | processRequest(parsed_chunks); 69 | }); 70 | 71 | // Connector::connect() 72 | 73 | try { 74 | connector_ptr_->connect(num_connect_attempts_); 75 | } catch (PCPClient::connection_config_error& e) { 76 | std::string err_msg { "failed to configure WebSocket: " }; 77 | throw agent_error { err_msg + e.what() }; 78 | } catch (PCPClient::connection_fatal_error& e) { 79 | std::string err_msg { "failed to connect to " + BROKER_URL + " after " 80 | + std::to_string(num_connect_attempts_) 81 | + " attempts: " }; 82 | throw agent_error { err_msg + e.what() }; 83 | } 84 | 85 | // Connector::isConnected() 86 | 87 | if (connector_ptr_->isConnected()) { 88 | std::cout << "Successfully connected to " << BROKER_URL << "\n"; 89 | } else { 90 | std::cout << "The connection has dropped; the monitoring task " 91 | "will take care of re-establishing it\n"; 92 | } 93 | 94 | // Conneection::monitorConnection() 95 | 96 | try { 97 | connector_ptr_->monitorConnection(num_connect_attempts_); 98 | } catch (PCPClient::connection_fatal_error& e) { 99 | std::string err_msg { "failed to reconnect to " + BROKER_URL + ": " }; 100 | throw agent_error { err_msg + e.what() }; 101 | } 102 | } 103 | 104 | void Agent::processRequest(const PCPClient::ParsedChunks& parsed_chunks) { 105 | auto request_id = parsed_chunks.envelope.get("id"); 106 | auto requester_endpoint = parsed_chunks.envelope.get("sender"); 107 | 108 | std::cout << "Received message " << request_id 109 | << " from " << requester_endpoint << ":\n" 110 | << parsed_chunks.toString() << "\n"; 111 | } 112 | 113 | // 114 | // main 115 | // 116 | 117 | int main(int argc, char *argv[]) { 118 | try { 119 | Agent agent {}; 120 | 121 | try { 122 | agent.start(); 123 | } catch (agent_error& e) { 124 | std::cout << "Failed to start the agent: " << e.what() << std::endl; 125 | return 2; 126 | } 127 | } catch (agent_error& e) { 128 | std::cout << "Failed to initialize the agent: " << e.what() << std::endl; 129 | return 1; 130 | } 131 | 132 | return 0; 133 | } 134 | 135 | } // namespace Tutorial 136 | 137 | int main(int argc, char** argv) { 138 | return Tutorial::main(argc, argv); 139 | } 140 | -------------------------------------------------------------------------------- /lib/src/util/logging.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #define LEATHERMAN_LOGGING_NAMESPACE "puppetlabs.pcp_client.configuration" 4 | #include 5 | 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | #include 16 | #include 17 | 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | 24 | namespace PCPClient { 25 | namespace Util { 26 | 27 | namespace lth_log = leatherman::logging; 28 | namespace sinks = boost::log::sinks; 29 | namespace expr = boost::log::expressions; 30 | namespace attrs = boost::log::attributes; 31 | 32 | static bool access_logger_enabled { false }; 33 | 34 | // 35 | // PCP Access Logging - sink backend 36 | // 37 | 38 | BOOST_LOG_ATTRIBUTE_KEYWORD(access_outcome, "AccessOutcome", std::string) 39 | 40 | class access_writer : public sinks::basic_sink_backend 41 | { 42 | public: 43 | explicit access_writer(std::shared_ptr sink_stream_ptr); 44 | void consume(boost::log::record_view const& rec); 45 | 46 | private: 47 | std::shared_ptr _sink_stream_ptr; 48 | }; 49 | 50 | // cppcheck-suppress passedByValue 51 | access_writer::access_writer(std::shared_ptr sink_stream_ptr) 52 | : _sink_stream_ptr {std::move(sink_stream_ptr)} 53 | { 54 | } 55 | 56 | void access_writer::consume(boost::log::record_view const &rec) 57 | { 58 | auto timestamp = boost::log::extract("TimeStamp", rec); 59 | auto access_outcome = boost::log::extract("AccessOutcome", rec); 60 | *_sink_stream_ptr << '[' << boost::gregorian::to_iso_extended_string(timestamp->date()) 61 | << ' ' << boost::posix_time::to_simple_string(timestamp->time_of_day()) 62 | << "] " << access_outcome << std::endl; 63 | } 64 | 65 | // 66 | // PCP Access Logging - logger 67 | // 68 | 69 | void logAccess(std::string const& message) 70 | { 71 | if (access_logger_enabled) { 72 | boost::log::sources::severity_logger slg; 73 | static attrs::constant namespace_attr { 74 | "puppetlabs.pcp_client.connector" }; 75 | slg.add_attribute("AccessOutcome", 76 | attrs::constant(message)); 77 | BOOST_LOG_SEV(slg, lth_log::log_level::none); 78 | } 79 | } 80 | 81 | // 82 | // setupLogging 83 | // 84 | 85 | static void setupLoggingImp(std::ostream& log_stream, 86 | bool force_colorization, 87 | lth_log::log_level const& lvl, 88 | std::shared_ptr access_stream) 89 | { 90 | // General Logging 91 | lth_log::setup_logging(log_stream); 92 | lth_log::set_level(lvl); 93 | 94 | if (force_colorization) 95 | lth_log::set_colorization(true); 96 | 97 | // PCP Access Logging 98 | if (access_stream) { 99 | access_logger_enabled = true; 100 | using sink_t = sinks::synchronous_sink; 101 | auto sink = boost::make_shared(boost::make_shared(std::move(access_stream))); 102 | sink->set_filter(expr::has_attr(access_outcome)); 103 | auto core = boost::log::core::get(); 104 | core->add_sink(sink); 105 | } else { 106 | access_logger_enabled = false; 107 | } 108 | } 109 | 110 | void setupLogging(std::ostream &log_stream, 111 | bool force_colorization, 112 | std::string const& loglevel_label, 113 | std::shared_ptr access_stream) 114 | { 115 | const std::map label_to_log_level { 116 | { "none", lth_log::log_level::none }, 117 | { "trace", lth_log::log_level::trace }, 118 | { "debug", lth_log::log_level::debug }, 119 | { "info", lth_log::log_level::info }, 120 | { "warning", lth_log::log_level::warning }, 121 | { "error", lth_log::log_level::error }, 122 | { "fatal", lth_log::log_level::fatal } 123 | }; 124 | 125 | auto lvl = label_to_log_level.at(loglevel_label); 126 | setupLoggingImp(log_stream, force_colorization, lvl, std::move(access_stream)); 127 | } 128 | 129 | void setupLogging(std::ostream &log_stream, 130 | bool force_colorization, 131 | lth_log::log_level const& lvl, 132 | std::shared_ptr access_stream) 133 | { 134 | setupLoggingImp(log_stream, force_colorization, lvl, std::move(access_stream)); 135 | } 136 | 137 | } // namespace Util 138 | } // namespace PCPClient 139 | -------------------------------------------------------------------------------- /tutorial/04/agent/main.cpp: -------------------------------------------------------------------------------- 1 | #include // Connector 2 | #include // connection_config_error 3 | 4 | #include // ParsedChunk 5 | 6 | #include // Schema, ContentType 7 | 8 | #include 9 | #include 10 | #include // unique_ptr 11 | #include 12 | #include 13 | 14 | namespace Tutorial { 15 | 16 | const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 17 | 18 | const std::string AGENT_CLIENT_TYPE { "tutorial_agent" }; 19 | 20 | const std::string CA { "../../resources/agent_certs/ca.pem" }; 21 | const std::string CERT { "../../resources/agent_certs/crt.pem" }; 22 | const std::string KEY { "../../resources/agent_certs/key.pem" }; 23 | 24 | const std::string REQUEST_SCHEMA_NAME { "tutorial_request_schema" }; 25 | 26 | // 27 | // agent_error 28 | // 29 | 30 | class agent_error : public std::runtime_error { 31 | public: 32 | explicit agent_error(std::string const& msg) : std::runtime_error(msg) {} 33 | }; 34 | 35 | // 36 | // Agent 37 | // 38 | 39 | class Agent { 40 | public: 41 | Agent(); 42 | void start(); 43 | 44 | private: 45 | int num_connect_attempts_; 46 | PCPClient::Schema request_schema_; 47 | std::unique_ptr connector_ptr_; 48 | 49 | void processRequest(const PCPClient::ParsedChunks& parsed_chunks); 50 | }; 51 | 52 | Agent::Agent() 53 | try 54 | : num_connect_attempts_ { 4 }, 55 | request_schema_ { REQUEST_SCHEMA_NAME, 56 | PCPClient::ContentType::Json }, 57 | connector_ptr_ { new PCPClient::Connector { BROKER_URL, 58 | AGENT_CLIENT_TYPE, 59 | CA, 60 | CERT, 61 | KEY } } { 62 | // Add constraints to the request message schema 63 | using T_C = PCPClient::TypeConstraint; 64 | request_schema_.addConstraint("request", T_C::Object, true); // mandatory 65 | request_schema_.addConstraint("details", T_C::String, false); // optional 66 | } catch (PCPClient::connection_config_error& e) { 67 | std::string err_msg { "failed to configure the PCP Connector: " }; 68 | throw agent_error { err_msg + e.what() }; 69 | } 70 | 71 | void Agent::start() { 72 | // Connector::registerMessageCallback() 73 | 74 | connector_ptr_->registerMessageCallback( 75 | request_schema_, 76 | [this](const PCPClient::ParsedChunks& parsed_chunks) { 77 | processRequest(parsed_chunks); 78 | }); 79 | 80 | // Connector::connect() 81 | 82 | try { 83 | connector_ptr_->connect(num_connect_attempts_); 84 | } catch (PCPClient::connection_config_error& e) { 85 | std::string err_msg { "failed to configure WebSocket: " }; 86 | throw agent_error { err_msg + e.what() }; 87 | } catch (PCPClient::connection_fatal_error& e) { 88 | std::string err_msg { "failed to connect to " + BROKER_URL + " after " 89 | + std::to_string(num_connect_attempts_) 90 | + " attempts: " }; 91 | throw agent_error { err_msg + e.what() }; 92 | } 93 | 94 | // Connector::isConnected() 95 | 96 | if (connector_ptr_->isConnected()) { 97 | std::cout << "Successfully connected to " << BROKER_URL << "\n"; 98 | } else { 99 | std::cout << "The connection has dropped; the monitoring task " 100 | "will take care of re-establishing it\n"; 101 | } 102 | 103 | // Conneection::monitorConnection() 104 | 105 | try { 106 | connector_ptr_->monitorConnection(num_connect_attempts_); 107 | } catch (PCPClient::connection_fatal_error& e) { 108 | std::string err_msg { "failed to reconnect to " + BROKER_URL + ": " }; 109 | throw agent_error { err_msg + e.what() }; 110 | } 111 | } 112 | 113 | void Agent::processRequest(const PCPClient::ParsedChunks& parsed_chunks) { 114 | auto request_id = parsed_chunks.envelope.get("id"); 115 | auto requester_endpoint = parsed_chunks.envelope.get("sender"); 116 | 117 | std::cout << "Received message " << request_id 118 | << " from " << requester_endpoint << ":\n" 119 | << parsed_chunks.toString() << "\n"; 120 | } 121 | 122 | // 123 | // main 124 | // 125 | 126 | int main(int argc, char *argv[]) { 127 | try { 128 | Agent agent {}; 129 | 130 | try { 131 | agent.start(); 132 | } catch (agent_error& e) { 133 | std::cout << "Failed to start the agent: " << e.what() << std::endl; 134 | return 2; 135 | } 136 | } catch (agent_error& e) { 137 | std::cout << "Failed to initialize the agent: " << e.what() << std::endl; 138 | return 1; 139 | } 140 | 141 | return 0; 142 | } 143 | 144 | } // namespace Tutorial 145 | 146 | int main(int argc, char** argv) { 147 | return Tutorial::main(argc, argv); 148 | } 149 | -------------------------------------------------------------------------------- /tutorial/05/agent/main.cpp: -------------------------------------------------------------------------------- 1 | #include // Connector 2 | #include // connection_config_error 3 | 4 | #include // ParsedChunk 5 | 6 | #include // Schema, ContentType 7 | 8 | #include 9 | #include 10 | #include // unique_ptr 11 | #include 12 | #include 13 | 14 | namespace Tutorial { 15 | 16 | const std::string BROKER_URL { "wss://127.0.0.1:8142/pcp/" }; 17 | 18 | const std::string AGENT_CLIENT_TYPE { "tutorial_agent" }; 19 | 20 | const std::string CA { "../../resources/agent_certs/ca.pem" }; 21 | const std::string CERT { "../../resources/agent_certs/crt.pem" }; 22 | const std::string KEY { "../../resources/agent_certs/key.pem" }; 23 | 24 | const std::string REQUEST_SCHEMA_NAME { "tutorial_request_schema" }; 25 | 26 | // 27 | // agent_error 28 | // 29 | 30 | class agent_error : public std::runtime_error { 31 | public: 32 | explicit agent_error(std::string const& msg) : std::runtime_error(msg) {} 33 | }; 34 | 35 | // 36 | // Agent 37 | // 38 | 39 | class Agent { 40 | public: 41 | Agent(); 42 | void start(); 43 | 44 | private: 45 | int num_connect_attempts_; 46 | PCPClient::Schema request_schema_; 47 | std::unique_ptr connector_ptr_; 48 | 49 | void processRequest(const PCPClient::ParsedChunks& parsed_chunks); 50 | }; 51 | 52 | Agent::Agent() 53 | try 54 | : num_connect_attempts_ { 4 }, 55 | request_schema_ { REQUEST_SCHEMA_NAME, 56 | PCPClient::ContentType::Json }, 57 | connector_ptr_ { new PCPClient::Connector { BROKER_URL, 58 | AGENT_CLIENT_TYPE, 59 | CA, 60 | CERT, 61 | KEY } } { 62 | // Add constraints to the request message schema 63 | using T_C = PCPClient::TypeConstraint; 64 | request_schema_.addConstraint("request", T_C::Object, true); // mandatory 65 | request_schema_.addConstraint("details", T_C::String, false); // optional 66 | } catch (PCPClient::connection_config_error& e) { 67 | std::string err_msg { "failed to configure the PCP Connector: " }; 68 | throw agent_error { err_msg + e.what() }; 69 | } 70 | 71 | void Agent::start() { 72 | // Connector::registerMessageCallback() 73 | 74 | connector_ptr_->registerMessageCallback( 75 | request_schema_, 76 | [this](const PCPClient::ParsedChunks& parsed_chunks) { 77 | processRequest(parsed_chunks); 78 | }); 79 | 80 | // Connector::connect() 81 | 82 | try { 83 | connector_ptr_->connect(num_connect_attempts_); 84 | } catch (PCPClient::connection_config_error& e) { 85 | std::string err_msg { "failed to configure WebSocket: " }; 86 | throw agent_error { err_msg + e.what() }; 87 | } catch (PCPClient::connection_fatal_error& e) { 88 | std::string err_msg { "failed to connect to " + BROKER_URL + " after " 89 | + std::to_string(num_connect_attempts_) 90 | + " attempts: " }; 91 | throw agent_error { err_msg + e.what() }; 92 | } 93 | 94 | // Connector::isConnected() 95 | 96 | if (connector_ptr_->isConnected()) { 97 | std::cout << "Successfully connected to " << BROKER_URL << "\n"; 98 | } else { 99 | std::cout << "The connection has dropped; the monitoring task " 100 | "will take care of re-establishing it\n"; 101 | } 102 | 103 | // Conneection::monitorConnection() 104 | 105 | try { 106 | connector_ptr_->monitorConnection(num_connect_attempts_); 107 | } catch (PCPClient::connection_fatal_error& e) { 108 | std::string err_msg { "failed to reconnect to " + BROKER_URL + ": " }; 109 | throw agent_error { err_msg + e.what() }; 110 | } 111 | } 112 | 113 | void Agent::processRequest(const PCPClient::ParsedChunks& parsed_chunks) { 114 | auto request_id = parsed_chunks.envelope.get("id"); 115 | auto requester_endpoint = parsed_chunks.envelope.get("sender"); 116 | 117 | std::cout << "Received message " << request_id 118 | << " from " << requester_endpoint << ":\n" 119 | << parsed_chunks.toString() << "\n"; 120 | } 121 | 122 | // 123 | // main 124 | // 125 | 126 | int main(int argc, char *argv[]) { 127 | try { 128 | Agent agent {}; 129 | 130 | try { 131 | agent.start(); 132 | } catch (agent_error& e) { 133 | std::cout << "Failed to start the agent: " << e.what() << std::endl; 134 | return 2; 135 | } 136 | } catch (agent_error& e) { 137 | std::cout << "Failed to initialize the agent: " << e.what() << std::endl; 138 | return 1; 139 | } 140 | 141 | 142 | return 0; 143 | } 144 | 145 | } // namespace Tutorial 146 | 147 | int main(int argc, char** argv) { 148 | return Tutorial::main(argc, argv); 149 | } 150 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/protocol/v1/serialization.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | #include 11 | #include 12 | #include // uint8_t 13 | #include 14 | #include 15 | 16 | // TODO(ale): disable assert() once we're confident with the code... 17 | // To disable assert() 18 | // #define NDEBUG 19 | #include 20 | 21 | namespace PCPClient { 22 | namespace v1 { 23 | 24 | typedef std::vector SerializedMessage; 25 | 26 | // 27 | // Utility functions 28 | // 29 | 30 | #ifdef BOOST_ENDIAN_LITTLE_BYTE 31 | 32 | LIBCPP_PCP_CLIENT_EXPORT uint32_t getNetworkNumber(const uint32_t& number); 33 | LIBCPP_PCP_CLIENT_EXPORT uint32_t getHostNumber(const uint32_t& number); 34 | 35 | #else // we're using big endian (!) 36 | 37 | inline uint32_t getNetworkNumber(const uint32_t& number) { 38 | return number; 39 | } 40 | 41 | inline uint32_t getHostNumber(const uint32_t& number) { 42 | return number; 43 | } 44 | 45 | #endif // BOOST_ENDIAN_LITTLE_BYTE 46 | 47 | // 48 | // Serialize 49 | // 50 | 51 | // Internal template function and specializations 52 | 53 | template 54 | inline void serialize_(const T& thing, 55 | SerializedMessage::iterator& buffer_itr); 56 | 57 | template<> 58 | inline void serialize_(const std::string& txt, 59 | SerializedMessage::iterator& buffer_itr) { 60 | for (const auto& c : txt) { 61 | auto ptr = reinterpret_cast(&c); 62 | std::copy(ptr, ptr + sizeof(c), buffer_itr); 63 | buffer_itr += sizeof(c); 64 | } 65 | } 66 | 67 | template<> 68 | inline void serialize_(const uint32_t& number, 69 | SerializedMessage::iterator& buffer_itr) { 70 | auto network_number = getNetworkNumber(number); 71 | auto byte_ptr = reinterpret_cast(&network_number); 72 | std::copy(byte_ptr, byte_ptr + 4, buffer_itr); 73 | buffer_itr += 4; 74 | } 75 | 76 | 77 | template<> 78 | inline void serialize_(const uint8_t& number, 79 | SerializedMessage::iterator& buffer_itr) { 80 | auto byte_ptr = reinterpret_cast(&number); 81 | std::copy(byte_ptr, byte_ptr + 1, buffer_itr); 82 | buffer_itr += 1; 83 | } 84 | 85 | // Serialize the specified type T object, of given size. The encoded 86 | // bytes will be stored in the passed buffer, which will be resized. 87 | // Throw a message_serialization_error in case it fails to resize the 88 | // buffer. 89 | template 90 | inline void serialize(const T& thing, 91 | size_t thing_size, 92 | SerializedMessage& buffer) { 93 | auto offset = buffer.size(); 94 | 95 | try { 96 | buffer.resize(offset + thing_size); 97 | } catch (std::bad_alloc) { 98 | throw message_serialization_error { 99 | leatherman::locale::translate("serialization: bad allocation") }; 100 | } catch (const std::exception& e) { 101 | throw message_serialization_error { e.what() }; 102 | } catch (...) { 103 | throw message_serialization_error { 104 | leatherman::locale::translate("serialization: unexpected error when " 105 | "allocating memory") }; 106 | } 107 | 108 | SerializedMessage::iterator buffer_itr = buffer.begin() + offset; 109 | serialize_(thing, buffer_itr); 110 | assert(buffer_itr == buffer.begin() + offset + thing_size); 111 | } 112 | 113 | // 114 | // Deserialize 115 | // 116 | 117 | // Deserialize the type T thing of given size (byte); this thing is 118 | // assumed stored in consecutive memory - the specified iterator 119 | // should point to its starting byte. Return the deserialized value of 120 | // the thing and increment the iterator, that will point to what comes 121 | // right after the deserialized thing. 122 | // Note that it's up to the caller to guarantee that the thing is 123 | // actually stored in consecutive memory. 124 | template 125 | inline T deserialize(size_t thing_size, 126 | SerializedMessage::const_iterator& start); 127 | 128 | // Specializations 129 | 130 | template<> 131 | inline std::string deserialize(size_t thing_size, 132 | SerializedMessage::const_iterator& start) { 133 | auto value = std::string(start, start + thing_size); 134 | start += thing_size; 135 | return value; 136 | } 137 | 138 | template<> 139 | inline uint8_t deserialize(size_t thing_size, 140 | SerializedMessage::const_iterator& start) { 141 | assert(thing_size == 1); 142 | uint8_t value; 143 | uint8_t* byte_ptr = reinterpret_cast(&value); 144 | std::copy(start, start + thing_size, byte_ptr); 145 | start += thing_size; 146 | return value; 147 | } 148 | 149 | template<> 150 | inline uint32_t deserialize(size_t thing_size, 151 | SerializedMessage::const_iterator& start) { 152 | assert(thing_size == 4); 153 | uint32_t value; 154 | uint8_t* byte_ptr = reinterpret_cast(&value); 155 | std::copy(start, start + thing_size, byte_ptr); 156 | start += thing_size; 157 | return getHostNumber(value); 158 | } 159 | 160 | } // namespace v1 161 | } // namespace PCPClient 162 | -------------------------------------------------------------------------------- /tutorial/07/controller/main.cpp: -------------------------------------------------------------------------------- 1 | #include "../common.h" 2 | 3 | #include // Connector 4 | #include // connection_config_error 5 | 6 | #include // JsonContainer 7 | 8 | #include 9 | #include 10 | #include // unique_ptr 11 | #include // sleep 12 | 13 | namespace Tutorial { 14 | 15 | const std::string CA { "../../resources/controller_certs/ca.pem" }; 16 | const std::string CERT { "../../resources/controller_certs/crt.pem" }; 17 | const std::string KEY { "../../resources/controller_certs/key.pem" }; 18 | 19 | // 20 | // controller_error 21 | // 22 | 23 | class controller_error : public std::runtime_error { 24 | public: 25 | explicit controller_error(std::string const& msg) : std::runtime_error(msg) {} 26 | }; 27 | 28 | // 29 | // Controller 30 | // 31 | 32 | class Controller { 33 | public: 34 | Controller(); 35 | void sendRequest(); 36 | 37 | private: 38 | int num_connect_attempts_; 39 | PCPClient::Schema response_schema_; 40 | std::unique_ptr connector_ptr_; 41 | 42 | void processResponse(const PCPClient::ParsedChunks& parsed_chunks); 43 | }; 44 | 45 | Controller::Controller() 46 | try 47 | : num_connect_attempts_ { 2 }, 48 | response_schema_ { getResponseMessageSchema() }, 49 | connector_ptr_ { new PCPClient::Connector { BROKER_URL, 50 | CONTROLLER_CLIENT_TYPE, 51 | CA, 52 | CERT, 53 | KEY } } { 54 | // Connector::registerMessageCallback() 55 | 56 | connector_ptr_->registerMessageCallback( 57 | response_schema_, 58 | [this](const PCPClient::ParsedChunks& parsed_chunks) { 59 | processResponse(parsed_chunks); 60 | }); 61 | } catch (PCPClient::connection_config_error& e) { 62 | std::string err_msg { "failed to configure the PCP Connector: " }; 63 | throw controller_error { err_msg + e.what() }; 64 | } 65 | 66 | void Controller::sendRequest() { 67 | // Connector::connect() 68 | 69 | try { 70 | connector_ptr_->connect(num_connect_attempts_); 71 | } catch (PCPClient::connection_config_error& e) { 72 | std::string err_msg { "failed to configure WebSocket: " }; 73 | throw controller_error { err_msg + e.what() }; 74 | } catch (PCPClient::connection_fatal_error& e) { 75 | std::string err_msg { "failed to connect to " + BROKER_URL + " after " 76 | + std::to_string(num_connect_attempts_) 77 | + " attempts: " }; 78 | throw controller_error { err_msg + e.what() }; 79 | } 80 | 81 | // Connector::isConnected() 82 | 83 | if (connector_ptr_->isConnected()) { 84 | std::cout << "Successfully connected to " << BROKER_URL << "\n"; 85 | } else { 86 | // The connection has dropped; we can't send anything 87 | throw controller_error { "connection dropped" }; 88 | } 89 | 90 | // Connector::send() - specify message fields; use JsonContainer 91 | 92 | leatherman::json_container::JsonContainer track { 93 | "{\"artist\" : \"Captain Beefheart\"}" }; 94 | leatherman::json_container::JsonContainer data_entries {}; 95 | data_entries.set("request", track); 96 | data_entries.set("details", "please send some good music"); 97 | 98 | std::vector endpoints { "pcp://*/" + AGENT_CLIENT_TYPE }; 99 | 100 | try { 101 | connector_ptr_->send(endpoints, 102 | REQUEST_SCHEMA_NAME, 103 | MSG_TIMEOUT_S, 104 | data_entries); 105 | std::cout << "Request message sent\n"; 106 | } catch (PCPClient::connection_processing_error& e) { 107 | std::string err_msg { "failed to send the request message: " }; 108 | throw controller_error { err_msg + e.what() }; 109 | } 110 | 111 | // Wait for the response 112 | sleep(2); 113 | } 114 | 115 | void Controller::processResponse(const PCPClient::ParsedChunks& parsed_chunks) { 116 | auto response_id = parsed_chunks.envelope.get("id"); 117 | auto agent_endpoint = parsed_chunks.envelope.get("sender"); 118 | 119 | std::cout << "Received response " << response_id 120 | << " from " << agent_endpoint << ":\n" 121 | << parsed_chunks.data.get("response") << "\n"; 122 | } 123 | 124 | // 125 | // main 126 | // 127 | 128 | int main(int argc, char *argv[]) { 129 | try { 130 | Controller controller {}; 131 | 132 | try { 133 | controller.sendRequest(); 134 | } catch (controller_error& e) { 135 | std::cout << "Failed to process requests: " << e.what() << std::endl; 136 | return 2; 137 | } 138 | } catch (controller_error& e) { 139 | std::cout << "Failed to initialize the controller: " << e.what() 140 | << std::endl; 141 | return 1; 142 | } 143 | 144 | return 0; 145 | } 146 | 147 | } // namespace Tutorial 148 | 149 | int main(int argc, char** argv) { 150 | return Tutorial::main(argc, argv); 151 | } 152 | -------------------------------------------------------------------------------- /lib/tests/unit/connector/v1/connector_test.cc: -------------------------------------------------------------------------------- 1 | #include "tests/test.hpp" 2 | #include "tests/unit/connector/certs.hpp" 3 | #include "tests/unit/connector/mock_server.hpp" 4 | #include "tests/unit/connector/connector_utils.hpp" 5 | 6 | #include 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | 13 | using namespace PCPClient; 14 | using namespace v1; 15 | 16 | TEST_CASE("v1::Connector::Connector", "[connector]") { 17 | SECTION("can instantiate") { 18 | REQUIRE_NOTHROW(Connector("wss://localhost:8142/pcp", "test_client", 19 | getCaPath(), getCertPath(), getKeyPath(), getEmptyCrlPath(), "", 20 | WS_TIMEOUT_MS, 21 | ASSOCIATION_TIMEOUT_S, ASSOCIATION_REQUEST_TTL_S, 22 | PONG_TIMEOUTS_BEFORE_RETRY, PONG_TIMEOUT)); 23 | } 24 | } 25 | 26 | TEST_CASE("v1::Connector::getAssociationTimings", "[connector]") { 27 | Connector c { "wss://localhost:8142/pcp", 28 | "test_client", 29 | getCaPath(), getCertPath(), getKeyPath(), getEmptyCrlPath(), "", 30 | WS_TIMEOUT_MS, ASSOCIATION_TIMEOUT_S, 31 | ASSOCIATION_REQUEST_TTL_S, 32 | PONG_TIMEOUTS_BEFORE_RETRY, PONG_TIMEOUT }; 33 | 34 | SECTION("can get PCP Association timings") { 35 | REQUIRE_NOTHROW(c.getAssociationTimings()); 36 | } 37 | 38 | SECTION("timings will say if the session was not associated") { 39 | auto ass_timings = c.getAssociationTimings(); 40 | 41 | REQUIRE_FALSE(ass_timings.completed); 42 | } 43 | } 44 | 45 | TEST_CASE("v1::Connector::connect", "[connector]") { 46 | SECTION("successfully connects and update WebSocket and Association timings") { 47 | std::unique_ptr c_ptr; 48 | { 49 | MockServer mock_server(0, getCertPath(), getKeyPath(), MockServer::Version::v1); 50 | bool connected = false; 51 | mock_server.set_open_handler( 52 | [&connected](websocketpp::connection_hdl hdl) { 53 | connected = true; 54 | }); 55 | mock_server.go(); 56 | auto port = mock_server.port(); 57 | 58 | c_ptr.reset(new Connector { "wss://localhost:" + std::to_string(port) + "/pcp", 59 | "test_client", 60 | getCaPath(), getCertPath(), getKeyPath(), getEmptyCrlPath(), "", 61 | WS_TIMEOUT_MS, ASSOCIATION_TIMEOUT_S, 62 | ASSOCIATION_REQUEST_TTL_S, 63 | PONG_TIMEOUTS_BEFORE_RETRY, PONG_TIMEOUT }); 64 | REQUIRE_FALSE(connected); 65 | REQUIRE_NOTHROW(c_ptr->connect(1)); 66 | 67 | // NB: ConnectorBase::connect is blocking, but the onOpen handler isn't 68 | wait_for([&](){return connected;}); 69 | REQUIRE(c_ptr->isConnected()); 70 | REQUIRE(connected); 71 | 72 | wait_for([&c_ptr](){return c_ptr->isAssociated();}); 73 | auto ws_timings = c_ptr->getConnectionTimings(); 74 | auto ass_timings = c_ptr->getAssociationTimings(); 75 | auto us_zero = ConnectionTimings::Duration_us::zero(); 76 | auto min_zero = ConnectionTimings::Duration_min::zero(); 77 | 78 | REQUIRE(c_ptr->isAssociated()); 79 | REQUIRE(ass_timings.completed); 80 | REQUIRE(ass_timings.success); 81 | REQUIRE(ass_timings.start < ass_timings.association); 82 | REQUIRE(us_zero < ws_timings.getOpeningHandshakeInterval()); 83 | REQUIRE(us_zero < ws_timings.getWebSocketInterval()); 84 | REQUIRE(us_zero < ws_timings.getOverallConnectionInterval_us()); 85 | REQUIRE(min_zero == ws_timings.getOverallConnectionInterval_min()); 86 | REQUIRE(us_zero == ws_timings.getClosingHandshakeInterval()); 87 | } 88 | 89 | // The broker does not exist anymore (RAII) 90 | wait_for([&c_ptr](){return !c_ptr->isConnected();}); 91 | auto ass_timings = c_ptr->getAssociationTimings(); 92 | 93 | REQUIRE_FALSE(c_ptr->isConnected()); 94 | REQUIRE(ass_timings.closed); 95 | // NB: using timepoints directly as getOverallSessionInterval_min 96 | // returns minutes 97 | REQUIRE(ass_timings.close > ass_timings.start); 98 | } 99 | SECTION("When pcp-broker cert is included in CRL connection is not established") { 100 | std::unique_ptr c_ptr; 101 | { 102 | MockServer mock_server(0, getBadCertPath(), getBadKeyPath(), MockServer::Version::v1); 103 | bool connected = false; 104 | mock_server.set_open_handler( 105 | [&connected](websocketpp::connection_hdl hdl) { 106 | connected = true; 107 | }); 108 | mock_server.go(); 109 | auto port = mock_server.port(); 110 | 111 | c_ptr.reset(new Connector { "wss://localhost:" + std::to_string(port) + "/pcp", 112 | "test_client", 113 | getCaPath(), getCertPath(), getKeyPath(), getRevokedCrlPath(), "", 114 | WS_TIMEOUT_MS, ASSOCIATION_TIMEOUT_S, 115 | ASSOCIATION_REQUEST_TTL_S, 116 | PONG_TIMEOUTS_BEFORE_RETRY, PONG_TIMEOUT }); 117 | REQUIRE_FALSE(connected); 118 | REQUIRE_THROWS_AS(c_ptr->connect(1), connection_fatal_error); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/connector/timings.hpp: -------------------------------------------------------------------------------- 1 | #ifndef CPP_PCP_CLIENT_SRC_CONNECTOR_TIMINGS_H_ 2 | #define CPP_PCP_CLIENT_SRC_CONNECTOR_TIMINGS_H_ 3 | 4 | #include 5 | 6 | #include 7 | 8 | #include 9 | 10 | namespace PCPClient { 11 | 12 | // 13 | // ConnectionTimings 14 | // 15 | 16 | struct LIBCPP_PCP_CLIENT_EXPORT ConnectionTimings { 17 | using Duration_us = boost::chrono::duration; 18 | using Duration_min = boost::chrono::minutes; 19 | 20 | boost::chrono::high_resolution_clock::time_point start; 21 | boost::chrono::high_resolution_clock::time_point tcp_pre_init; 22 | boost::chrono::high_resolution_clock::time_point tcp_post_init; 23 | boost::chrono::high_resolution_clock::time_point open; 24 | boost::chrono::high_resolution_clock::time_point closing_handshake; 25 | boost::chrono::high_resolution_clock::time_point close; 26 | 27 | bool isOpen() const; 28 | bool isClosingStarted() const; 29 | bool isFailed() const; 30 | bool isClosed() const; 31 | 32 | /// Sets the `start` time_point member to the current instant, 33 | /// the other time_points to epoch, and all state flags to false 34 | void reset(); 35 | 36 | /// Sets the `open` time_point member to the current instant 37 | /// and sets the related flag 38 | void setOpen(); 39 | 40 | /// Sets the `closing_handshake` time_point member to the 41 | /// current instant and flags `closing_started` 42 | void setClosing(); 43 | 44 | /// Sets the `close` time_point member to the current instant, 45 | /// flags `closed` and sets `connection_failed` to onFail_event 46 | void setClosed(bool onFail_event = false); 47 | 48 | /// Time interval to establish the TCP connection [us] 49 | Duration_us getTCPInterval() const; 50 | 51 | /// Time interval to perform the WebSocket Opening Handshake [us]; 52 | /// it will return: 53 | /// - a null duration, if the WebSocket is not open; 54 | /// - the (tcp_post_init - tcp_pre_init) duration, otherwise. 55 | Duration_us getOpeningHandshakeInterval() const; 56 | 57 | /// Time interval to establish the overall WebSocket connection [us]; 58 | /// it will return: 59 | /// - a null duration, if the WebSocket is not open; 60 | /// - the (open - start) duration, otherwise. 61 | Duration_us getWebSocketInterval() const; 62 | 63 | /// Time interval to perform the WebSocket Closing Handshake [us]; 64 | /// it will return: 65 | /// - a null duration, if: 66 | /// * the Websocket is not open; 67 | /// * the Closing Handshake was not started by this client; 68 | /// * the Closing Handshake did not complete. 69 | /// - the (close - closing_handshake) duration, otherwise. 70 | Duration_us getClosingHandshakeInterval() const; 71 | 72 | /// Duration of the WebSocket connection [minutes]; it will return: 73 | /// - a null duration, if the WebSocket connection was not established; 74 | /// - the (close - start) duration, if the connection was established 75 | /// and, then, closed; 76 | /// - the (now - start) duration, otherwise. 77 | Duration_min getOverallConnectionInterval_min() const; 78 | 79 | /// As getOverallConnectionInterval_min, but the duration is in us. 80 | Duration_us getOverallConnectionInterval_us() const; 81 | 82 | /// Returns a string with the WebSocket Opening Handshake timings 83 | std::string toString() const; 84 | 85 | private: 86 | bool _open { false }; 87 | bool _closing_started { false }; 88 | bool _failed { false }; 89 | bool _closed { false }; 90 | 91 | std::string getOverallDurationTxt() const; 92 | }; 93 | 94 | // 95 | // AssociationTimings 96 | // 97 | 98 | struct LIBCPP_PCP_CLIENT_EXPORT AssociationTimings { 99 | using Duration_ms = boost::chrono::duration; 100 | using Duration_min = boost::chrono::minutes; 101 | 102 | boost::chrono::high_resolution_clock::time_point start; 103 | boost::chrono::high_resolution_clock::time_point association; 104 | boost::chrono::high_resolution_clock::time_point close; 105 | 106 | bool completed { false }; 107 | bool success { false }; 108 | bool closed { false }; 109 | 110 | /// Sets the `start` time_point member to the current instant, 111 | /// the other time_points to epoch, and all flags to false 112 | void reset(); 113 | 114 | /// Sets the Association time_point member to the current instant 115 | /// if `closed` was not flagged, otherwise use the close one, then 116 | /// flags `completed` and sets `success` as specified 117 | void setCompleted(bool _success = true); 118 | 119 | /// Sets the session closure time_point member to the current instant 120 | /// and flags `closed` 121 | void setClosed(); 122 | 123 | /// Time interval to perform the Session Association [ms]; 124 | /// it will return: 125 | /// - a null duration, if the Session Association was not completed; 126 | /// - the (association - start) duration, otherwise. 127 | Duration_ms getAssociationInterval() const; 128 | 129 | /// Duration of the PCP Session [minutes]; it will return: 130 | /// - a null duration, in case the Association was not completed; 131 | /// - the (close - start) duration, in case the session was closed; 132 | /// - the (now - start) duration, otherwise. 133 | Duration_min getOverallSessionInterval_min() const; 134 | 135 | /// As getOverallSessionInterval_min, but the duration is in ms. 136 | Duration_ms getOverallSessionInterval_ms() const; 137 | 138 | /// Returns a string with the Association interval [ms]; 139 | /// if `include_completion` is flagged and the Association was 140 | /// previously set as successfully completed, the string will 141 | /// also include the duration of the PCP Session 142 | std::string toString(bool include_completion = true) const; 143 | }; 144 | 145 | } // namespace PCPClient 146 | 147 | #endif // CPP_PCP_CLIENT_SRC_CONNECTOR_TIMINGS_H_ 148 | -------------------------------------------------------------------------------- /lib/inc/cpp-pcp-client/connector/v2/connector.hpp: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include 6 | 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | namespace PCPClient { 13 | namespace v2 { 14 | 15 | // 16 | // Connector 17 | // 18 | 19 | class LIBCPP_PCP_CLIENT_EXPORT Connector : public ConnectorBase { 20 | public: 21 | Connector() = delete; 22 | 23 | /// The timeout for the call that establishes the WebSocket 24 | /// connection is set, by default, to 5000 ms. 25 | /// Throws a connection_config_error in case the client 26 | /// certificate file does not exist or is invalid; it fails to 27 | /// retrieve the client identity from the file; the client 28 | /// certificate and private key are not paired 29 | 30 | // legacy constructor: pre proxy 31 | Connector(std::string broker_ws_uri, 32 | std::string client_type, 33 | std::string ca_crt_path, 34 | std::string client_crt_path, 35 | std::string client_key_path, 36 | long ws_connection_timeout_ms = 5000, 37 | uint32_t pong_timeouts_before_retry = 3, 38 | long ws_pong_timeout_ms = 5000); 39 | 40 | // constructor for proxy addition 41 | Connector(std::string broker_ws_uri, 42 | std::string client_type, 43 | std::string ca_crt_path, 44 | std::string client_crt_path, 45 | std::string client_key_path, 46 | std::string ws_proxy, 47 | long ws_connection_timeout_ms = 5000, 48 | uint32_t pong_timeouts_before_retry = 3, 49 | long ws_pong_timeout_ms = 5000); 50 | 51 | // constructor for crl addition 52 | Connector(std::string broker_ws_uri, 53 | std::string client_type, 54 | std::string ca_crt_path, 55 | std::string client_crt_path, 56 | std::string client_key_path, 57 | std::string client_crl_path, 58 | std::string ws_proxy, 59 | long ws_connection_timeout_ms = 5000, 60 | uint32_t pong_timeouts_before_retry = 3, 61 | long ws_pong_timeout_ms = 5000); 62 | 63 | // legacy constructor: pre proxy 64 | Connector(std::vector broker_ws_uris, 65 | std::string client_type, 66 | std::string ca_crt_path, 67 | std::string client_crt_path, 68 | std::string client_key_path, 69 | long ws_connection_timeout_ms = 5000, 70 | uint32_t pong_timeouts_before_retry = 3, 71 | long ws_pong_timeout_ms = 5000); 72 | 73 | // constructor for proxy addition 74 | Connector(std::vector broker_ws_uris, 75 | std::string client_type, 76 | std::string ca_crt_path, 77 | std::string client_crt_path, 78 | std::string client_key_path, 79 | std::string ws_proxy, 80 | long ws_connection_timeout_ms = 5000, 81 | uint32_t pong_timeouts_before_retry = 3, 82 | long ws_pong_timeout_ms = 5000); 83 | 84 | // constructor for crl addition 85 | Connector(std::vector broker_ws_uris, 86 | std::string client_type, 87 | std::string ca_crt_path, 88 | std::string client_crt_path, 89 | std::string client_key_path, 90 | std::string client_crl_path, 91 | std::string ws_proxy, 92 | long ws_connection_timeout_ms = 5000, 93 | uint32_t pong_timeouts_before_retry = 3, 94 | long ws_pong_timeout_ms = 5000); 95 | 96 | // constructor for logging addition 97 | Connector(std::vector broker_ws_uris, 98 | std::string client_type, 99 | std::string ca_crt_path, 100 | std::string client_crt_path, 101 | std::string client_key_path, 102 | std::string client_crl_path, 103 | std::string ws_proxy, 104 | leatherman::logging::log_level loglevel, 105 | std::ostream* logstream, 106 | long ws_connection_timeout_ms = 5000, 107 | uint32_t pong_timeouts_before_retry = 3, 108 | long ws_pong_timeout_ms = 5000); 109 | 110 | /// Send the specified message. 111 | /// Throw a connection_processing_error in case of failure; 112 | /// throw a connection_not_init_error in case the connection 113 | /// has not been opened previously. 114 | void send(const Message& msg); 115 | 116 | /// send() overloads that create and send a message as specified. 117 | /// Return the ID of the message, as a string. 118 | /// 119 | /// The caller may specify: 120 | /// - target: a PCP URI string 121 | /// - message_type: schema name that identifies the message type 122 | /// - data: as stringified JSON 123 | /// 124 | /// All methods: 125 | /// - throw a connection_processing_error in case of failure; 126 | /// - throw a connection_not_init_error in case the connection 127 | /// has not been opened previously. 128 | std::string send(const std::string& target, 129 | const std::string& message_type, 130 | const lth_jc::JsonContainer& data_json, 131 | const std::string& in_reply_to = ""); 132 | 133 | std::string send(const std::string& target, 134 | const std::string& message_type, 135 | const std::string& data_txt, 136 | const std::string& in_reply_to = ""); 137 | 138 | std::string sendError(const std::string& target, 139 | const std::string& in_reply_to, 140 | const std::string& description); 141 | 142 | protected: 143 | // WebSocket Callback for the Connection instance to handle all 144 | // incoming messages. 145 | // Parse and validate the passed message; execute the callback 146 | // associated with the schema specified in the envelope. 147 | void processMessage(const std::string& msg_txt) override; 148 | 149 | private: 150 | // PCP Callback executed by processMessage when an error message 151 | // is received. 152 | void errorMessageCallback(const ParsedChunks&); 153 | }; 154 | 155 | } // namespace v2 156 | } // namespace PCPClient 157 | -------------------------------------------------------------------------------- /lib/src/validator/schema.cc: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #pragma GCC diagnostic push 4 | #pragma GCC diagnostic ignored "-Wextra" 5 | #pragma GCC diagnostic ignored "-Wignored-qualifiers" 6 | #include 7 | #include 8 | #pragma GCC diagnostic pop 9 | 10 | #include 11 | 12 | #include 13 | 14 | namespace PCPClient { 15 | 16 | namespace lth_jc = leatherman::json_container; 17 | namespace lth_loc = leatherman::locale; 18 | 19 | // 20 | // Free functions 21 | // 22 | 23 | valijson::Schema parseSchema(const lth_jc::JsonContainer& metadata) { 24 | valijson::Schema schema {}; 25 | valijson::SchemaParser parser {}; 26 | valijson::adapters::RapidJsonAdapter r_j_schema { metadata.getRaw() }; 27 | 28 | parser.populateSchema(r_j_schema, schema); 29 | return schema; 30 | } 31 | 32 | // 33 | // Public API 34 | // 35 | 36 | Schema::Schema(std::string name, 37 | ContentType content_type, 38 | TypeConstraint type) 39 | : name_ { std::move(name) }, 40 | content_type_ { std::move(content_type) }, 41 | parsed_json_schema_ { new valijson::Schema() }, 42 | parsed_ { false }, 43 | type_ { std::move(type) }, 44 | properties_ { new V_C::PropertiesConstraint::PropertySchemaMap() }, 45 | pattern_properties_ { new V_C::PropertiesConstraint::PropertySchemaMap() }, 46 | required_properties_ { new V_C::RequiredConstraint::RequiredProperties() } { 47 | } 48 | 49 | Schema::Schema(std::string name, 50 | ContentType content_type) 51 | : Schema(std::move(name), std::move(content_type), TypeConstraint::Object) { 52 | } 53 | 54 | Schema::Schema(std::string name, 55 | TypeConstraint type) 56 | : Schema(std::move(name), ContentType::Json, std::move(type)) { 57 | } 58 | 59 | Schema::Schema(std::string name) 60 | : Schema(std::move(name), ContentType::Json, TypeConstraint::Object) { 61 | } 62 | 63 | Schema::Schema(const Schema& s) 64 | : name_ { s.name_ }, 65 | content_type_ { s.content_type_ }, 66 | type_ { s.type_ }, 67 | parsed_json_schema_ { new valijson::Schema(*s.parsed_json_schema_) }, 68 | parsed_ { s.parsed_ }, 69 | properties_ { 70 | new V_C::PropertiesConstraint::PropertySchemaMap(*s.properties_) }, 71 | pattern_properties_ { 72 | new V_C::PropertiesConstraint::PropertySchemaMap(*s.pattern_properties_) }, 73 | required_properties_ { 74 | new V_C::RequiredConstraint::RequiredProperties(*s.required_properties_)} { 75 | } 76 | 77 | Schema::Schema(std::string name, const lth_jc::JsonContainer& metadata) 78 | try : name_ { std::move(name) }, 79 | content_type_ { ContentType::Json }, 80 | parsed_json_schema_ { new valijson::Schema(parseSchema(metadata)) }, 81 | parsed_ { true }, 82 | type_ { TypeConstraint::Object }, 83 | properties_ { new V_C::PropertiesConstraint::PropertySchemaMap() }, 84 | pattern_properties_ { new V_C::PropertiesConstraint::PropertySchemaMap() }, 85 | required_properties_ { new V_C::RequiredConstraint::RequiredProperties() } { 86 | } catch (std::exception& e) { 87 | throw schema_error { lth_loc::format("failed to parse schema: {1}", e.what()) }; 88 | } catch (...) { 89 | throw schema_error { lth_loc::translate("failed to parse schema") }; 90 | } 91 | 92 | // unique_ptr requires a complete type at time of destruction. this forces us to 93 | // either have an empty destructor or use a shared_ptr instead. 94 | Schema::~Schema() {} 95 | 96 | void Schema::addConstraint(std::string field, TypeConstraint type, bool required) { 97 | checkAddConstraint(); 98 | 99 | V_C::TypeConstraint constraint { getConstraint(type) }; 100 | 101 | // Add optional type constraint 102 | (*properties_)[field].addConstraint(constraint); 103 | 104 | if (required) { 105 | // add required constraint 106 | required_properties_->insert(field); 107 | } 108 | } 109 | 110 | void Schema::addConstraint(std::string field, Schema sub_schema, bool required) { 111 | checkAddConstraint(); 112 | 113 | V_C::ItemsConstraint sub_schema_constraint { sub_schema.getRaw() }; 114 | 115 | // Add optional schema constraint 116 | (*properties_)[field].addConstraint(sub_schema_constraint); 117 | 118 | if (required) { 119 | required_properties_->insert(field); 120 | } 121 | } 122 | 123 | const std::string Schema::getName() const { 124 | return name_; 125 | } 126 | 127 | ContentType Schema::getContentType() const { 128 | return content_type_; 129 | } 130 | 131 | const valijson::Schema Schema::getRaw() const { 132 | if (parsed_) { 133 | return *parsed_json_schema_; 134 | } 135 | 136 | valijson::Schema schema {}; 137 | auto constraint = getConstraint(type_); 138 | schema.addConstraint(constraint); 139 | 140 | if (!properties_->empty()) { 141 | schema.addConstraint(new V_C::PropertiesConstraint(*properties_, 142 | *pattern_properties_)); 143 | } 144 | 145 | if (!required_properties_->empty()) { 146 | schema.addConstraint(new V_C::RequiredConstraint(*required_properties_)); 147 | } 148 | 149 | return schema; 150 | } 151 | 152 | // 153 | // Private methods 154 | // 155 | 156 | V_C::TypeConstraint Schema::getConstraint(TypeConstraint type) const { 157 | switch (type) { 158 | case TypeConstraint::Object : 159 | return V_C::TypeConstraint::kObject; 160 | case TypeConstraint::Array : 161 | return V_C::TypeConstraint::kArray; 162 | case TypeConstraint::String : 163 | return V_C::TypeConstraint::kString; 164 | case TypeConstraint::Int : 165 | return V_C::TypeConstraint::kInteger; 166 | case TypeConstraint::Bool : 167 | return V_C::TypeConstraint::kBoolean; 168 | case TypeConstraint::Double : 169 | return V_C::TypeConstraint::kNumber; 170 | case TypeConstraint::Null : 171 | return V_C::TypeConstraint::kNull; 172 | default: 173 | return V_C::TypeConstraint::kAny; 174 | } 175 | } 176 | 177 | void Schema::checkAddConstraint() { 178 | if (parsed_) { 179 | throw schema_error { 180 | lth_loc::translate("schema was populate by parsing JSON") }; 181 | } 182 | 183 | if (type_ != TypeConstraint::Object) { 184 | throw schema_error { lth_loc::translate("type is not JSON Object") }; 185 | } 186 | } 187 | 188 | } // namespace PCPClient 189 | --------------------------------------------------------------------------------