diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index 4d7c057..9f90095 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -140,6 +140,8 @@ set(FS_FILES src/bofpath.cpp include/bofstd/bofuri.h src/bofuri.cpp + include/bofstd/bofserviceendpoint.h + src/bofserviceendpoint.cpp include/bofstd/bofhttprequest.h src/bofhttprequest.cpp include/bofstd/boffs.h diff --git a/lib/include/bofstd/bofserviceendpoint.h b/lib/include/bofstd/bofserviceendpoint.h new file mode 100644 index 0000000..407700e --- /dev/null +++ b/lib/include/bofstd/bofserviceendpoint.h @@ -0,0 +1,56 @@ +/* + * Copyright (c) 2013-2033, Onbings. All rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + * PURPOSE. + * + * This module defines the bofserviceendpoint module of the bofstd library + * + * Name: bofserviceendpoint.h + * Author: Bernard HARMEL: onbings@gmail.com + * Web: onbings.dscloud.me + * Revision: 1.0 + * + * Rem: None + * + * History: + * + * V 1.00 Aug 15 2024 BHA : Initial release + */ +#pragma once + +#include + +BEGIN_BOF_NAMESPACE() + +class BOFSTD_EXPORT BofServiceEndpoint +{ +public: + BofServiceEndpoint(); + BofServiceEndpoint(const std::string &_rServiceUrl_S); + BofServiceEndpoint(const std::string &_rServiceName_S, const std::string &_rServiceInstance_S, const std::string &_rServiceUrl_S, + const std::set &_rServiceTagCollection); + ~BofServiceEndpoint(); + bool IsValid(); + std::string ToString(bool _ShowName_B); + + bool SetServiceName(const std::string &_rServiceName_S); + bool SetServiceInstance(const std::string &_rServiceInstance_S); + bool SetServiceUrl(const std::string &_rServiceUrl_S); + bool SetServiceTagCollection(const std::set &_rServiceTagCollection); + + const std::string GetServiceName() const; + const std::string GetServiceInstance() const; + const std::string GetServiceUrl() const; + const std::set GetServiceTagCollection() const; + +private: + BofUri mUri; + std::string mServiceName_S; + std::string mServiceInstance_S; + std::set mServiceTagCollection; +}; + +END_BOF_NAMESPACE() \ No newline at end of file diff --git a/lib/include/bofstd/bofsocketos.h b/lib/include/bofstd/bofsocketos.h index 033fde4..6c90c67 100644 --- a/lib/include/bofstd/bofsocketos.h +++ b/lib/include/bofstd/bofsocketos.h @@ -26,6 +26,7 @@ #include #include #include +#include #if defined(_WIN32) #define NOMINMAX @@ -789,4 +790,6 @@ BOFSTD_EXPORT BOFERR Bof_GetCompatibleIpAddress(const std::vector &_rQueryParamCollection); }; + END_BOF_NAMESPACE() \ No newline at end of file diff --git a/lib/src/bofserviceendpoint.cpp b/lib/src/bofserviceendpoint.cpp new file mode 100644 index 0000000..a6dc961 --- /dev/null +++ b/lib/src/bofserviceendpoint.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2013-2033, Onbings. All rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + * PURPOSE. + * + * This module implements the bofserviceendpoint module of the bofstd library + * + * Name: bofserviceendpoint.cpp + * Author: Bernard HARMEL: onbings@dscloud.me + * Web: onbings.dscloud.me + * Revision: 1.0 + * + * Rem: Nothing + * + * History: + * + * V 1.00 Aug 15 2024 BHA : Initial release + */ +#include + +BEGIN_BOF_NAMESPACE() + +BofServiceEndpoint::BofServiceEndpoint():mUri() +{ +} +BofServiceEndpoint::BofServiceEndpoint(const std::string &_rServiceUrl_S) :mUri(_rServiceUrl_S) +{ +} +BofServiceEndpoint::BofServiceEndpoint(const std::string &_rServiceName_S, const std::string &_rServiceInstance_S, const std::string &_rServiceUrl_S, + const std::set &_rServiceTagCollection) + : mServiceName_S(_rServiceName_S), mServiceInstance_S(_rServiceInstance_S), mUri(_rServiceUrl_S),mServiceTagCollection(_rServiceTagCollection) +{ +} +BofServiceEndpoint::~BofServiceEndpoint() +{ +} +bool BofServiceEndpoint::IsValid() +{ + return mUri.IsValid(); +} +std::string BofServiceEndpoint::ToString(bool _ShowName_B) +{ + std::string Rts; + + if (_ShowName_B) + { + Rts = mServiceName_S + '(' + mServiceInstance_S + ")@"; + } + Rts += mUri.ToString(); + return Rts; +} +bool BofServiceEndpoint::SetServiceName(const std::string &_rServiceName_S) +{ + mServiceName_S = _rServiceName_S; + return true; +} + +bool BofServiceEndpoint::SetServiceInstance(const std::string &_rServiceInstance_S) +{ + mServiceInstance_S = _rServiceInstance_S; + return true; +} + +bool BofServiceEndpoint::SetServiceUrl(const std::string &_rServiceUrl_S) +{ + return (mUri.SetAuthority(_rServiceUrl_S) == BOF_ERR_NO_ERROR); +} + +bool BofServiceEndpoint::SetServiceTagCollection(const std::set &_rServiceTagCollection) +{ + mServiceTagCollection = _rServiceTagCollection; + return true; +} + +const std::string BofServiceEndpoint::GetServiceName() const +{ + return mServiceName_S; +} + +const std::string BofServiceEndpoint::GetServiceInstance() const +{ + return mServiceInstance_S; +} + +const std::string BofServiceEndpoint::GetServiceUrl() const +{ + std::string Rts_S; + BOF::BOF_SOCKET_ADDRESS SocketAddress_X; + + SocketAddress_X = mUri.IpAddress(Rts_S); + return Rts_S; +} + +const std::set BofServiceEndpoint::GetServiceTagCollection() const +{ + return mServiceTagCollection; +} + +END_BOF_NAMESPACE() \ No newline at end of file diff --git a/lib/src/bofsocketos.cpp b/lib/src/bofsocketos.cpp index a275e50..111bebd 100644 --- a/lib/src/bofsocketos.cpp +++ b/lib/src/bofsocketos.cpp @@ -23,6 +23,7 @@ #include #include #include +#include #include #include @@ -1495,14 +1496,46 @@ var regexMailto = / ^ (mailto) : ((? : [a - z0 - 9 - ._~!$ & '()*+,;=:@]|%[0-9A- BOFERR Bof_SplitUri(const std::string &_rUri_S, BOF_SOCKET_ADDRESS_COMPONENT &_rUri_X, std::string &_rPath_S, std::string &_rQuery_S, std::string &_rFragment_S) { BOFERR Rts_E = BOF_ERR_FORMAT; + static const std::regex S_RegExUri( "^([a-z][a-z0-9+.-]*):(?:\\/\\/((?:(?=((?:[a-z0-9-._~!$&'()*+,;=:]|%[0-9A-F]{2})*))(\\3)@)?(?=(\\[[0-9A-F:.]{2,}\\]|(?:[a-z0-9-._~!$&'()*+,;=]|%[0-9A-F]{2})*))\\5(?::(?=(\\d*))\\6)?)(\\/(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\8)?|(\\/" "?(?!\\/)(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/]|%[0-9A-F]{2})*))\\10)?)(?:\\?(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\11)?(?:#(?=((?:[a-z0-9-._~!$&'()*+,;=:@\\/?]|%[0-9A-F]{2})*))\\12)?$"); // Static as it can takes time (on gcc 4.9 for - // example) + std::smatch MatchString; std::string::size_type PosColumn; std::vector ListOfIpAddress_X; + /* +{ size=13 } +[0] "myprotocol://john.doe:password@1.2.3.4:123/forum/questions/file.txt?justkey&order=newest;tag=networking#top" +[1] "myprotocol" +[2] "john.doe:password@1.2.3.4:123" +[3] "john.doe:password" +[4] "john.doe:password" +[5] "1.2.3.4" +[6] "123" +[7] "/forum/questions/file.txt" +[8] "forum/questions/file.txt" +[9] false +[10] false +[11] "justkey&order=newest;tag=networking" +[12] "top" + +[0] "protocol:/forum/questions/thefile.txt?justkey;order=newest&tag=networking#top" +[1] "protocol" +[2] false +[3] false +[4] false +[5] false +[6] false +[7] false +[8] false +[9] "/forum/questions/thefile.txt" +[10] "forum/questions/thefile.txt" +[11] "justkey;order=newest&tag=networking" +[12] "top" + */ + _rUri_X.Reset(); if (std::regex_search(_rUri_S, MatchString, S_RegExUri)) { @@ -2338,6 +2371,37 @@ bool Bof_IsIpAddressPingable(uint32_t _TimeoutInMs_U32, const std::string &_rIpA int ExitCode_i; #if defined(_WIN32) + /* + https://stackoverflow.com/questions/9329749/batch-errorlevel-ping-response + ping errorlevel (exit code) is always 0 and in case of bad ip address it need a n value of 2 to "see" the problem.... + + Problem: + C:\tmp>ping -n 1 -w 1000 1.2.3.4 + Pinging 1.2.3.4 with 32 bytes of data: + Reply from 194.42.74.25: Destination net unreachable. + Ping statistics for 1.2.3.4: + Packets: Sent = 1, Received = 1, Lost = 0 (0% loss),<=====!!!! + + Batch example: + set error=failure + ping -n 2 -w 1000 1.2.3.4 2>&1 && set error=success + REM ping -n 2 -w 1000 10.129.170.14 2>&1 && set error=success + echo %errorlevel% + echo %error% + + Case Ok in ipv4: + C:\tmp>ping 10.129.170.14 + Pinging 10.129.170.14 with 32 bytes of data: + Reply from 10.129.170.14: bytes=32 time=1ms TTL=63 + Reply from 10.129.170.14: bytes=32 time=1ms TTL=63 + + Ping statistics for 10.129.170.14: + Packets: Sent = 2, Received = 2, Lost = 0 (0% loss), + Approximate round trip times in milli-seconds: + Minimum = 1ms, Maximum = 1ms, Average = 1ms + + =>Check that TTL= is present in output + */ snprintf(pPingCmd_c, sizeof(pPingCmd_c), "ping -n 1 -w %d %s", _TimeoutInMs_U32, _rIpAddress_S.c_str()); #else snprintf(pPingCmd_c, sizeof(pPingCmd_c), "ping -c 1 -w %d %s", _TimeoutInMs_U32 / 1000, _rIpAddress_S.c_str()); @@ -2345,6 +2409,15 @@ bool Bof_IsIpAddressPingable(uint32_t _TimeoutInMs_U32, const std::string &_rIpA if (BofProcess::S_Execute_popen(pPingCmd_c, Output_S, ExitCode_i) == BOF_ERR_NO_ERROR) { Rts_B = (ExitCode_i == 0); +#if defined(_WIN32) + if (Rts_B) + { + if (Output_S.find("TTL=") == std::string::npos) + { + Rts_B = false; + } + } +#endif } return Rts_B; } @@ -2402,4 +2475,6 @@ bool Bof_IsIpAddressOpened(uint32_t _TimeoutInMs_U32, const std::string &_rIpAdd } return Rts_B; } + + END_BOF_NAMESPACE() \ No newline at end of file diff --git a/lib/src/bofuri.cpp b/lib/src/bofuri.cpp index f96dc75..bd65218 100644 --- a/lib/src/bofuri.cpp +++ b/lib/src/bofuri.cpp @@ -335,11 +335,13 @@ std::string BofUri::Authority() const return Rts_X.ToString(true, true, true, true); } + const BOF_SOCKET_ADDRESS &BofUri::IpAddress(std::string &_rIpAddress_S) const { _rIpAddress_S = mSchemeAuthority_X.Protocol_S + "://" + Bof_SocketAddressToString(mSchemeAuthority_X.Ip_X, false, true); return mSchemeAuthority_X.Ip_X; } + const BofPath &BofUri::Path(std::string &_rPath_S) const { _rPath_S = mPath.FullPathName(false); @@ -423,6 +425,7 @@ BOFERR BofUri::InitUriField(const std::string &_rUri_S) if (Rts_E == BOF_ERR_NO_ERROR) { mSchemeAuthority_X = Uri_X; + Bof_IpAddressToSocketAddress(mSchemeAuthority_X.ToString(true,false,false,true), mSchemeAuthority_X.Ip_X); mPath = BofPath(Path_S); mFragment_S = Fragment_S; if (mPath.IsValid()) diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index d057c3a..40af8ad 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -96,6 +96,7 @@ set(ENUM_FLAG_FILES set(FS_FILES src/ut_path.cpp src/ut_uri.cpp + src/ut_serviceendpoint.cpp src/ut_fs.cpp ) diff --git a/tests/src/main.cpp b/tests/src/main.cpp index 7ce527e..ec55fa1 100644 --- a/tests/src/main.cpp +++ b/tests/src/main.cpp @@ -139,7 +139,8 @@ int main(int argc, char *argv[]) //::testing::GTEST_FLAG(filter) = "RawCircularBuffer_Test.*:CircularBuffer_Test.*:RawCircularBufferInSlotMode_Test.*"; //::testing::GTEST_FLAG(filter) = "BofThreadPool_Test.Dispatch"; //::testing::GTEST_FLAG(filter) = "ConIo_Test.*"; - //::testing::GTEST_FLAG(filter) = "SocketOs_Test.*"; +// ::testing::GTEST_FLAG(filter) = "ServiceEndPoint_Test.*:SocketOs_Test.*:Uri_Test.*"; + //::testing::GTEST_FLAG(filter) = "ServiceEndPoint_Test.*"; // ::testing::GTEST_FLAG(filter) = "RawCircularBufferAlwaysContiguous_Test.*:RawCircularBuffer_Test.*:RawCircularBufferInSlotMode_Test.*"; // std::string CrtDir_S; // BOF::Bof_GetCurrentDirectory(CrtDir_S); diff --git a/tests/src/ut_serviceendpoint.cpp b/tests/src/ut_serviceendpoint.cpp new file mode 100644 index 0000000..4ebef3c --- /dev/null +++ b/tests/src/ut_serviceendpoint.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2013-2023, OnBings All rights reserved. + * + * THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY + * KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR + * PURPOSE. + * + * This module implements the unit testing of the comchannel class + * + * Name: ut_serviceendpoint.cpp + * Author: Bernard HARMEL: onbings@dscloud.me + * Web: onbings.dscloud.me + * Revision: 1.0 + * + * Rem: Nothing + * + * History: + * + * V 1.00 Aug 15 2024 BHA : Initial release + */ +#include "bofstd/bofserviceendpoint.h" + +#include "gtestrunner.h" + +USE_BOF_NAMESPACE() + +TEST(ServiceEndPoint_Test, ServiceEndpoint) +{ + BOF::BofServiceEndpoint Endpoint; + std::set TagCollection; + + BOF_SOCKET_ADDRESS_COMPONENT Uri_X; + std::string Path_S, Query_S, Fragment_S; + BOFERR Sts_E; + // john.doe:password@www.google.com:123/forum/questions/file.txt?justkey&order=newest;tag=networking#top + /// myprotocol: / forum / questions / file.txt ? justkey & order = newest; tag = networking#top + Sts_E = Bof_SplitUri("myprotocol://john.doe:password@1.2.3.4:123/forum/questions/file.txt?justkey&order=newest;tag=networking#top", Uri_X, Path_S, Query_S, Fragment_S); + EXPECT_EQ(Sts_E, BOF_ERR_NO_ERROR); + EXPECT_STREQ(Uri_X.Protocol_S.c_str(), "myprotocol"); + EXPECT_STREQ(Uri_X.User_S.c_str(), "john.doe"); + EXPECT_STREQ(Uri_X.Password_S.c_str(), "password"); + EXPECT_STREQ(Uri_X.IpAddress_S.c_str(), "1.2.3.4"); + EXPECT_EQ(Uri_X.Port_U16, 123); + EXPECT_STREQ(Path_S.c_str(), "/forum/questions/file.txt"); + EXPECT_STREQ(Query_S.c_str(), "justkey&order=newest;tag=networking"); + EXPECT_STREQ(Fragment_S.c_str(), "top"); + + Sts_E = Bof_SplitUri("protocol:/forum/questions/thefile.txt?justkey;order=newest&tag=networking#top", Uri_X, Path_S, Query_S, Fragment_S); + EXPECT_EQ(Sts_E, BOF_ERR_NO_ERROR); + EXPECT_STREQ(Uri_X.Protocol_S.c_str(), "protocol"); + EXPECT_STREQ(Uri_X.User_S.c_str(), ""); + EXPECT_STREQ(Uri_X.Password_S.c_str(), ""); + EXPECT_STREQ(Uri_X.IpAddress_S.c_str(), ""); + EXPECT_EQ(Uri_X.Port_U16, 0); + EXPECT_STREQ(Path_S.c_str(), "/forum/questions/thefile.txt"); + EXPECT_STREQ(Query_S.c_str(), "justkey;order=newest&tag=networking"); + EXPECT_STREQ(Fragment_S.c_str(), "top"); + + + EXPECT_FALSE(Endpoint.IsValid()); + EXPECT_STREQ(Endpoint.ToString(false).c_str(), ""); + EXPECT_STREQ(Endpoint.ToString(true).c_str(), "()@"); + EXPECT_STREQ(Endpoint.GetServiceName().c_str(), ""); + EXPECT_STREQ(Endpoint.GetServiceInstance().c_str(), ""); + EXPECT_STREQ(Endpoint.GetServiceUrl().c_str(), "://0.0.0.0:0"); + TagCollection = Endpoint.GetServiceTagCollection(); + EXPECT_EQ(TagCollection.size(), 0); + + Endpoint = BOF::BofServiceEndpoint("http://10.129.170.14"); + EXPECT_TRUE(Endpoint.IsValid()); + EXPECT_STREQ(Endpoint.ToString(false).c_str(), "http://10.129.170.14"); + EXPECT_STREQ(Endpoint.ToString(true).c_str(), "()@http://10.129.170.14"); + EXPECT_STREQ(Endpoint.GetServiceName().c_str(), ""); + EXPECT_STREQ(Endpoint.GetServiceInstance().c_str(), ""); + EXPECT_STREQ(Endpoint.GetServiceUrl().c_str(), "http://10.129.170.14:0"); + TagCollection = Endpoint.GetServiceTagCollection(); + EXPECT_EQ(TagCollection.size(), 0); + + Endpoint = BOF::BofServiceEndpoint("http://10.129.170.14:8500"); + EXPECT_TRUE(Endpoint.IsValid()); + EXPECT_STREQ(Endpoint.ToString(false).c_str(), "http://10.129.170.14:8500"); + EXPECT_STREQ(Endpoint.ToString(true).c_str(), "()@http://10.129.170.14:8500"); + EXPECT_STREQ(Endpoint.GetServiceName().c_str(), ""); + EXPECT_STREQ(Endpoint.GetServiceInstance().c_str(), ""); + EXPECT_STREQ(Endpoint.GetServiceUrl().c_str(), "http://10.129.170.14:8500"); + TagCollection = Endpoint.GetServiceTagCollection(); + EXPECT_EQ(TagCollection.size(), 0); + + TagCollection.insert("Tag1"); + TagCollection.insert("Tag2=Deux"); + TagCollection.insert("Tag3:Three"); + Endpoint = BOF::BofServiceEndpoint("ServiceName", "ServiceInstance","http://10.129.170.14:8500", TagCollection); + EXPECT_TRUE(Endpoint.IsValid()); + EXPECT_STREQ(Endpoint.ToString(false).c_str(), "http://10.129.170.14:8500"); + EXPECT_STREQ(Endpoint.ToString(true).c_str(), "ServiceName(ServiceInstance)@http://10.129.170.14:8500"); + EXPECT_STREQ(Endpoint.GetServiceName().c_str(), "ServiceName"); + EXPECT_STREQ(Endpoint.GetServiceInstance().c_str(), "ServiceInstance"); + EXPECT_STREQ(Endpoint.GetServiceUrl().c_str(), "http://10.129.170.14:8500"); + TagCollection = Endpoint.GetServiceTagCollection(); + EXPECT_EQ(TagCollection.size(), 3); + auto It = TagCollection.find("Tag1"); + EXPECT_TRUE(It != TagCollection.end()); + It = TagCollection.find("Tag2=Deux"); + EXPECT_TRUE(It != TagCollection.end()); + It = TagCollection.find("Tag3:Three"); + EXPECT_TRUE(It != TagCollection.end()); +} + diff --git a/tests/src/ut_socketos.cpp b/tests/src/ut_socketos.cpp index 2f9d625..4139047 100644 --- a/tests/src/ut_socketos.cpp +++ b/tests/src/ut_socketos.cpp @@ -31,9 +31,9 @@ USE_BOF_NAMESPACE() TEST(SocketOs_Test, IsIpAddressOpened) { EXPECT_TRUE(Bof_IsIpAddressOpened(1000, "tcp://10.129.170.15:7")); // Echo protocol: https://medium.com/@bzami.ayman/echo-protocol-4cb25db9ae28 - EXPECT_TRUE(Bof_IsIpAddressOpened(1000, "tcp://10.129.170.15:8")); // Echo protocol: https://medium.com/@bzami.ayman/echo-protocol-4cb25db9ae28 + EXPECT_FALSE(Bof_IsIpAddressOpened(1000, "tcp://10.129.170.15:8")); // Echo protocol: https://medium.com/@bzami.ayman/echo-protocol-4cb25db9ae28 EXPECT_TRUE(Bof_IsIpAddressOpened(1000, "udp://10.129.170.15:7")); // Echo protocol: https://medium.com/@bzami.ayman/echo-protocol-4cb25db9ae28 - EXPECT_TRUE(Bof_IsIpAddressOpened(1000, "udp://10.129.170.15:8")); // Echo protocol: https://medium.com/@bzami.ayman/echo-protocol-4cb25db9ae28 + EXPECT_FALSE(Bof_IsIpAddressOpened(1000, "udp://10.129.170.15:8")); // Echo protocol: https://medium.com/@bzami.ayman/echo-protocol-4cb25db9ae28 } TEST(SocketOs_Test, Ping) diff --git a/vcpkg.json b/vcpkg.json index 95459fe..a010ca9 100644 --- a/vcpkg.json +++ b/vcpkg.json @@ -1,6 +1,6 @@ { "name": "bofstd", - "version": "5.7.4.3", + "version": "5.8.1.2", "description": "The onbings general purpose C++ Multiplatform library", "dependencies": [ {