This is an automated email from the ASF dual-hosted git repository. cmcfarlen pushed a commit to branch 10.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 9c9c149a617d3f1c49fb802b38e1d54f2547c4e1 Author: Damian Meden <[email protected]> AuthorDate: Tue Mar 12 10:29:39 2024 +0100 libsowc - remove legacy Errata. (#10986) libsowc - remove legacy Errata. --------- Co-authored-by: Alan M. Carroll <[email protected]> (cherry picked from commit ab952036020bb2ba15a69caaec362719f209e16b) --- include/iocore/net/YamlSNIConfig.h | 5 +- include/mgmt/config/FileManager.h | 22 +- include/mgmt/rpc/handlers/common/ErrorUtils.h | 22 +- include/mgmt/rpc/handlers/common/RecordsUtils.h | 3 +- include/mgmt/rpc/handlers/config/Configuration.h | 4 +- include/mgmt/rpc/handlers/plugins/Plugins.h | 2 +- include/mgmt/rpc/handlers/records/Records.h | 16 +- include/mgmt/rpc/handlers/server/Server.h | 4 +- include/mgmt/rpc/handlers/storage/Storage.h | 4 +- include/mgmt/rpc/jsonrpc/Context.h | 8 +- include/mgmt/rpc/jsonrpc/Defs.h | 11 +- include/mgmt/rpc/jsonrpc/JsonRPCManager.h | 9 +- include/mgmt/rpc/jsonrpc/json/YAMLCodec.h | 14 +- include/mgmt/rpc/server/CommBase.h | 2 +- include/mgmt/rpc/server/IPCSocketServer.h | 4 +- include/tscore/Errata.h | 1047 -------------------- include/tsutil/ts_errata.h | 82 +- lib/swoc/include/swoc/Errata.h | 1 + src/api/InkAPI.cc | 4 +- src/iocore/net/SSLSNIConfig.cc | 4 +- src/iocore/net/YamlSNIConfig.cc | 14 +- src/iocore/net/unit_tests/test_YamlSNIConfig.cc | 10 +- src/mgmt/config/AddConfigFilesHere.cc | 1 - src/mgmt/config/FileManager.cc | 45 +- src/mgmt/rpc/CMakeLists.txt | 14 +- src/mgmt/rpc/handlers/common/ErrorUtils.cc | 69 ++ src/mgmt/rpc/handlers/config/Configuration.cc | 22 +- src/mgmt/rpc/handlers/plugins/Plugins.cc | 4 +- src/mgmt/rpc/handlers/records/Records.cc | 10 +- src/mgmt/rpc/handlers/server/Server.cc | 14 +- src/mgmt/rpc/handlers/storage/Storage.cc | 16 +- src/mgmt/rpc/jsonrpc/Context.cc | 4 +- src/mgmt/rpc/jsonrpc/JsonRPCManager.cc | 45 +- .../rpc/jsonrpc/unit_tests/test_basic_protocol.cc | 46 +- src/mgmt/rpc/server/IPCSocketServer.cc | 16 +- src/mgmt/rpc/server/unit_tests/test_rpcserver.cc | 6 +- src/proxy/CMakeLists.txt | 2 +- src/proxy/HostStatus.cc | 18 +- src/proxy/http/HttpConfig.cc | 2 + src/traffic_cache_tool/CacheTool.cc | 1 + src/tscore/CMakeLists.txt | 2 - src/tscore/Errata.cc | 269 ----- src/tscore/unit_tests/test_Errata.cc | 60 -- src/tsutil/CMakeLists.txt | 2 + src/tsutil/ts_errata.cc | 34 + .../jsonrpc/plugins/jsonrpc_plugin_handler_test.cc | 2 +- 46 files changed, 417 insertions(+), 1579 deletions(-) diff --git a/include/iocore/net/YamlSNIConfig.h b/include/iocore/net/YamlSNIConfig.h index 6ac8d76119..aa5ec2008d 100644 --- a/include/iocore/net/YamlSNIConfig.h +++ b/include/iocore/net/YamlSNIConfig.h @@ -32,8 +32,7 @@ #include "iocore/net/SSLTypes.h" #include "tsutil/ts_ip.h" - -#include "tscore/Errata.h" +#include "tsutil/ts_errata.h" #define TSDECL(id) constexpr char TS_##id[] = #id TSDECL(fqdn); @@ -129,7 +128,7 @@ struct YamlSNIConfig { void populate_sni_actions(action_vector_t &actions); }; - ts::Errata loader(const std::string &cfgFilename); + swoc::Errata loader(const std::string &cfgFilename); std::vector<YamlSNIConfig::Item> items; }; diff --git a/include/mgmt/config/FileManager.h b/include/mgmt/config/FileManager.h index 60792e7142..28262acf51 100644 --- a/include/mgmt/config/FileManager.h +++ b/include/mgmt/config/FileManager.h @@ -23,19 +23,19 @@ #pragma once -#include "tscore/ink_mutex.h" -#include "tscore/List.h" - -#include "tscore/Errata.h" - -#include "mgmt/rpc/jsonrpc/JsonRPC.h" - #include <unordered_map> #include <string_view> #include <forward_list> #include <mutex> #include <functional> +#include "tsutil/ts_errata.h" + +#include "tscore/ink_mutex.h" +#include "tscore/List.h" + +#include <mgmt/rpc/jsonrpc/JsonRPC.h> + class ConfigUpdateCbTable; class FileManager @@ -121,7 +121,7 @@ public: time_t fileLastModified = 0; }; - using CallbackType = std::function<ts::Errata(std::string const &, std::string const &)>; + using CallbackType = std::function<swoc::Errata(std::string const &, std::string const &)>; ~FileManager(); FileManager(const FileManager &obj) = delete; @@ -139,8 +139,8 @@ public: _configCallbacks.push_front(std::move(f)); } - ts::Errata fileChanged(std::string const &fileName, std::string const &configName); - ts::Errata rereadConfig(); + swoc::Errata fileChanged(std::string const &fileName, std::string const &configName); + swoc::Errata rereadConfig(); bool isConfigStale(); void configFileChild(const char *parent, const char *child); @@ -169,7 +169,7 @@ private: void addFileHelper(const char *fileName, const char *configName, bool root_access_needed, bool isRequired, ConfigManager *parentConfig); /// JSONRPC endpoint - ts::Rv<YAML::Node> get_files_registry_rpc_endpoint(std::string_view const &id, YAML::Node const ¶ms); + swoc::Rv<YAML::Node> get_files_registry_rpc_endpoint(std::string_view const &id, YAML::Node const ¶ms); }; void initializeRegistry(); // implemented in AddConfigFilesHere.cc diff --git a/include/mgmt/rpc/handlers/common/ErrorUtils.h b/include/mgmt/rpc/handlers/common/ErrorUtils.h index 1cf1f4b881..8d7d750c0a 100644 --- a/include/mgmt/rpc/handlers/common/ErrorUtils.h +++ b/include/mgmt/rpc/handlers/common/ErrorUtils.h @@ -26,7 +26,8 @@ #include <system_error> #include <string_view> -#include "tscore/Errata.h" +#include "tsutil/ts_errata.h" +#include "tsutil/ts_bw_format.h" namespace rpc::handlers::errors { @@ -38,7 +39,7 @@ namespace rpc::handlers::errors // With this we try to avoid error codes collision. You can also use same error Code for all your // errors. enum Codes : unsigned int { - CONFIGURATION = 1, + CONFIGURATION = 999, // go past @c errno METRIC = 1000, RECORD = 2000, SERVER = 3000, @@ -48,19 +49,24 @@ enum Codes : unsigned int { GENERIC = 30000 }; -static constexpr int ERRATA_DEFAULT_ID{1}; +std::error_code make_error_code(rpc::handlers::errors::Codes e); template <typename... Args> -static inline ts::Errata +static inline swoc::Errata make_errata(int code, std::string_view fmt, Args &&...args) { - std::string text; - return ts::Errata{}.push(ERRATA_DEFAULT_ID, code, swoc::bwprint(text, fmt, std::forward<Args>(args)...)); + return swoc::Errata(std::error_code(code, std::generic_category()), fmt, std::forward<Args>(args)...); } -static inline ts::Errata +static inline swoc::Errata make_errata(int code, std::string_view text) { - return ts::Errata{}.push(ERRATA_DEFAULT_ID, code, text); + return swoc::Errata(std::error_code(code, std::generic_category()), std::string(text)); } } // namespace rpc::handlers::errors +namespace std +{ +template <> struct is_error_code_enum<rpc::handlers::errors::Codes> : true_type { +}; + +} // namespace std diff --git a/include/mgmt/rpc/handlers/common/RecordsUtils.h b/include/mgmt/rpc/handlers/common/RecordsUtils.h index a8b047a614..22be1e9416 100644 --- a/include/mgmt/rpc/handlers/common/RecordsUtils.h +++ b/include/mgmt/rpc/handlers/common/RecordsUtils.h @@ -22,13 +22,14 @@ #include <tuple> +#include "tsutil/ts_errata.h" + #include "mgmt/rpc/handlers/common/convert.h" #include "mgmt/rpc/handlers/common/ErrorUtils.h" #include "records/RecCore.h" #include "../../../../../src/records/P_RecCore.h" #include "tscore/Diags.h" -#include "tscore/Errata.h" #include <yaml-cpp/yaml.h> diff --git a/include/mgmt/rpc/handlers/config/Configuration.h b/include/mgmt/rpc/handlers/config/Configuration.h index a79fc6d483..e6f00b5aea 100644 --- a/include/mgmt/rpc/handlers/config/Configuration.h +++ b/include/mgmt/rpc/handlers/config/Configuration.h @@ -24,7 +24,7 @@ namespace rpc::handlers::config { -ts::Rv<YAML::Node> set_config_records(std::string_view const &id, YAML::Node const ¶ms); -ts::Rv<YAML::Node> reload_config(std::string_view const &id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> set_config_records(std::string_view const &id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> reload_config(std::string_view const &id, YAML::Node const ¶ms); } // namespace rpc::handlers::config diff --git a/include/mgmt/rpc/handlers/plugins/Plugins.h b/include/mgmt/rpc/handlers/plugins/Plugins.h index 4f9e079e8a..250b90d4e6 100644 --- a/include/mgmt/rpc/handlers/plugins/Plugins.h +++ b/include/mgmt/rpc/handlers/plugins/Plugins.h @@ -24,5 +24,5 @@ namespace rpc::handlers::plugins { -ts::Rv<YAML::Node> plugin_send_basic_msg(std::string_view const &id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> plugin_send_basic_msg(std::string_view const &id, YAML::Node const ¶ms); } // namespace rpc::handlers::plugins diff --git a/include/mgmt/rpc/handlers/records/Records.h b/include/mgmt/rpc/handlers/records/Records.h index 40fd9696b4..a1d86cab7f 100644 --- a/include/mgmt/rpc/handlers/records/Records.h +++ b/include/mgmt/rpc/handlers/records/Records.h @@ -22,7 +22,7 @@ #include <string_view> #include <yaml-cpp/yaml.h> -#include "tscore/Errata.h" +#include "tsutil/ts_errata.h" namespace rpc::handlers::records { @@ -34,27 +34,27 @@ namespace rpc::handlers::records /// lead the search. /// @param id JSONRPC client's id. /// @param params lookup_records query structure. -/// @return ts::Rv<YAML::Node> A node or an error. If ok, the node will hold the @c "recordList" sequence with the findings. In case -/// of any missed search, ie: when paseed types didn't match the found record(s), the particular error will be added to the @c +/// @return swoc::Rv<YAML::Node> A node or an error. If ok, the node will hold the @c "recordList" sequence with the findings. In +/// case of any missed search, ie: when paseed types didn't match the found record(s), the particular error will be added to the @c /// "errorList" field. /// -ts::Rv<YAML::Node> lookup_records(std::string_view const &id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> lookup_records(std::string_view const &id, YAML::Node const ¶ms); /// /// @brief A RPC function handler that clear all the metrics. /// /// @param id JSONRPC client's id. /// @param params Nothing, this will be ignored. -/// @return ts::Rv<YAML::Node> An empty YAML::Node or the proper Errata with the tracked error. +/// @return swoc::Rv<YAML::Node> An empty YAML::Node or the proper Errata with the tracked error. /// -ts::Rv<YAML::Node> clear_all_metrics_records(std::string_view const &id, YAML::Node const &); +swoc::Rv<YAML::Node> clear_all_metrics_records(std::string_view const &id, YAML::Node const &); /// /// @brief A RPC function handler that clear a specific set of metrics. /// The @c "errorList" field will only be set if there is any error cleaning a specific metric. /// @param id JSONRPC client's id. /// @param params A list of records to update. @see RequestRecordElement -/// @return ts::Rv<YAML::Node> A YAML::Node or the proper Errata with the tracked error. +/// @return swoc::Rv<YAML::Node> A YAML::Node or the proper Errata with the tracked error. /// -ts::Rv<YAML::Node> clear_metrics_records(std::string_view const &id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> clear_metrics_records(std::string_view const &id, YAML::Node const ¶ms); } // namespace rpc::handlers::records diff --git a/include/mgmt/rpc/handlers/server/Server.h b/include/mgmt/rpc/handlers/server/Server.h index 6b6bd26bc5..b66b535ca1 100644 --- a/include/mgmt/rpc/handlers/server/Server.h +++ b/include/mgmt/rpc/handlers/server/Server.h @@ -24,7 +24,7 @@ namespace rpc::handlers::server { -ts::Rv<YAML::Node> server_start_drain(std::string_view const &id, YAML::Node const ¶ms); -ts::Rv<YAML::Node> server_stop_drain(std::string_view const &id, YAML::Node const &); +swoc::Rv<YAML::Node> server_start_drain(std::string_view const &id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> server_stop_drain(std::string_view const &id, YAML::Node const &); void server_shutdown(YAML::Node const &); } // namespace rpc::handlers::server diff --git a/include/mgmt/rpc/handlers/storage/Storage.h b/include/mgmt/rpc/handlers/storage/Storage.h index 47738e565f..d07282b5a4 100644 --- a/include/mgmt/rpc/handlers/storage/Storage.h +++ b/include/mgmt/rpc/handlers/storage/Storage.h @@ -24,6 +24,6 @@ namespace rpc::handlers::storage { -ts::Rv<YAML::Node> set_storage_offline(std::string_view const &id, YAML::Node const ¶ms); -ts::Rv<YAML::Node> get_storage_status(std::string_view const &id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> set_storage_offline(std::string_view const &id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> get_storage_status(std::string_view const &id, YAML::Node const ¶ms); } // namespace rpc::handlers::storage diff --git a/include/mgmt/rpc/jsonrpc/Context.h b/include/mgmt/rpc/jsonrpc/Context.h index 920d1fbd3c..54d6086fde 100644 --- a/include/mgmt/rpc/jsonrpc/Context.h +++ b/include/mgmt/rpc/jsonrpc/Context.h @@ -23,7 +23,7 @@ #include <functional> #include <string_view> -#include "tscore/Errata.h" +#include "tsutil/ts_errata.h" #include "ts/apidefs.h" #include "mgmt/rpc/handlers/common/ErrorUtils.h" @@ -40,15 +40,15 @@ constexpr bool NON_RESTRICTED_API{false}; /// class Context { - using checker_cb = std::function<void(TSRPCHandlerOptions const &, ts::Errata &)>; + using checker_cb = std::function<void(TSRPCHandlerOptions const &, swoc::Errata &)>; /// @brief Internal class to hold the permission checker part. struct Auth { /// Checks for permissions. This function checks for every registered permission checker. /// /// @param options Registered handler options. - /// @return ts::Errata The errata will be filled by each of the registered checkers, if there was any issue validating the + /// @return swoc::Errata The errata will be filled by each of the registered checkers, if there was any issue validating the /// call, then the errata reflects that. - ts::Errata is_blocked(TSRPCHandlerOptions const &options) const; + swoc::Errata is_blocked(TSRPCHandlerOptions const &options) const; /// Add permission checkers. template <typename F> diff --git a/include/mgmt/rpc/jsonrpc/Defs.h b/include/mgmt/rpc/jsonrpc/Defs.h index 22bcf021f1..00cf2a3e0f 100644 --- a/include/mgmt/rpc/jsonrpc/Defs.h +++ b/include/mgmt/rpc/jsonrpc/Defs.h @@ -23,6 +23,8 @@ #include <string> #include <optional> +#include "tsutil/ts_errata.h" + #include <yaml-cpp/yaml.h> #include "mgmt/rpc/jsonrpc/error/RPCError.h" @@ -40,17 +42,16 @@ const std::string JSONRPC_VERSION{"2.0"}; class RPCHandlerResponse { public: - YAML::Node result; //!< The response from the registered handler. - ts::Errata errata; //!< The error response from the registered handler. + YAML::Node result; //!< The response from the registered handler. + swoc::Errata errata; //!< The error response from the registered handler. }; struct RPCResponseInfo { RPCResponseInfo(std::string const &id_) : id{id_} {} // Convenient RPCResponseInfo() = default; - struct Error { - std::error_code ec; - ts::Errata data; + std::error_code ec; // protocol error track. + swoc::Errata data; // internal error detail. }; std::string id; //!< incoming request id (only used for method calls, empty means it's a notification as requests with empty id diff --git a/include/mgmt/rpc/jsonrpc/JsonRPCManager.h b/include/mgmt/rpc/jsonrpc/JsonRPCManager.h index 56b2d71e80..9506493465 100644 --- a/include/mgmt/rpc/jsonrpc/JsonRPCManager.h +++ b/include/mgmt/rpc/jsonrpc/JsonRPCManager.h @@ -28,7 +28,6 @@ #include <variant> #include <mutex> -#include "tscore/Errata.h" #include "tscore/Diags.h" #include "ts/apidefs.h" @@ -66,7 +65,7 @@ private: public: // Possible RPC method signatures. - using MethodHandlerSignature = std::function<ts::Rv<YAML::Node>(std::string_view const &, const YAML::Node &)>; + using MethodHandlerSignature = std::function<swoc::Rv<YAML::Node>(std::string_view const &, const YAML::Node &)>; using PluginMethodHandlerSignature = std::function<void(std::string_view const &, const YAML::Node &)>; using NotificationHandlerSignature = std::function<void(const YAML::Node &)>; @@ -216,8 +215,8 @@ private: bool remove_handler(std::string_view name); // JSONRPC API - here for now. - ts::Rv<YAML::Node> show_registered_handlers(std::string_view const &, const YAML::Node &); - ts::Rv<YAML::Node> get_service_descriptor(std::string_view const &, const YAML::Node &); + swoc::Rv<YAML::Node> show_registered_handlers(std::string_view const &, const YAML::Node &); + swoc::Rv<YAML::Node> get_service_descriptor(std::string_view const &, const YAML::Node &); // Supported handler endpoint types. using Method = FunctionWrapper<MethodHandlerSignature>; @@ -248,7 +247,7 @@ private: explicit operator bool() const; bool operator!() const; /// Invoke the actual handler callback. - ts::Rv<YAML::Node> invoke(specs::RPCRequestInfo const &request) const; + swoc::Rv<YAML::Node> invoke(specs::RPCRequestInfo const &request) const; /// Check if the handler was registered as method. bool is_method() const; diff --git a/include/mgmt/rpc/jsonrpc/json/YAMLCodec.h b/include/mgmt/rpc/jsonrpc/json/YAMLCodec.h index a8b3d3972a..ce8eb5a7e4 100644 --- a/include/mgmt/rpc/jsonrpc/json/YAMLCodec.h +++ b/include/mgmt/rpc/jsonrpc/json/YAMLCodec.h @@ -115,7 +115,7 @@ class yamlcpp_json_decoder return {request, error::RPCErrorCode::PARSE_ERROR}; } // TODO We may want to extend the error handling and inform the user if there is more than one invalid field in the request, - // so far we notify only the first one, we can use the data field to add more errors in it. ts::Errata + // so far we notify only the first one, we can use the data field to add more errors in it. swoc::Errata return {request, {/*ok*/}}; } @@ -182,19 +182,19 @@ class yamlcpp_json_encoder /// @param json output parameter. YAML::Emitter. /// static void - encode_error(std::error_code error, ts::Errata const &errata, YAML::Emitter &json) + encode_error(std::error_code error, swoc::Errata const &errata, YAML::Emitter &json) { json << YAML::Key << "error"; json << YAML::BeginMap; json << YAML::Key << "code" << YAML::Value << error.value(); json << YAML::Key << "message" << YAML::Value << error.message(); - if (!errata.isOK()) { + if (!errata.is_ok()) { json << YAML::Key << "data"; json << YAML::BeginSeq; for (auto const &err : errata) { json << YAML::BeginMap; - json << YAML::Key << "code" << YAML::Value << err.getCode(); - json << YAML::Key << "message" << YAML::Value << err.text(); + json << YAML::Key << "code" << YAML::Value << errata.code().value(); + json << YAML::Key << "message" << YAML::Value << std::string{err.text().data(), err.text().size()}; json << YAML::EndMap; } json << YAML::EndSeq; @@ -204,7 +204,7 @@ class yamlcpp_json_encoder /// Convenience functions to call encode_error. static void - encode_error(ts::Errata const &errata, YAML::Emitter &json) + encode_error(swoc::Errata const &errata, YAML::Emitter &json) { encode_error({error::RPCErrorCode::ExecutionError}, errata, json); } @@ -229,7 +229,7 @@ class yamlcpp_json_encoder } // Registered handler error: They have set the error on the response from the registered handler. This uses ExecutionError as // top error. - else if (!resp.callResult.errata.isOK()) { + else if (!resp.callResult.errata.is_ok()) { encode_error(resp.callResult.errata, json); } // A valid response: The registered handler have set the proper result and no error was flagged. diff --git a/include/mgmt/rpc/server/CommBase.h b/include/mgmt/rpc/server/CommBase.h index 30767d82aa..23bbb36503 100644 --- a/include/mgmt/rpc/server/CommBase.h +++ b/include/mgmt/rpc/server/CommBase.h @@ -21,7 +21,7 @@ #include <system_error> -#include <tscore/Errata.h> +#include <tsutil/ts_errata.h> #include "mgmt/rpc/config/JsonRPCConfig.h" diff --git a/include/mgmt/rpc/server/IPCSocketServer.h b/include/mgmt/rpc/server/IPCSocketServer.h index 7c3ccfba30..854e571dc9 100644 --- a/include/mgmt/rpc/server/IPCSocketServer.h +++ b/include/mgmt/rpc/server/IPCSocketServer.h @@ -29,6 +29,8 @@ #include "swoc/MemSpan.h" #include "swoc/BufferWriter.h" +#include "tsutil/ts_bw_format.h" +#include "tsutil/ts_errata.h" #include "tscore/Layout.h" #include "mgmt/rpc/server/CommBase.h" @@ -134,7 +136,7 @@ private: void bind(std::error_code &ec); void listen(std::error_code &ec); void close(); - void late_check_peer_credentials(int peedFd, TSRPCHandlerOptions const &options, ts::Errata &errata) const; + void late_check_peer_credentials(int peedFd, TSRPCHandlerOptions const &options, swoc::Errata &errata) const; std::atomic_bool _running; diff --git a/include/tscore/Errata.h b/include/tscore/Errata.h deleted file mode 100644 index 3db677d429..0000000000 --- a/include/tscore/Errata.h +++ /dev/null @@ -1,1047 +0,0 @@ -/** @file - Stacking error message handling. - - The problem addressed by this library is the ability to pass back - detailed error messages from failures. It is hard to get good - diagnostics because the specific failures and general context are - located in very different stack frames. This library allows local - functions to pass back local messages which can be easily - augmented as the error travels up the stack frame. - - This could be done with exceptions but - - That is more effort to implementation - - Generally more expensive. - - Each message on a stack contains text and a numeric identifier. - The identifier value zero is reserved for messages that are not - errors so that information can be passed back even in the success - case. - - The implementation takes the position that success is fast and - failure is expensive. Therefore it is optimized for the success - path, imposing very little overhead. On the other hand, if an - error occurs and is handled, that is generally so expensive that - optimizations are pointless (although, of course, one should not - be gratuitiously expensive). - - The library also provides the @c Rv ("return value") template to - make returning values and status easier. This template allows a - function to return a value and status pair with minimal changes. - The pair acts like the value type in most situations, while - providing access to the status. - - Each instance of an erratum is a wrapper class that emulates value - semantics (copy on write). This means passing even large message - stacks is inexpensive, involving only a pointer copy and reference - counter increment and decrement. A success value is represented by - an internal @c NULL so it is even cheaper to copy. - - To further ease use, the library has the ability to define @a - sinks. A sink is a function that acts on an erratum when it - becomes unreferenced. The indended use is to send the messages to - an output log. This makes reporting errors to a log from even - deeply nested functions easy while preserving the ability of the - top level logic to control such logging. - - @section license License - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#pragma once -#include <memory> -#include <string> -#include <iosfwd> -#include <sstream> -#include <deque> -#include <system_error> -#include "tscore/NumericType.h" -#include "tscore/IntrusivePtr.h" - -namespace ts -{ -/** Class to hold a stack of error messages (the "errata"). - This is a smart handle class, which wraps the actual data - and can therefore be treated a value type with cheap copy - semantics. Default construction is very cheap. - */ -class Errata -{ -protected: - /// Implementation class. - struct Data; - /// Handle for implementation class instance. - using ImpPtr = IntrusivePtr<Data>; - -public: - using self = Errata; /// Self reference type. - - /// Message ID. - using Id = NumericType<unsigned int, struct MsgIdTag>; - - /* Tag / level / code severity. - This is intended for clients to use to provide additional - classification of a message. A severity code, as for syslog, - is a common use. - - */ - using Code = NumericType<unsigned int, struct CodeTag>; - struct Message; - - using Container = std::deque<Message>; ///< Storage type for messages. - // We iterate backwards to look like a stack. - // using iterator = Container::reverse_iterator; ///< Message iteration. - /// Message const iteration. - // using const_iterator = Container::const_reverse_iterator; - /// Reverse message iteration. - // using reverse_iterator = Container::iterator; - /// Reverse constant message iteration. - // using const_reverse_iterator = Container::const_iterator; - - /// Default constructor - empty errata, very fast. - Errata(); - /// Copy constructor, very fast. - Errata(self const &that ///< Object to copy - ); - /// Construct from string. - /// Message Id and Code are default. - explicit Errata(std::string const &text ///< Finalized message text. - ); - /// Construct with @a id and @a text. - /// Code is default. - Errata(Id id, ///< Message id. - std::string const &text ///< Message text. - ); - /// Construct with @a id, @a code, and @a text. - Errata(Id id, ///< Message text. - Code code, ///< Message code. - std::string const &text ///< Message text. - ); - /** Construct from a message instance. - This is equivalent to default constructing an @c errata and then - invoking @c push with an argument of @a msg. - */ - Errata(Message const &msg ///< Message to push - ); - - /// Constructor with @a id and @a std::error_code - Errata(std::error_code const &ec ///< Standard error code. - ); - /// Move constructor. - Errata(self &&that); - /// Move constructor from @c Message. - Errata(Message &&msg); - - /// destructor - ~Errata(); - - /// Self assignment. - /// @return A reference to this object. - self &operator=(const self &that ///< Source instance. - ); - - /// Move assignment. - self &operator=(self &&that); - - /** Assign message. - All other messages are discarded. - @return A reference to this object. - */ - self &operator=(Message const &msg ///< Source message. - ); - - /** Push @a text as a message. - The message is constructed from just the @a text. - It becomes the top message. - @return A reference to this object. - */ - self &push(std::string const &text); - /** Push @a text as a message with message @a id. - The message is constructed from @a text and @a id. - It becomes the top message. - @return A reference to this object. - */ - self &push(Id id, std::string const &text); - /** Push @a text as a message with message @a id and @a code. - The message is constructed from @a text and @a id. - It becomes the top message. - @return A reference to this object. - */ - self &push(Id id, Code code, std::string const &text); - /** Push a message. - @a msg becomes the top message. - @return A reference to this object. - */ - self &push(Message const &msg); - self &push(Message &&msg); - - /** Push a constructed @c Message. - The @c Message is set to have the @a id and @a code. The other arguments are converted - to strings and concatenated to form the message text. - @return A reference to this object. - */ - template <typename... Args> self &push(Id id, Code code, Args const &...args); - - /** Push a nested status. - @a err becomes the top item. - @return A reference to this object. - */ - self &push(self const &err); - - /** Access top message. - @return If the errata is empty, a default constructed message - otherwise the most recent message. - */ - Message const &top() const; - - /** Move messages from @a that to @c this errata. - Messages from @a that are put on the top of the - stack in @c this and removed from @a that. - */ - self &pull(self &that); - - /// Remove last message. - void pop(); - - /// Remove all messages. - void clear(); - - /** Inhibit logging. - @note This only affects @c this as a top level @c errata. - It has no effect on this @c this being logged as a nested - @c errata. - */ - self &doNotLog(); - - friend std::ostream &operator<<(std::ostream &, self const &); - - /// Default glue value (a newline) for text rendering. - static std::string const DEFAULT_GLUE; - - /** Test status. - - Equivalent to @c success but more convenient for use in - control statements. - - @return @c true if no messages or last message has a zero - message ID, @c false otherwise. - */ - operator bool() const; - - /** Test errata for no failure condition. - - Equivalent to @c operator @c bool but easier to invoke. - - @return @c true if no messages or last message has a zero - message ID, @c false otherwise. - */ - bool isOK() const; - - /// Number of messages in the errata. - size_t size() const; - - /* Forward declares. - We have to make our own iterators as the least bad option. The problem - is that we have recursive structures so declaration order is difficult. - We can't use the container iterators here because the element type is - not yet defined. If we define the element type here, it can't contain - an Errata and we have to do funky things to get around that. So we - have our own iterators, which are just shadowing sublclasses of the - container iterators. - */ - class iterator; - class const_iterator; - - /// Reference to top item on the stack. - iterator begin(); - /// Reference to top item on the stack. - const_iterator begin() const; - //! Reference one past bottom item on the stack. - iterator end(); - //! Reference one past bottom item on the stack. - const_iterator end() const; - - // Logging support. - - /** Base class for erratum sink. - When an errata is abandoned, this will be called on it to perform - any client specific logging. It is passed around by handle so that - it doesn't have to support copy semantics (and is not destructed - until application shutdown). Clients can subclass this class in order - to preserve arbitrary data for the sink or retain a handle to the - sink for runtime modifications. - */ - class Sink : public IntrusivePtrCounter - { - public: - using self = Sink; ///< Self reference type. - using Handle = IntrusivePtr<self>; ///< Handle type. - - /// Handle an abandoned errata. - virtual void operator()(Errata const &) const = 0; - /// Force virtual destructor. - virtual ~Sink() {} - }; - - //! Register a sink for discarded erratum. - static void registerSink(Sink::Handle const &s); - - /// Register a function as a sink. - using SinkHandlerFunction = void (*)(Errata const &); - - // Wrapper class to support registering functions as sinks. - struct SinkFunctionWrapper : public Sink { - /// Constructor. - SinkFunctionWrapper(SinkHandlerFunction f) : m_f(f) {} - /// Operator to invoke the function. - void - operator()(Errata const &e) const override - { - m_f(e); - } - SinkHandlerFunction m_f; ///< Client supplied handler. - }; - - /// Register a sink function for abandoned erratum. - static void - registerSink(SinkHandlerFunction f) - { - registerSink(Sink::Handle(new SinkFunctionWrapper(f))); - } - - /** Simple formatted output. - - Each message is written to a line. All lines are indented with - whitespace @a offset characters. Lines are indented an - additional @a indent. This value is increased by @a shift for - each level of nesting of an @c Errata. if @a lead is not @c - NULL the indentation is overwritten by @a lead if @a indent is - non-zero. It acts as a "continuation" marker for nested - @c Errata. - - */ - std::ostream &write(std::ostream &out, ///< Output stream. - int offset, ///< Lead white space for every line. - int indent, ///< Additional indentation per line for messages. - int shift, ///< Additional @a indent for nested @c Errata. - char const *lead ///< Leading text for nested @c Errata. - ) const; - /// Simple formatted output to fixed sized buffer. - /// @return Number of characters written to @a buffer. - size_t write(char *buffer, ///< Output buffer. - size_t n, ///< Buffer size. - int offset, ///< Lead white space for every line. - int indent, ///< Additional indentation per line for messages. - int shift, ///< Additional @a indent for nested @c Errata. - char const *lead ///< Leading text for nested @c Errata. - ) const; - -protected: - /// Construct from implementation pointer. - /// Used internally by nested classes. - Errata(ImpPtr const &ptr); - /// Implementation instance. - ImpPtr m_data; - - /// Return the implementation instance, allocating and unsharing as needed. - Data *pre_write(); - /// Force and return an implementation instance. - /// Does not follow copy on write. - Data const *instance(); - - /// Used for returns when no data is present. - static Message const NIL_MESSAGE; - - friend struct Data; - friend class Item; -}; - -extern std::ostream &operator<<(std::ostream &os, Errata const &stat); - -/// Storage for a single message. -struct Errata::Message { - using self = Message; ///< Self reference type. - - /// Default constructor. - /// The message has Id = 0, default code, and empty text. - Message() = default; - - /// Construct from text. - /// Id is zero and Code is default. - Message(std::string const &text ///< Finalized message text. - ); - - /// Construct with @a id and @a text. - /// Code is default. - Message(Id id, ///< ID of message in table. - std::string const &text ///< Final text for message. - ); - - /// Construct with @a id, @a code, and @a text. - Message(Id id, ///< Message Id. - Code code, ///< Message Code. - std::string const &text ///< Final text for message. - ); - - /// Construct with an @a id, @a code, and a @a message. - /// The message contents are created by converting the variable arguments - /// to strings using the stream operator and concatenated in order. - template <typename... Args> - Message(Id id, ///< Message Id. - Code code, ///< Message Code. - Args const &...text); - - /// Reset to the message to default state. - self &clear(); - - /// Set the message Id. - self &set(Id id ///< New message Id. - ); - - /// Set the code. - self &set(Code code ///< New code for message. - ); - - /// Set the text. - self &set(std::string const &text ///< New message text. - ); - - /// Set the text. - self &set(char const *text ///< New message text. - ); - - /// Set the errata. - self &set(Errata const &err ///< Errata to store. - ); - - /// Get the text of the message. - std::string const &text() const; - - /// Get the code. - Code getCode() const; - /// Get the nested status. - /// @return A status object, which is not @c NULL if there is a - /// nested status stored in this item. - Errata getErrata() const; - - /** The default message code. - - This value is used as the Code value for constructing and - clearing messages. It can be changed to control the value - used for empty messages. - */ - static Code Default_Code; - - /// Type for overriding success message test. - using SuccessTest = bool (*)(Message const &m); - - /** Success message test. - - When a message is tested for being "successful", this - function is called. It may be overridden by a client. - The initial value is @c DEFAULT_SUCCESS_TEST. - - @note This is only called when there are Messages in the - Errata. An empty Errata (@c NULL or empty stack) is always - a success. Only the @c top Message is checked. - - @return @c true if the message indicates success, - @c false otherwise. - */ - static SuccessTest Success_Test; - - /// Indicate success if the message code is zero. - /// @note Used as the default success test. - static bool isCodeZero(Message const &m); - - static SuccessTest const DEFAULT_SUCCESS_TEST; - - template <typename... Args> static std::string stringify(Args const &...items); - - Id m_id = 0; ///< Message ID. - Code m_code = Default_Code; ///< Message code. - std::string m_text; ///< Final text. - Errata m_errata; ///< Nested errata. -}; - -/** This is the implementation class for Errata. - - It holds the actual messages and is treated as a passive data - object with nice constructors. - - We implement reference counting semantics by hand for two - reasons. One is that we need to do some specialized things, but - mainly because the client can't see this class so we can't -*/ -struct Errata::Data : public IntrusivePtrCounter { - using self = Data; ///< Self reference type. - - //! Default constructor. - Data(); - - /// Destructor, to do logging. - ~Data(); - - //! Number of messages. - size_t size() const; - - /// Get the top message on the stack. - Message const &top() const; - - /// Put a message on top of the stack. - void push(Message const &msg); - void push(Message &&msg); - - /// Log this when it is deleted. - mutable bool m_log_on_delete = true; - - //! The message stack. - Container m_items; -}; - -/// Forward iterator for @c Messages in an @c Errata. -class Errata::iterator : public Errata::Container::reverse_iterator -{ -public: - using self = iterator; ///< Self reference type. - using super = Errata::Container::reverse_iterator; ///< Parent type. - iterator(); ///< Default constructor. - /// Copy constructor. - iterator(self const &that ///< Source instance. - ); - /// Construct from super class. - iterator(super const &that ///< Source instance. - ); - /// Assignment. - self &operator=(self const &that); - /// Assignment from super class. - self &operator=(super const &that); - /// Prefix increment. - self &operator++(); - /// Prefix decrement. - self &operator--(); -}; - -/// Forward constant iterator for @c Messages in an @c Errata. -class Errata::const_iterator : public Errata::Container::const_reverse_iterator -{ -public: - using self = const_iterator; ///< Self reference type. - using super = Errata::Container::const_reverse_iterator; ///< Parent type. - const_iterator(); ///< Default constructor. - /// Copy constructor. - const_iterator(self const &that ///< Source instance. - ); - const_iterator(super const &that ///< Source instance. - ); - /// Assignment. - self &operator=(self const &that); - /// Assignment from super class. - self &operator=(super const &that); - /// Prefix increment. - self &operator++(); - /// Prefix decrement. - self &operator--(); -}; - -/** Helper class for @c Rv. - This class enables us to move the implementation of non-templated methods - and members out of the header file for a cleaner API. - */ -struct RvBase { - Errata _errata; ///< The status from the function. - - /** Default constructor. */ - RvBase(); - - /** Construct with specific status. - */ - RvBase(Errata const &s ///< Status to copy - ); - - /** Construct with specific status. - */ - RvBase(Errata &&s ///< Status to move - ); - - //! Test the return value for success. - bool isOK() const; - - /** Clear any stacked errors. - This is useful during shutdown, to silence irrelevant errors caused - by the shutdown process. - */ - void clear(); - - /// Inhibit logging of the errata. - void doNotLog(); -}; - -/** Return type for returning a value and status (errata). In - general, a method wants to return both a result and a status so - that errors are logged properly. This structure is used to do that - in way that is more usable than just @c std::pair. - Simpler and - shorter typography - Force use of @c errata rather than having to - remember it (and the order) each time - Enable assignment directly - to @a R for ease of use and compatibility so clients can upgrade - asynchronously. - */ -template <typename R> struct Rv : public RvBase { - using self = Rv; ///< Standard self reference type. - using super = RvBase; ///< Standard super class reference type. - using Result = R; ///< Type of result value. - - Result _result; ///< The actual result of the function. - - /** Default constructor. - The default constructor for @a R is used. - The status is initialized to SUCCESS. - */ - Rv(); - - /** Standard (success) constructor. - - This copies the result and sets the status to SUCCESS. - - @note Not @c explicit so that clients can return just a result - and have it be marked as SUCCESS. - */ - Rv(Result const &r ///< The function result - ); - - /** Construct from a result and a pre-existing status object. - - @internal No constructor from just an Errata to avoid - potential ambiguity with constructing from result type. - */ - Rv(Result const &r, ///< The function result - Errata const &s ///< A pre-existing status object - ); - - Rv(Errata &&errata); - /** User conversion to the result type. - - This makes it easy to use the function normally or to pass the - result only to other functions without having to extract it by - hand. - */ - operator Result const &() const; - - /** Assignment from result type. - - This allows the result to be assigned to a pre-declared return - value structure. The return value is a reference to the - internal result so that this operator can be chained in other - assignments to instances of result type. This is most commonly - used when the result is computed in to a local variable to be - both returned and stored in a member. - - @code - Rv<int> zret; - int value; - // ... complex computations, result in value - this->m_value = zret = value; - // ... - return zret; - @endcode - - @return A reference to the copy of @a r stored in this object. - */ - Result & - operator=(Result const &r ///< Result to assign - ) - { - _result = r; - return _result; - } - - /** Add the status from another instance to this one. - @return A reference to @c this object. - */ - template <typename U> - self &push(Rv<U> const &that ///< Source of status messages - ); - - /** Set the result. - - This differs from assignment of the function result in that the - return value is a reference to the @c Rv, not the internal - result. This makes it useful for assigning a result local - variable and then returning. - - @code - Rv<int> zret; - int value; - // ... complex computation, result in value - return zret.set(value); - @endcode - */ - self &set(Result const &r ///< Result to store - ); - - /** Return the result. - @return A reference to the result value in this object. - */ - Result &result(); - - /** Return the result. - @return A reference to the result value in this object. - */ - Result const &result() const; - - /** Return the status. - @return A reference to the @c errata in this object. - */ - Errata &errata(); - - /** Return the status. - @return A reference to the @c errata in this object. - */ - Errata const &errata() const; - - /// Directly set the errata - self &operator=(Errata const &status ///< Errata to assign. - ); - - /// Push a message on to the status. - self &push(Errata::Message const &msg); -}; - -/** Combine a function result and status in to an @c Rv. - This is useful for clients that want to declare the status object - and result independently. - */ -template <typename R> -Rv<R> -MakeRv(R const &r, ///< The function result - Errata const &s ///< The pre-existing status object -) -{ - return Rv<R>(r, s); -} -/* ----------------------------------------------------------------------- */ -/* ----------------------------------------------------------------------- */ -// Inline methods. -inline Errata::Message::Message(std::string const &text) : m_text(text) {} -inline Errata::Message::Message(Id id, std::string const &text) : m_id(id), m_text(text) {} -inline Errata::Message::Message(Id id, Code code, std::string const &text) : m_id(id), m_code(code), m_text(text) {} -template <typename... Args> -Errata::Message::Message(Id id, Code code, Args const &...text) : m_id(id), m_code(code), m_text(stringify(text...)) -{ -} - -inline Errata::Message & -Errata::Message::clear() -{ - m_id = 0; - m_code = Default_Code; - m_text.erase(); - m_errata.clear(); - return *this; -} - -inline std::string const & -Errata::Message::text() const -{ - return m_text; -} -inline Errata::Code -Errata::Message::getCode() const -{ - return m_code; -} -inline Errata -Errata::Message::getErrata() const -{ - return m_errata; -} - -inline Errata::Message & -Errata::Message::set(Id id) -{ - m_id = id; - return *this; -} -inline Errata::Message & -Errata::Message::set(Code code) -{ - m_code = code; - return *this; -} -inline Errata::Message & -Errata::Message::set(std::string const &text) -{ - m_text = text; - return *this; -} -inline Errata::Message & -Errata::Message::set(char const *text) -{ - m_text = text; - return *this; -} -inline Errata::Message & -Errata::Message::set(Errata const &err) -{ - m_errata = err; - m_errata.doNotLog(); - return *this; -} - -template <typename... Args> -std::string -Errata::Message::stringify(Args const &...items) -{ - std::ostringstream s; - (void)(int[]){0, ((s << items), 0)...}; - return s.str(); -} - -inline Errata::Errata() {} -inline Errata::Errata(Id id, Code code, std::string const &text) -{ - this->push(Message(id, code, text)); -} -inline Errata::Errata(Message const &msg) -{ - this->push(msg); -} -inline Errata::Errata(Message &&msg) -{ - this->push(std::move(msg)); -} -inline Errata::Errata(std::error_code const &ec) -{ - auto cond = ec.category().default_error_condition(ec.value()); - this->push(cond.value(), // we use the classification from the error_condition. - ec.value(), ec.message()); -} - -inline Errata::operator bool() const -{ - return this->isOK(); -} - -inline size_t -Errata::size() const -{ - return m_data ? m_data->m_items.size() : 0; -} - -inline bool -Errata::isOK() const -{ - return nullptr == m_data || 0 == m_data->size() || Message::Success_Test(this->top()); -} - -inline Errata & -Errata::push(std::string const &text) -{ - this->push(Message(text)); - return *this; -} - -inline Errata & -Errata::push(Id id, std::string const &text) -{ - this->push(Message(id, text)); - return *this; -} - -inline Errata & -Errata::push(Id id, Code code, std::string const &text) -{ - this->push(Message(id, code, text)); - return *this; -} - -template <typename... Args> -auto -Errata::push(Id id, Code code, Args const &...args) -> self & -{ - this->push(Message(id, code, args...)); - return *this; -} - -inline Errata & -Errata::push(Errata const &err) -{ - for (auto const &e : err) { - this->push(e.m_id, e.m_code, e.m_text); - // e.m_errata?? - } - return *this; -} - -inline Errata::Message const & -Errata::top() const -{ - return m_data ? m_data->top() : NIL_MESSAGE; -} -inline Errata & -Errata::doNotLog() -{ - this->instance()->m_log_on_delete = false; - return *this; -} - -inline Errata::Data::Data() {} -inline size_t -Errata::Data::size() const -{ - return m_items.size(); -} - -inline Errata::iterator::iterator() {} -inline Errata::iterator::iterator(self const &that) : super(that) {} -inline Errata::iterator::iterator(super const &that) : super(that) {} -inline Errata::iterator & -Errata::iterator::operator=(self const &that) -{ - this->super::operator=(that); - return *this; -} -inline Errata::iterator & -Errata::iterator::operator=(super const &that) -{ - this->super::operator=(that); - return *this; -} -inline Errata::iterator & -Errata::iterator::operator++() -{ - this->super::operator++(); - return *this; -} -inline Errata::iterator & -Errata::iterator::operator--() -{ - this->super::operator--(); - return *this; -} - -inline Errata::const_iterator::const_iterator() {} -inline Errata::const_iterator::const_iterator(self const &that) : super(that) {} -inline Errata::const_iterator::const_iterator(super const &that) : super(that) {} -inline Errata::const_iterator & -Errata::const_iterator::operator=(self const &that) -{ - super::operator=(that); - return *this; -} -inline Errata::const_iterator & -Errata::const_iterator::operator=(super const &that) -{ - super::operator=(that); - return *this; -} -inline Errata::const_iterator & -Errata::const_iterator::operator++() -{ - this->super::operator++(); - return *this; -} -inline Errata::const_iterator & -Errata::const_iterator::operator--() -{ - this->super::operator--(); - return *this; -} - -inline RvBase::RvBase() {} -inline RvBase::RvBase(Errata const &errata) : _errata(errata) {} -inline RvBase::RvBase(Errata &&errata) : _errata(std::move(errata)) {} -inline bool -RvBase::isOK() const -{ - return _errata; -} -inline void -RvBase::clear() -{ - _errata.clear(); -} -inline void -RvBase::doNotLog() -{ - _errata.doNotLog(); -} - -template <typename T> Rv<T>::Rv() : _result() {} -template <typename T> Rv<T>::Rv(Result const &r) : _result(r) {} -template <typename T> Rv<T>::Rv(Result const &r, Errata const &errata) : super(errata), _result(r) {} -template <typename T> Rv<T>::Rv(Errata &&errata) : super(std::move(errata)) {} -template <typename T> Rv<T>::operator Result const &() const -{ - return _result; -} -template <typename T> -T const & -Rv<T>::result() const -{ - return _result; -} -template <typename T> -T & -Rv<T>::result() -{ - return _result; -} -template <typename T> -Errata const & -Rv<T>::errata() const -{ - return _errata; -} -template <typename T> -Errata & -Rv<T>::errata() -{ - return _errata; -} -template <typename T> -Rv<T> & -Rv<T>::set(Result const &r) -{ - _result = r; - return *this; -} -template <typename T> -Rv<T> & -Rv<T>::operator=(Errata const &errata) -{ - _errata = errata; - return *this; -} -template <typename T> -Rv<T> & -Rv<T>::push(Errata::Message const &msg) -{ - _errata.push(msg); - return *this; -} -template <typename T> -template <typename U> -Rv<T> & -Rv<T>::push(Rv<U> const &that) -{ - _errata.push(that.errata()); - return *this; -} -/* ----------------------------------------------------------------------- */ -/* ----------------------------------------------------------------------- */ -} // namespace ts diff --git a/include/tsutil/ts_errata.h b/include/tsutil/ts_errata.h index 427983bbbb..313d5ab54d 100644 --- a/include/tsutil/ts_errata.h +++ b/include/tsutil/ts_errata.h @@ -22,6 +22,7 @@ limitations under the License. #pragma once #include <utility> +#include <unordered_map> #include "tsutil/ts_diag_levels.h" #include "swoc/TextView.h" @@ -44,27 +45,20 @@ diags_level_of(swoc::Errata::Severity s) return static_cast<DiagsLevel>(static_cast<int>(s)); } -inline std::error_code -ec_for() -{ - return std::error_code(errno, std::system_category()); -} -inline std::error_code -ec_for(int e) -{ - return std::error_code(e, std::system_category()); -} - // This is treated as an array so must numerically match with @c DiagsLevel static constexpr std::array<swoc::TextView, 9> Severity_Names{ {"Diag", "Debug", "Status", "Note", "Warn", "Error", "Fatal", "Alert", "Emergency"} }; +namespace ts +{ + inline std::error_code make_errno_code() { return {errno, std::system_category()}; } + inline std::error_code make_errno_code(int err) { @@ -77,3 +71,69 @@ bw_log(DiagsLevel lvl, swoc::TextView fmt, Args &&...args) { swoc::bwprint_v(ts::bw_dbg, fmt, std::forward_as_tuple(args...)); } + +class err_category : public std::error_category +{ + using self_type = err_category; + +public: + /// @return Name of the category. + char const *name() const noexcept override; + + /** Convert code to condition. + * + * @param code Numeric error code. + * @return The correspodning condition. + */ + std::error_condition default_error_condition(int code) const noexcept override; + + /** Is numeric error code equivalent to condition. + * + * @param code Numeric error code. + * @param condition Condition object. + * @return @a true if the arguments are equivalent. + */ + bool equivalent(int code, std::error_condition const &condition) const noexcept override; + + /** Is error code enumeration value equivalent to error code object. + * + * @param ec Condition object. + * @param code Numeric error code. + * @return @a true if the arguments are equivalent. + */ + bool equivalent(std::error_code const &ec, int code) const noexcept override; + + /// @return Text for @c code. + std::string message(int code) const override; + +protected: + using table_type = std::unordered_map<int, std::string>; + /// Mapping from numeric error code to message string. + static table_type const _table; +}; + +inline char const * +ts::err_category::name() const noexcept +{ + return "trafficserver"; +} + +inline std::error_condition +err_category::default_error_condition(int code) const noexcept +{ + return {code, *this}; +} + +inline bool +err_category::equivalent(const std::error_code &ec, int code) const noexcept +{ + return ec.value() == code; +} + +inline bool +err_category::equivalent(int code, const std::error_condition &condition) const noexcept +{ + return code == condition.value(); +} + +} // namespace ts diff --git a/lib/swoc/include/swoc/Errata.h b/lib/swoc/include/swoc/Errata.h index 6ee0be5c25..fff9e17db1 100644 --- a/lib/swoc/include/swoc/Errata.h +++ b/lib/swoc/include/swoc/Errata.h @@ -1544,4 +1544,5 @@ get(swoc::Rv<R> const &rv) { throw std::domain_error("Errata index value out of bounds"); } /// @endcond + }} // namespace swoc::SWOC_VERSION_NS diff --git a/src/api/InkAPI.cc b/src/api/InkAPI.cc index 97717fc5ed..dffa5d236a 100644 --- a/src/api/InkAPI.cc +++ b/src/api/InkAPI.cc @@ -117,7 +117,7 @@ namespace rpc { extern std::mutex g_rpcHandlingMutex; extern std::condition_variable g_rpcHandlingCompletion; -extern ts::Rv<YAML::Node> g_rpcHandlerResponseData; +extern swoc::Rv<YAML::Node> g_rpcHandlerResponseData; extern bool g_rpcHandlerProcessingCompleted; } // namespace rpc @@ -8765,7 +8765,7 @@ TSRPCHandlerError(int ec, const char *descr, size_t descr_len) { Debug("rpc.api", ">> Handler seems to be done with an error"); std::lock_guard<std::mutex> lock(rpc::g_rpcHandlingMutex); - ::rpc::g_rpcHandlerResponseData = ts::Errata{}.push(1, ec, std::string{descr, descr_len}); + ::rpc::g_rpcHandlerResponseData = swoc::Errata(ts::make_errno_code(ec), "{}", swoc::TextView{descr, descr_len}); ::rpc::g_rpcHandlerProcessingCompleted = true; ::rpc::g_rpcHandlingCompletion.notify_one(); Debug("rpc.api", ">> error flagged."); diff --git a/src/iocore/net/SSLSNIConfig.cc b/src/iocore/net/SSLSNIConfig.cc index 357abd9f16..5a0cd99e93 100644 --- a/src/iocore/net/SSLSNIConfig.cc +++ b/src/iocore/net/SSLSNIConfig.cc @@ -306,8 +306,8 @@ SNIConfigParams::initialize(std::string const &sni_filename) } YamlSNIConfig yaml_sni_tmp; - ts::Errata zret = yaml_sni_tmp.loader(sni_filename); - if (!zret.isOK()) { + auto zret = yaml_sni_tmp.loader(sni_filename); + if (!zret.is_ok()) { std::stringstream errMsg; errMsg << zret; if (TSSystemState::is_initializing()) { diff --git a/src/iocore/net/YamlSNIConfig.cc b/src/iocore/net/YamlSNIConfig.cc index e7c6919453..26d12b6e3a 100644 --- a/src/iocore/net/YamlSNIConfig.cc +++ b/src/iocore/net/YamlSNIConfig.cc @@ -38,6 +38,7 @@ #include "swoc/TextView.h" #include "swoc/bwf_base.h" +#include "iocore/net/YamlSNIConfig.h" #include "SNIActionPerformer.h" #include "P_SSLConfig.h" #include "P_SSLNetVConnection.h" @@ -47,7 +48,6 @@ #include "swoc/bwf_fwd.h" #include "tscore/Diags.h" #include "tscore/EnumDescriptor.h" -#include "tscore/Errata.h" #include "tscore/ink_assert.h" #include "records/RecCore.h" @@ -76,32 +76,32 @@ load_tunnel_alpn(std::vector<int> &dst, const YAML::Node &node) } // namespace -ts::Errata +swoc::Errata YamlSNIConfig::loader(const std::string &cfgFilename) { try { YAML::Node config = YAML::LoadFile(cfgFilename); if (config.IsNull()) { - return ts::Errata(); + return {}; } if (!config["sni"]) { - return ts::Errata::Message(1, 1, "expected a toplevel 'sni' node"); + return swoc::Errata("expected a toplevel 'sni' node"); } config = config["sni"]; if (!config.IsSequence()) { - return ts::Errata::Message(1, 1, "expected sequence"); + return swoc::Errata("expected sequence"); } for (auto it = config.begin(); it != config.end(); ++it) { items.push_back(it->as<YamlSNIConfig::Item>()); } } catch (std::exception &ex) { - return ts::Errata::Message(1, 1, ex.what()); + return swoc::Errata("exception - {}", ex.what()); } - return ts::Errata(); + return swoc::Errata(); } void diff --git a/src/iocore/net/unit_tests/test_YamlSNIConfig.cc b/src/iocore/net/unit_tests/test_YamlSNIConfig.cc index 58932567d2..9c809d9840 100644 --- a/src/iocore/net/unit_tests/test_YamlSNIConfig.cc +++ b/src/iocore/net/unit_tests/test_YamlSNIConfig.cc @@ -48,13 +48,13 @@ check_port_range(const YamlSNIConfig::Item &item, in_port_t min_expected, in_por TEST_CASE("YamlSNIConfig sets port ranges appropriately") { YamlSNIConfig conf{}; - ts::Errata zret{conf.loader(_XSTR(LIBINKNET_UNIT_TEST_DIR) "/sni_conf_test.yaml")}; - if (!zret.isOK()) { + swoc::Errata zret{conf.loader(_XSTR(LIBINKNET_UNIT_TEST_DIR) "/sni_conf_test.yaml")}; + if (!zret.is_ok()) { std::stringstream errorstream; errorstream << zret; FAIL(errorstream.str()); } - REQUIRE(zret.isOK()); + REQUIRE(zret.is_ok()); REQUIRE(conf.items.size() == 7); SECTION("If no ports were specified, port range should contain all ports.") @@ -113,11 +113,11 @@ TEST_CASE("YamlConfig handles bad ports appropriately.") std::string filepath; swoc::bwprint(filepath, "{}/sni_conf_test_bad_port_{}.yaml", _XSTR(LIBINKNET_UNIT_TEST_DIR), port_str); - ts::Errata zret{conf.loader(filepath)}; + swoc::Errata zret{conf.loader(filepath)}; std::stringstream errorstream; errorstream << zret; std::string expected; - swoc::bwprint(expected, "1 [1]: yaml-cpp: error at line 20, column 5: bad port range: {}\n", port_str); + swoc::bwprint(expected, "Error: exception - yaml-cpp: error at line 20, column 5: bad port range: {}\n", port_str); CHECK(errorstream.str() == expected); } diff --git a/src/mgmt/config/AddConfigFilesHere.cc b/src/mgmt/config/AddConfigFilesHere.cc index 9e3fa3ef2d..e589a1bd90 100644 --- a/src/mgmt/config/AddConfigFilesHere.cc +++ b/src/mgmt/config/AddConfigFilesHere.cc @@ -26,7 +26,6 @@ #include "../../records/P_RecCore.h" #include "tscore/Diags.h" #include "mgmt/config/FileManager.h" -#include "tscore/Errata.h" static constexpr bool REQUIRED{true}; static constexpr bool NOT_REQUIRED{false}; diff --git a/src/mgmt/config/FileManager.cc b/src/mgmt/config/FileManager.cc index 3df746258c..8d9644b06e 100644 --- a/src/mgmt/config/FileManager.cc +++ b/src/mgmt/config/FileManager.cc @@ -45,18 +45,17 @@ static constexpr auto logTag{"filemanager"}; namespace { -ts::Errata +swoc::Errata handle_file_reload(std::string const &fileName, std::string const &configName) { Debug(logTag, "handling reload %s - %s", fileName.c_str(), configName.c_str()); - ts::Errata ret; + swoc::Errata ret; // TODO: make sure records holds the name after change, if not we should change it. if (fileName == ts::filename::RECORDS) { if (auto zret = RecReadYamlConfigFile(); zret) { RecConfigWarnIfUnregistered(); } else { - std::string str; - ret.push(1, swoc::bwprint(str, "Error reading {}. {}", fileName)); + ret.note("Error reading {}", fileName).note(zret); } } else { RecT rec_type; @@ -64,8 +63,7 @@ handle_file_reload(std::string const &fileName, std::string const &configName) if (RecGetRecordType(data, &rec_type) == REC_ERR_OKAY && rec_type == RECT_CONFIG) { RecSetSyncRequired(data); } else { - std::string str; - ret.push(1, swoc::bwprint(str, "Unknown file change {}.", configName)); + ret.note("Unknown file change {}.", configName); } } @@ -90,7 +88,7 @@ FileManager::FileManager() // Register the files registry jsonrpc endpoint rpc::add_method_handler("filemanager.get_files_registry", - [this](std::string_view const &id, const YAML::Node &req) -> ts::Rv<YAML::Node> { + [this](std::string_view const &id, const YAML::Node &req) -> swoc::Rv<YAML::Node> { return get_files_registry_rpc_endpoint(id, req); }, &rpc::core_ats_rpc_service_provider_handle, {{rpc::NON_RESTRICTED_API}}); @@ -163,17 +161,20 @@ FileManager::getConfigObj(const char *fileName, ConfigManager **rbPtr) return found; } -ts::Errata +swoc::Errata FileManager::fileChanged(std::string const &fileName, std::string const &configName) { Debug("filemanager", "file changed %s", fileName.c_str()); - ts::Errata ret; + swoc::Errata ret; std::lock_guard<std::mutex> guard(_callbacksMutex); for (auto const &call : _configCallbacks) { if (auto const &r = call(fileName, configName); !r) { Debug("filemanager", "something back from callback %s", fileName.c_str()); - std::for_each(r.begin(), r.end(), [&ret](auto &&e) { ret.push(e); }); + if (ret.empty()) { + ret.note("Errors while reloading configurations."); + } + ret.note(r); } } @@ -206,10 +207,10 @@ FileManager::invokeConfigPluginCallbacks() // although it is tempting, DO NOT CALL FROM SIGNAL HANDLERS // This function is not Async-Signal Safe. It // is thread safe -ts::Errata +swoc::Errata FileManager::rereadConfig() { - ts::Errata ret; + swoc::Errata ret; ConfigManager *rb; std::vector<ConfigManager *> changedFiles; @@ -225,7 +226,8 @@ FileManager::rereadConfig() auto const &r = fileChanged(rb->getFileName(), rb->getConfigName()); if (!r) { - std::for_each(r.begin(), r.end(), [&ret](auto &&e) { ret.push(e); }); + ret.note("Errors while reloading configurations."); + ret.note(r); } changedFiles.push_back(rb); @@ -265,7 +267,10 @@ FileManager::rereadConfig() for (size_t i = 0; i < n; i++) { if (std::find(changedFiles.begin(), changedFiles.end(), parentFileNeedChange[i]) == changedFiles.end()) { if (auto const &r = fileChanged(parentFileNeedChange[i]->getFileName(), parentFileNeedChange[i]->getConfigName()); !r) { - std::for_each(r.begin(), r.end(), [&ret](auto &&e) { ret.push(e); }); + if (ret.empty()) { + ret.note("Error while handling parent file name changed."); + } + ret.note(r); } } } @@ -277,13 +282,19 @@ FileManager::rereadConfig() if (found && enabled) { if (auto const &r = fileChanged("proxy.config.body_factory.template_sets_dir", "proxy.config.body_factory.template_sets_dir"); !r) { - std::for_each(r.begin(), r.end(), [&ret](auto &&e) { ret.push(e); }); + if (ret.empty()) { + ret.note("Error while loading body factory templates"); + } + ret.note(r); } } if (auto const &r = fileChanged("proxy.config.ssl.server.ticket_key.filename", "proxy.config.ssl.server.ticket_key.filename"); !r) { - std::for_each(r.begin(), r.end(), [&ret](auto &&e) { ret.push(e); }); + if (ret.empty()) { + ret.note("Error while loading ticket keys"); + } + ret.note(r); } return ret; @@ -325,7 +336,7 @@ FileManager::configFileChild(const char *parent, const char *child) } auto -FileManager::get_files_registry_rpc_endpoint(std::string_view const &id, YAML::Node const ¶ms) -> ts::Rv<YAML::Node> +FileManager::get_files_registry_rpc_endpoint(std::string_view const &id, YAML::Node const ¶ms) -> swoc::Rv<YAML::Node> { // If any error, the rpc manager will catch it and respond with it. YAML::Node configs{YAML::NodeType::Sequence}; diff --git a/src/mgmt/rpc/CMakeLists.txt b/src/mgmt/rpc/CMakeLists.txt index f9d5674405..36446217b7 100644 --- a/src/mgmt/rpc/CMakeLists.txt +++ b/src/mgmt/rpc/CMakeLists.txt @@ -38,8 +38,14 @@ add_library(ts::jsonrpc_server ALIAS jsonrpc_server) target_link_libraries(jsonrpc_server PUBLIC ts::jsonrpc_protocol) add_library( - rpcpublichandlers STATIC handlers/common/RecordsUtils.cc handlers/config/Configuration.cc handlers/records/Records.cc - handlers/storage/Storage.cc handlers/server/Server.cc handlers/plugins/Plugins.cc + rpcpublichandlers STATIC + handlers/common/ErrorUtils.cc + handlers/common/RecordsUtils.cc + handlers/config/Configuration.cc + handlers/records/Records.cc + handlers/storage/Storage.cc + handlers/server/Server.cc + handlers/plugins/Plugins.cc ) add_library(ts::rpcpublichandlers ALIAS rpcpublichandlers) @@ -51,7 +57,9 @@ target_link_libraries( if(BUILD_TESTING) add_executable(test_jsonrpc jsonrpc/unit_tests/unit_test_main.cc jsonrpc/unit_tests/test_basic_protocol.cc) - target_link_libraries(test_jsonrpc ts::tsutil catch2::catch2 ts::jsonrpc_protocol libswoc::libswoc) + target_link_libraries( + test_jsonrpc ts::tsutil catch2::catch2 ts::rpcpublichandlers ts::jsonrpc_protocol libswoc::libswoc + ) add_test(NAME test_jsonrpc COMMAND test_jsonrpc) add_executable( diff --git a/src/mgmt/rpc/handlers/common/ErrorUtils.cc b/src/mgmt/rpc/handlers/common/ErrorUtils.cc new file mode 100644 index 0000000000..2b9bcad7d9 --- /dev/null +++ b/src/mgmt/rpc/handlers/common/ErrorUtils.cc @@ -0,0 +1,69 @@ +/** + @section license License + + Licensed to the Apache Software Foundation (ASF) under one + or more contributor license agreements. See the NOTICE file + distributed with this work for additional information + regarding copyright ownership. The ASF licenses this file + to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance + with the License. You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +*/ +#include "mgmt/rpc/handlers/common/ErrorUtils.h" + +#include <system_error> +#include <string> + +namespace +{ // anonymous namespace + +struct RPCHandlerLogicErrorCategory : std::error_category { + const char *name() const noexcept override; + std::string message(int ev) const override; +}; + +const char * +RPCHandlerLogicErrorCategory::name() const noexcept +{ + return "rpc_handler_logic_error"; +} +std::string +RPCHandlerLogicErrorCategory::message(int ev) const +{ + switch (static_cast<rpc::handlers::errors::Codes>(ev)) { + case rpc::handlers::errors::Codes::CONFIGURATION: + return {"Configuration handling error."}; + case rpc::handlers::errors::Codes::METRIC: + return {"Metric handling error."}; + case rpc::handlers::errors::Codes::RECORD: + return {"Record handling error."}; + case rpc::handlers::errors::Codes::SERVER: + return {"Server handling error."}; + case rpc::handlers::errors::Codes::STORAGE: + return {"Storage handling error."}; + case rpc::handlers::errors::Codes::PLUGIN: + return {"Plugin handling error."}; + default: + return "Generic handling error: " + std::to_string(ev); + } +} + +const RPCHandlerLogicErrorCategory rpcHandlerLogicErrorCategory{}; +} // anonymous namespace + +namespace rpc::handlers::errors +{ +std::error_code +make_error_code(rpc::handlers::errors::Codes e) +{ + return {static_cast<int>(e), rpcHandlerLogicErrorCategory}; +} +} // namespace rpc::handlers::errors diff --git a/src/mgmt/rpc/handlers/config/Configuration.cc b/src/mgmt/rpc/handlers/config/Configuration.cc index 773532553d..9a0a0b80e0 100644 --- a/src/mgmt/rpc/handlers/config/Configuration.cc +++ b/src/mgmt/rpc/handlers/config/Configuration.cc @@ -102,10 +102,10 @@ namespace } } // namespace -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> set_config_records(std::string_view const &id, YAML::Node const ¶ms) { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; // we need the type and the update type for now. using LookupContext = std::tuple<RecDataT, RecCheckT, const char *, RecUpdateT>; @@ -115,7 +115,7 @@ set_config_records(std::string_view const &id, YAML::Node const ¶ms) try { info = kv.as<SetRecordCmdInfo>(); } catch (YAML::Exception const &ex) { - resp.errata().push({err::RecordError::RECORD_NOT_FOUND}); + resp.errata().assign(std::error_code{errors::Codes::RECORD}).note("{}", std::error_code{err::RecordError::RECORD_NOT_FOUND}); continue; } @@ -139,7 +139,7 @@ set_config_records(std::string_view const &id, YAML::Node const ¶ms) // make sure if exist. If not, we stop it and do not keep forward. if (ret != REC_ERR_OKAY) { - resp.errata().push({err::RecordError::RECORD_NOT_FOUND}); + resp.errata().assign(std::error_code{errors::Codes::RECORD}).note("{}", std::error_code{err::RecordError::RECORD_NOT_FOUND}); continue; } @@ -148,7 +148,9 @@ set_config_records(std::string_view const &id, YAML::Node const ¶ms) // run the check only if we have something to check against it. if (pattern != nullptr && utils::recordValidityCheck(info.value.c_str(), checkType, pattern) == false) { - resp.errata().push({err::RecordError::VALIDITY_CHECK_ERROR}); + resp.errata() + .assign(std::error_code{errors::Codes::RECORD}) + .note("{}", std::error_code{err::RecordError::VALIDITY_CHECK_ERROR}); continue; } @@ -173,7 +175,7 @@ set_config_records(std::string_view const &id, YAML::Node const ¶ms) updatedRecord[utils::RECORD_UPDATE_TYPE_KEY] = std::to_string(updateType); resp.result().push_back(updatedRecord); } else { - resp.errata().push({err::RecordError::GENERAL_ERROR}); + resp.errata().assign(std::error_code{errors::Codes::RECORD}).note("{}", std::error_code{err::RecordError::GENERAL_ERROR}); continue; } } @@ -181,17 +183,17 @@ set_config_records(std::string_view const &id, YAML::Node const ¶ms) return resp; } -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> reload_config(std::string_view const &id, YAML::Node const ¶ms) { ts::Metrics &metrics = ts::Metrics::instance(); static auto reconf_time = metrics.lookup("proxy.process.proxy.reconfigure_time"); static auto reconf_req = metrics.lookup("proxy.process.proxy.reconfigure_required"); - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; Debug("RPC", "invoke plugin callbacks"); // if there is any error, report it back. - if (auto err = FileManager::instance().rereadConfig(); err.size()) { - resp = err; + if (auto err = FileManager::instance().rereadConfig(); !err.empty()) { + resp.note(err); } // If any callback was register(TSMgmtUpdateRegister) for config notifications, then it will be eventually notify. FileManager::instance().invokeConfigPluginCallbacks(); diff --git a/src/mgmt/rpc/handlers/plugins/Plugins.cc b/src/mgmt/rpc/handlers/plugins/Plugins.cc index b5e130ac3a..9cd4eb565e 100644 --- a/src/mgmt/rpc/handlers/plugins/Plugins.cc +++ b/src/mgmt/rpc/handlers/plugins/Plugins.cc @@ -56,7 +56,7 @@ namespace rpc::handlers::plugins { namespace err = rpc::handlers::errors; -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> plugin_send_basic_msg(std::string_view const &id, YAML::Node const ¶ms) { // The rpc could be ready before plugins are initialized. @@ -65,7 +65,7 @@ plugin_send_basic_msg(std::string_view const &id, YAML::Node const ¶ms) return err::make_errata(err::Codes::PLUGIN, "Plugin is not yet ready to handle any messages."); } - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; try { // keep the data. PluginMsgInfo info = params.as<PluginMsgInfo>(); diff --git a/src/mgmt/rpc/handlers/records/Records.cc b/src/mgmt/rpc/handlers/records/Records.cc index a1f3f809f9..de49f8aae5 100644 --- a/src/mgmt/rpc/handlers/records/Records.cc +++ b/src/mgmt/rpc/handlers/records/Records.cc @@ -204,7 +204,7 @@ namespace rpc::handlers::records { namespace err = rpc::handlers::errors; -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> lookup_records(std::string_view const &id, YAML::Node const ¶ms) { // TODO: we may want to deal with our own object instead of a node here. @@ -247,19 +247,19 @@ lookup_records(std::string_view const &id, YAML::Node const ¶ms) return resp; } -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> clear_all_metrics_records(std::string_view const &id, YAML::Node const ¶ms) { using namespace rpc::handlers::records::utils; - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; if (RecResetStatRecord(RECT_NULL, true) != REC_ERR_OKAY) { - return ts::Errata{rpc::handlers::errors::RecordError::RECORD_WRITE_ERROR}; + return swoc::Errata{std::error_code{errors::Codes::METRIC}, "Failed to clear stats"}; } return resp; } -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> clear_metrics_records(std::string_view const &id, YAML::Node const ¶ms) { using namespace rpc::handlers::records::utils; diff --git a/src/mgmt/rpc/handlers/server/Server.cc b/src/mgmt/rpc/handlers/server/Server.cc index e6639cc6dd..8e74ed3098 100644 --- a/src/mgmt/rpc/handlers/server/Server.cc +++ b/src/mgmt/rpc/handlers/server/Server.cc @@ -79,10 +79,10 @@ set_server_drain(bool drain) metrics[drain_id].store(TSSystemState::is_draining() ? 1 : 0); } -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> server_start_drain(std::string_view const &id, YAML::Node const ¶ms) { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; try { if (!params.IsNull()) { DrainInfo di = params.as<DrainInfo>(); @@ -93,23 +93,23 @@ server_start_drain(std::string_view const &id, YAML::Node const ¶ms) if (!is_server_draining()) { set_server_drain(true); } else { - resp.errata().push(err::make_errata(err::Codes::SERVER, "Server already draining.")); + resp.errata().assign(std::error_code{errors::Codes::SERVER}).note("Server already draining."); } } catch (std::exception const &ex) { Debug("rpc.handler.server", "Got an error DrainInfo decoding: %s", ex.what()); - resp.errata().push(err::make_errata(err::Codes::SERVER, "Error found during server drain: {}", ex.what())); + resp.errata().assign(std::error_code{errors::Codes::SERVER}).note("Error found during server drain: {}", ex.what()); } return resp; } -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> server_stop_drain(std::string_view const &id, [[maybe_unused]] YAML::Node const ¶ms) { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; if (is_server_draining()) { set_server_drain(false); } else { - resp.errata().push(err::make_errata(err::Codes::SERVER, "Server is not draining.")); + resp.errata().assign(std::error_code{errors::Codes::SERVER}).note("Server is not draining."); } return resp; diff --git a/src/mgmt/rpc/handlers/storage/Storage.cc b/src/mgmt/rpc/handlers/storage/Storage.cc index 10619caab6..0a66c7f500 100644 --- a/src/mgmt/rpc/handlers/storage/Storage.cc +++ b/src/mgmt/rpc/handlers/storage/Storage.cc @@ -57,10 +57,10 @@ namespace rpc::handlers::storage { namespace err = rpc::handlers::errors; -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> set_storage_offline(std::string_view const &id, YAML::Node const ¶ms) { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; for (auto &&it : params) { std::string device = it.as<std::string>(); @@ -75,16 +75,18 @@ set_storage_offline(std::string_view const &id, YAML::Node const ¶ms) n["has_online_storage_left"] = ret ? "true" : "false"; resp.result().push_back(std::move(n)); } else { - resp.errata().push(err::make_errata(err::Codes::STORAGE, "Passed device:'{}' does not match any defined storage", device)); + resp.errata() + .assign(std::error_code{errors::Codes::STORAGE}) + .note("Passed device: '{}' does not match any defined storage", device); } } return resp; } -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> get_storage_status(std::string_view const &id, YAML::Node const ¶ms) { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; for (auto &&it : params) { std::string device = it.as<std::string>(); @@ -93,7 +95,9 @@ get_storage_status(std::string_view const &id, YAML::Node const ¶ms) if (d) { resp.result().push_back(*d); } else { - resp.errata().push(err::make_errata(err::Codes::STORAGE, "Passed device:'{}' does not match any defined storage", device)); + resp.errata() + .assign(std::error_code{errors::Codes::STORAGE}) + .note("Passed device: '{}' does not match any defined storage", device); } } return resp; diff --git a/src/mgmt/rpc/jsonrpc/Context.cc b/src/mgmt/rpc/jsonrpc/Context.cc index 4c7d790691..f4f19682f4 100644 --- a/src/mgmt/rpc/jsonrpc/Context.cc +++ b/src/mgmt/rpc/jsonrpc/Context.cc @@ -22,10 +22,10 @@ namespace rpc { // --- Call Context impl -ts::Errata +swoc::Errata Context::Auth::is_blocked(TSRPCHandlerOptions const &options) const { - ts::Errata out; + swoc::Errata out; // check every registered callback and see if they have something to say. Then report back to the manager for (auto &&check : _checkers) { check(options, out); diff --git a/src/mgmt/rpc/jsonrpc/JsonRPCManager.cc b/src/mgmt/rpc/jsonrpc/JsonRPCManager.cc index b05e35279c..dbe695934b 100644 --- a/src/mgmt/rpc/jsonrpc/JsonRPCManager.cc +++ b/src/mgmt/rpc/jsonrpc/JsonRPCManager.cc @@ -27,6 +27,7 @@ #include <condition_variable> #include <swoc/swoc_meta.h> + #include "mgmt/rpc/jsonrpc/json/YAMLCodec.h" namespace @@ -57,15 +58,16 @@ RPCRegistryInfo core_ats_rpc_service_provider_handle = { // plugin rpc handling variables. std::mutex g_rpcHandlingMutex; std::condition_variable g_rpcHandlingCompletion; -ts::Rv<YAML::Node> g_rpcHandlerResponseData; +swoc::Rv<YAML::Node> g_rpcHandlerResponseData; bool g_rpcHandlerProcessingCompleted{false}; // --- Helpers -std::pair<ts::Errata, error::RPCErrorCode> +swoc::Errata check_for_blockers(Context const &ctx, TSRPCHandlerOptions const &options) { - if (auto err = ctx.get_auth().is_blocked(options); !err.isOK()) { - return {err, error::RPCErrorCode::Unauthorized}; + if (auto err = ctx.get_auth().is_blocked(options); !err.is_ok()) { + return std::move(err.note(swoc::Errata(std::error_code(unsigned(error::RPCErrorCode::Unauthorized), std::generic_category()), + ERRATA_ERROR, swoc::Errata::AUTO))); } return {}; } @@ -80,7 +82,7 @@ JsonRPCManager::Dispatcher::register_service_descriptor_handler() { if (!this->add_handler<Dispatcher::Method, MethodHandlerSignature>( "show_registered_handlers", - [this](std::string_view const &id, const YAML::Node &req) -> ts::Rv<YAML::Node> { + [this](std::string_view const &id, const YAML::Node &req) -> swoc::Rv<YAML::Node> { return show_registered_handlers(id, req); }, &core_ats_rpc_service_provider_handle, {{NON_RESTRICTED_API}})) { @@ -89,7 +91,9 @@ JsonRPCManager::Dispatcher::register_service_descriptor_handler() if (!this->add_handler<Dispatcher::Method, MethodHandlerSignature>( "get_service_descriptor", - [this](std::string_view const &id, const YAML::Node &req) -> ts::Rv<YAML::Node> { return get_service_descriptor(id, req); }, + [this](std::string_view const &id, const YAML::Node &req) -> swoc::Rv<YAML::Node> { + return get_service_descriptor(id, req); + }, &core_ats_rpc_service_provider_handle, {{NON_RESTRICTED_API}})) { Warning("Handler already registered."); } @@ -108,10 +112,10 @@ JsonRPCManager::Dispatcher::dispatch(Context const &ctx, specs::RPCRequestInfo c } // We have got a valid handler, we will now check if the context holds any restriction for this handler to be called. - if (auto &&[errata, ec] = check_for_blockers(ctx, handler.get_options()); !errata.isOK()) { + if (auto errata = check_for_blockers(ctx, handler.get_options()); !errata.is_ok()) { specs::RPCResponseInfo resp{request.id}; resp.error.ec = ec; - resp.error.data = errata; + resp.error.data = std::move(errata); return resp; } @@ -152,13 +156,13 @@ JsonRPCManager::Dispatcher::invoke_method_handler(JsonRPCManager::Dispatcher::In specs::RPCResponseInfo response{request.id}; try { - auto const &rv = handler.invoke(request); + auto rv = handler.invoke(request); - if (rv.isOK()) { + if (rv.is_ok()) { response.callResult.result = rv.result(); } else { // if we have some errors to log, then include it. - response.callResult.errata = rv.errata(); + response.callResult.errata = std::move(rv.errata()); } } catch (std::exception const &e) { Debug(logTag, "Oops, something happened during the callback invocation: %s", e.what()); @@ -238,6 +242,7 @@ JsonRPCManager::handle_call(Context const &ctx, std::string const &request) } else { // If the request was marked as an error(decode error), we still need to send the error back, so we save it. specs::RPCResponseInfo resp{req.id}; + // resp.error.assign(swoc::Errata(decode_error)); resp.error.ec = decode_error; response.add_message(std::move(resp)); } @@ -250,20 +255,22 @@ JsonRPCManager::handle_call(Context const &ctx, std::string const &request) resp = Encoder::encode(response); Debug(logTagMsg, "<-- JSONRPC Response\n '%s'", (*resp).c_str()); } + return resp; } catch (std::exception const &ex) { ec = error::RPCErrorCode::INTERNAL_ERROR; } + specs::RPCResponseInfo resp; resp.error.ec = ec; return {Encoder::encode(resp)}; } // ---------------------------- InternalHandler --------------------------------- -inline ts::Rv<YAML::Node> +inline swoc::Rv<YAML::Node> JsonRPCManager::Dispatcher::InternalHandler::invoke(specs::RPCRequestInfo const &request) const { - ts::Rv<YAML::Node> ret; + swoc::Rv<YAML::Node> ret; std::visit(swoc::meta::vary{[](std::monostate) -> void { /* no op */ }, [&request](Notification const &handler) -> void { // Notification handler call. Ignore response, there is no completion cv check in here basically @@ -284,11 +291,11 @@ JsonRPCManager::Dispatcher::InternalHandler::invoke(specs::RPCRequestInfo const std::unique_lock<std::mutex> lock(g_rpcHandlingMutex); g_rpcHandlingCompletion.wait(lock, []() { return g_rpcHandlerProcessingCompleted; }); g_rpcHandlerProcessingCompleted = false; - // seems to be done, set the response. As the response data is a ts::Rv this will handle both, + // seems to be done, set the response. As the response data is a swoc::Rv this will handle both, // error and non error cases. - ret = g_rpcHandlerResponseData; + ret = std::move(g_rpcHandlerResponseData); // clean up the shared data. - g_rpcHandlerResponseData.clear(); + // g_rpcHandlerResponseData.clear(); // moved so no cleanup? lock.unlock(); }}, this->_func); @@ -310,10 +317,10 @@ JsonRPCManager::Dispatcher::InternalHandler::is_method() const return false; } -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> JsonRPCManager::Dispatcher::show_registered_handlers(std::string_view const &, const YAML::Node &) { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; std::lock_guard<std::mutex> lock(_mutex); for (auto const &[name, handler] : _handlers) { std::string const &key = handler.is_method() ? RPC_SERVICE_METHODS_KEY : RPC_SERVICE_NOTIFICATIONS_KEY; @@ -324,7 +331,7 @@ JsonRPCManager::Dispatcher::show_registered_handlers(std::string_view const &, c // ----------------------------------------------------------------------------- // This jsonrpc handler can provides a service descriptor for the RPC -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> JsonRPCManager::Dispatcher::get_service_descriptor(std::string_view const &, const YAML::Node &) { YAML::Node rpcService; diff --git a/src/mgmt/rpc/jsonrpc/unit_tests/test_basic_protocol.cc b/src/mgmt/rpc/jsonrpc/unit_tests/test_basic_protocol.cc index 117c597f20..68e1146751 100644 --- a/src/mgmt/rpc/jsonrpc/unit_tests/test_basic_protocol.cc +++ b/src/mgmt/rpc/jsonrpc/unit_tests/test_basic_protocol.cc @@ -28,8 +28,6 @@ namespace { -const int ErratId{1}; - // Not using the singleton logic. struct JsonRpcUnitTest : rpc::JsonRPCManager { JsonRpcUnitTest() : JsonRPCManager() {} @@ -60,16 +58,20 @@ struct JsonRpcUnitTest : rpc::JsonRPCManager { }; enum class TestErrors { ERR1 = 9999, ERR2 }; -inline ts::Rv<YAML::Node> +static const std::error_code ERR1{ts::make_errno_code(9999)}; +static const std::error_code ERR2{ts::make_errno_code(10000)}; +static std::string_view err{"Just an error message to add more meaning to the failure"}; + +inline swoc::Rv<YAML::Node> test_callback_ok_or_error(std::string_view const &id, YAML::Node const ¶ms) { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; // play with the req.id if needed. if (YAML::Node n = params["return_error"]) { auto yesOrNo = n.as<std::string>(); if (yesOrNo == "yes") { - resp.errata().push(ErratId, static_cast<int>(TestErrors::ERR1), "Just an error message to add more meaning to the failure"); + resp.errata().assign(ERR1).note(err); } else { resp.result()["ran"] = "ok"; } @@ -334,12 +336,12 @@ TEST_CASE("Basic test with member functions(add, remove)", "[basic][member_funct { return rpc.add_method_handler( "member_function", - [this](std::string_view const &id, const YAML::Node &req) -> ts::Rv<YAML::Node> { return test(id, req); }); + [this](std::string_view const &id, const YAML::Node &req) -> swoc::Rv<YAML::Node> { return test(id, req); }); } - ts::Rv<YAML::Node> + swoc::Rv<YAML::Node> test(std::string_view const &id, const YAML::Node &req) { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; resp.result() = "grand!"; return resp; } @@ -379,10 +381,10 @@ TEST_CASE("Test Dispatcher rpc method", "[dispatcher]") R"({"jsonrpc": "2.0", "result": {"methods": ["get_service_descriptor", "show_registered_handlers"]}, "id": "AbC"})"); } -[[maybe_unused]] static ts::Rv<YAML::Node> +[[maybe_unused]] static swoc::Rv<YAML::Node> subtract(std::string_view const &id, YAML::Node const &numbers) { - ts::Rv<YAML::Node> res; + swoc::Rv<YAML::Node> res; if (numbers.Type() == YAML::NodeType::Sequence) { auto it = numbers.begin(); @@ -402,10 +404,10 @@ subtract(std::string_view const &id, YAML::Node const &numbers) return res; } -[[maybe_unused]] static ts::Rv<YAML::Node> +[[maybe_unused]] static swoc::Rv<YAML::Node> sum(std::string_view const &id, YAML::Node const ¶ms) { - ts::Rv<YAML::Node> res; + swoc::Rv<YAML::Node> res; int total{0}; for (auto n : params) { total += n.as<int>(); @@ -414,10 +416,10 @@ sum(std::string_view const &id, YAML::Node const ¶ms) return res; } -[[maybe_unused]] static ts::Rv<YAML::Node> +[[maybe_unused]] static swoc::Rv<YAML::Node> get_data(std::string_view const &id, YAML::Node const ¶ms) { - ts::Rv<YAML::Node> res; + swoc::Rv<YAML::Node> res; res.result().push_back("hello"); res.result().push_back("5"); return res; @@ -555,10 +557,10 @@ TEST_CASE("Handle un-handle handler's error", "[throw]") JsonRpcUnitTest rpc; SECTION("Basic exception thrown") { - REQUIRE( - rpc.add_method_handler("oops_i_did_it_again", [](std::string_view const &id, const YAML::Node ¶ms) -> ts::Rv<YAML::Node> { - throw std::runtime_error("Oops, I did it again"); - })); + REQUIRE(rpc.add_method_handler("oops_i_did_it_again", + [](std::string_view const &id, const YAML::Node ¶ms) -> swoc::Rv<YAML::Node> { + throw std::runtime_error("Oops, I did it again"); + })); const auto resp = rpc.handle_call(R"({"jsonrpc": "2.0", "method": "oops_i_did_it_again", "id": "1"})"); std::string_view expected = R"({"jsonrpc": "2.0", "error": {"code": 9, "message": "Error during execution"}, "id": "1"})"; REQUIRE(*resp == expected); @@ -570,10 +572,10 @@ TEST_CASE("Call registered method with no ID", "[no-id]") JsonRpcUnitTest rpc; SECTION("Basic test, no id on method call") { - REQUIRE( - rpc.add_method_handler("call_me_with_no_id", [](std::string_view const &id, const YAML::Node ¶ms) -> ts::Rv<YAML::Node> { - throw std::runtime_error("Oops, I did it again"); - })); + REQUIRE(rpc.add_method_handler("call_me_with_no_id", + [](std::string_view const &id, const YAML::Node ¶ms) -> swoc::Rv<YAML::Node> { + throw std::runtime_error("Oops, I did it again"); + })); const auto resp = rpc.handle_call(R"({"jsonrpc": "2.0", "method": "call_me_with_no_id"})"); std::string_view expected = R"({"jsonrpc": "2.0", "error": {"code": -32600, "message": "Invalid Request"}})"; REQUIRE(*resp == expected); diff --git a/src/mgmt/rpc/server/IPCSocketServer.cc b/src/mgmt/rpc/server/IPCSocketServer.cc index 3b6e3b568f..f476bb0183 100644 --- a/src/mgmt/rpc/server/IPCSocketServer.cc +++ b/src/mgmt/rpc/server/IPCSocketServer.cc @@ -226,7 +226,7 @@ IPCSocketServer::run() rpc::Context ctx; // we want to make sure the peer's credentials are ok. ctx.get_auth().add_checker( - [&](TSRPCHandlerOptions const &opt, ts::Errata &errata) -> void { return late_check_peer_credentials(fd, opt, errata); }); + [&](TSRPCHandlerOptions const &opt, swoc::Errata &errata) -> void { late_check_peer_credentials(fd, opt, errata); }); if (auto response = rpc::JsonRPCManager::instance().handle_call(ctx, json); response) { // seems a valid response. @@ -446,19 +446,23 @@ IPCSocketServer::Config::Config() } void -IPCSocketServer::late_check_peer_credentials(int peedFd, TSRPCHandlerOptions const &options, ts::Errata &errata) const +IPCSocketServer::late_check_peer_credentials(int peedFd, TSRPCHandlerOptions const &options, swoc::Errata &errata) const { swoc::LocalBufferWriter<256> w; // For privileged calls, ensure we have caller credentials and that the caller is privileged. + auto ecode = [](UnauthorizedErrorCode c) -> std::error_code { + return std::error_code(static_cast<unsigned>(c), std::generic_category()); + }; + if (has_peereid() && options.auth.restricted) { uid_t euid = -1; gid_t egid = -1; if (get_peereid(peedFd, &euid, &egid) == -1) { - errata.push(1, static_cast<int>(UnauthorizedErrorCode::PEER_CREDENTIALS_ERROR), - w.print("Error getting peer credentials: {}\0", swoc::bwf::Errno{}).data()); + errata.assign(ecode(UnauthorizedErrorCode::PEER_CREDENTIALS_ERROR)) + .note("Error getting peer credentials: {}", swoc::bwf::Errno{}); } else if (euid != 0 && euid != geteuid()) { - errata.push(1, static_cast<int>(UnauthorizedErrorCode::PERMISSION_DENIED), - w.print("Denied privileged API access for uid={} gid={}\0", euid, egid).data()); + errata.assign(ecode(UnauthorizedErrorCode::PERMISSION_DENIED)) + .note("Denied privileged API access for uid={} gid={}", euid, egid); } } } diff --git a/src/mgmt/rpc/server/unit_tests/test_rpcserver.cc b/src/mgmt/rpc/server/unit_tests/test_rpcserver.cc index c08183e34a..c28abd21bc 100644 --- a/src/mgmt/rpc/server/unit_tests/test_rpcserver.cc +++ b/src/mgmt/rpc/server/unit_tests/test_rpcserver.cc @@ -45,7 +45,7 @@ #include "tscore/Layout.h" #include "iocore/utils/diags.i" -#define DEFINE_JSONRPC_PROTO_FUNCTION(fn) ts::Rv<YAML::Node> fn(std::string_view const &id, const YAML::Node ¶ms) +#define DEFINE_JSONRPC_PROTO_FUNCTION(fn) swoc::Rv<YAML::Node> fn(std::string_view const &id, const YAML::Node ¶ms) namespace fs = swoc::file; @@ -127,7 +127,7 @@ RPCServerTestListener::~RPCServerTestListener() {} DEFINE_JSONRPC_PROTO_FUNCTION(some_foo) // id, params { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; int dur{1}; try { dur = params["duration"].as<int>(); @@ -309,7 +309,7 @@ random_string(std::string::size_type length) DEFINE_JSONRPC_PROTO_FUNCTION(do_nothing) // id, params, resp { - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; resp.result()["size"] = params["msg"].as<std::string>().size(); return resp; } diff --git a/src/proxy/CMakeLists.txt b/src/proxy/CMakeLists.txt index 508ba7c1d8..6b25c6683d 100644 --- a/src/proxy/CMakeLists.txt +++ b/src/proxy/CMakeLists.txt @@ -47,7 +47,7 @@ endif() target_link_libraries( proxy PUBLIC ts::inkcache ts::inkevent ts::tsutil ts::tscore - PRIVATE ts::jsonrpc_protocol ts::inkutils ts::tsapibackend + PRIVATE ts::rpcpublichandlers ts::jsonrpc_protocol ts::inkutils ts::tsapibackend ) add_subdirectory(hdrs) diff --git a/src/proxy/HostStatus.cc b/src/proxy/HostStatus.cc index 121770fbe9..9e8e7fb540 100644 --- a/src/proxy/HostStatus.cc +++ b/src/proxy/HostStatus.cc @@ -44,8 +44,8 @@ struct HostCmdInfo { } // namespace -ts::Rv<YAML::Node> server_get_status(std::string_view const id, YAML::Node const ¶ms); -ts::Rv<YAML::Node> server_set_status(std::string_view const id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> server_get_status(std::string_view const id, YAML::Node const ¶ms); +swoc::Rv<YAML::Node> server_set_status(std::string_view const id, YAML::Node const ¶ms); HostStatRec::HostStatRec() : status(TS_HOST_STATUS_UP), @@ -419,11 +419,11 @@ template <> struct convert<HostCmdInfo> { } // namespace YAML // JSON-RPC method to retrieve host status information. -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> server_get_status(std::string_view id, YAML::Node const ¶ms) { namespace err = rpc::handlers::errors; - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; YAML::Node statusList{YAML::NodeType::Sequence}, errorList{YAML::NodeType::Sequence}; try { @@ -470,12 +470,12 @@ server_get_status(std::string_view id, YAML::Node const ¶ms) } // JSON-RPC method to mark up or down a host. -ts::Rv<YAML::Node> +swoc::Rv<YAML::Node> server_set_status(std::string_view id, YAML::Node const ¶ms) { Debug("host_statuses", "id=%s", id.data()); namespace err = rpc::handlers::errors; - ts::Rv<YAML::Node> resp; + swoc::Rv<YAML::Node> resp; try { if (!params.IsNull()) { @@ -489,7 +489,7 @@ server_set_status(std::string_view id, YAML::Node const ¶ms) hs.setHostStatus(name.c_str(), cmdInfo.type, cmdInfo.time, cmdInfo.reasonType); } } else { - resp.errata().push(err::make_errata(err::Codes::SERVER, "Invalid input parameters, null")); + resp.errata().assign(std::error_code{rpc::handlers::errors::Codes::SERVER}).note("Invalid input parameters, null"); } // schedule a write to the persistent store. @@ -497,7 +497,9 @@ server_set_status(std::string_view id, YAML::Node const ¶ms) eventProcessor.schedule_imm(new HostStatusSync, ET_TASK); } catch (std::exception const &ex) { Debug("host_statuses", "Got an error HostCmdInfo decoding: %s", ex.what()); - resp.errata().push(err::make_errata(err::Codes::SERVER, "Error found during host status set: {}", ex.what())); + resp.errata() + .assign(std::error_code{rpc::handlers::errors::Codes::SERVER}) + .note("Error found during host status set: {}", ex.what()); } return resp; } diff --git a/src/proxy/http/HttpConfig.cc b/src/proxy/http/HttpConfig.cc index aadc77e8a9..1cfde695b2 100644 --- a/src/proxy/http/HttpConfig.cc +++ b/src/proxy/http/HttpConfig.cc @@ -22,6 +22,8 @@ limitations under the License. */ +#include <deque> + #include "tscore/ink_config.h" #include "tscore/Filenames.h" #include "tscore/Tokenizer.h" diff --git a/src/traffic_cache_tool/CacheTool.cc b/src/traffic_cache_tool/CacheTool.cc index 6b187e2cd5..8d1b07652f 100644 --- a/src/traffic_cache_tool/CacheTool.cc +++ b/src/traffic_cache_tool/CacheTool.cc @@ -47,6 +47,7 @@ using swoc::MemSpan; using swoc::Errata; +using ts::make_errno_code; using ts::Bytes; using ts::Megabytes; diff --git a/src/tscore/CMakeLists.txt b/src/tscore/CMakeLists.txt index 68ed20e878..b7722e99a4 100644 --- a/src/tscore/CMakeLists.txt +++ b/src/tscore/CMakeLists.txt @@ -39,7 +39,6 @@ add_library( CryptoHash.cc Diags.cc Encoding.cc - Errata.cc EventNotify.cc Extendible.cc FrequencyCounter.cc @@ -145,7 +144,6 @@ if(BUILD_TESTING) unit_tests/test_AcidPtr.cc unit_tests/test_ArgParser.cc unit_tests/test_CryptoHash.cc - unit_tests/test_Errata.cc unit_tests/test_Extendible.cc unit_tests/test_Encoding.cc unit_tests/test_FrequencyCounter.cc diff --git a/src/tscore/Errata.cc b/src/tscore/Errata.cc deleted file mode 100644 index ded3bf5e80..0000000000 --- a/src/tscore/Errata.cc +++ /dev/null @@ -1,269 +0,0 @@ -/** @file - Errata implementation. - - @section license License - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. - */ - -#include "tscore/Errata.h" - -#include <iostream> -#include <sstream> -#include <iomanip> -#include <algorithm> -#include <memory.h> - -namespace ts -{ -/** List of sinks for abandoned erratum. - */ -namespace -{ - std::deque<Errata::Sink::Handle> Sink_List; -} - -std::string const Errata::DEFAULT_GLUE("\n"); -Errata::Message const Errata::NIL_MESSAGE; -Errata::Code Errata::Message::Default_Code = 0; -Errata::Message::SuccessTest const Errata::Message::DEFAULT_SUCCESS_TEST = &Errata::Message::isCodeZero; -Errata::Message::SuccessTest Errata::Message::Success_Test = Errata::Message::DEFAULT_SUCCESS_TEST; - -bool -Errata::Message::isCodeZero(Message const &msg) -{ - return msg.m_code == 0; -} - -void -Errata::Data::push(Message const &msg) -{ - m_items.push_back(msg); -} - -void -Errata::Data::push(Message &&msg) -{ - m_items.push_back(std::move(msg)); -} - -Errata::Message const & -Errata::Data::top() const -{ - return m_items.size() ? m_items.back() : NIL_MESSAGE; -} - -inline Errata::Errata(ImpPtr const &ptr) : m_data(ptr) {} - -Errata::Data::~Data() -{ - if (m_log_on_delete) { - Errata tmp(ImpPtr(this)); // because client API requires a wrapper. - for (auto &f : Sink_List) { - (*f)(tmp); - } - tmp.m_data.release(); // don't delete this again. - } -} - -Errata::Errata(self const &that) : m_data(that.m_data) {} - -Errata::Errata(self &&that) : m_data(that.m_data) {} - -Errata::Errata(std::string const &text) -{ - this->push(text); -} - -Errata::Errata(Id id, std::string const &text) -{ - this->push(id, text); -} - -Errata::~Errata() {} - -/* This forces the errata to have a data object that only it references. - If we're sharing the data, clone. If there's no data, allocate. - This is used just before a write operation to have copy on write semantics. - */ -Errata::Data * -Errata::pre_write() -{ - if (m_data) { - if (m_data.use_count() > 1) { - m_data.reset(new Data(*m_data)); // clone current data - } - } else { // create new data - m_data.reset(new Data); - } - return m_data.get(); -} - -// Just create an instance if needed. -Errata::Data const * -Errata::instance() -{ - if (!m_data) { - m_data.reset(new Data); - } - return m_data.get(); -} - -Errata & -Errata::push(Message const &msg) -{ - this->pre_write()->push(msg); - return *this; -} - -Errata & -Errata::push(Message &&msg) -{ - this->pre_write()->push(std::move(msg)); - return *this; -} - -Errata & -Errata::operator=(self const &that) -{ - m_data = that.m_data; - return *this; -} - -Errata & -Errata::operator=(Message const &msg) -{ - // Avoid copy on write in the case where we discard. - if (!m_data || m_data.use_count() > 1) { - this->clear(); - this->push(msg); - } else { - m_data->m_items.clear(); - m_data->push(msg); - } - return *this; -} - -Errata & -Errata::operator=(self &&that) -{ - m_data = that.m_data; - return *this; -} - -Errata & -Errata::pull(self &that) -{ - if (that.m_data) { - this->pre_write(); - m_data->m_items.insert(m_data->m_items.end(), that.m_data->m_items.begin(), that.m_data->m_items.end()); - that.m_data->m_items.clear(); - } - return *this; -} - -void -Errata::pop() -{ - if (m_data && m_data->size()) { - this->pre_write()->m_items.pop_front(); - } - return; -} - -void -Errata::clear() -{ - m_data.reset(nullptr); -} - -/* We want to allow iteration on empty / nil containers because that's very - convenient for clients. We need only return the same value for begin() - and end() and everything works as expected. - - However we need to be a bit more clever for VC 8. It checks for - iterator compatibility, i.e. that the iterators are not - invalidated and that they are for the same container. It appears - that default iterators are not compatible with anything. So we - use static container for the nil data case. - */ -static Errata::Container NIL_CONTAINER; - -Errata::iterator -Errata::begin() -{ - return m_data ? m_data->m_items.rbegin() : NIL_CONTAINER.rbegin(); -} - -Errata::const_iterator -Errata::begin() const -{ - return m_data ? static_cast<Data const &>(*m_data).m_items.rbegin() : static_cast<Container const &>(NIL_CONTAINER).rbegin(); -} - -Errata::iterator -Errata::end() -{ - return m_data ? m_data->m_items.rend() : NIL_CONTAINER.rend(); -} - -Errata::const_iterator -Errata::end() const -{ - return m_data ? static_cast<Data const &>(*m_data).m_items.rend() : static_cast<Container const &>(NIL_CONTAINER).rend(); -} - -void -Errata::registerSink(Sink::Handle const &s) -{ - Sink_List.push_back(s); -} - -std::ostream & -Errata::write(std::ostream &out, int offset, int indent, int shift, char const *lead) const -{ - for (auto m : *this) { - if ((offset + indent) > 0) { - out << std::setw(indent + offset) << std::setfill(' ') << ((indent > 0 && lead) ? lead : " "); - } - - out << m.m_id << " [" << m.m_code << "]: " << m.m_text << std::endl; - if (m.getErrata().size()) { - m.getErrata().write(out, offset, indent + shift, shift, lead); - } - } - return out; -} - -size_t -Errata::write(char *buff, size_t n, int offset, int indent, int shift, char const *lead) const -{ - std::ostringstream out; - std::string text; - this->write(out, offset, indent, shift, lead); - text = out.str(); - memcpy(buff, text.data(), std::min(n, text.size())); - return text.size(); -} - -std::ostream & -operator<<(std::ostream &os, Errata const &err) -{ - return err.write(os, 0, 0, 2, "> "); -} - -} // namespace ts diff --git a/src/tscore/unit_tests/test_Errata.cc b/src/tscore/unit_tests/test_Errata.cc deleted file mode 100644 index f19273a782..0000000000 --- a/src/tscore/unit_tests/test_Errata.cc +++ /dev/null @@ -1,60 +0,0 @@ -/** - @file Test for Errata - - @section license License - - Licensed to the Apache Software Foundation (ASF) under one - or more contributor license agreements. See the NOTICE file - distributed with this work for additional information - regarding copyright ownership. The ASF licenses this file - to you under the Apache License, Version 2.0 (the - "License"); you may not use this file except in compliance - with the License. You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. -*/ - -#include "catch.hpp" - -#include "tscore/Errata.h" - -TEST_CASE("Basic Errata with text only", "[errata]") -{ - ts::Errata err; - std::string text{"Some error text"}; - err.push(text); - REQUIRE(err.isOK()); // as code is 0 by default. - REQUIRE(err.top().text() == text); -} - -TEST_CASE("Basic Errata test with id and text", "[errata]") -{ - ts::Errata err; - int id{1}; - std::string text{"Some error text"}; - - err.push(id, text); - - REQUIRE(err.isOK()); // as code is 0 by default. - REQUIRE(err.top().text() == text); -} - -TEST_CASE("Basic Errata test with id,code and text", "[errata]") -{ - ts::Errata err; - int id{1}; - unsigned int code{2}; - std::string text{"Some error text"}; - - err.push(id, code, text); - - REQUIRE(!err.isOK()); // This should not be ok as code now is 2 - REQUIRE(err.top().getCode() == code); - REQUIRE(err.top().text() == text); -} diff --git a/src/tsutil/CMakeLists.txt b/src/tsutil/CMakeLists.txt index 0cd0ab8fd4..17f7f19b2d 100644 --- a/src/tsutil/CMakeLists.txt +++ b/src/tsutil/CMakeLists.txt @@ -37,6 +37,7 @@ set(TSUTIL_PUBLIC_HEADERS ${PROJECT_SOURCE_DIR}/include/tsutil/ts_meta.h ${PROJECT_SOURCE_DIR}/include/tsutil/ts_time_parser.h ${PROJECT_SOURCE_DIR}/include/tsutil/ts_unit_parser.h + ${PROJECT_SOURCE_DIR}/include/tsutil/ts_errata.h ) add_library( tsutil @@ -49,6 +50,7 @@ add_library( YamlCfg.cc ts_unit_parser.cc Regex.cc + ts_errata.cc ) add_library(ts::tsutil ALIAS tsutil) diff --git a/src/tsutil/ts_errata.cc b/src/tsutil/ts_errata.cc new file mode 100644 index 0000000000..fc10b11eb8 --- /dev/null +++ b/src/tsutil/ts_errata.cc @@ -0,0 +1,34 @@ +/** @file + + Utilities for @c swoc::Errata + + @section license License + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with this + work for additional information regarding copyright ownership. The ASF + licenses this file to you under the Apache License, Version 2.0 (the + "License"); you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, WITHOUT + WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the + License for the specific language governing permissions and limitations under + the License. +*/ + +#include "tsutil/ts_errata.h" + +namespace ts +{ +std::string +err_category::message(int code) const +{ + std::string s; + swoc::bwprint(s, "{}", swoc::bwf::Errno(code)); + return s; +} +} // namespace ts diff --git a/tests/gold_tests/jsonrpc/plugins/jsonrpc_plugin_handler_test.cc b/tests/gold_tests/jsonrpc/plugins/jsonrpc_plugin_handler_test.cc index 41d94bbe53..9bf7fe937f 100644 --- a/tests/gold_tests/jsonrpc/plugins/jsonrpc_plugin_handler_test.cc +++ b/tests/gold_tests/jsonrpc/plugins/jsonrpc_plugin_handler_test.cc @@ -27,9 +27,9 @@ #include <fstream> #include "swoc/swoc_file.h" +#include "tsutil/ts_bw_format.h" #include "yaml-cpp/yaml.h" -#include "tscore/Errata.h" #include "tscore/Layout.h" #include "tsutil/ts_bw_format.h" #include "mgmt/rpc/jsonrpc/JsonRPC.h"
