diff --git a/CMakeLists.txt b/CMakeLists.txt index 43682b6..c71d770 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -251,14 +251,14 @@ if (WITH_FESAPI) ${FETPAPI_FESAPI_SOURCES} ${FETPAPI_FESAPI_HEADERS} ) - target_include_directories(${PROJECT_NAME} PRIVATE ${HDF5_C_INCLUDE_DIR} ${FESAPI_INCLUDE_DIR}) + target_include_directories(${PROJECT_NAME} PRIVATE ${FESAPI_INCLUDE_DIR}) endif (WITH_FESAPI) target_sources(${PROJECT_NAME} PRIVATE ${ALL_SOURCES_AND_HEADERS}) -target_include_directories(${PROJECT_NAME} PRIVATE ${AVRO_INCLUDE_DIR} ${Boost_INCLUDE_DIR}) +target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE ${AVRO_INCLUDE_DIR} ${Boost_INCLUDE_DIR}) if (WITH_ETP_SSL) - target_include_directories(${PROJECT_NAME} PRIVATE ${OPENSSL_INCLUDE_DIR}) + target_include_directories(${PROJECT_NAME} SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) endif () target_include_directories(${PROJECT_NAME} INTERFACE @@ -284,6 +284,14 @@ endif (WIN32) add_subdirectory(doc) +if (WITH_FESAPI) + set (WITH_TEST OFF CACHE BOOL "Also build the unit tests.") + if (WITH_TEST) + enable_testing() + add_subdirectory(test) + endif (WITH_TEST) +endif (WITH_FESAPI) + # ============================================================================ # Install Fetpapi library # ============================================================================ diff --git a/cmake/swigEtp1_2Include.i.in b/cmake/swigEtp1_2Include.i.in index 533a83c..a996a07 100644 --- a/cmake/swigEtp1_2Include.i.in +++ b/cmake/swigEtp1_2Include.i.in @@ -142,8 +142,6 @@ typedef long long time_t; %{ #include "../src/etp/ClientSessionLaunchers.h" #include "../src/etp/EtpHelpers.h" -#include "../src/etp/PlainServerSession.h" -#include "../src/etp/Server.h" %} #ifdef WITH_FESAPI @@ -165,8 +163,6 @@ typedef long long time_t; %nspace ETP_NS::AbstractSession; %nspace ETP_NS::PlainClientSession; %nspace ETP_NS::InitializationParameters; - %nspace ETP_NS::ServerInitializationParameters; - %nspace ETP_NS::Server; #ifdef WITH_FESAPI %nspace ETP_NS::FesapiHdfProxyFactory; #endif @@ -1074,26 +1070,10 @@ namespace Energistics { %feature("director") ETP_NS::TransactionHandlers; %feature("director") ETP_NS::DataspaceHandlers; %feature("director") ETP_NS::InitializationParameters; -%feature("director") ETP_NS::ServerInitializationParameters; /* Following extensions aims at preventing the Python garbage collector from garbage collecting a protocol handler that may be still used by a session. */ #ifdef SWIGPYTHON - %fragment("server_initialization_parameters_reference_init", "init") { - server_initialization_parameters_reference(); - } - %fragment("server_initialization_parameters_reference_function", "header", fragment="server_initialization_parameters_reference_init") { - static PyObject *server_initialization_parameters_reference() { - static PyObject *server_initialization_parameters_reference_string = SWIG_Python_str_FromChar("__server_initialization_parameters_reference"); - return server_initialization_parameters_reference_string; - } - } - %extend ETP_NS::Server { - %typemap(ret, fragment="server_initialization_parameters_reference_function") void listen(ServerInitializationParameters* serverInitializationParams, int threadCount) %{ - PyObject_SetAttr($self, server_initialization_parameters_reference(), args); - %} - } - %fragment("core_handler_reference_init", "init") { core_handler_reference(); } @@ -1213,15 +1193,6 @@ namespace Energistics { #endif #ifdef SWIGCSHARP -%typemap(cscode) ETP_NS::Server %{ - private ServerInitializationParameters serverInitializationParametersReference; -%} - -%typemap(csin, - post=" serverInitializationParametersReference = $csinput;" - ) ETP_NS::ServerInitializationParameters* serverInitializationParams "ServerInitializationParameters.getCPtr($csinput)" - - %typemap(cscode) ETP_NS::AbstractSession %{ private CoreHandlers coreHandlersReference = null; private DiscoveryHandlers discoveryHandlersReference = null; @@ -1262,14 +1233,6 @@ namespace Energistics { #endif #ifdef SWIGJAVA -%typemap(javacode) ETP_NS::Server %{ - private ServerInitializationParameters serverInitializationParametersReference; -%} - -%typemap(javain, - post=" serverInitializationParametersReference = $javainput;" - ) ETP_NS::ServerInitializationParameters* serverInitializationParams "ServerInitializationParameters.getCPtr($javainput)" - %typemap(javacode) ETP_NS::AbstractSession %{ private CoreHandlers coreHandlersReference; private DiscoveryHandlers discoveryHandlersReference; @@ -1967,29 +1930,6 @@ namespace ETP_NS #endif } - /******************* SERVER ***************************/ - - class ServerInitializationParameters : public InitializationParameters - { - public: - ServerInitializationParameters(const std::string & serverUuid, const std::string & host, unsigned short port); - virtual ~ServerInitializationParameters(); - - virtual std::string getContactEmail() const; - virtual std::string getContactName() const; - virtual std::string getContactPhone() const; - virtual std::string getOrganizationName() const; - - virtual std::vector makeSupportedEncodings() const; - }; - - class Server - { - public: - Server(); - void listen(ServerInitializationParameters* serverInitializationParams, int threadCount); - }; - #ifdef WITH_FESAPI %typemap(javaimports) FesapiHdfProxyFactory %{ import com.f2i_consulting.fesapi.common.HdfProxyFactory; diff --git a/src/etp/AbstractClientSession.h b/src/etp/AbstractClientSession.h index 7264ad6..74b05b3 100644 --- a/src/etp/AbstractClientSession.h +++ b/src/etp/AbstractClientSession.h @@ -154,7 +154,7 @@ namespace ETP_NS { if (ec) { std::cerr << "on_handshake : " << ec.message() << std::endl; - std::cerr << "Sometimes some ETP server require a trailing slash at the end of their URL" << std::endl; + std::cerr << "Sometimes some ETP server require a trailing slash at the end of their URL. Did you also check your optional \"data-partition-id\" additional Header Field?" << std::endl; return; } diff --git a/src/etp/AbstractSession.cpp b/src/etp/AbstractSession.cpp index 35d6903..43105d1 100644 --- a/src/etp/AbstractSession.cpp +++ b/src/etp/AbstractSession.cpp @@ -112,6 +112,7 @@ void AbstractSession::on_read(boost::system::error_code ec, std::size_t bytes_tr } } // Scope for specificProtocolHandlersLock + size_t receivedMhProtocol = static_cast(receivedMh.protocol); if (specificProtocolHandler) { // Receive a message which has been asked to be processed with a specific protocol handler specificProtocolHandler->decodeMessageBody(receivedMh, d); @@ -121,13 +122,13 @@ void AbstractSession::on_read(boost::system::error_code ec, std::size_t bytes_tr specificProtocolHandlers.erase(specificProtocolHandlerIt); } } - else if (receivedMh.protocol < protocolHandlers.size() && protocolHandlers[receivedMh.protocol] != nullptr) { + else if (receivedMhProtocol < protocolHandlers.size() && protocolHandlers[receivedMhProtocol] != nullptr) { // Receive a message to be processed with a common protocol handler in case for example an unsollicited notification - protocolHandlers[receivedMh.protocol]->decodeMessageBody(receivedMh, d); + protocolHandlers[receivedMhProtocol]->decodeMessageBody(receivedMh, d); } else { - std::cerr << "Received a message with id " << receivedMh.messageId << " for which non protocol handlers is associated. Protocol " << receivedMh.protocol << std::endl; - send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(4, "The agent does not support the protocol " + std::to_string(receivedMh.protocol) + " identified in a message header."), receivedMh.messageId, 0x02); + std::cerr << "Received a message with id " << receivedMh.messageId << " for which non protocol handlers is associated. Protocol " << receivedMhProtocol << std::endl; + send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(4, "The agent does not support the protocol " + std::to_string(receivedMhProtocol) + " identified in a message header."), receivedMh.messageId, 0x02); } } flushReceivingBuffer(); @@ -311,7 +312,7 @@ std::string AbstractSession::startTransaction(std::vector dataspace throw std::logic_error("You did not register any transaction protocol handlers."); } if (handlers->isInAnActiveTransaction()) { - throw std::logic_error("You cannot start a transaction before the current transaction is rolled back or committed. ETP1.2 intentionally supports a single open transaction on a session."); + throw std::logic_error("You cannot start a transaction before the current transaction is rolled back or committed. ETP1.2 intentionally supports a single opened transaction on a session."); } Energistics::Etp::v12::Protocol::Transaction::StartTransaction startTransactionMsg; diff --git a/src/etp/EtpMessages.h b/src/etp/EtpMessages.h index 15a015c..2f2c62a 100644 --- a/src/etp/EtpMessages.h +++ b/src/etp/EtpMessages.h @@ -1946,7 +1946,7 @@ namespace Energistics { namespace v12 { namespace Datatypes { struct Uuid{ - std::array array; + std::array array{}; }; } } diff --git a/src/etp/PlainServerSession.cpp b/src/etp/PlainServerSession.cpp deleted file mode 100644 index e5f5908..0000000 --- a/src/etp/PlainServerSession.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/*----------------------------------------------------------------------- -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 "PlainServerSession.h" - -using namespace ETP_NS; - -PlainServerSession::PlainServerSession(tcp::socket socket, ServerInitializationParameters* serverInitializationParams) - : AbstractPlainOrSslServerSession(static_cast(socket.get_executor().context()), serverInitializationParams), - ws_(std::move(socket)) -{ - ws_.binary(true); -} diff --git a/src/etp/PlainServerSession.h b/src/etp/PlainServerSession.h deleted file mode 100644 index c01d313..0000000 --- a/src/etp/PlainServerSession.h +++ /dev/null @@ -1,43 +0,0 @@ -/*----------------------------------------------------------------------- -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 "AbstractPlainOrSslServerSession.h" - -namespace ETP_NS -{ - class PlainServerSession : public AbstractPlainOrSslServerSession - { - private: - websocket::stream ws_; - - public: - FETPAPI_DLL_IMPORT_OR_EXPORT PlainServerSession(tcp::socket socket, ServerInitializationParameters* serverInitializationParams); - - virtual ~PlainServerSession() = default; - - // Called by the base class - FETPAPI_DLL_IMPORT_OR_EXPORT websocket::stream& ws() { return ws_; } - - FETPAPI_DLL_IMPORT_OR_EXPORT bool run(boost::beast::http::request req) { - on_handshake(boost::system::error_code(), std::move(req)); - return true; - } - }; -} diff --git a/src/etp/ProtocolHandlers/CoreHandlers.cpp b/src/etp/ProtocolHandlers/CoreHandlers.cpp index 3ef9b9b..86f3475 100644 --- a/src/etp/ProtocolHandlers/CoreHandlers.cpp +++ b/src/etp/ProtocolHandlers/CoreHandlers.cpp @@ -18,12 +18,8 @@ under the License. -----------------------------------------------------------------------*/ #include "CoreHandlers.h" -#include "../PlainServerSession.h" -#ifdef WITH_ETP_SSL -#include "../ssl/SslServerSession.h" -#endif +#include "../AbstractSession.h" #include "../EtpHelpers.h" -#include "../ServerInitializationParameters.h" using namespace ETP_NS; @@ -101,103 +97,9 @@ void CoreHandlers::decodeMessageBody(const Energistics::Etp::v12::Datatypes::Mes } } -void CoreHandlers::on_RequestSession(const Energistics::Etp::v12::Protocol::Core::RequestSession & rs, int64_t correlationId) +void CoreHandlers::on_RequestSession(const Energistics::Etp::v12::Protocol::Core::RequestSession&, int64_t correlationId) { - ServerInitializationParameters const* serverInitializationParams = nullptr; - - PlainServerSession* pss = dynamic_cast(session); - if (pss != nullptr) { - serverInitializationParams = pss->getServerInitializationParameters(); - } -#ifdef WITH_ETP_SSL - else { - SslServerSession* sss = dynamic_cast(session); - if (sss != nullptr) { - serverInitializationParams = sss->getServerInitializationParameters(); - } - } -#endif - - if (serverInitializationParams == nullptr) { - std::cerr << "Request Session message must be received on a server session." << std::endl; - } - - // Check format - if (std::find(rs.supportedFormats.begin(), rs.supportedFormats.end(), "xml") == rs.supportedFormats.end()) { - session->send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(21, "Per the ETP1.2 official specification, \"xml\" format MUST BE supported."), correlationId, 0x02); - session->close(); - return; - } - - // Check requested protocols - auto supportedProtocols = serverInitializationParams->makeSupportedProtocols(); - std::vector requestedAndSupportedProtocols; - for (auto& rp : rs.requestedProtocols) { - const auto validatedProtocol = std::find_if(supportedProtocols.begin(), supportedProtocols.end(), - [rp](const Energistics::Etp::v12::Datatypes::SupportedProtocol & sp) -> bool { - return sp.protocol == rp.protocol && - sp.role == rp.role && - sp.protocolVersion.major == rp.protocolVersion.major && - sp.protocolVersion.minor == rp.protocolVersion.minor && - sp.protocolVersion.patch == rp.protocolVersion.patch && - sp.protocolVersion.revision == rp.protocolVersion.revision; - } - ); - if (validatedProtocol != std::end(supportedProtocols)) { - requestedAndSupportedProtocols.push_back(*validatedProtocol); - } - } - - if (requestedAndSupportedProtocols.empty()) { - session->send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(2, "The server does not support any of the requested protocols."), correlationId, 0x02); - session->close(); - return; - } - - // Check requested dataobjects - auto supportedDataobjects = serverInitializationParams->makeSupportedDataObjects(); - std::vector requestedAndSupportedDataObjects; - for (auto& rd : rs.supportedDataObjects) { - const auto validatedDataObject = std::find_if(supportedDataobjects.begin(), supportedDataobjects.end(), - [rd](const Energistics::Etp::v12::Datatypes::SupportedDataObject & sd) -> bool { - return sd.qualifiedType == rd.qualifiedType; - } - ); - if (validatedDataObject != std::end(supportedDataobjects)) { - requestedAndSupportedDataObjects.push_back(*validatedDataObject); - } - } - - // Check MaxWebSocketMessagePayloadSize endpoint capability - auto supportedEndPointCaps = serverInitializationParams->makeEndpointCapabilities(); - const auto requestedMaxWebSocketMessagePayloadSizeIt = rs.endpointCapabilities.find("MaxWebSocketMessagePayloadSize"); - if (requestedMaxWebSocketMessagePayloadSizeIt != rs.endpointCapabilities.end() && requestedMaxWebSocketMessagePayloadSizeIt->second.item.idx() == 3) { - const int64_t requestedMaxWebSocketMessagePayloadSize = requestedMaxWebSocketMessagePayloadSizeIt->second.item.get_long(); - if (requestedMaxWebSocketMessagePayloadSize > 0 && requestedMaxWebSocketMessagePayloadSize != session->getMaxWebSocketMessagePayloadSize()) { - session->setMaxWebSocketMessagePayloadSize(requestedMaxWebSocketMessagePayloadSize); - - Energistics::Etp::v12::Datatypes::DataValue value; - value.item.set_long(requestedMaxWebSocketMessagePayloadSize); - supportedEndPointCaps["MaxWebSocketFramePayloadSize"] = value; - supportedEndPointCaps["MaxWebSocketMessagePayloadSize"] = value; - } - } - - // Build Open Session message - Energistics::Etp::v12::Protocol::Core::OpenSession openSession; - openSession.applicationName = serverInitializationParams->getApplicationName(); - openSession.applicationVersion = serverInitializationParams->getApplicationVersion(); - std::copy(std::begin(session->getIdentifier().data), std::end(session->getIdentifier().data), openSession.sessionId.array.begin()); - std::copy(std::begin(serverInitializationParams->getInstanceId().data), std::end(serverInitializationParams->getInstanceId().data), openSession.serverInstanceId.array.begin()); - openSession.supportedFormats.push_back("xml"); - openSession.supportedProtocols = requestedAndSupportedProtocols; - openSession.endpointCapabilities = supportedEndPointCaps; - openSession.supportedDataObjects = requestedAndSupportedDataObjects; - openSession.currentDateTime = std::chrono::duration_cast(std::chrono::system_clock::now().time_since_epoch()).count(); - - session->send(openSession, correlationId, 0x02); - - session->fesapi_log("A new websocket session", session->getIdentifier(), "has been opened by a client"); + session->send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(1, "You cannot request a session to a client."), correlationId, 0x02); } void CoreHandlers::on_OpenSession(const Energistics::Etp::v12::Protocol::Core::OpenSession &, int64_t) diff --git a/src/etp/ProtocolHandlers/DataArrayHandlers.cpp b/src/etp/ProtocolHandlers/DataArrayHandlers.cpp index 432a144..cb10380 100644 --- a/src/etp/ProtocolHandlers/DataArrayHandlers.cpp +++ b/src/etp/ProtocolHandlers/DataArrayHandlers.cpp @@ -93,7 +93,7 @@ void DataArrayHandlers::decodeMessageBody(const Energistics::Etp::v12::Datatypes session->send(EtpHelpers::buildSingleMessageProtocolException(5, "The data array dimensions cannot be empty"), mh.messageId, 0x02); valid = false; } - for (auto dimIndex = 0; dimIndex < daMetadata.dimensions.size(); ++dimIndex) { + for (size_t dimIndex = 0; dimIndex < daMetadata.dimensions.size(); ++dimIndex) { if (daMetadata.dimensions[dimIndex] <= 0) { session->fesapi_log("A data array dimension cannot be <= 0"); session->send(EtpHelpers::buildSingleMessageProtocolException(5, "The data array dimension " + std::to_string(dimIndex) + " cannot be <= 0"), mh.messageId, 0x02); @@ -105,7 +105,7 @@ void DataArrayHandlers::decodeMessageBody(const Energistics::Etp::v12::Datatypes session->send(EtpHelpers::buildSingleMessageProtocolException(5, "The data array preferredSubarrayDimensions must be the same count as the data array dimensions"), mh.messageId, 0x02); valid = false; } - for (auto dimIndex = 0; dimIndex < daMetadata.preferredSubarrayDimensions.size(); ++dimIndex) { + for (size_t dimIndex = 0; dimIndex < daMetadata.preferredSubarrayDimensions.size(); ++dimIndex) { if (daMetadata.preferredSubarrayDimensions[dimIndex] <= 0 || daMetadata.preferredSubarrayDimensions[dimIndex] > daMetadata.dimensions[dimIndex]) { session->fesapi_log("A data array preferredSubarrayDimension is <=0 or > to the corresponding data array dimension"); session->send(EtpHelpers::buildSingleMessageProtocolException(5, "The data array preferredSubarrayDimension " + std::to_string(dimIndex) + " is <=0 or > to the corresponding data array dimension"), mh.messageId, 0x02); @@ -139,60 +139,61 @@ void DataArrayHandlers::on_GetDataArrays(const Energistics::Etp::v12::Protocol:: session->send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(7, "The DataArrayHandlers::on_GetDataArrays method has not been overriden by the agent."), correlationId, 0x02); } -void DataArrayHandlers::on_GetDataArraysResponse(const Energistics::Etp::v12::Protocol::DataArray::GetDataArraysResponse& msg, int64_t) +void DataArrayHandlers::on_GetDataArraysResponse(const Energistics::Etp::v12::Protocol::DataArray::GetDataArraysResponse& msg, int64_t correlationId) { + session->fesapi_log("Received GetDataArraysResponse to message id", std::to_string(correlationId)); for (const auto& element : msg.dataArrays) { const Energistics::Etp::v12::Datatypes::DataArrayTypes::DataArray& da = element.second; session->fesapi_log("Data Array received :"); session->fesapi_log("Dimension count :", da.dimensions.size()); - for (auto i = 0; i < da.dimensions.size(); ++i) { + for (size_t i = 0; i < da.dimensions.size(); ++i) { session->fesapi_log("Dimension", std::to_string(i), "with count :", da.dimensions[i]); } if (da.data.item.idx() == 0) { Energistics::Etp::v12::Datatypes::ArrayOfBoolean avroArray = da.data.item.get_ArrayOfBoolean(); auto values = avroArray.values; - for (auto i = 0; i < values.size() && i < 20; ++i) { + for (size_t i = 0; i < values.size() && i < 20; ++i) { session->fesapi_log("bool value", std::to_string(i), "==", std::to_string(values[i])); } } else if (da.data.item.idx() == 1) { Energistics::Etp::v12::Datatypes::ArrayOfInt avroArray = da.data.item.get_ArrayOfInt(); auto values = avroArray.values; - for (auto i = 0; i < values.size() && i < 20; ++i) { + for (size_t i = 0; i < values.size() && i < 20; ++i) { session->fesapi_log("int value", std::to_string(i), "==", std::to_string(values[i])); } } else if (da.data.item.idx() == 2) { Energistics::Etp::v12::Datatypes::ArrayOfLong avroArray = da.data.item.get_ArrayOfLong(); auto values = avroArray.values; - for (auto i = 0; i < values.size() && i < 20; ++i) { + for (size_t i = 0; i < values.size() && i < 20; ++i) { session->fesapi_log("int 64 bits value", std::to_string(i), "==", std::to_string(values[i])); } } else if (da.data.item.idx() == 3) { Energistics::Etp::v12::Datatypes::ArrayOfFloat avroArray = da.data.item.get_ArrayOfFloat(); auto values = avroArray.values; - for (auto i = 0; i < values.size() && i < 20; ++i) { + for (size_t i = 0; i < values.size() && i < 20; ++i) { session->fesapi_log("float value", std::to_string(i), "==", std::to_string(values[i])); } } else if (da.data.item.idx() == 4) { Energistics::Etp::v12::Datatypes::ArrayOfDouble avroArray = da.data.item.get_ArrayOfDouble(); auto values = avroArray.values; - for (auto i = 0; i < values.size() && i < 20; ++i) { + for (size_t i = 0; i < values.size() && i < 20; ++i) { session->fesapi_log("double value", std::to_string(i), "==", std::to_string(values[i])); } } else if (da.data.item.idx() == 5) { Energistics::Etp::v12::Datatypes::ArrayOfString avroArray = da.data.item.get_ArrayOfString(); auto values = avroArray.values; - for (auto i = 0; i < values.size() && i < 20; ++i) { + for (size_t i = 0; i < values.size() && i < 20; ++i) { session->fesapi_log("string value ", std::to_string(i), "==", values[i]); } } else if (da.data.item.idx() == 6) { std::string values = da.data.item.get_bytes(); - for (auto i = 0; i < values.size() && i < 20; ++i) { + for (size_t i = 0; i < values.size() && i < 20; ++i) { session->fesapi_log("char value ", std::to_string(i), "==", std::to_string(values[i])); } } @@ -204,9 +205,12 @@ void DataArrayHandlers::on_PutDataArrays(const Energistics::Etp::v12::Protocol:: session->send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(7, "The DataArrayHandlers::on_PutDataArrays method has not been overriden by the agent."), correlationId, 0x02); } -void DataArrayHandlers::on_PutDataArraysResponse(const Energistics::Etp::v12::Protocol::DataArray::PutDataArraysResponse&, int64_t correlationId) +void DataArrayHandlers::on_PutDataArraysResponse(const Energistics::Etp::v12::Protocol::DataArray::PutDataArraysResponse& response, int64_t correlationId) { session->fesapi_log("Received PutDataArraysResponse to message id", std::to_string(correlationId)); + for (const auto& element : response.success) { + session->fesapi_log(element.first + " -> " + element.second); + } } void DataArrayHandlers::on_GetDataSubarrays(const Energistics::Etp::v12::Protocol::DataArray::GetDataSubarrays&, int64_t correlationId) @@ -224,9 +228,12 @@ void DataArrayHandlers::on_PutDataSubarrays(const Energistics::Etp::v12::Protoco session->send(ETP_NS::EtpHelpers::buildSingleMessageProtocolException(7, "The DataArrayHandlers::on_PutDataArraySlices method has not been overriden by the agent."), correlationId, 0x02); } -void DataArrayHandlers::on_PutDataSubarraysResponse(const Energistics::Etp::v12::Protocol::DataArray::PutDataSubarraysResponse&, int64_t correlationId) +void DataArrayHandlers::on_PutDataSubarraysResponse(const Energistics::Etp::v12::Protocol::DataArray::PutDataSubarraysResponse& response, int64_t correlationId) { session->fesapi_log("Received PutDataSubarraysResponse to message id", std::to_string(correlationId)); + for (const auto& element : response.success) { + session->fesapi_log(element.first + " -> " + element.second); + } } void DataArrayHandlers::on_GetDataArrayMetadata(const Energistics::Etp::v12::Protocol::DataArray::GetDataArrayMetadata&, int64_t correlationId) @@ -275,7 +282,7 @@ void DataArrayHandlers::on_GetDataArrayMetadataResponse(const Energistics::Etp:: case Energistics::Etp::v12::Datatypes::AnyLogicalArrayType::arrayOfUInt8: session->fesapi_log("arrayOfUInt8"); break; default: session->fesapi_log("unrecognized logicalArrayType"); } - for (auto i = 0; i < daMetadata.dimensions.size(); ++i) { + for (size_t i = 0; i < daMetadata.dimensions.size(); ++i) { session->fesapi_log("Dimension", std::to_string(i), "with count : ", std::to_string(daMetadata.dimensions[i])); } } diff --git a/src/etp/ProtocolHandlers/TransactionHandlers.h b/src/etp/ProtocolHandlers/TransactionHandlers.h index 9bd83dd..29e4bdf 100644 --- a/src/etp/ProtocolHandlers/TransactionHandlers.h +++ b/src/etp/ProtocolHandlers/TransactionHandlers.h @@ -51,7 +51,7 @@ namespace ETP_NS const std::string& getLastTransactionFailure() const { return lastTransactionFailure; } private: /// ETP1.2 intentionally supports a single open transaction on a session. - Energistics::Etp::v12::Datatypes::Uuid transactionUuid = {}; + Energistics::Etp::v12::Datatypes::Uuid transactionUuid; std::string lastTransactionFailure; }; } diff --git a/src/etp/Server.h b/src/etp/Server.h deleted file mode 100644 index 8acc01f..0000000 --- a/src/etp/Server.h +++ /dev/null @@ -1,1263 +0,0 @@ -/*----------------------------------------------------------------------- -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 -#include -#include -#include -#include - -#include -#include -#ifdef WITH_ETP_SSL -#include -#endif -#include -#include -#include - -#include - -#include "avro/Compiler.hh" - -#include "PlainServerSession.h" -#ifdef WITH_ETP_SSL -#include "ssl/SslServerSession.h" -#endif - -namespace beast = boost::beast; // from -namespace http = beast::http; // from -#ifdef WITH_ETP_SSL -namespace ssl = boost::asio::ssl; // from -#endif -using tcp = boost::asio::ip::tcp; // from - -namespace ETP_NS -{ - // Return a reasonable mime type based on the extension of a file. - beast::string_view - mime_type(beast::string_view path) - { - using beast::iequals; - auto const ext = [&path] - { - auto const pos = path.rfind("."); - if (pos == beast::string_view::npos) - return beast::string_view{}; - return path.substr(pos); - }(); - if (iequals(ext, ".htm")) return "text/html"; - if (iequals(ext, ".html")) return "text/html"; - if (iequals(ext, ".php")) return "text/html"; - if (iequals(ext, ".css")) return "text/css"; - if (iequals(ext, ".txt")) return "text/plain"; - if (iequals(ext, ".js")) return "application/javascript"; - if (iequals(ext, ".json")) return "application/json"; - if (iequals(ext, ".xml")) return "application/xml"; - if (iequals(ext, ".swf")) return "application/x-shockwave-flash"; - if (iequals(ext, ".flv")) return "video/x-flv"; - if (iequals(ext, ".png")) return "image/png"; - if (iequals(ext, ".jpe")) return "image/jpeg"; - if (iequals(ext, ".jpeg")) return "image/jpeg"; - if (iequals(ext, ".jpg")) return "image/jpeg"; - if (iequals(ext, ".gif")) return "image/gif"; - if (iequals(ext, ".bmp")) return "image/bmp"; - if (iequals(ext, ".ico")) return "image/vnd.microsoft.icon"; - if (iequals(ext, ".tiff")) return "image/tiff"; - if (iequals(ext, ".tif")) return "image/tiff"; - if (iequals(ext, ".svg")) return "image/svg+xml"; - if (iequals(ext, ".svgz")) return "image/svg+xml"; - return "application/text"; - } - - // Append an HTTP rel-path to a local filesystem path. - // The returned path is normalized for the platform. - std::string - path_cat( - beast::string_view base, - beast::string_view path) - { - if (base.empty()) - return std::string(path.data()); - std::string result = std::string(base.data()); -#ifdef BOOST_MSVC - char constexpr path_separator = '\\'; - if (result.back() == path_separator) - result.resize(result.size() - 1); - result.append(path.data(), path.size()); - for (auto& c : result) - if (c == '/') - c = path_separator; -#else - char constexpr path_separator = '/'; - if (result.back() == path_separator) - result.resize(result.size() - 1); - result.append(path.data(), path.size()); -#endif - return result; - } - - // This function produces an HTTP response for the given - // request. The type of the response object depends on the - // contents of the request, so the interface requires the - // caller to pass a generic lambda for receiving the response. - template< - class Body, class Allocator, - class Send> - void - handle_request( - beast::string_view doc_root, - http::request>&& req, - Send&& send, ServerInitializationParameters* serverInitializationParams) - { - // Returns a bad request response - auto const bad_request = - [&req, serverInitializationParams](beast::string_view why) - { - http::response res{ http::status::bad_request, req.version() }; - res.set(http::field::server, serverInitializationParams->getApplicationName()); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = std::string(why); - res.prepare_payload(); - return res; - }; - - // Returns a not found response - auto const not_found = - [&req, serverInitializationParams](beast::string_view target) - { - http::response res{ http::status::not_found, req.version() }; - res.set(http::field::server, serverInitializationParams->getApplicationName()); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "The resource '" + std::string(target) + "' was not found."; - res.prepare_payload(); - return res; - }; - - // Returns an unprocessable_entity response - auto const unprocessable_entity = - [&req, serverInitializationParams](beast::string_view) - { - http::response res{ http::status::unprocessable_entity, req.version() }; - res.set(http::field::server, serverInitializationParams->getApplicationName()); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "This server does not support ETP1.1. Add a query string ?GetVersion=etp12.energistics.org if you want to discover these server capabilities."; - res.prepare_payload(); - return res; - }; - - // Returns a server error response - auto const server_error = - [&req, serverInitializationParams](beast::string_view what) - { - http::response res{ http::status::internal_server_error, req.version() }; - res.set(http::field::server, serverInitializationParams->getApplicationName()); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req.keep_alive()); - res.body() = "An error occurred: '" + std::string(what) + "'"; - res.prepare_payload(); - return res; - }; - - // Make sure we can handle the method - if (req.method() != http::verb::get && - req.method() != http::verb::head) - return send(bad_request("Unknown HTTP-method")); - - // Request path must be absolute and not contain "..". - if (req.target().empty() || - req.target()[0] != '/' || - req.target().find("..") != beast::string_view::npos) - return send(bad_request("Illegal request-target")); - - // Build the path to the requested file - std::string path = path_cat(doc_root, req.target()); - if (req.target().back() == '/') - path.append("index.html"); - - // Attempt to open the file - beast::error_code ec; - http::string_body::value_type body; - /* - body.open(path.c_str(), beast::file_mode::scan, ec); - - // Handle the case where the file doesn't exist - if (ec == boost::system::errc::no_such_file_or_directory) - return send(not_found(req.target())); - */ - - while (path.find("//") != std::string::npos) { - path = std::regex_replace(path, std::regex("//"), std::string("/")); - } - while (path.find("\\\\") != std::string::npos) { - // See https://stackoverflow.com/questions/4025482/cant-escape-the-backslash-with-regex - path = std::regex_replace(path, std::regex("\\\\\\\\"), std::string("\\")); - } - - if (path == "./.well-known/etp-server-capabilities" || - path == ".\\.well-known\\etp-server-capabilities") { - return send(unprocessable_entity(req.target())); - } - - if (path != "./.well-known/etp-server-capabilities?GetVersion=etp12.energistics.org" && - path != ".\\.well-known\\etp-server-capabilities?GetVersion=etp12.energistics.org" && - path != "./.well-known/etp-server-capabilities?GetVersions=true" && - path != ".\\.well-known\\etp-server-capabilities?GetVersions=true") { - return send(not_found(req.target())); - } - - std::ostringstream oss; - - if (path == "./.well-known/etp-server-capabilities?GetVersion=etp12.energistics.org" || - path == ".\\.well-known\\etp-server-capabilities?GetVersion=etp12.energistics.org") { - avro::ValidSchema vs = avro::compileJsonSchemaFromString( - "{" - " \"type\": \"record\"," - " \"name\": \"ServerCapabilities\"," - " \"fields\": [" - " {" - " \"name\": \"applicationName\"," - " \"type\": \"string\"" - " }," - " {" - " \"name\": \"applicationVersion\"," - " \"type\": \"string\"" - " }," - " {" - " \"name\": \"contactInformation\"," - " \"type\": {" - " \"type\": \"record\"," - " \"name\": \"Contact\"," - " \"fields\": [" - " {" - " \"name\": \"organizationName\"," - " \"type\": \"string\"" - " }," - " {" - " \"name\": \"contactName\"," - " \"type\": \"string\"" - " }," - " {" - " \"name\": \"contactPhone\"," - " \"type\": \"string\"" - " }," - " {" - " \"name\": \"contactEmail\"," - " \"type\": \"string\"" - " }" - " ]" - " }" - " }," - " {" - " \"name\": \"supportedCompression\"," - " \"type\": {" - " \"type\": \"array\"," - " \"items\": \"string\"" - " }" - " }," - " {" - " \"name\": \"supportedEncodings\"," - " \"type\": {" - " \"type\": \"array\"," - " \"items\": \"string\"" - " }" - " }," - " {" - " \"name\": \"supportedFormats\"," - " \"type\": {" - " \"type\": \"array\"," - " \"items\": \"string\"" - " }" - " }," - " {" - " \"name\": \"supportedDataObjects\"," - " \"type\": {" - " \"type\": \"array\"," - " \"items\": {" - " \"type\": \"record\"," - " \"name\" : \"SupportedDataObject\"," - " \"fields\" : [" - " {" - " \"name\": \"qualifiedType\"," - " \"type\" : \"string\"" - " }," - " {" - " \"name\": \"dataObjectCapabilities\"," - " \"type\" : {" - " \"type\": \"map\"," - " \"values\": {" - " \"type\": \"record\"," - " \"name\": \"DataValue\"," - " \"fields\": [" - " {" - " \"name\": \"item\"," - " \"type\": [" - " \"null\"," - " \"boolean\"," - " \"int\"," - " \"long\"," - " \"float\"," - " \"double\"," - " \"string\"," - " \"bytes\"" - " ]" - " }" - " ]" - " }" - " }" - " }" - " ]" - " }" - " }" - " }," - " {" - " \"name\": \"supportedProtocols\"," - " \"type\": {" - " \"type\": \"array\"," - " \"items\": {" - " \"type\": \"record\"," - " \"name\": \"SupportedProtocol\"," - " \"fields\": [" - " {" - " \"name\": \"protocol\"," - " \"type\": \"int\"" - " }," - " {" - " \"name\": \"protocolVersion\"," - " \"type\": {" - " \"type\": \"record\"," - " \"name\": \"Version\"," - " \"fields\": [" - " {" - " \"name\": \"major\"," - " \"type\": \"int\"" - " }," - " {" - " \"name\": \"minor\"," - " \"type\": \"int\"" - " }," - " {" - " \"name\": \"revision\"," - " \"type\": \"int\"" - " }," - " {" - " \"name\": \"patch\"," - " \"type\": \"int\"" - " }" - " ]" - " }" - " }," - " {" - " \"name\": \"role\"," - " \"type\": \"string\"" - " }," - " {" - " \"name\": \"protocolCapabilities\"," - " \"type\": {" - " \"type\": \"map\"," - " \"values\": {" - " \"type\": \"record\"," - " \"name\": \"DataValue\"," - " \"fields\": [" - " {" - " \"name\": \"item\"," - " \"type\": [" - " \"null\"," - " \"boolean\"," - " \"int\"," - " \"long\"," - " \"float\"," - " \"double\"," - " \"string\"," - " \"bytes\"" - " ]" - " }" - " ]" - " }" - " }" - " }" - " ]" - " }" - " }" - " }," - " {" - " \"name\": \"endpointCapabilities\"," - " \"type\": {" - " \"type\": \"map\"," - " \"values\": {" - " \"type\": \"record\"," - " \"name\": \"DataValue\"," - " \"fields\": [" - " {" - " \"name\": \"item\"," - " \"type\": [" - " \"null\"," - " \"boolean\"," - " \"int\"," - " \"long\"," - " \"float\"," - " \"double\"," - " \"string\"," - " \"bytes\"" - " ]" - " }" - " ]" - " }" - " }" - " }" - " ]" - "}" - ); - - Energistics::Etp::v12::Datatypes::ServerCapabilities serverCap; - serverCap.applicationName = serverInitializationParams->getApplicationName(); - serverCap.applicationVersion = serverInitializationParams->getApplicationVersion(); - serverCap.contactInformation.contactEmail = serverInitializationParams->getContactEmail(); - serverCap.contactInformation.contactName = serverInitializationParams->getContactName(); - serverCap.contactInformation.contactPhone = serverInitializationParams->getContactPhone(); - serverCap.contactInformation.organizationName = serverInitializationParams->getOrganizationName(); - serverCap.endpointCapabilities = serverInitializationParams->makeEndpointCapabilities(); - serverCap.supportedEncodings = serverInitializationParams->makeSupportedEncodings(); - serverCap.supportedFormats.push_back("xml"); - serverCap.supportedDataObjects = serverInitializationParams->makeSupportedDataObjects(); - serverCap.supportedProtocols = serverInitializationParams->makeSupportedProtocols(); - - avro::OutputStreamPtr out = avro::ostreamOutputStream(oss); - avro::EncoderPtr e = avro::jsonPrettyEncoder(vs); - e->init(*out); - avro::encode(*e, serverCap); - e->flush(); - body = oss.str(); - } - else { - body = "[ \"etp12.energistics.org\" ]"; - } - - // Handle an unknown error - if (ec) - return send(server_error(ec.message())); - - // Cache the size since we need it after the move - auto const size = body.size(); - - // Respond to HEAD request - if (req.method() == http::verb::head) - { - http::response res{ http::status::ok, req.version() }; - res.set(http::field::server, serverInitializationParams->getApplicationName()); - res.set(http::field::content_type, mime_type("*.json")); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } - - // Respond to GET request - http::response res{ - std::piecewise_construct, - std::make_tuple(std::move(body)), - std::make_tuple(http::status::ok, req.version()) }; - res.set(http::field::server, serverInitializationParams->getApplicationName()); - res.set(http::field::content_type, mime_type("*.json")); - res.content_length(size); - res.keep_alive(req.keep_alive()); - return send(std::move(res)); - } - - class Server - { - private: - - ServerInitializationParameters* serverInitializationParams_; - std::vector< std::shared_ptr > sessions_; - - template - static void - make_websocket_session( - tcp::socket socket, - http::request> req, - std::vector< std::shared_ptr >& sessions, - ServerInitializationParameters* serverInitializationParams) - { - auto session = std::make_shared(std::move(socket), serverInitializationParams); - serverInitializationParams->postSessionCreationOperation(session.get()); - session->run(std::move(req)); - session->fesapi_log("Opening the plain websocket session", std::to_string(sessions.size())); - sessions.push_back(session); - } - -#ifdef WITH_ETP_SSL - template - static void - make_websocket_session( - boost::beast::ssl_stream stream, - http::request> req, - std::vector< std::shared_ptr >& sessions, - ServerInitializationParameters* serverInitializationParams) - { - auto session = std::make_shared(std::move(stream), serverInitializationParams); - serverInitializationParams->postSessionCreationOperation(session.get()); - session->run(std::move(req)); - session->fesapi_log("Opening the secured websocket session", std::to_string(sessions.size())); - sessions.push_back(session); - } -#endif - - // Handles an HTTP server connection. - // This uses the Curiously Recurring Template Pattern so that - // the same code works with both SSL streams and regular sockets. - template - class http_session - { - // Access the derived class, this is part of - // the Curiously Recurring Template Pattern idiom. - Derived& - derived() - { - return static_cast(*this); - } - - - // This queue is used for HTTP pipelining. - class queue - { - enum - { - // Maximum number of responses we will queue - limit = 8 - }; - - // The type-erased, saved work item - struct work - { - virtual ~work() = default; - virtual void operator()() = 0; - }; - - http_session& self_; - std::vector> items_; - - public: - explicit - queue(http_session& self) - : self_(self) - { - static_assert(limit > 0, "queue limit must be positive"); - items_.reserve(limit); - } - - // Returns `true` if we have reached the queue limit - bool - is_full() const - { - return items_.size() >= limit; - } - - // Called when a message finishes sending - // Returns `true` if the caller should initiate a read - bool - on_write() - { - BOOST_ASSERT(!items_.empty()); - auto const was_full = is_full(); - items_.erase(items_.begin()); - if (!items_.empty()) - (*items_.front())(); - return was_full; - } - - // Called by the HTTP handler to send a response. - template - void - operator()(http::message&& msg) - { - // This holds a work item - struct work_impl : work - { - http_session& self_; - http::message msg_; - - work_impl( - http_session& self, - http::message&& msg) - : self_(self) - , msg_(std::move(msg)) - { - } - - void - operator()() - { - http::async_write( - self_.derived().stream(), - msg_, - boost::asio::bind_executor( - self_.strand_, - std::bind( - &http_session::on_write, - self_.derived().shared_from_this(), - std::placeholders::_1, - msg_.need_eof()))); - } - }; - - // Allocate and store the work - items_.push_back( - boost::make_unique(self_, std::move(msg))); - - // If there was no previous work, start this one - if (items_.size() == 1) - (*items_.front())(); - } - }; - - std::shared_ptr doc_root_; - http::request req_; - queue queue_; - std::vector< std::shared_ptr >& sessions_; - ServerInitializationParameters* serverInitializationParams_; - - protected: - boost::asio::steady_timer timer_; - boost::asio::strand< - boost::asio::io_context::executor_type> strand_; - beast::flat_buffer buffer_; - - public: - // Take ownership of the socket - explicit - http_session( - boost::asio::io_context& ioc, - std::shared_ptr const& doc_root, - std::vector< std::shared_ptr >& sessions, - ServerInitializationParameters* serverInitializationParams - ) - : doc_root_(doc_root) - , queue_(*this) - , timer_(ioc, - (std::chrono::steady_clock::time_point::max)()) - , strand_(ioc.get_executor()) - , sessions_(sessions) - , serverInitializationParams_(serverInitializationParams) - { - } - - void - do_read() - { - // Set the timer - timer_.expires_after(std::chrono::seconds(15)); - - // Make the request empty before reading, - // otherwise the operation behavior is undefined. - req_ = {}; - - // Read a request - http::async_read( - derived().stream(), - buffer_, - req_, - boost::asio::bind_executor( - strand_, - std::bind( - &http_session::on_read, - derived().shared_from_this(), - std::placeholders::_1))); - } - - // Called when the timer expires. - void - on_timer(boost::system::error_code ec) - { - if (ec && ec != boost::asio::error::operation_aborted) { - std::cerr << "timer : " << ec.message() << std::endl; - return; - } - - // Check if this has been upgraded to Websocket - if (timer_.expires_at() == (std::chrono::steady_clock::time_point::min)()) - return; - - // Verify that the timer really expired since the deadline may have moved. - if (timer_.expiry() <= std::chrono::steady_clock::now()) - return derived().do_timeout(); - - // Wait on the timer - timer_.async_wait( - boost::asio::bind_executor( - strand_, - std::bind( - &http_session::on_timer, - derived().shared_from_this(), - std::placeholders::_1))); - } - - void - on_read(boost::system::error_code ec) - { - // Happens when the timer closes the socket - if (ec == boost::asio::error::operation_aborted) - return; - - // This means they closed the connection - if (ec == http::error::end_of_stream) - return derived().do_eof(); - - if (ec) { - std::cerr << "HTTP server read issue : " << ec.message() << std::endl; - return; - } - - // See if it is a WebSocket Upgrade - if (websocket::is_upgrade(req_)) - { - if (req_.count(boost::beast::http::field::sec_websocket_protocol) != 1 || - req_.at(boost::beast::http::field::sec_websocket_protocol).find("etp12.energistics.org") == std::string::npos) { - auto const precondition_failed = [this]() - { - http::response res{ http::status::precondition_failed, req_.version() }; - res.set(http::field::server, serverInitializationParams_->getApplicationName()); - res.set(http::field::content_type, "text/html"); - res.keep_alive(req_.keep_alive()); - res.body() = "The sec-websocket-protocol HTTP header field does not contain etp12.energistics.org."; - res.prepare_payload(); - return res; - }; - queue_(precondition_failed()); - } - else { - // Make timer expire immediately, by setting expiry to time_point::min we can detect - // the upgrade to websocket in the timer handler - timer_.expires_at((std::chrono::steady_clock::time_point::min)()); - - // Transfer the stream to a new WebSocket session - return make_websocket_session( - derived().release_stream(), - std::move(req_), - sessions_, - serverInitializationParams_); - } - } - else { - // Send the response - handle_request(*doc_root_, std::move(req_), queue_, serverInitializationParams_); - } - - // If we aren't at the queue limit, try to pipeline another request - if (!queue_.is_full()) - do_read(); - } - - void - on_write(boost::system::error_code ec, bool close) - { - // Happens when the timer closes the socket - if (ec == boost::asio::error::operation_aborted) - return; - - if (ec) { - std::cerr << "write : " << ec.message() << std::endl; - return; - } - - if (close) - { - // This means we should close the connection, usually because - // the response indicated the "Connection: close" semantic. - return derived().do_eof(); - } - - // Inform the queue that a write completed - if (queue_.on_write()) - { - // Read another request - do_read(); - } - } - }; - - // Handles a plain HTTP connection - class plain_http_session - : public http_session - , public std::enable_shared_from_this - { - tcp::socket socket_; - boost::asio::strand< - boost::asio::io_context::executor_type> strand_; - - public: - // Create the http_session - plain_http_session( - tcp::socket socket, - std::shared_ptr const& doc_root, - std::vector< std::shared_ptr >& sessions, - ServerInitializationParameters* serverInitializationParams) - : http_session( -#if BOOST_VERSION < 107000 - socket.get_executor().context(), -#else - static_cast(socket.get_executor().context()), -#endif - doc_root, - sessions, - serverInitializationParams) - , socket_(std::move(socket)) -#if BOOST_VERSION < 107000 - , strand_(socket_.get_executor()) -#else - , strand_(static_cast(socket_.get_executor().context()).get_executor()) -#endif - - { - } - - // Called by the base class - tcp::socket& - stream() - { - return socket_; - } - - // Called by the base class - tcp::socket - release_stream() - { - return std::move(socket_); - } - - // Start the asynchronous operation - void - run() - { - // Make sure we run on the strand - if (!strand_.running_in_this_thread()) - return boost::asio::post( - boost::asio::bind_executor( - strand_, - std::bind( - &plain_http_session::run, - shared_from_this()))); - - // Run the timer. The timer is operated - // continuously, this simplifies the code. - on_timer({}); - - do_read(); - } - - void - do_eof() - { - // Send a TCP shutdown - boost::system::error_code ec; - socket_.shutdown(tcp::socket::shutdown_send, ec); - - // At this point the connection is closed gracefully - } - - void - do_timeout() - { - // Closing the socket cancels all outstanding operations. They - // will complete with boost::asio::error::operation_aborted - boost::system::error_code ec; - socket_.shutdown(tcp::socket::shutdown_both, ec); - socket_.close(ec); - } - }; -#ifdef WITH_ETP_SSL - // Handles an SSL HTTP connection - class ssl_http_session - : public http_session - , public std::enable_shared_from_this - { - boost::beast::ssl_stream stream_; - boost::asio::strand< - boost::asio::io_context::executor_type> strand_; - bool eof_ = false; - - public: - // Create the http_session - ssl_http_session( - tcp::socket socket, - ssl::context& ctx, - std::shared_ptr const& doc_root, - std::vector< std::shared_ptr >& sessions, - ServerInitializationParameters* serverInitializationParams) - : http_session( -#if BOOST_VERSION < 107000 - socket.get_executor().context(), -#else - static_cast(socket.get_executor().context()), -#endif - doc_root, - sessions, - serverInitializationParams) - , stream_(std::move(socket), ctx) -#if BOOST_VERSION < 107000 - , strand_(stream_.get_executor()) -#else - , strand_(static_cast(stream_.get_executor().context()).get_executor()) -#endif - { - } - - // Called by the base class - boost::beast::ssl_stream& - stream() - { - return stream_; - } - - // Called by the base class - boost::beast::ssl_stream - release_stream() - { - return std::move(stream_); - } - - // Start the asynchronous operation - void - run() - { - // Make sure we run on the strand - if (!strand_.running_in_this_thread()) - return boost::asio::post( - boost::asio::bind_executor( - strand_, - std::bind( - &ssl_http_session::run, - shared_from_this()))); - - // Run the timer. The timer is operated - // continuously, this simplifies the code. - on_timer({}); - - // Set the timer - timer_.expires_after(std::chrono::seconds(15)); - - // Perform the SSL handshake - // Note, this is the buffered version of the handshake. - stream_.async_handshake( - ssl::stream_base::server, - buffer_.data(), - boost::asio::bind_executor( - strand_, - std::bind( - &ssl_http_session::on_handshake, - shared_from_this(), - std::placeholders::_1, - std::placeholders::_2))); - } - - void - on_handshake( - boost::system::error_code ec, - std::size_t bytes_used) - { - // Happens when the handshake times out - if (ec == boost::asio::error::operation_aborted) - return; - - if (ec) { - std::cerr << "handshake : " << ec.message() << std::endl; - return; - } - - // Consume the portion of the buffer used by the handshake - buffer_.consume(bytes_used); - - do_read(); - } - - void - do_eof() - { - eof_ = true; - - // Set the timer - timer_.expires_after(std::chrono::seconds(15)); - - // Perform the SSL shutdown - stream_.async_shutdown( - boost::asio::bind_executor( - strand_, - std::bind( - &ssl_http_session::on_shutdown, - shared_from_this(), - std::placeholders::_1))); - } - - void - on_shutdown(boost::system::error_code ec) - { - // Happens when the shutdown times out - if (ec == boost::asio::error::operation_aborted) - return; - - if (ec) { - std::cerr << "shutdown : " << ec.message() << std::endl; - return; - } - - // At this point the connection is closed gracefully - } - - void - do_timeout() - { - // If this is true it means we timed out performing the shutdown - if (eof_) - return; - - // Start the timer again - timer_.expires_at( - (std::chrono::steady_clock::time_point::max)()); - on_timer({}); - do_eof(); - } - }; -#endif - // Accepts incoming connections and launches the sessions - class listener : public std::enable_shared_from_this - { - tcp::acceptor acceptor_; - tcp::socket socket_; - std::vector< std::shared_ptr >& sessions_; - ServerInitializationParameters* serverInitializationParams_; -#ifdef WITH_ETP_SSL - ssl::context& ctx_; - bool useSsl_; -#endif - - public: -#ifdef WITH_ETP_SSL - listener( - boost::asio::io_context& ioc, - tcp::endpoint endpoint, - std::vector< std::shared_ptr >& sessions, - ServerInitializationParameters* serverInitializationParams, - ssl::context& ctx, - bool useSsl - ) - : acceptor_(ioc) - , socket_(ioc) - , sessions_(sessions) - , serverInitializationParams_(serverInitializationParams) - , ctx_(ctx) - , useSsl_(useSsl) - { - beast::error_code ec; - - // Open the acceptor - acceptor_.open(endpoint.protocol(), ec); - if (ec) { - std::cerr << "listener open : " << ec.message() << std::endl; - return; - } - - // Allow address reuse - acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec); - if (ec) { - std::cerr << "listener set_option : " << ec.message() << std::endl; - return; - } - - // Bind to the server address - acceptor_.bind(endpoint, ec); - if (ec) { - std::cerr << "listener bind : " << ec.message() << std::endl; - return; - } - - // Start listening for connections - acceptor_.listen( - boost::asio::socket_base::max_listen_connections, ec); - if (ec) { - std::cerr << "listener listen : " << ec.message() << std::endl; - return; - } - } -#else - listener( - boost::asio::io_context& ioc, - tcp::endpoint endpoint, - std::vector< std::shared_ptr >& sessions, - ServerInitializationParameters* serverInitializationParams - ) - : acceptor_(ioc) - , socket_(ioc) - , sessions_(sessions) - , serverInitializationParams_(serverInitializationParams) - { - beast::error_code ec; - - // Open the acceptor - acceptor_.open(endpoint.protocol(), ec); - if (ec) { - std::cerr << "listener open : " << ec.message() << std::endl; - return; - } - - // Allow address reuse - acceptor_.set_option(boost::asio::socket_base::reuse_address(true), ec); - if (ec) { - std::cerr << "listener set_option : " << ec.message() << std::endl; - return; - } - - // Bind to the server address - acceptor_.bind(endpoint, ec); - if (ec) { - std::cerr << "listener bind : " << ec.message() << std::endl; - return; - } - - // Start listening for connections - acceptor_.listen( - boost::asio::socket_base::max_listen_connections, ec); - if (ec) { - std::cerr << "listener listen : " << ec.message() << std::endl; - return; - } - } -#endif - - // Start accepting incoming connections - void run() { - if (!acceptor_.is_open()) { return; } - do_accept(); - } - - void do_accept() { - acceptor_.async_accept( - socket_, - std::bind( - &listener::on_accept, - this->shared_from_this(), - std::placeholders::_1)); - } - - void on_accept(boost::system::error_code ec) { - if (ec) { - std::cerr << "listener on_accept : " << ec.message() << std::endl; - } - else { - // Create the http session and run it -#ifdef WITH_ETP_SSL - if (useSsl_) { - std::make_shared(std::move(socket_), ctx_, std::make_shared("."), sessions_, serverInitializationParams_)->run(); - } - else -#endif - std::make_shared(std::move(socket_), std::make_shared("."), sessions_, serverInitializationParams_)->run(); - } - - // Accept another connection - do_accept(); - } - }; - - public: - Server() : serverInitializationParams_(nullptr) {} - - std::vector< std::shared_ptr >& getSessions() { return sessions_; } - -#ifdef WITH_ETP_SSL - void listen(ServerInitializationParameters* serverInitializationParams, int threadCount, - const std::string & cert = "", const std::string & key = "", const std::string & dh = "") { -#else - void listen(ServerInitializationParameters* serverInitializationParams, int threadCount) { -#endif - serverInitializationParams_ = serverInitializationParams; - if (threadCount < 1) { - throw std::invalid_argument("You need to run your server on at least one thread."); - } - auto const address = boost::asio::ip::make_address(serverInitializationParams->getHost()); - - // The io_context is required for all I/O - boost::asio::io_context ioc{ threadCount }; - -#ifdef WITH_ETP_SSL - // The context life scope must the server life scope. That's why it is not inside the below condition. - ssl::context ctx{ ssl::context::tlsv12_server }; - - // Create and launch a listening port - if (!dh.empty()) { - - ctx.set_password_callback( - [](std::size_t, - boost::asio::ssl::context_base::password_purpose) - { - return "test"; - }); - - ctx.set_options( - boost::asio::ssl::context::default_workarounds | - boost::asio::ssl::context::no_sslv2 | - boost::asio::ssl::context::no_sslv3 | - boost::asio::ssl::context::single_dh_use); - - ctx.use_certificate_chain( - boost::asio::buffer(cert.data(), cert.size())); - - ctx.use_private_key( - boost::asio::buffer(key.data(), key.size()), - boost::asio::ssl::context::file_format::pem); - - ctx.use_tmp_dh( - boost::asio::buffer(dh.data(), dh.size())); - - std::make_shared(ioc, tcp::endpoint{ address, serverInitializationParams->getPort() }, sessions_, serverInitializationParams_, ctx, true)->run(); - } - else { - std::make_shared(ioc, tcp::endpoint{ address, serverInitializationParams->getPort() }, sessions_, serverInitializationParams_, ctx, false)->run(); - } -#else - // Create and launch a listening port - std::make_shared(ioc, tcp::endpoint{ address, serverInitializationParams->getPort() }, sessions_, serverInitializationParams_)->run(); -#endif - - // Capture SIGINT and SIGTERM to perform a clean shutdown - boost::asio::signal_set signals(ioc, SIGINT, SIGTERM); - signals.async_wait( - [&](beast::error_code const&, int) - { - // Stop the `io_context`. This will cause `run()` - // to return immediately, eventually destroying the - // `io_context` and all of the sockets in it. - ioc.stop(); - }); - - // Run the I/O service on the requested number of threads - std::vector v; - v.reserve(threadCount - 1); - for (auto i = threadCount - 1; i > 0; --i) - v.emplace_back( - [&ioc] - { - ioc.run(); - }); - ioc.run(); - } - }; -} diff --git a/src/etp/ServerInitializationParameters.cpp b/src/etp/ServerInitializationParameters.cpp deleted file mode 100644 index 0be1030..0000000 --- a/src/etp/ServerInitializationParameters.cpp +++ /dev/null @@ -1,63 +0,0 @@ -/*----------------------------------------------------------------------- -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 "ServerInitializationParameters.h" - -using namespace ETP_NS; - -std::vector ServerInitializationParameters::makeSupportedEncodings() const -{ - return std::vector(1, "binary"); -} - -std::vector ServerInitializationParameters::makeSupportedProtocols() const -{ - std::vector result; - Energistics::Etp::v12::Datatypes::Version protocolVersion; - protocolVersion.major = 1; - protocolVersion.minor = 2; - protocolVersion.patch = 0; - protocolVersion.revision = 0; - - Energistics::Etp::v12::Datatypes::SupportedProtocol protocol; - protocol.protocol = static_cast(Energistics::Etp::v12::Datatypes::Protocol::Discovery); - protocol.protocolVersion = protocolVersion; - protocol.role = "store"; - result.push_back(protocol); - - protocol.protocol = static_cast(Energistics::Etp::v12::Datatypes::Protocol::Store); - result.push_back(protocol); - - protocol.protocol = static_cast(Energistics::Etp::v12::Datatypes::Protocol::StoreNotification); - Energistics::Etp::v12::Datatypes::DataValue value; - value.item.set_long((std::numeric_limits::max)()); - protocol.protocolCapabilities["MaxDataArraySize"] = value; - result.push_back(protocol); - protocol.protocolCapabilities.clear(); - - protocol.protocol = static_cast(Energistics::Etp::v12::Datatypes::Protocol::DataArray); - result.push_back(protocol); - - protocol.protocol = static_cast(Energistics::Etp::v12::Datatypes::Protocol::Transaction); - result.push_back(protocol); - - protocol.protocol = static_cast(Energistics::Etp::v12::Datatypes::Protocol::Dataspace); - result.push_back(protocol); - - return result; -} diff --git a/src/etp/ServerInitializationParameters.h b/src/etp/ServerInitializationParameters.h deleted file mode 100644 index 25259eb..0000000 --- a/src/etp/ServerInitializationParameters.h +++ /dev/null @@ -1,49 +0,0 @@ -/*----------------------------------------------------------------------- -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 "InitializationParameters.h" - -namespace ETP_NS -{ - class ServerInitializationParameters : public InitializationParameters - { - public: - /** - * Set the identifier of the server a default value. - * You should set the identifer as wanted in your own derived class. - */ - ServerInitializationParameters(boost::uuids::uuid instanceUuid, const std::string & host, unsigned short port) : InitializationParameters(instanceUuid, host, port) {} - /** - * Mainly for use with SWIG i.e. boost uuid structure is not easily portable whereas strings are. - */ - ServerInitializationParameters(const std::string & serverUuid, const std::string & host, unsigned short port) : InitializationParameters(serverUuid, host, port) {} - virtual ~ServerInitializationParameters() = default; - - FETPAPI_DLL_IMPORT_OR_EXPORT std::string getApplicationName() const override { return "F2I-CONSULTING ETP SERVER"; } - FETPAPI_DLL_IMPORT_OR_EXPORT virtual std::string getContactEmail() const { return "philippe.verney AT f2i-consulting.com"; } - FETPAPI_DLL_IMPORT_OR_EXPORT virtual std::string getContactName() const { return "Philippe Verney"; } - FETPAPI_DLL_IMPORT_OR_EXPORT virtual std::string getContactPhone() const { return "Please use Zoom or Slack"; } - FETPAPI_DLL_IMPORT_OR_EXPORT virtual std::string getOrganizationName() const { return "F2I-CONSULTING"; } - - FETPAPI_DLL_IMPORT_OR_EXPORT virtual std::vector makeSupportedEncodings() const; - FETPAPI_DLL_IMPORT_OR_EXPORT std::vector makeSupportedProtocols() const override; - - }; -} diff --git a/src/etp/fesapi/FesapiHdfProxy.cpp b/src/etp/fesapi/FesapiHdfProxy.cpp index bb405e7..5a908b0 100644 --- a/src/etp/fesapi/FesapiHdfProxy.cpp +++ b/src/etp/fesapi/FesapiHdfProxy.cpp @@ -115,245 +115,77 @@ std::vector FesapiHdfProxy::getElementCountPerDimension(const std::str return result; } -template -void FesapiHdfProxy::populateSubValuesNd( - size_t dimensionIndex, - std::vector& totalCounts, - std::vector& starts, - std::vector& counts, - size_t& valueIndex, - const T* values, - T* subValues) -{ - // [Base Condition] If dimensionIndex exceeds last dimension. - if (dimensionIndex >= starts.size()) { - // Add value in subValues. - subValues[valueIndex] = - values[getRowMajorIndex(0, totalCounts, starts)]; // Convert nD indices to row major order index. - - ++valueIndex; - } - else { - // Save starting index. - int64_t start = starts[dimensionIndex]; - - for (size_t i = 0; i < counts[dimensionIndex]; ++i) { - // Recursively populate subValues for next dimensions. - populateSubValuesNd( - dimensionIndex + 1, - totalCounts, - starts, counts, - valueIndex, values, subValues); - - starts[dimensionIndex]++; - } - - // Restore starting index. - starts[dimensionIndex] = start; - } -} - -int64_t FesapiHdfProxy::getRowMajorIndex( - size_t dimensionIndex, - std::vector& totalCounts, - std::vector& starts) -{ - // [Base Condition] If dimensionIndex is the last dimension. - if (dimensionIndex == (starts.size() - 1)) { - return starts[dimensionIndex]; - } - else { - return starts[dimensionIndex] * getCountsProduct(dimensionIndex + 1, totalCounts) + - getRowMajorIndex((dimensionIndex + 1), totalCounts, starts); - } -} - -int64_t FesapiHdfProxy::getCountsProduct( - size_t dimensionIndex, - std::vector& totalCounts) -{ - // [Base Condition] If dimensionIndex exceeds the last dimension. - if (dimensionIndex >= totalCounts.size()) { - return 1; - } - else { - return totalCounts[dimensionIndex] * - getCountsProduct(dimensionIndex + 1, totalCounts); - } -} - -template -void FesapiHdfProxy::writeSubArrayNd( - size_t dimensionIndex, - const std::string& uri, - const std::string& pathInResource, - std::vector& totalCounts, - std::vector starts, - std::vector counts, - const void* values) -{ - // Calculate array size - size_t totalCount{ 1 }; - - for (const auto& count : counts) { - totalCount *= count; - } - - // [Base Condition] If subarray can be transmitted. - if ((totalCount * sizeof(T)) <= maxArraySize_) { - // PUT DATA SUBARRAYS - Energistics::Etp::v12::Protocol::DataArray::PutDataSubarrays pdsa{}; - pdsa.dataSubarrays["0"].uid.uri = uri; - pdsa.dataSubarrays["0"].uid.pathInResource = pathInResource; - pdsa.dataSubarrays["0"].starts = starts; - pdsa.dataSubarrays["0"].counts = counts; - - // Cast values in T values. - const T* typeValues{ static_cast(values) }; - - // Create 1D Array for Sub Values. - T* subValues = new T[totalCount]; - size_t valueIndex{ 0 }; - - // Recursively populate subValues starting from first dimension. - populateSubValuesNd( - 0, - totalCounts, starts, counts, - valueIndex, typeValues, subValues); - - // Create AVRO Array +namespace { + Energistics::Etp::v12::Datatypes::AnyArray convertVoidArrayIntoAvroAnyArray( + COMMON_NS::AbstractObject::numericalDatatypeEnum datatype, + const void * values, size_t totalCount) + { Energistics::Etp::v12::Datatypes::AnyArray data; - createAnyArray(data, totalCount, subValues); // Type-specific code is written in explicit specializations for createAnyArray(). - pdsa.dataSubarrays["0"].data = data; - - std::cout << "Writing subarray..." << std::endl; + if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::DOUBLE) { + Energistics::Etp::v12::Datatypes::ArrayOfDouble avroArray; - // Send putDataSubarrays Message - session_->sendAndBlock(pdsa, 0, 0x02); + avroArray.values = std::vector( + static_cast(values), + static_cast(values) + totalCount); - // Delete Array - delete[] subValues; - } - // Again divide all dimensions starting from first dimension. - else if (dimensionIndex >= starts.size()) { - writeSubArrayNd( - 0, - uri, pathInResource, totalCounts, - starts, counts, values); - } - // Divide the values of current dimension in halves. - else { - int64_t numberOfValues = counts[dimensionIndex]; - - int64_t firstHalfValues = numberOfValues / 2; - int64_t secondHalfValues = numberOfValues - firstHalfValues; - - std::vector newCounts{ counts }; - newCounts[dimensionIndex] = firstHalfValues; - - // Recursively divide next dimension. - writeSubArrayNd( - dimensionIndex + 1, - uri, pathInResource, totalCounts, - starts, - newCounts, - values); - - std::vector newStarts{ starts }; - newStarts[dimensionIndex] = newStarts[dimensionIndex] + firstHalfValues; - newCounts[dimensionIndex] = secondHalfValues; - - // Recursively divide next dimension. - writeSubArrayNd( - dimensionIndex + 1, - uri, pathInResource, totalCounts, - newStarts, - newCounts, - values); - } -} + data.item.set_ArrayOfDouble(avroArray); + } + else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::FLOAT) { + Energistics::Etp::v12::Datatypes::ArrayOfFloat avroArray; -template -void FesapiHdfProxy::createAnyArray( - Energistics::Etp::v12::Datatypes::AnyArray& data, - size_t totalCount, - T* values) -{ - throw logic_error( - "Subarrays are implemented for primitive types only: double, float, int64, int32, short, char"); -} + avroArray.values = std::vector( + static_cast(values), + static_cast(values) + totalCount); -// Template Specializations for createAnyArray() -template<> -void FesapiHdfProxy::createAnyArray( - Energistics::Etp::v12::Datatypes::AnyArray& data, - size_t totalCount, - double* values) -{ - Energistics::Etp::v12::Datatypes::ArrayOfDouble avroArray; - avroArray.values = std::vector(values, values + totalCount); - data.item.set_ArrayOfDouble(avroArray); -} + data.item.set_ArrayOfFloat(avroArray); + } + else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT64 || + datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT64) { + Energistics::Etp::v12::Datatypes::ArrayOfLong avroArray; -template<> -void FesapiHdfProxy::createAnyArray( - Energistics::Etp::v12::Datatypes::AnyArray& data, - size_t totalCount, - float* values) -{ - Energistics::Etp::v12::Datatypes::ArrayOfFloat avroArray; - avroArray.values = std::vector(values, values + totalCount); - data.item.set_ArrayOfFloat(avroArray); -} + avroArray.values = std::vector( + static_cast(values), + static_cast(values) + totalCount); -template<> -void FesapiHdfProxy::createAnyArray( - Energistics::Etp::v12::Datatypes::AnyArray& data, - size_t totalCount, - int64_t* values) -{ - Energistics::Etp::v12::Datatypes::ArrayOfLong avroArray; - avroArray.values = std::vector(values, values + totalCount); - data.item.set_ArrayOfLong(avroArray); -} + data.item.set_ArrayOfLong(avroArray); + } + else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT32 || + datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT32) { + Energistics::Etp::v12::Datatypes::ArrayOfInt avroArray; -template<> -void FesapiHdfProxy::createAnyArray( - Energistics::Etp::v12::Datatypes::AnyArray& data, - size_t totalCount, - int32_t* values) -{ - Energistics::Etp::v12::Datatypes::ArrayOfInt avroArray; - avroArray.values = std::vector(values, values + totalCount); - data.item.set_ArrayOfInt(avroArray); -} + avroArray.values = std::vector( + static_cast(values), + static_cast(values) + totalCount); -template<> -void FesapiHdfProxy::createAnyArray( - Energistics::Etp::v12::Datatypes::AnyArray& data, - size_t totalCount, - short* values) -{ - Energistics::Etp::v12::Datatypes::ArrayOfInt avroArray; + data.item.set_ArrayOfInt(avroArray); + } + else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT16 || + datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT16) { + Energistics::Etp::v12::Datatypes::ArrayOfInt avroArray; - for (size_t i = 0; i < totalCount; ++i) - avroArray.values.push_back(values[i]); + for (size_t i = 0; i < totalCount; ++i) { + avroArray.values.push_back(static_cast(values)[i]); + } - data.item.set_ArrayOfInt(avroArray); -} + data.item.set_ArrayOfInt(avroArray); + } + else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT8 || + datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT8) { + std::string avroArray; -template<> -void FesapiHdfProxy::createAnyArray( - Energistics::Etp::v12::Datatypes::AnyArray& data, - size_t totalCount, - char* values) -{ - std::string avroArray{}; + for (size_t i = 0; i < totalCount; ++i) { + avroArray.push_back(static_cast(values)[i]); + } - for (size_t i = 0; i < totalCount; ++i) - avroArray.push_back(values[i]); + data.item.set_bytes(avroArray); + } + else { + throw logic_error( + "You need to give a COMMON_NS::AbstractObject::numericalDatatypeEnum as the datatype"); + } - data.item.set_bytes(avroArray); + return data; + } } void FesapiHdfProxy::writeArrayNd(const std::string & groupName, @@ -382,37 +214,30 @@ void FesapiHdfProxy::writeArrayNd(const std::string & groupName, } // Determine Value Size (bytes) and Any Array Type - int valueSize{ 1 }; - Energistics::Etp::v12::Datatypes::AnyArrayType anyArrayType{}; + size_t valueSize{ 1 }; switch (datatype) { case COMMON_NS::AbstractObject::numericalDatatypeEnum::DOUBLE: valueSize = sizeof(double); - anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfDouble; break; case COMMON_NS::AbstractObject::numericalDatatypeEnum::FLOAT: valueSize = sizeof(float); - anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfFloat; break; case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT64: case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT64: valueSize = sizeof(int64_t); - anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfLong; break; case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT32: case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT32: valueSize = sizeof(int32_t); - anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfInt; break; case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT16: case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT16: valueSize = sizeof(short); - anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfInt; break; case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT8: case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT8: valueSize = sizeof(char); - anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::bytes; break; default: throw std::logic_error( @@ -421,150 +246,30 @@ void FesapiHdfProxy::writeArrayNd(const std::string & groupName, if (totalCount * valueSize <= maxArraySize_) { // PUT DATA ARRAYS - Energistics::Etp::v12::Protocol::DataArray::PutDataArrays pda{}; + Energistics::Etp::v12::Protocol::DataArray::PutDataArrays pda; pda.dataArrays["0"].uid.uri = uri; pda.dataArrays["0"].uid.pathInResource = pathInResource; pda.dataArrays["0"].array.dimensions = dimensions; // Create AVRO Array - Energistics::Etp::v12::Datatypes::AnyArray data; - if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::DOUBLE) { - Energistics::Etp::v12::Datatypes::ArrayOfDouble avroArray; - - avroArray.values = std::vector( - static_cast(values), - static_cast(values) + totalCount); - - data.item.set_ArrayOfDouble(avroArray); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::FLOAT) { - Energistics::Etp::v12::Datatypes::ArrayOfFloat avroArray; - - avroArray.values = std::vector( - static_cast(values), - static_cast(values) + totalCount); - - data.item.set_ArrayOfFloat(avroArray); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT64 || - datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT64) { - Energistics::Etp::v12::Datatypes::ArrayOfLong avroArray; - - avroArray.values = std::vector( - static_cast(values), - static_cast(values) + totalCount); - - data.item.set_ArrayOfLong(avroArray); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT32 || - datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT32) { - Energistics::Etp::v12::Datatypes::ArrayOfInt avroArray; - - avroArray.values = std::vector( - static_cast(values), - static_cast(values) + totalCount); - - data.item.set_ArrayOfInt(avroArray); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT16 || - datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT16) { - Energistics::Etp::v12::Datatypes::ArrayOfInt avroArray; - - for (size_t i = 0; i < totalCount; ++i) { - avroArray.values.push_back(static_cast(values)[i]); - } - - data.item.set_ArrayOfInt(avroArray); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT8 || - datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT8) { - std::string avroArray; - - for (size_t i = 0; i < totalCount; ++i) { - avroArray.push_back(static_cast(values)[i]); - } - - data.item.set_bytes(avroArray); - } - else { - throw logic_error( - "You need to give a COMMON_NS::AbstractObject::numericalDatatypeEnum as the datatype"); - } - - pda.dataArrays["0"].array.data = data; + pda.dataArrays["0"].array.data = convertVoidArrayIntoAvroAnyArray(datatype, values, totalCount); // Send Data Arrays - session_->send(pda, 0, 0x02); + session_->sendAndBlock(pda, 0, 0x02); } else { - // PUT UNINITIALIZED DATA ARRAYS - Energistics::Etp::v12::Protocol::DataArray::PutUninitializedDataArrays puda; - puda.dataArrays["0"].uid.uri = uri; - puda.dataArrays["0"].uid.pathInResource = pathInResource; - puda.dataArrays["0"].metadata.dimensions = dimensions; - puda.dataArrays["0"].metadata.transportArrayType = anyArrayType; - - // Send Uninitialized Data Arrays - session_->sendAndBlock(puda, 0, 0x02); + createArrayNd(groupName, name, + datatype, + numValuesInEachDimension, numDimensions); // SEND MULTIPLE PUT DATA SUBARRAYS MESSAGES - std::cout << "Writing Subarrays: This may take some time." << std::endl; - std::cout << "Please wait..." << std::endl; - - // Initial Starts and Counts - std::vector starts{}; - std::vector counts{}; + session_->fesapi_log("Writing Subarrays: This may take some time."); + session_->fesapi_log("Please wait..."); - for (size_t i = 0; i < numDimensions; ++i) { - starts.push_back(0); - counts.push_back(numValuesInEachDimension[i]); - } - - // Recursively Write Subarrays - if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::DOUBLE) { - writeSubArrayNd(0, uri, pathInResource, counts, - starts, - counts, - values); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::FLOAT) { - writeSubArrayNd(0, uri, pathInResource, counts, - starts, - counts, - values); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT64 || - datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT64) { - writeSubArrayNd(0, uri, pathInResource, counts, - starts, - counts, - values); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT32 || - datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT32) { - writeSubArrayNd(0, uri, pathInResource, counts, - starts, - counts, - values); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT16 || - datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT16) { - writeSubArrayNd(0, uri, pathInResource, counts, - starts, - counts, - values); - } - else if (datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::INT8 || - datatype == COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT8) { - writeSubArrayNd(0, uri, pathInResource, counts, - starts, - counts, - values); - } - else { - throw logic_error( - "You need to give a COMMON_NS::AbstractObject::numericalDatatypeEnum as the datatype"); - } + std::array offsets = { 0, 0, 0 }; + writeArrayNdSlab(groupName, name, datatype, + values, numValuesInEachDimension, + offsets.data(), numDimensions); } } @@ -575,7 +280,52 @@ void FesapiHdfProxy::createArrayNd( const uint64_t* numValuesInEachDimension, unsigned int numDimensions) { - throw logic_error("createArrayNdNot implemented yet"); + std::string pathInResource{ (groupName.back() == '/' ? + groupName : groupName + '/') + datasetName }; + + std::vector dimensions{}; + for (size_t i = 0; i < numDimensions; ++i) { + dimensions.push_back(numValuesInEachDimension[i]); + } + Energistics::Etp::v12::Datatypes::AnyArrayType anyArrayType; + + switch (datatype) { + case COMMON_NS::AbstractObject::numericalDatatypeEnum::DOUBLE: + anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfDouble; + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::FLOAT: + anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfFloat; + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT64: + case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT64: + anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfLong; + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT32: + case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT32: + anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfInt; + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT16: + case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT16: + anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::arrayOfInt; + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT8: + case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT8: + anyArrayType = Energistics::Etp::v12::Datatypes::AnyArrayType::bytes; + break; + default: + throw std::logic_error( + "You need to give a COMMON_NS::AbstractObject::numericalDatatypeEnum as the datatype"); + } + + // PUT UNINITIALIZED DATA ARRAYS + Energistics::Etp::v12::Protocol::DataArray::PutUninitializedDataArrays puda; + puda.dataArrays["0"].uid.uri = buildEtp12Uri(); + puda.dataArrays["0"].uid.pathInResource = pathInResource; + puda.dataArrays["0"].metadata.dimensions = dimensions; + puda.dataArrays["0"].metadata.transportArrayType = anyArrayType; + + // Send Uninitialized Data Arrays + session_->sendAndBlock(puda, 0, 0x02); } void FesapiHdfProxy::writeArrayNdSlab( @@ -587,7 +337,107 @@ void FesapiHdfProxy::writeArrayNdSlab( const uint64_t* offsetInEachDimension, unsigned int numDimensions) { - throw logic_error("writeArrayNdSlab Not implemented yet"); + if (!isOpened()) + open(); + + // URI AND PATH + std::string uri{ buildEtp12Uri() }; + + std::string pathInResource{ (groupName.back() == '/' ? + groupName : groupName + '/') + datasetName }; + + // Create Total Count + size_t totalCount = std::accumulate(numValuesInEachDimension, numValuesInEachDimension + numDimensions, 1, std::multiplies()); + + // Determine Value Size (bytes) and Any Array Type + size_t valueSize{ 1 }; + + switch (datatype) { + case COMMON_NS::AbstractObject::numericalDatatypeEnum::DOUBLE: + valueSize = sizeof(double); + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::FLOAT: + valueSize = sizeof(float); + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT64: + case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT64: + valueSize = sizeof(int64_t); + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT32: + case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT32: + valueSize = sizeof(int32_t); + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT16: + case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT16: + valueSize = sizeof(int16_t); + break; + case COMMON_NS::AbstractObject::numericalDatatypeEnum::INT8: + case COMMON_NS::AbstractObject::numericalDatatypeEnum::UINT8: + valueSize = sizeof(int8_t); + break; + default: + throw std::logic_error( + "You need to give a COMMON_NS::AbstractObject::numericalDatatypeEnum as the datatype"); + } + + if (totalCount * valueSize <= maxArraySize_) { + std::vector counts; + std::vector starts; + for (size_t i = 0; i < numDimensions; ++i) { + counts.push_back(numValuesInEachDimension[i]); + starts.push_back(offsetInEachDimension[i]); + } + + // PUT DATA SUBARRAYS + Energistics::Etp::v12::Protocol::DataArray::PutDataSubarrays pdsa; + pdsa.dataSubarrays["0"].uid.uri = uri; + pdsa.dataSubarrays["0"].uid.pathInResource = pathInResource; + pdsa.dataSubarrays["0"].starts = starts; + pdsa.dataSubarrays["0"].counts = counts; + + // Create AVRO Array + pdsa.dataSubarrays["0"].data = convertVoidArrayIntoAvroAnyArray(datatype, values, totalCount); + + // Send putDataSubarrays Message + session_->sendAndBlock(pdsa, 0, 0x02); + } + else { + std::unique_ptr counts(new uint64_t[numDimensions]); + std::unique_ptr starts(new uint64_t[numDimensions]); + for (size_t i = 0; i < numDimensions; ++i) { + counts[i] = numValuesInEachDimension[i]; + starts[i] = offsetInEachDimension[i]; + } + + size_t writtenTotalCount = 0; + size_t dimIdx = 0; + for (; dimIdx < numDimensions; ++dimIdx) { + if (numValuesInEachDimension[dimIdx] > 1) { + uint64_t previousCount = counts[dimIdx]; + counts[dimIdx] /= 2; + + writeArrayNdSlab(groupName, datasetName, + datatype, values, counts.get(), + starts.get(), numDimensions); + + writtenTotalCount = std::accumulate(counts.get(), counts.get() + numDimensions, 1, std::multiplies()); + + starts[dimIdx] += counts[dimIdx]; + counts[dimIdx] = previousCount - counts[dimIdx]; + + break; + } + } + // Defensive code + if (dimIdx >= numDimensions) { + throw std::logic_error("The " + std::to_string(valueSize) + " bytes size of a single value is bigger than the allowed server maximum array size which is " + + std::to_string(maxArraySize_) + " bytes."); + } + + writeArrayNdSlab(groupName, datasetName, datatype, + (int8_t*)values + (writtenTotalCount * valueSize), counts.get(), + starts.get(), numDimensions); + } } void FesapiHdfProxy::readArrayNdOfDoubleValues( @@ -664,8 +514,3 @@ bool FesapiHdfProxy::exist(const std::string & absolutePathInHdfFile) const { throw logic_error("exist Not implemented yet"); } - -bool FesapiHdfProxy::isCompressed(const std::string & datasetName) -{ - throw logic_error("isCompressed Not implemented yet"); -} diff --git a/src/etp/fesapi/FesapiHdfProxy.h b/src/etp/fesapi/FesapiHdfProxy.h index ab23825..170a1cd 100644 --- a/src/etp/fesapi/FesapiHdfProxy.h +++ b/src/etp/fesapi/FesapiHdfProxy.h @@ -130,108 +130,6 @@ namespace ETP_NS */ void setCompressionLevel(unsigned int newCompressionLevel) final { if (newCompressionLevel > 9) compressionLevel = 9; else compressionLevel = newCompressionLevel; } - /** - * Recursively write sub arrays (potentially with 2 dimensions) of a specific datatype into the HDF file by means of a single dataset. - * @param uri The uri of the original array. - * @param pathInResource The path of the original array. - * @param totalCounts The total number of values in each dimension of the original array. - * @param starts The starting indices in each dimension of the subarray to be written. - * @param counts The number of values in each dimension of the subarray to be written. - * @param values 1d array of specific datatype ordered firstly by fastest direction. - */ - /*template - void writeSubArrayNd( - const std::string& uri, - const std::string& pathInResource, - std::vector& totalCounts, - std::vector starts, - std::vector counts, - const void* values);*/ - - /** - * Recursively populate subValues array from original values array. - * @param dimensionIndex The index of dimension in nD array. - * @param totalCounts The total number of values in each dimension of the original array. - * @param starts The starting indices in each dimension of the subarray to be written. - * @param counts The number of values in each dimension of the subarray to be written. - * @param valueIndex The index of subValues array. - * @param values 1d array of specific datatype ordered firstly by fastest direction. - * @param subValues 1d subarray to be populated. - */ - template - void populateSubValuesNd( - size_t dimensionIndex, - std::vector& totalCounts, - std::vector& starts, - std::vector& counts, - size_t& valueIndex, - const T* values, - T* subValues); - - /* - HOW ROW MAJOR INDEX IS CALCULATED FROM N INDICES - index = i0 * (dim1 * dim2 * ... * dimn) + i1 * (dim2 * dim3 * ... * dimn) + i2 * (dim3 * dim4 * ... * dimn) + ... + in-1 * dimn + in - Here: - - 1. index is the calculated index value. - 2. 'i0, i1, i2, ..., in-1, in' represent the indices of the element in each dimension. - For example, i0 is the index in the first dimension, i1 is the index in the second dimension, and so on. - 3. dim1, dim2, dim3, ..., dimn are the sizes of the array along each dimension. - These represent the number of elements in each dimension. - */ - - /** - * Recursively calculate row major index from n indices. - * @param dimensionIndex The index of dimension in nD array. - * @param totalCounts The total number of values in each dimension of the original array. - * @param starts The starting indices in each dimension of the subarray to be written. - */ - int64_t getRowMajorIndex( - size_t dimensionIndex, - std::vector& totalCounts, - std::vector& starts); - - /** - * Recursively calculate product of total counts for calculating row major index. - * @param dimensionIndex The index of dimension in nD array. - * @param totalCounts The total number of values in each dimension of the original array. - */ - int64_t getCountsProduct( - size_t dimensionIndex, - std::vector& totalCounts); - - /** - * Recursively write sub arrays (potentially with 2 dimensions) of a specific datatype into the HDF file by means of a single dataset. - * @param dimensionIndex The index of dimension in nD array. - * @param uri The uri of the original array. - * @param pathInResource The path of the original array. - * @param totalCounts The total number of values in each dimension of the original array. - * @param starts The starting indices in each dimension of the subarray to be written. - * @param counts The number of values in each dimension of the subarray to be written. - * @param values 1d array of specific datatype ordered firstly by fastest direction. - */ - template - void writeSubArrayNd( - size_t dimensionIndex, - const std::string& uri, - const std::string& pathInResource, - std::vector& totalCounts, - std::vector starts, - std::vector counts, - const void* values); - - /** - * Create AnyArray from given data array of type T. - * @param data The reference to AnyArray to be populated. - * @param totalCount Total number of values. - * @param values 1d array of specific datatype ordered firstly by fastest direction. - */ - template - void createAnyArray( - Energistics::Etp::v12::Datatypes::AnyArray& data, - size_t totalCount, - T* values); - /** * Write an array (potentially with multi dimensions) of a specific datatype into the HDF file by means of a single dataset. * @param groupName The name of the group where to create the array of values. @@ -556,8 +454,9 @@ namespace ETP_NS /** * Check wether a dataset is compressed or not. + * From an ETP client point of view, the dataset is not compressed even if it may be on server storage or even on the websocket. */ - bool isCompressed(const std::string & datasetName) final; + bool isCompressed(const std::string &) final { return false; } /** * Get the number of elements in each chunk dimension of an HDF5 dataset. @@ -575,7 +474,7 @@ namespace ETP_NS AbstractSession* session_; unsigned int compressionLevel; std::string xmlNs_; - int maxArraySize_{ 12000000 }; // Bytes + size_t maxArraySize_{ 12000000 }; // Bytes Energistics::Etp::v12::Datatypes::DataArrayTypes::DataArrayIdentifier buildDataArrayIdentifier(const std::string & datasetName) const; Energistics::Etp::v12::Protocol::DataArray::GetDataArrays buildGetDataArraysMessage(const std::string & datasetName) const; diff --git a/src/etp/ssl/HttpsClientSession.h b/src/etp/ssl/HttpsClientSession.h index 6c7e893..a1150d0 100644 --- a/src/etp/ssl/HttpsClientSession.h +++ b/src/etp/ssl/HttpsClientSession.h @@ -60,13 +60,18 @@ namespace ETP_NS int version, std::string authorization = "") { + size_t hostSizeWithNullTermChar = strlen(host) + 1; + char* copyHost = new char[hostSizeWithNullTermChar]; + std::memcpy(copyHost, host, hostSizeWithNullTermChar); // Copy host because it must be non const in SSL_set_tlsext_host_name // Set SNI Hostname (many hosts need this to handshake successfully) - if (!SSL_set_tlsext_host_name(stream_.native_handle(), host)) + if (!SSL_set_tlsext_host_name(stream_.native_handle(), copyHost)) { boost::system::error_code ec{ static_cast(::ERR_get_error()), boost::asio::error::get_ssl_category() }; std::cerr << ec.message() << "\n"; + delete[] copyHost; return; } + delete[] copyHost; // Set up an HTTP GET request message req_.version(version); diff --git a/src/etp/ssl/SslServerSession.cpp b/src/etp/ssl/SslServerSession.cpp deleted file mode 100644 index ec24bc6..0000000 --- a/src/etp/ssl/SslServerSession.cpp +++ /dev/null @@ -1,29 +0,0 @@ -/*----------------------------------------------------------------------- -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 "SslServerSession.h" - -using namespace ETP_NS; - -SslServerSession::SslServerSession(boost::beast::ssl_stream stream, ServerInitializationParameters* serverInitializationParams) - : AbstractPlainOrSslServerSession(static_cast(stream.get_executor().context()), serverInitializationParams), - ws_(std::move(stream)) -{ - ws_.binary(true); -} diff --git a/src/etp/ssl/SslServerSession.h b/src/etp/ssl/SslServerSession.h deleted file mode 100644 index 5ffdb18..0000000 --- a/src/etp/ssl/SslServerSession.h +++ /dev/null @@ -1,52 +0,0 @@ -/*----------------------------------------------------------------------- -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 "../AbstractPlainOrSslServerSession.h" - -#if BOOST_VERSION < 106800 -#include "ssl_stream.h" -#elif BOOST_VERSION < 107000 -#include -#else -#include -#include -#endif - -namespace ETP_NS -{ - class SslServerSession : public AbstractPlainOrSslServerSession - { - private: - websocket::stream> ws_; - - public: - FETPAPI_DLL_IMPORT_OR_EXPORT SslServerSession(boost::beast::ssl_stream stream, ServerInitializationParameters* serverInitializationParams); - - virtual ~SslServerSession() {} - - // Called by the base class - FETPAPI_DLL_IMPORT_OR_EXPORT websocket::stream>& ws() { return ws_; } - - FETPAPI_DLL_IMPORT_OR_EXPORT bool run(boost::beast::http::request req) { - on_handshake(boost::system::error_code(), std::move(req)); - return true; - } - }; -} diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt new file mode 100644 index 0000000..8b6b8a3 --- /dev/null +++ b/test/CMakeLists.txt @@ -0,0 +1,104 @@ +cmake_minimum_required (VERSION 3.12) + +add_executable (unitTest "") + +# ============================================================================ +# Getting source files +# ============================================================================ + +set(FETPAPI_TEST_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}/unitTest.cpp ) +set(FETPAPI_TEST_HEADERS ${CMAKE_CURRENT_SOURCE_DIR}/catch.hpp ) + +target_sources(unitTest PRIVATE ${FETPAPI_TEST_SOURCES} ${FETPAPI_TEST_HEADERS}) + +# ============================================================================ +# include directories +# ============================================================================ + +target_include_directories(unitTest PRIVATE ${FESAPI_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}/test/ ${CMAKE_SOURCE_DIR}/src/) +target_include_directories(unitTest SYSTEM PRIVATE ${AVRO_INCLUDE_DIR} ${Boost_INCLUDE_DIR} ${FETPAPI_INCLUDE_DIR}) +if (WITH_ETP_SSL) + target_include_directories(unitTest SYSTEM PRIVATE ${OPENSSL_INCLUDE_DIR}) +endif () + +if (WIN32) + #FROM https://stackoverflow.com/a/40217291 + macro(get_WIN32_WINNT version) + if(CMAKE_SYSTEM_VERSION) + set(ver ${CMAKE_SYSTEM_VERSION}) + string(REGEX MATCH "^([0-9]+).([0-9])" ver ${ver}) + string(REGEX MATCH "^([0-9]+)" verMajor ${ver}) + # Check for Windows 10, b/c we'll need to convert to hex 'A'. + if("${verMajor}" MATCHES "10") + set(verMajor "A") + string(REGEX REPLACE "^([0-9]+)" ${verMajor} ver ${ver}) + endif() + # Remove all remaining '.' characters. + string(REPLACE "." "" ver ${ver}) + # Prepend each digit with a zero. + string(REGEX REPLACE "([0-9A-Z])" "0\\1" ver ${ver}) + set(${version} "0x${ver}") + endif() + endmacro() + + get_WIN32_WINNT(ver) + target_compile_definitions(unitTest PRIVATE _WIN32_WINNT=${ver}) + + target_link_libraries (unitTest PRIVATE bcrypt.lib) + + set_target_properties (unitTest PROPERTIES + LINK_FLAGS "/INCREMENTAL:NO" + RUNTIME_OUTPUT_DIRECTORY ${FETPAPI_BINARY_DIR}) +endif (WIN32) + +if (NOT Boost_USE_STATIC_LIBS) + # https://www.boost.org/doc/libs/1_75_0/libs/log/doc/html/log/rationale/namespace_mangling.html + target_compile_definitions(unitTest PRIVATE BOOST_LOG_DYN_LINK) +endif () + +target_compile_definitions(unitTest PRIVATE BOOST_ALL_NO_LIB) +find_package( Threads ) +target_link_libraries (unitTest PRIVATE ${FESAPI_LIBRARY_RELEASE} ${Boost_SYSTEM_LIBRARY} ${CMAKE_THREAD_LIBS_INIT}) + +#OpenSSL +if (WITH_ETP_SSL) + target_compile_definitions(unitTest PRIVATE WITH_ETP_SSL) + if (NOT TARGET OpenSSL::SSL OR NOT TARGET OpenSSL::Crypto) + find_package(OpenSSL REQUIRED) + endif() + target_link_libraries(unitTest PRIVATE OpenSSL::SSL OpenSSL::Crypto) +endif () + +#AVRO +set (AVRO_INCLUDE_DIR AVRO_INCLUDE_DIR-NOTFOUND CACHE PATH "Path to the directory which contains the avro header files") +if (NOT IS_DIRECTORY ${AVRO_INCLUDE_DIR}) + message(FATAL_ERROR "The Avro include dir (AVRO_INCLUDE_DIR variable) does not look to be a valid directory. Please modify it.") +endif () +set (AVRO_LIBRARY_RELEASE AVRO_LIBRARY_RELEASE-NOTFOUND CACHE FILEPATH "Path to the file which contains the avro library release") +if (NOT EXISTS ${AVRO_LIBRARY_RELEASE}) + message(WARNING "The avro library (AVRO_LIBRARY_RELEASE variable) does not look to be a valid file. Please modify it.") +endif () +set (AVRO_LIBRARY_DEBUG AVRO_LIBRARY_DEBUG-NOTFOUND CACHE FILEPATH "Path to the file which contains the AVRO library DEBUG") +if (NOT EXISTS ${AVRO_LIBRARY_DEBUG}) + message(WARNING "The AVRO library (AVRO_LIBRARY_DEBUG variable) does not look to be a valid file. Please modify it.") +endif () + +if (WIN32) + if (EXISTS unitTest AND EXISTS ${AVRO_LIBRARY_DEBUG}) + target_link_libraries (unitTest PRIVATE optimized ${AVRO_LIBRARY_RELEASE} debug ${AVRO_LIBRARY_DEBUG}) + elseif (EXISTS ${AVRO_LIBRARY_RELEASE}) + target_link_libraries (unitTest PRIVATE ${AVRO_LIBRARY_RELEASE}) + elseif (EXISTS ${AVRO_LIBRARY_DEBUG}) + target_link_libraries (unitTest PRIVATE ${AVRO_LIBRARY_DEBUG}) + endif () +else (WIN32) + target_link_libraries (unitTest PRIVATE ${AVRO_LIBRARY_RELEASE}) +endif (WIN32) + +# ============================================================================ +# build fesapiTest +# ============================================================================ + +add_test(NAME allTests COMMAND unitTest) +add_dependencies (unitTest ${PROJECT_NAME}) +target_link_libraries (unitTest PRIVATE ${PROJECT_NAME}) diff --git a/test/catch.hpp b/test/catch.hpp new file mode 100644 index 0000000..9b309bd --- /dev/null +++ b/test/catch.hpp @@ -0,0 +1,17976 @@ +/* + * Catch v2.13.10 + * Generated: 2022-10-16 11:01:23.452308 + * ---------------------------------------------------------- + * This file has been merged from multiple headers. Please don't edit it directly + * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. + * + * Distributed under the Boost Software License, Version 1.0. (See accompanying + * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) + */ +#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED +// start catch.hpp + + +#define CATCH_VERSION_MAJOR 2 +#define CATCH_VERSION_MINOR 13 +#define CATCH_VERSION_PATCH 10 + +#ifdef __clang__ +# pragma clang system_header +#elif defined __GNUC__ +# pragma GCC system_header +#endif + +// start catch_suppress_warnings.h + +#ifdef __clang__ +# ifdef __ICC // icpc defines the __clang__ macro +# pragma warning(push) +# pragma warning(disable: 161 1682) +# else // __ICC +# pragma clang diagnostic push +# pragma clang diagnostic ignored "-Wpadded" +# pragma clang diagnostic ignored "-Wswitch-enum" +# pragma clang diagnostic ignored "-Wcovered-switch-default" +# endif +#elif defined __GNUC__ + // Because REQUIREs trigger GCC's -Wparentheses, and because still + // supported version of g++ have only buggy support for _Pragmas, + // Wparentheses have to be suppressed globally. +# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details + +# pragma GCC diagnostic push +# pragma GCC diagnostic ignored "-Wunused-variable" +# pragma GCC diagnostic ignored "-Wpadded" +#endif +// end catch_suppress_warnings.h +#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) +# define CATCH_IMPL +# define CATCH_CONFIG_ALL_PARTS +#endif + +// In the impl file, we want to have access to all parts of the headers +// Can also be used to sanely support PCHs +#if defined(CATCH_CONFIG_ALL_PARTS) +# define CATCH_CONFIG_EXTERNAL_INTERFACES +# if defined(CATCH_CONFIG_DISABLE_MATCHERS) +# undef CATCH_CONFIG_DISABLE_MATCHERS +# endif +# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) +# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER +# endif +#endif + +#if !defined(CATCH_CONFIG_IMPL_ONLY) +// start catch_platform.h + +// See e.g.: +// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html +#ifdef __APPLE__ +# include +# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ + (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) +# define CATCH_PLATFORM_MAC +# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) +# define CATCH_PLATFORM_IPHONE +# endif + +#elif defined(linux) || defined(__linux) || defined(__linux__) +# define CATCH_PLATFORM_LINUX + +#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) +# define CATCH_PLATFORM_WINDOWS +#endif + +// end catch_platform.h + +#ifdef CATCH_IMPL +# ifndef CLARA_CONFIG_MAIN +# define CLARA_CONFIG_MAIN_NOT_DEFINED +# define CLARA_CONFIG_MAIN +# endif +#endif + +// start catch_user_interfaces.h + +namespace Catch { + unsigned int rngSeed(); +} + +// end catch_user_interfaces.h +// start catch_tag_alias_autoregistrar.h + +// start catch_common.h + +// start catch_compiler_capabilities.h + +// Detect a number of compiler features - by compiler +// The following features are defined: +// +// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? +// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? +// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? +// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? +// **************** +// Note to maintainers: if new toggles are added please document them +// in configuration.md, too +// **************** + +// In general each macro has a _NO_ form +// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. +// Many features, at point of detection, define an _INTERNAL_ macro, so they +// can be combined, en-mass, with the _NO_ forms later. + +#ifdef __cplusplus + +# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) +# define CATCH_CPP14_OR_GREATER +# endif + +# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) +# define CATCH_CPP17_OR_GREATER +# endif + +#endif + +// Only GCC compiler should be used in this block, so other compilers trying to +// mask themselves as GCC should be ignored. +#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) + +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) + +#endif + +#if defined(__clang__) + +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) + +// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug +// which results in calls to destructors being emitted for each temporary, +// without a matching initialization. In practice, this can result in something +// like `std::string::~string` being called on an uninitialized value. +// +// For example, this code will likely segfault under IBM XL: +// ``` +// REQUIRE(std::string("12") + "34" == "1234") +// ``` +// +// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. +# if !defined(__ibmxl__) && !defined(__CUDACC__) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ +# endif + +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ + _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") + +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) + +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) + +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ + _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) + +#endif // __clang__ + +//////////////////////////////////////////////////////////////////////////////// +// Assume that non-Windows platforms support posix signals by default +#if !defined(CATCH_PLATFORM_WINDOWS) + #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS +#endif + +//////////////////////////////////////////////////////////////////////////////// +// We know some environments not to support full POSIX signals +#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) + #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +#endif + +#ifdef __OS400__ +# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS +# define CATCH_CONFIG_COLOUR_NONE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Android somehow still does not support std::to_string +#if defined(__ANDROID__) +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING +# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Not all Windows environments support SEH properly +#if defined(__MINGW32__) +# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH +#endif + +//////////////////////////////////////////////////////////////////////////////// +// PS4 +#if defined(__ORBIS__) +# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE +#endif + +//////////////////////////////////////////////////////////////////////////////// +// Cygwin +#ifdef __CYGWIN__ + +// Required for some versions of Cygwin to declare gettimeofday +// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin +# define _BSD_SOURCE +// some versions of cygwin (most) do not support std::to_string. Use the libstd check. +// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 +# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ + && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) + +# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING + +# endif +#endif // __CYGWIN__ + +//////////////////////////////////////////////////////////////////////////////// +// Visual C++ +#if defined(_MSC_VER) + +// Universal Windows platform does not support SEH +// Or console colours (or console at all...) +# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) +# define CATCH_CONFIG_COLOUR_NONE +# else +# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH +# endif + +# if !defined(__clang__) // Handle Clang masquerading for msvc + +// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ +// _MSVC_TRADITIONAL == 0 means new conformant preprocessor +// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor +# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) +# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +# endif // MSVC_TRADITIONAL + +// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) +# endif // __clang__ + +#endif // _MSC_VER + +#if defined(_REENTRANT) || defined(_MSC_VER) +// Enable async processing, as -pthread is specified or no additional linking is required +# define CATCH_INTERNAL_CONFIG_USE_ASYNC +#endif // _MSC_VER + +//////////////////////////////////////////////////////////////////////////////// +// Check if we are compiled with -fno-exceptions or equivalent +#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) +# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED +#endif + +//////////////////////////////////////////////////////////////////////////////// +// DJGPP +#ifdef __DJGPP__ +# define CATCH_INTERNAL_CONFIG_NO_WCHAR +#endif // __DJGPP__ + +//////////////////////////////////////////////////////////////////////////////// +// Embarcadero C++Build +#if defined(__BORLANDC__) + #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// Use of __COUNTER__ is suppressed during code analysis in +// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly +// handled by it. +// Otherwise all supported compilers support COUNTER macro, +// but user still might want to turn it off +#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) + #define CATCH_INTERNAL_CONFIG_COUNTER +#endif + +//////////////////////////////////////////////////////////////////////////////// + +// RTX is a special version of Windows that is real time. +// This means that it is detected as Windows, but does not provide +// the same set of capabilities as real Windows does. +#if defined(UNDER_RTSS) || defined(RTX64_BUILD) + #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH + #define CATCH_INTERNAL_CONFIG_NO_ASYNC + #define CATCH_CONFIG_COLOUR_NONE +#endif + +#if !defined(_GLIBCXX_USE_C99_MATH_TR1) +#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Various stdlib support checks that require __has_include +#if defined(__has_include) + // Check if string_view is available and usable + #if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW + #endif + + // Check if optional is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if byte is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # include + # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) + # define CATCH_INTERNAL_CONFIG_CPP17_BYTE + # endif + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) + + // Check if variant is available and usable + # if __has_include() && defined(CATCH_CPP17_OR_GREATER) + # if defined(__clang__) && (__clang_major__ < 8) + // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 + // fix should be in clang 8, workaround in libstdc++ 8.2 + # include + # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # define CATCH_CONFIG_NO_CPP17_VARIANT + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) + # else + # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT + # endif // defined(__clang__) && (__clang_major__ < 8) + # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) +#endif // defined(__has_include) + +#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) +# define CATCH_CONFIG_COUNTER +#endif +#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) +# define CATCH_CONFIG_WINDOWS_SEH +#endif +// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. +#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) +# define CATCH_CONFIG_POSIX_SIGNALS +#endif +// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. +#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) +# define CATCH_CONFIG_WCHAR +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) +# define CATCH_CONFIG_CPP11_TO_STRING +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) +# define CATCH_CONFIG_CPP17_OPTIONAL +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) +# define CATCH_CONFIG_CPP17_STRING_VIEW +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) +# define CATCH_CONFIG_CPP17_VARIANT +#endif + +#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) +# define CATCH_CONFIG_CPP17_BYTE +#endif + +#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) +# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) +# define CATCH_CONFIG_NEW_CAPTURE +#endif + +#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +# define CATCH_CONFIG_DISABLE_EXCEPTIONS +#endif + +#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) +# define CATCH_CONFIG_POLYFILL_ISNAN +#endif + +#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) +# define CATCH_CONFIG_USE_ASYNC +#endif + +#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) +# define CATCH_CONFIG_ANDROID_LOGWRITE +#endif + +#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) +# define CATCH_CONFIG_GLOBAL_NEXTAFTER +#endif + +// Even if we do not think the compiler has that warning, we still have +// to provide a macro that can be used by the code. +#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) +# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS +#endif +#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS +#endif + +// The goal of this macro is to avoid evaluation of the arguments, but +// still have the compiler warn on problems inside... +#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) +# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) +#endif + +#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#elif defined(__clang__) && (__clang_major__ < 5) +# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) +# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS +#endif + +#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) +#define CATCH_TRY if ((true)) +#define CATCH_CATCH_ALL if ((false)) +#define CATCH_CATCH_ANON(type) if ((false)) +#else +#define CATCH_TRY try +#define CATCH_CATCH_ALL catch (...) +#define CATCH_CATCH_ANON(type) catch (type) +#endif + +#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) +#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#endif + +// end catch_compiler_capabilities.h +#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line +#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) +#ifdef CATCH_CONFIG_COUNTER +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) +#else +# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) +#endif + +#include +#include +#include + +// We need a dummy global operator<< so we can bring it into Catch namespace later +struct Catch_global_namespace_dummy {}; +std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); + +namespace Catch { + + struct CaseSensitive { enum Choice { + Yes, + No + }; }; + + class NonCopyable { + NonCopyable( NonCopyable const& ) = delete; + NonCopyable( NonCopyable && ) = delete; + NonCopyable& operator = ( NonCopyable const& ) = delete; + NonCopyable& operator = ( NonCopyable && ) = delete; + + protected: + NonCopyable(); + virtual ~NonCopyable(); + }; + + struct SourceLineInfo { + + SourceLineInfo() = delete; + SourceLineInfo( char const* _file, std::size_t _line ) noexcept + : file( _file ), + line( _line ) + {} + + SourceLineInfo( SourceLineInfo const& other ) = default; + SourceLineInfo& operator = ( SourceLineInfo const& ) = default; + SourceLineInfo( SourceLineInfo&& ) noexcept = default; + SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; + + bool empty() const noexcept { return file[0] == '\0'; } + bool operator == ( SourceLineInfo const& other ) const noexcept; + bool operator < ( SourceLineInfo const& other ) const noexcept; + + char const* file; + std::size_t line; + }; + + std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); + + // Bring in operator<< from global namespace into Catch namespace + // This is necessary because the overload of operator<< above makes + // lookup stop at namespace Catch + using ::operator<<; + + // Use this in variadic streaming macros to allow + // >> +StreamEndStop + // as well as + // >> stuff +StreamEndStop + struct StreamEndStop { + std::string operator+() const; + }; + template + T const& operator + ( T const& value, StreamEndStop ) { + return value; + } +} + +#define CATCH_INTERNAL_LINEINFO \ + ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) + +// end catch_common.h +namespace Catch { + + struct RegistrarForTagAliases { + RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); + }; + +} // end namespace Catch + +#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ + CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ + CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ + namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ + CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION + +// end catch_tag_alias_autoregistrar.h +// start catch_test_registry.h + +// start catch_interfaces_testcase.h + +#include + +namespace Catch { + + class TestSpec; + + struct ITestInvoker { + virtual void invoke () const = 0; + virtual ~ITestInvoker(); + }; + + class TestCase; + struct IConfig; + + struct ITestCaseRegistry { + virtual ~ITestCaseRegistry(); + virtual std::vector const& getAllTests() const = 0; + virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; + }; + + bool isThrowSafe( TestCase const& testCase, IConfig const& config ); + bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); + std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); + std::vector const& getAllTestCasesSorted( IConfig const& config ); + +} + +// end catch_interfaces_testcase.h +// start catch_stringref.h + +#include +#include +#include +#include + +namespace Catch { + + /// A non-owning string class (similar to the forthcoming std::string_view) + /// Note that, because a StringRef may be a substring of another string, + /// it may not be null terminated. + class StringRef { + public: + using size_type = std::size_t; + using const_iterator = const char*; + + private: + static constexpr char const* const s_empty = ""; + + char const* m_start = s_empty; + size_type m_size = 0; + + public: // construction + constexpr StringRef() noexcept = default; + + StringRef( char const* rawChars ) noexcept; + + constexpr StringRef( char const* rawChars, size_type size ) noexcept + : m_start( rawChars ), + m_size( size ) + {} + + StringRef( std::string const& stdString ) noexcept + : m_start( stdString.c_str() ), + m_size( stdString.size() ) + {} + + explicit operator std::string() const { + return std::string(m_start, m_size); + } + + public: // operators + auto operator == ( StringRef const& other ) const noexcept -> bool; + auto operator != (StringRef const& other) const noexcept -> bool { + return !(*this == other); + } + + auto operator[] ( size_type index ) const noexcept -> char { + assert(index < m_size); + return m_start[index]; + } + + public: // named queries + constexpr auto empty() const noexcept -> bool { + return m_size == 0; + } + constexpr auto size() const noexcept -> size_type { + return m_size; + } + + // Returns the current start pointer. If the StringRef is not + // null-terminated, throws std::domain_exception + auto c_str() const -> char const*; + + public: // substrings and searches + // Returns a substring of [start, start + length). + // If start + length > size(), then the substring is [start, size()). + // If start > size(), then the substring is empty. + auto substr( size_type start, size_type length ) const noexcept -> StringRef; + + // Returns the current start pointer. May not be null-terminated. + auto data() const noexcept -> char const*; + + constexpr auto isNullTerminated() const noexcept -> bool { + return m_start[m_size] == '\0'; + } + + public: // iterators + constexpr const_iterator begin() const { return m_start; } + constexpr const_iterator end() const { return m_start + m_size; } + }; + + auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; + auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; + + constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { + return StringRef( rawChars, size ); + } +} // namespace Catch + +constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { + return Catch::StringRef( rawChars, size ); +} + +// end catch_stringref.h +// start catch_preprocessor.hpp + + +#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ +#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) +#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) + +#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ +// MSVC needs more evaluations +#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) +#else +#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) +#endif + +#define CATCH_REC_END(...) +#define CATCH_REC_OUT + +#define CATCH_EMPTY() +#define CATCH_DEFER(id) id CATCH_EMPTY() + +#define CATCH_REC_GET_END2() 0, CATCH_REC_END +#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 +#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 +#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT +#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) +#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) + +#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) + +#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) +#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) + +// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, +// and passes userdata as the first parameter to each invocation, +// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) +#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) + +#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) +#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ +#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ +#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) +#else +// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF +#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) +#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ +#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) +#endif + +#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ +#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) + +#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) + +#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) +#else +#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) +#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) +#endif + +#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ + CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) + +#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) +#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) +#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) +#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) +#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) +#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) +#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) +#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) +#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) +#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) +#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) + +#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N + +#define INTERNAL_CATCH_TYPE_GEN\ + template struct TypeList {};\ + template\ + constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ + template class...> struct TemplateTypeList{};\ + template class...Cs>\ + constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ + template\ + struct append;\ + template\ + struct rewrap;\ + template class, typename...>\ + struct create;\ + template class, typename>\ + struct convert;\ + \ + template \ + struct append { using type = T; };\ + template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ + struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ + template< template class L1, typename...E1, typename...Rest>\ + struct append, TypeList, Rest...> { using type = L1; };\ + \ + template< template class Container, template class List, typename...elems>\ + struct rewrap, List> { using type = TypeList>; };\ + template< template class Container, template class List, class...Elems, typename...Elements>\ + struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ + \ + template