From 7852153db56737d6cac8a0f82336880b7b31301c Mon Sep 17 00:00:00 2001 From: Aleksandar Fabijanic Date: Sun, 15 May 2022 11:05:56 -0500 Subject: [PATCH] Devel (#3586) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Enable unit test in cmake build * add BLOB SQLite test * accept notifications by name (if they have one) * catch std::exception on parsing * fix a leak, add some table features * few PostgreSQL fixes * GH #2351: WebSocket docs * Rename pcre internal symbols used by Poco to avoid symbol collision https://github.com/pocoproject/poco/issues/2916 This patch was backported from https://github.com/pld-linux/poco/blob/master/pcre.patch * Fix warning in clang * Fix MSVC clang build fail * Zip and SevenZip do not depend on Util, XML, JSON * Added Test and new Pattern 'O' to only log the Filename not the full Path. * Updated Comment * Configuration to receive OCSP stapling response for client connections and callback implementation to verify the response if the server returns any response * removed SDK version from project files * run Application::initialize() in try-catch block * fix Invalid condition [ICMPv4PacketImpl.cpp:234] #2783 * style fixes for #2935; check OpenSSL version * Fixed issue 2945 (#2946) * Fixed #2945 * Added unit tests for #2945 * Dissalow iterator on empty Var (#2945) * Updated unit tests for #2945 * More concise unit tests for #2945 * Removed some more clutter (#2945) * NetSSL_Win: fix potential endless loop due to wrong error handling * fixed GH #2970: Poco::Data::TypeHandler>::prepare() must prepare with underlying type, not Poco::Data::Keywords::null * Fixed linking with Data ODBC error on some platforms * Fix set padding call for new versions of OpenSSL * PatternFormatter priorityNames fix * PKCS12Container: fix memory leaks on exceptions * Fix constness of URI::getPathSegments * Fix typo in the ThreadPool's docs * cmake: use GNUInstallDirs * Changed EventHandlerMap key (#3116) * Changed EventHandlerMap key Changed EventHandlerMap key from Socket to poco_socket_t to avoid errors in removing/access EventHandlerMap when for example we make an SSL handshake * Changed EventHandlerMap key Changed EventHandlerMap key from Socket to poco_socket_t to avoid errors in removing/access EventHandlerMap when for example we make an SSL handshake * avoid too much call to sockfd() and impl() * Fix configuration error while cross compiling (#3127) During the configuration phase in a cross compilation scenario, `include(InstallRequiredSystemLibraries)` fails even if `MSVC_REDIST_DIR` is provided. This should not be an hard error, in case someone wants to compile/use the library, and not package it. As explained on https://reviews.llvm.org/D41220, the most sensible fix is to include `InstallRequiredSystemLibraries` only on a Windows host. * crash when remove key from JSON::Object(JSON_PRESERVE_KEY_ORDER). (#3151) * #3153: Poco::Data::ODBC [N]VARCHAR(MAX) wrong maxDataSize() result * [SharedPtr] Poco::makeSharedArray #3200 * #3202: JWT: ESxxx signature must include padding for ECDSA R and S values * feat(HashRange): port HashRange from boost * chore(cmake): spelling typo fix * fix(hashRange): change function names casing * merge changes from 1.10.2 * formatting * merge JSON formatting changes from 1.10.2 * added Application::windowSize() * RemoteSyslogChannel/RemoteSyslogListener: make UDP buffer size configurable * merge fix from 1.10.2 * merge Postgres fixes from 1.10.2 * #2993: The Sec-WebSocket-Key of WebSocket is always the same one * formatting * #2927 * remove HowToGetHelp page due to outdated information * formatting * #3044: Upgrade PCRE to 8.44 * #3086: Use POCO_IOS_INIT_HACK for Linux in combination with libc++ * #3095: Digest proxy authentication does not work in 1.10.1 * #3136: Fixed null character issue when parsing a JSON * #3114: Added JSON Array::empty() method * #3230: ECDSADigestEngine: include missing header * fix include order * include order * fix(PollSet): #3248 #3249 * chore(UDPServer): fix spelling * feat(SocketReactor): extract and expose poll() as a member function * feat(Endpoint): add Endpoint (socket address directly wrapping native structures) * fix(Endpoint): osx build, align family enum with patform, some other adjustments * fix(EndpointTest): add missing include * feat(Endpoint): add some low-level accessors * feat(IPAddress): add functions returning addres as raw bytes * feat(DatagramSocket): DatagramSocket does not allow IPV6_V6ONLY #3283 * feat(SocketReactor): Add completion handling to SocketReactor #3290 * feat(SocketReactor): Add completion handling to SocketReactor #3290 (add scheduled handlers and runOne()) * chore(Net-testsuite): fix some tests warnings * feat: move semantics for sockets and addresses * fix(NetworkInterface): Unterminated string possible in NetworkInterfaceImpl::setPhyParams() #3301 * feat(Net): Add move semantics to Net (sockets and addresses) #3296 * fix(HostEntry): DNS HostEntry returns multiple entries #3303 * fix(SocketReactor): completion handling fixes and tests, separation of i/o and completion mutexes * feat(SocketReactor): execute permanent completion handlers on when there are I/O handlers and the expired ones whenever they expire * feat(Socket): expose lastError() * fix(SocketReactor): windows compile * windows fixes, remove Endnpoint * feat(Socket): expose error() * feat(PollSet): Use select() on windows for PollSet #3339 * add ci.yml * fix compile errors * revert(SocketReactor): back to devel branch * feat(SocketProactor): initial version w/ completion handler and executor * chore(SocketProactor): Sockets package * chore(ci): add dir and run script * feat(SocketProactor): add socket error handling #3357 * modify(SocketProactor): wait for completion handlers availability #3357 * feat(PollSet): Make PollSet::poll() interruptible #3359 * modify(SocketProactor): make addSend() public #3357 * modify(SocketProactor): platform non-interrupt sleep without Poco thread #3357 * modify(SocketProactor): allow restart #3357 * fix(SocketProactor): windows fixes and VS build * fix(SocketProactor): race when stop() is called before run() * fix(Socket): Windows SO_REUSEADDR is neither reliable nor safe #3380 * fix(SocketProactor): rvalue refs should not be const * fix(DNS): DNS::hostByAddress not thread-safe #3381 * chore(SocketProactor): remove unnecessary this capture * fix(IPAddress): IPAddress::tryParse does not work for :: #3385 * fix(SoccketProactor): add some try/catch safety nets; broaden the work list mutex coverage to protect all function access * fix(SocketProactor): use Poco::Mutex only * fix(SocketProactor): scheduled work skipped in the absence of socket events * fix(SocketProactor): test * chore (SocketProactor): add some state members * Fix clang linker problem by defining POCO_NO_AUTOMATIC_LIBS (#3177) * Dev/devel 1.12.0 (#3585) * fix(PollSet): #3248 #3249 * bump version to 1.11.0 * updated changelog| * #3299: NetSSL: Allow per-Context InvalidCertificateHandler * #3022: Process::isRunning(PID pid) causes handle leak on Windows * #3022: fix for WinCE * upgrade bundled pdjson to latest master * update build configs - add support for Apple Silicon * #2906, #2904: Support environments without hardware floating point * #3130: fix error handling: report original error before close() * #3107: remove unused variable * #3219: SMTPClientSession: invalid SMTP command if empty recipients list in MailMessage * Poco::trim*() code cleanup - use ptrdiff_t instead of int; additional test cases * #3182 Poco::Process:launch on MacOS BigSur: limit maximum number of file descriptors to close before exec() to 100000 * #3278: Fixing no hardware floating point support - Part II * #3090: Do not initialize value with undefined behavior * #3163: Correct Var::parse null value * #3196: std::forward for Poco::Optional ctor with rvalue * #3068: Documented ENABLE_JWT option * #3041: PostgreSQL and TEXT column type * #3099: Fixed Postgres extraction into Dynamic::Var * #3138: Add support of arch riscv32 * #2825: riscv: Enable double operations when using double float abi * #3166: Fix PostgresSQL BLOB extractor * #3237: An error in the documentation for Poco/JSON/Parser.h * #3193: ServerApplication::registerService() unquoted path security vulnerability * #3266: Order of Util::Application::uninitialize() is not in reverse as documented * #3215: XML parser returns item from different element in a array * #3282: Update constant in setContentType documentation * #3089: HTTPSessionFactory does not support HTTPClientSession::ProxyConfig * #2418: SecureServerSocket doesn't work with IpV6 * fix warnings * #3019: ObjectPool wait on borrow condition fix * #3224: Remove SSL23 support from Poco/Crypto * #3191: Fixing a bug in the NetSSL_Win module (Host name verification failed error) * disallow SSLv3 * #3269: Poco::Net::Context initialization with empty certificateFile * #3307: Poco::Crypto::X509Certificate: obtain certificate fingerprint * #3260: Memory leak in EVPPKey::loadKey used with files & wrong password * #3157: fix(openssl): add missing dependency to OpenSSL components * #3066: CMake warning about MYSQL casing * #3135: Poco::Data::SQLite::Utility::fileToMemory unsuccessful if journal exists * #3217: CMake: warning message with -DPOCO_STATIC confusing * #3274: Fix localtime_r for VxWorks 6.9 and later * #2746, #3169: Fix race condition on TCPServerDispatcher stop * #3092: add more detailed error description when LoadLibrary fails| * #3074: Fix sessions may not return back to the pool * #3309: optimize parsing from stream (no copying of entire JSON to memory); limit maximum depth to avoid stack overflow with malicious documents (fuzzing - #3285); code cleanup * JSON Parser performance improvements * #3310: Upgrade bundled SQLite to 3.35.5 * fix UB/bad cast in TCPServerTest.cpp * add comment regarding potential UB in AnyTest::testCastToReference() * support sanitizers in build configs * bump version * fix 'catching polymorphic type by value' warnings * fix 'catching polymorphic type by value' warnings * fix 'catching polymorphic type by value' warnings * remove failing Android build; add sanitizer builds * update postgres version * fix warning * fix warning * add GitHub workflow * fix ci.yml * fix ci.yml * additional ci builds * fix ci.yml for macos and windows * fix(double-conversion): Upgrade bundled double-conversion #3313 * ci fixes * #3314: NetSSL_OpenSSL: any.pem certificate error: ca md too weak * testReuseSession: remove bad checks for session reuse * investigate failing test * investigate failing test * investigate failing test * investigate failing test * ci * remove travis and appveyor * ci, readme * ci fixes * fix ci * fix ci * fix ci * fix memory leak when ignoring test error/failure * fix ci * don't define UNREACHABLE as poco_bugcheck as it triggers 'control reaches end of non-void function' warning * add Linux cross build, build Data libs on macos * fix ci * add MySQL include/lib search paths for Homebrew * ci fixes * ci fixes * ci fixes * ci fixes * fix indluce paths for brew mysql * #3317: Data::MySQL MySQL headers and library search paths * fix ARM-Linux build config * fix MySQL.make * update FindMySQL.cmake * fix(SocketReactor): fix dataCollection test * chore: remove troubleshooting help leftovers * #3302: MSVC: Poco hides warnings (C4996) for the C++14 attribute [[deprecated]] * fix potential crash in testAsyncNotify: don't delete event object while async notification is still in progress * fix(PollSetTest): change connect to blocking * added ActiveRecord library and compiler * added dependencies file * update copyright dates * ActiveRecord: project files and fixes for MSVC * ci: enable ActiveRecord on Windows * fix(PollSetTest): remove poll timing check (fails on msvc ci) * fix ActiveRecord CMake build and configuration * feat(build): add gen directory (for generated sources) and macchina lib link dirs (if needed) * #3318: Data: Support Poco::UUID for data binding * ODBC tests for UUID, updated ActiveRecord projects * ActiveRecord user guide * update ActiveRecord documentation * documentation fixes * #3321: manually merge ODBC text encoding support * CppParser: merge changes from internal repository * updated Makefile * AbstractObserver::accepts() - add optional name parameter * fix SharedPtr::makeSharedArray() [merge from devel] * remove blank line * #2895, #2935: support OCSP stapling * style * clang support (merge from devel) * #3322: remove useless struct * link libmariadb instead of libmysql if headers indicate MariaDB * fix nullptr passed to memcmp/memcpy reported by ubsan * fix nullptr passed to memcmp/memcpy reported by ubsan * fix PageCompiler cross-compile; fix Content-Security-Policy header * remove Data release notes page * style, remove unused var * update docs * improve BLOB handling, clean-up code * fix(ICMPv4Packet): [asan] Undefined behavior in ICMPv4PacketImpl.cpp #3326 * fix(NumericString): Bug in NumericString with decSep != '.' #3159 * fix(HostEntry): DNS HostEntry returns multiple entries #3303 * fix(PollSet): #3248 #3249 * fix(NetworkInterface): Unterminated string possible in NetworkInterfaceImpl::setPhyParams() #3301 * style/whitespace * fix warnings * add version resources to executables * style * whitespace * update changelog * cpproj: also copy testsuite/include if it's there * branch off 1.11.1 * #3335: XML error when build 1.11.0 * #3353: add POCO_NO_FORK_EXEC CMake option * #3381: DNS::hostByAddress not thread-safe * #3400: fix std::localtime not thread safe * #3221: Crash reported on Windows in X509Certificate verification * #3344: [bug] MacOS bundle destination path is not set * #3360: Add POCO_PGSQL_{INCLUDE,LIB} variables * #3363: Fixed compilation error with MongoDB::Connection and Util::Application * #3377: Correct Poco::Path::configHome() and dataHome() documentation for Windows * #2823: error: implicit conversion from 'int' to 'float' changes value from 2147483647 to 2147483648 * #3425: Fixed suspend/resumeEvents pair in DirectoryWatcher * #2966: SocketReactor loads one core of CPU up to 100% * #3330: Poco::Data::ODBC::ODBCStatementImpl causes crash * use OpenSSL 1.1.1 on macOS * add missing include search path * upgrade bundled PCRE to 8.45 * upgrade bundled SQLite to 3.36.0 * updated changelog * fix brew OpenSSL version * branch off poco-1.11.2 * #3506: Upgrade bundled expat to 2.4.4 * manually merge #3448, part 1 (Crypto) * manually merge #3448, part 1 (NetSSL) * #3515: NetSSL_OpenSSL Testsuite: testInterop() and testProxy() fail due to changed certificate * #3448: fix version check * #3465: NetSSL_Win: bad error handling when decodeMessage() fails * #3458: encryptString() crash on redhat/centos 8 with FIPS enabled using md5 default digest * #3505: JSON::PrintHandler.value(bool) prints incorrect value * #3527: Upgrade bundled expat to 2.4.5 * #3470: bug in JSON ParseHandler.cpp (RFC 7159 should be valid) * #3507: Reference counting for bound configuration in Util::Option is broken * #3518: Expat version check in #defines incorrect * #3338: NamedMutex does not work on Linux distributions where fs.protected_regular=1 * CI: don't build PageCompiler in ARM cross build * detect ARM64 on Windows * updated README.md * ProGen: support generation of VS 2022 project files * ci: add windows 2022 * fix library name * remove unused CppUnit headers * added VS2022 project files * #3530: Upgrade bundled expat to 2.4.6 * #3538: Upgrade bundled expat to 2.4.7 * Add back NIOS2 double conversion detection to fix compile errors The commit https://github.com/pocoproject/poco/commit/558324f672d824300498060aff63356bc6bb8097 removed the nios2 support, which was originally added in https://github.com/pocoproject/poco/commit/e7b91e8125d6910b53f94de5be4bb53f38dc77c1 This commit add it back. Signed-off-by: Julien Olivain * #3466: DefinePlatformSpecific.cmake: handle RelWithDebInfo and MinSizeRel configurations * #3524: remove XML and Util dependencies in Zip/SevenZip * #3483: Adds Windows 11 and Server 2022 to Environment::osDisplayName() * #3495: Array::operator[] should not throw * #3268: Poco redis command set have a bug when you want to set nx ex or expireTime * #3509: fix dst and utcOffset handling for Dublin time zone * #2882: another attempt at fixing it that should also work on other platforms * remove unused method in Timezone_WIN32.cpp * use tm_gmtoff on Linux * Basic support for OpenSSL 3.0.0 (#3448) * updated README.md * Create close-inactive-issues.yml * check return codes of EVP_CIPHER_CTX_new and EVP_CipherInit Especially with OpenSSL 3, it is possible that EVP_CipherInit may fail even when passed a non-null cipher[1]. Without the checking, it will finally get to a segfault. [1] https://github.com/openssl/openssl/issues/16864 * Automatically load default and legacy providers with OpenSSL 3 Without the legacy provider [1], some ciphers are not available. For example, the 'des-ecb' one used by test sutie is missed and the test will fail. [1] OSSL_PROVIDER-LEGACY(7ossl) * Make p12 ca order the same as pem OpenSSL < 3 returns p12 ca order in reversed order. This is fixed in OpenSSL 3. We work around it with old OpenSSL. See: https://github.com/openssl/openssl/issues/16421 https://github.com/openssl/openssl/pull/12641 https://github.com/jeroen/openssl/commit/f5eb85eb0fd432406a24abda6511c449eaee6162 * Implement SSL abort handling on OpenSSL 3 On an unexpected EOF, versions before OpenSSL 3.0 returned SSL_ERROR_SYSCALL, nothing was added to the error stack, and errno was 0. Since OpenSSL 3.0 the returned error is SSL_ERROR_SSL with a meaningful error on the error stack.[1] [1] SSL_GET_ERROR(3ossl) Co-authored-by: Günter Obiltschnig Co-authored-by: Robin Lee Co-authored-by: Aleksandar Fabijanic * fix(Socket): shutdown fixes from pull #3448 * #3500: Sandbox all iFrames in PocoDoc * #3549; replace assert with assertTrue * #3553: Upgrade bundled zlib to 1.2.12 * #3525: Bad management of file in case of OpenSSLException in X509Certificate::readPEM and X509Certificate::writePEM * disable OpenSSL deprecation warnings * chore: cleanup * fix(X509Certificate): add missing string format * #3559: Poco::Data::PostgreSQL - DateTime extraction truncates fractional seconds * feat(EVP): 3.0 support - add EVPCipher - additional EVPPKey constructors - tests - fix and improve openssl-related exceptions Transition towards 3.0 support; deprecating direct EC and RSA interface portions. * fix(openssl): pre 3.0 compile * feat(Envelope): Add envelope to crypto #3561 * fix(Envelope): mac/clang compile * fix(Any): #3297 #3514 * #3562: fixed OpenSSL setup/shutdown * fix exception text * #3563: Remove support for OpenSSL < 1.0 * ci jobs for OpenSSL 1.1 and 3 * updated CHANGELOG * updated .vscode * Refactor/any soo (#3564) * refactor(Any): SOO - encapsulate data holders - add missing gets and ops - eliminate g++ warnings with enable_if's - default enable SOO * refactor(Placeholder): encapsulate SOO memory management and fix leaks; cf. #3297 #3514 * fix(Placeholder): asan errors and add tests cf. #3297 #3514 * fix(SSLManager): Race condition in SSLManager #3558 * remove unused include * updated copyright date * PocoDoc: fix iframe sandboxing * fix(SHA2Engine): cannot use HMACEngine with SHA2Engine #3421 * refactor(Placeholder): ifdef POCO_NO_SOO only in Placeholder and remove it anywhere else (#3566) * refactor(Placeholder): more SOO consolidation and optimization * fix(FPEnvironment): Visual Studio Warning C4244 #3543 * fix(Extractor): move extraction decoding to AbstractExtractor #3396 * Netssl/openssl3 (#3575) * feat(Context): DH init openssl3 port (1/2 hardcoded params) * create poco-1.11.3 branch, bump version * update copyright date * #3567: check legacy provider existence for legacy exception #3567 * fix(Placeholder): comparison for zero value * feat(Context): DH init openssl3 port (2/2 params from file) * test(HTTPSClientSession): try/catch to understand CI failure * chore(cmake): copy the DH parameters file * fix(OpenSSLInitializer): unload provider on uninitialize * chore(HTTPSClientSessionTest): remove try/catch * fix(OpenSSLInitializer): fix provider unloading * feat(CppUnit): make tests exceptions more descriptive * chore(CppUnit): a more descriptive name for callback Co-authored-by: Günter Obiltschnig * fix(Foundation): update VS 2019 platform version * chore(Data): update VS project files (add Transcoder #3396) * fix(Data): Poco::Data::ODBC-dbEncoding property not used for insert/update #3396 * fix(Data): add transcoder to Makefile #3396 * fix(JWT): remove duplicate test functions after merge Co-authored-by: Günter Obiltschnig Co-authored-by: Julien Olivain Co-authored-by: Robin Lee Co-authored-by: Robin Lee * #2755: Fix MySQL's LONGBLOB/LONGTEXT not allocating enough space (#3474) * Unit test for bug #2755. * Removed condition to set buffer length to 0. * Fixes to unit tests for LONGBLOB/TEXT data type. * Adjusted buffer sizes to accommodate LONGBLOBs. Co-authored-by: Hector Toledo Soto * fix(Data): MySQL UUID binding temporary string #3587 * feat(CI): add MySQL tests to CI #3588 * fix(CI): remove mysql client dev (using mariadb) #3588 * fix(CI): another shot at mysql * fix(Net/testsuite): add missing include * fix(DatagramSocket): Socket::available does not always return correct value for UDP #3589 * fix(SocketProactor): few improvements (#3357) * fix(ICMPsocketTest): change appinf (doesn't respond to ping) to github * fix(PollSet): windows fixes * fix(PollSet): windows implementation fixes (multi-fd_set select); minor reactor fixes * Extract JSON type as string (#3491) * Declared JSON as string data # Conflicts: # Data/include/Poco/Data/MetaColumn.h # Data/src/RecordSet.cpp # Data/src/StatementImpl.cpp * Added JSON extractor as string * Added unit test * Update comment Co-authored-by: Joerg-Christian Boehme Co-authored-by: Günter Obiltschnig Co-authored-by: tbarbier Co-authored-by: Linquize Co-authored-by: Jan Kevin Dick Co-authored-by: Neelima Patil Co-authored-by: akete Co-authored-by: YuriAzathoth Co-authored-by: Jonathan Horvath Co-authored-by: Maksim Kita Co-authored-by: Alexander Galanin Co-authored-by: Thomas Sablik Co-authored-by: Nikita Migunov Co-authored-by: Ben Wolsieffer Co-authored-by: micheleselea Co-authored-by: Federico Kircheis Co-authored-by: fenghao119 Co-authored-by: Alex Fabijanic Co-authored-by: linquize Co-authored-by: Julien Olivain Co-authored-by: Robin Lee Co-authored-by: Robin Lee Co-authored-by: hectots Co-authored-by: Hector Toledo Soto Co-authored-by: Hernan Martinez --- .github/workflows/ci.yml | 14 +- ApacheConnector/CMakeLists.txt | 2 +- CMakeLists.txt | 9 +- CppParser/CMakeLists.txt | 2 +- CppUnit/CMakeLists.txt | 7 +- CppUnit/CppUnit_vs140.vcxproj | 1 - CppUnit/CppUnit_vs150.vcxproj | 1 - CppUnit/CppUnit_vs160.vcxproj | 1 - Crypto/CMakeLists.txt | 2 +- Data/CMakeLists.txt | 2 +- Data/MySQL/CMakeLists.txt | 2 +- .../MySQL/include/Poco/Data/MySQL/Extractor.h | 4 + .../include/Poco/Data/MySQL/ResultMetadata.h | 8 +- Data/MySQL/src/Binder.cpp | 3 +- Data/MySQL/src/Extractor.cpp | 44 +- Data/MySQL/src/ResultMetadata.cpp | 31 +- Data/MySQL/testsuite/run-db-setup.sh | 15 + Data/MySQL/testsuite/src/MySQLTest.cpp | 37 + Data/MySQL/testsuite/src/MySQLTest.h | 4 + Data/MySQL/testsuite/src/SQLExecutor.cpp | 51 ++ Data/MySQL/testsuite/src/SQLExecutor.h | 2 + Data/ODBC/CMakeLists.txt | 2 +- Data/PostgreSQL/CMakeLists.txt | 2 +- Data/SQLite/CMakeLists.txt | 2 +- Data/SQLite/testsuite/src/SQLiteTest.cpp | 43 + Data/SQLite/testsuite/src/SQLiteTest.h | 1 + Data/include/Poco/Data/AbstractBinder.h | 3 + Data/include/Poco/Data/LOB.h | 2 +- Data/include/Poco/Data/MetaColumn.h | 1 + Data/src/AbstractBinder.cpp | 13 + Data/src/RecordSet.cpp | 1 + Data/src/StatementImpl.cpp | 2 + Encodings/CMakeLists.txt | 2 +- Foundation/CMakeLists.txt | 5 +- Foundation/Foundation_vs140.vcxproj | 1 - Foundation/Foundation_vs150.vcxproj | 1 - Foundation/include/Poco/AbstractObserver.h | 0 Foundation/include/Poco/Config.h | 15 + Foundation/include/Poco/Hash.h | 95 ++ Foundation/include/Poco/NObserver.h | 0 Foundation/include/Poco/NotificationQueue.h | 10 +- Foundation/include/Poco/Observer.h | 0 Foundation/include/Poco/Platform_WIN32.h | 10 +- Foundation/testsuite/TestApp_vs140.vcxproj | 1 - Foundation/testsuite/TestApp_vs150.vcxproj | 1 - .../testsuite/TestLibrary_vs140.vcxproj | 1 - .../testsuite/TestLibrary_vs150.vcxproj | 1 - Foundation/testsuite/TestSuite_vs140.vcxproj | 1 - Foundation/testsuite/TestSuite_vs150.vcxproj | 1 - JSON/CMakeLists.txt | 2 +- JWT/CMakeLists.txt | 2 +- MongoDB/CMakeLists.txt | 2 +- Net/CMakeLists.txt | 2 +- Net/Makefile | 2 +- Net/Net_vs160.vcxproj | 310 +++---- Net/Net_vs160.vcxproj.filters | 6 + Net/include/Poco/Net/DatagramSocket.h | 71 +- Net/include/Poco/Net/IPAddress.h | 37 +- Net/include/Poco/Net/MultiSocketPoller.h | 2 +- Net/include/Poco/Net/Net.h | 28 + Net/include/Poco/Net/PollSet.h | 7 + Net/include/Poco/Net/RawSocket.h | 45 +- Net/include/Poco/Net/SingleSocketPoller.h | 2 +- Net/include/Poco/Net/Socket.h | 176 +++- Net/include/Poco/Net/SocketAddress.h | 6 + Net/include/Poco/Net/SocketConnector.h | 11 +- Net/include/Poco/Net/SocketDefs.h | 15 +- Net/include/Poco/Net/SocketImpl.h | 24 + Net/include/Poco/Net/SocketProactor.h | 501 +++++++++++ Net/include/Poco/Net/SocketReactor.h | 2 +- Net/include/Poco/Net/StreamSocket.h | 41 + Net/include/Poco/Net/UDPHandler.h | 2 +- Net/include/Poco/Net/WebSocket.h | 38 +- Net/src/DatagramSocket.cpp | 62 +- Net/src/ICMPSocketImpl.cpp | 15 +- Net/src/IPAddress.cpp | 106 ++- Net/src/PollSet.cpp | 491 ++++++++--- Net/src/RawSocket.cpp | 34 + Net/src/Socket.cpp | 64 +- Net/src/SocketAddress.cpp | 13 + Net/src/SocketImpl.cpp | 59 +- Net/src/SocketNotification.cpp | 2 +- Net/src/SocketProactor.cpp | 808 ++++++++++++++++++ Net/src/SocketReactor.cpp | 11 +- Net/src/StreamSocket.cpp | 39 + Net/src/WebSocket.cpp | 44 + Net/testsuite/Makefile | 2 +- Net/testsuite/TestSuite_vs160.vcxproj | 206 ++--- Net/testsuite/TestSuite_vs160.vcxproj.filters | 6 + Net/testsuite/src/DatagramSocketTest.cpp | 218 ++++- Net/testsuite/src/DatagramSocketTest.h | 7 + Net/testsuite/src/ICMPClientTest.cpp | 4 +- Net/testsuite/src/ICMPSocketTest.cpp | 4 +- Net/testsuite/src/IPAddressTest.cpp | 47 +- Net/testsuite/src/PollSetTest.cpp | 140 ++- Net/testsuite/src/PollSetTest.h | 3 + Net/testsuite/src/RawSocketTest.cpp | 26 + Net/testsuite/src/RawSocketTest.h | 1 + Net/testsuite/src/SocketAddressTest.cpp | 26 +- Net/testsuite/src/SocketProactorTest.cpp | 314 +++++++ Net/testsuite/src/SocketProactorTest.h | 49 ++ Net/testsuite/src/SocketReactorTest.cpp | 33 +- Net/testsuite/src/SocketTest.cpp | 36 + Net/testsuite/src/SocketTest.h | 1 + Net/testsuite/src/SocketsTestSuite.cpp | 2 + Net/testsuite/src/WebSocketTest.cpp | 4 +- NetSSL_OpenSSL/CMakeLists.txt | 2 +- NetSSL_Win/CMakeLists.txt | 2 +- PDF/CMakeLists.txt | 2 +- PocoDoc/src/PocoDoc.cpp | 5 + Redis/CMakeLists.txt | 2 +- SevenZip/CMakeLists.txt | 2 +- Util/CMakeLists.txt | 2 +- XML/CMakeLists.txt | 2 +- Zip/CMakeLists.txt | 2 +- cmake/PocoMacros.cmake | 2 +- 116 files changed, 4136 insertions(+), 557 deletions(-) create mode 100755 Data/MySQL/testsuite/run-db-setup.sh mode change 100644 => 100755 Data/SQLite/testsuite/src/SQLiteTest.cpp mode change 100644 => 100755 Data/SQLite/testsuite/src/SQLiteTest.h mode change 100644 => 100755 Foundation/include/Poco/AbstractObserver.h mode change 100644 => 100755 Foundation/include/Poco/NObserver.h mode change 100644 => 100755 Foundation/include/Poco/Observer.h create mode 100644 Net/include/Poco/Net/SocketProactor.h create mode 100644 Net/src/SocketProactor.cpp create mode 100644 Net/testsuite/src/SocketProactorTest.cpp create mode 100644 Net/testsuite/src/SocketProactorTest.h diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 71e006d6b..6945857ea 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -3,13 +3,23 @@ on: [push] jobs: linux-gcc-make: runs-on: ubuntu-20.04 + services: + mysql: + image: mysql:latest + env: + MYSQL_ALLOW_EMPTY_PASSWORD: yes + MYSQL_USER: pocotest + MYSQL_PASSWORD: pocotest + MYSQL_DATABASE: pocotest + ports: + - 3306:3306 steps: - uses: actions/checkout@v2 - - run: sudo apt update && sudo apt install libssl-dev unixodbc-dev libmysqlclient-dev redis-server + - run: sudo apt update && sudo apt install libssl-dev unixodbc-dev redis-server libmysqlclient-dev - run: ./configure --everything --omit=PDF && make all -s -j4 && sudo make install - run: >- sudo -s - EXCLUDE_TESTS="Data/MySQL Data/ODBC Data/PostgreSQL MongoDB" + EXCLUDE_TESTS="Data/ODBC Data/PostgreSQL MongoDB" ./ci/runtests.sh linux-gcc-make-asan: diff --git a/ApacheConnector/CMakeLists.txt b/ApacheConnector/CMakeLists.txt index 86d7210a5..544904fe2 100644 --- a/ApacheConnector/CMakeLists.txt +++ b/ApacheConnector/CMakeLists.txt @@ -14,7 +14,7 @@ set_target_properties(mod_poco target_include_directories(mod_poco PUBLIC $ - $ + $ PRIVATE ${APACHE2_INCLUDE_DIRS} ${CMAKE_CURRENT_SOURCE_DIR}/src diff --git a/CMakeLists.txt b/CMakeLists.txt index 4338befe5..310d12756 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -50,6 +50,9 @@ if(NOT CMAKE_BUILD_TYPE) set(CMAKE_BUILD_TYPE "RelWithDebInfo") endif() +# Enable standard installation directories +include(GNUInstallDirs) + # Include some common macros to simpilfy the Poco CMake files include(PocoMacros) @@ -436,7 +439,9 @@ add_custom_target(uninstall ############################################################# # Enable packaging -include(InstallRequiredSystemLibraries) +if (CMAKE_HOST_SYSTEM_NAME STREQUAL "Windows") + include(InstallRequiredSystemLibraries) +endif() set(CPACK_PACKAGE_DESCRIPTION_SUMMARY "Poco Libraries") set(CPACK_PACKAGE_VENDOR "Applied Informatics Software Engineering GmbH") @@ -462,7 +467,7 @@ write_basic_package_version_file( if(WIN32) set(PocoConfigPackageLocation "cmake") else() - set(PocoConfigPackageLocation "lib${LIB_SUFFIX}/cmake/${PROJECT_NAME}") + set(PocoConfigPackageLocation "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") endif() configure_file(cmake/${PROJECT_NAME}Config.cmake.in "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}/${PROJECT_NAME}Config.cmake" @ONLY) diff --git a/CppParser/CMakeLists.txt b/CppParser/CMakeLists.txt index 7e41461e5..86ceca0c1 100644 --- a/CppParser/CMakeLists.txt +++ b/CppParser/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(CppParser PUBLIC Poco::Foundation) target_include_directories(CppParser PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/CppUnit/CMakeLists.txt b/CppUnit/CMakeLists.txt index c367bd2bb..78b89d442 100644 --- a/CppUnit/CMakeLists.txt +++ b/CppUnit/CMakeLists.txt @@ -18,10 +18,15 @@ target_link_libraries(CppUnit PUBLIC Poco::Foundation) target_include_directories(CppUnit PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) +if(WIN32) + if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") + target_compile_definitions(CppUnit PUBLIC POCO_NO_AUTOMATIC_LIBS) + endif() +endif() if(NOT BUILD_SHARED_LIBS) target_compile_definitions(CppUnit diff --git a/CppUnit/CppUnit_vs140.vcxproj b/CppUnit/CppUnit_vs140.vcxproj index dd1dc6737..9e23021a8 100644 --- a/CppUnit/CppUnit_vs140.vcxproj +++ b/CppUnit/CppUnit_vs140.vcxproj @@ -55,7 +55,6 @@ {138BB448-808A-4FE5-A66D-78D1F8770F59} CppUnit Win32Proj - 8.1 diff --git a/CppUnit/CppUnit_vs150.vcxproj b/CppUnit/CppUnit_vs150.vcxproj index 79e00b808..3e6611653 100644 --- a/CppUnit/CppUnit_vs150.vcxproj +++ b/CppUnit/CppUnit_vs150.vcxproj @@ -55,7 +55,6 @@ {138BB448-808A-4FE5-A66D-78D1F8770F59} CppUnit Win32Proj - 8.1 diff --git a/CppUnit/CppUnit_vs160.vcxproj b/CppUnit/CppUnit_vs160.vcxproj index de55fcfcf..7eeafbc10 100644 --- a/CppUnit/CppUnit_vs160.vcxproj +++ b/CppUnit/CppUnit_vs160.vcxproj @@ -55,7 +55,6 @@ {138BB448-808A-4FE5-A66D-78D1F8770F59} CppUnit Win32Proj - 10.0 diff --git a/Crypto/CMakeLists.txt b/Crypto/CMakeLists.txt index 9204a91cb..0a3b620df 100644 --- a/Crypto/CMakeLists.txt +++ b/Crypto/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(Crypto PUBLIC Poco::Foundation OpenSSL::SSL OpenSSL::Crypt target_include_directories(Crypto PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/Data/CMakeLists.txt b/Data/CMakeLists.txt index 7d1e99ea9..0772af648 100644 --- a/Data/CMakeLists.txt +++ b/Data/CMakeLists.txt @@ -30,7 +30,7 @@ target_link_libraries(Data PUBLIC Poco::Foundation) target_include_directories(Data PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/Data/MySQL/CMakeLists.txt b/Data/MySQL/CMakeLists.txt index f71b1453e..ce411cfea 100644 --- a/Data/MySQL/CMakeLists.txt +++ b/Data/MySQL/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(DataMySQL PUBLIC Poco::Data MySQL::client) target_include_directories(DataMySQL PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_compile_definitions(DataMySQL PUBLIC THREADSAFE NO_TCL) diff --git a/Data/MySQL/include/Poco/Data/MySQL/Extractor.h b/Data/MySQL/include/Poco/Data/MySQL/Extractor.h index 36226fc65..6952f8cd4 100644 --- a/Data/MySQL/include/Poco/Data/MySQL/Extractor.h +++ b/Data/MySQL/include/Poco/Data/MySQL/Extractor.h @@ -323,6 +323,10 @@ public: private: bool realExtractFixed(std::size_t pos, enum_field_types type, void* buffer, bool isUnsigned = false); + bool extractLongLOB(std::size_t pos); + + bool extractJSON(std::size_t pos); + // Prevent VC8 warning "operator= could not be generated" Extractor& operator=(const Extractor&); diff --git a/Data/MySQL/include/Poco/Data/MySQL/ResultMetadata.h b/Data/MySQL/include/Poco/Data/MySQL/ResultMetadata.h index 3a45387bc..bd6111f06 100644 --- a/Data/MySQL/include/Poco/Data/MySQL/ResultMetadata.h +++ b/Data/MySQL/include/Poco/Data/MySQL/ResultMetadata.h @@ -40,6 +40,9 @@ class ResultMetadata /// MySQL result metadata { public: + ~ResultMetadata(); + /// Destroys the ResultMetadata. + void reset(); /// Resets the metadata. @@ -64,10 +67,13 @@ public: bool isNull(std::size_t pos) const; /// Returns true if value at pos is null. + void adjustColumnSizeToFit(std::size_t pos); + /// Expands the size allocated for column to fit the length of the data. + private: std::vector _columns; std::vector _row; - std::vector _buffer; + std::vector _buffer; std::vector _lengths; std::vector _isNull; // using char instead of bool to avoid std::vector disaster }; diff --git a/Data/MySQL/src/Binder.cpp b/Data/MySQL/src/Binder.cpp index c1fb4e7df..9bb19ee9d 100644 --- a/Data/MySQL/src/Binder.cpp +++ b/Data/MySQL/src/Binder.cpp @@ -213,8 +213,7 @@ void Binder::bind(std::size_t pos, const Time& val, Direction dir) void Binder::bind(std::size_t pos, const UUID& val, Direction dir) { - std::string str = val.toString(); - bind(pos, str, dir); + bind(pos, toString(val), dir); } diff --git a/Data/MySQL/src/Extractor.cpp b/Data/MySQL/src/Extractor.cpp index 0ff16d340..0e0862ea3 100644 --- a/Data/MySQL/src/Extractor.cpp +++ b/Data/MySQL/src/Extractor.cpp @@ -128,9 +128,12 @@ bool Extractor::extract(std::size_t pos, std::string& val) //mysql reports TEXT types as FDT_BLOB when being extracted MetaColumn::ColumnDataType columnType = _metadata.metaColumn(static_cast(pos)).type(); - if (columnType != Poco::Data::MetaColumn::FDT_STRING && columnType != Poco::Data::MetaColumn::FDT_BLOB) + if (columnType != Poco::Data::MetaColumn::FDT_STRING && columnType != Poco::Data::MetaColumn::FDT_BLOB && columnType != Poco::Data::MetaColumn::FDT_JSON) throw MySQLException("Extractor: not a string"); + if (columnType == Poco::Data::MetaColumn::FDT_JSON && !extractJSON(pos)) + return false; + val.assign(reinterpret_cast(_metadata.rawData(pos)), _metadata.length(pos)); return true; } @@ -142,11 +145,14 @@ bool Extractor::extract(std::size_t pos, Poco::Data::BLOB& val) throw MySQLException("Extractor: attempt to extract more parameters, than query result contain"); if (_metadata.isNull(static_cast(pos))) - return false; + return false; if (_metadata.metaColumn(static_cast(pos)).type() != Poco::Data::MetaColumn::FDT_BLOB) throw MySQLException("Extractor: not a blob"); + if (_metadata.metaColumn(static_cast(pos)).length() == 0 && !extractLongLOB(pos)) + return false; + val.assignRaw(_metadata.rawData(pos), _metadata.length(pos)); return true; } @@ -163,6 +169,9 @@ bool Extractor::extract(std::size_t pos, Poco::Data::CLOB& val) if (_metadata.metaColumn(static_cast(pos)).type() != Poco::Data::MetaColumn::FDT_BLOB) throw MySQLException("Extractor: not a blob"); + if (_metadata.metaColumn(static_cast(pos)).length() == 0 && !extractLongLOB(pos)) + return false; + val.assignRaw(reinterpret_cast(_metadata.rawData(pos)), _metadata.length(pos)); return true; } @@ -263,6 +272,37 @@ bool Extractor::realExtractFixed(std::size_t pos, enum_field_types type, void* b return isNull == 0; } +bool Extractor::extractLongLOB(std::size_t pos) +{ + // Large LOBs (LONGBLOB and LONGTEXT) are fetched + // with a zero-length buffer to avoid allocating + // huge amounts of memory. Therefore, when extracting + // the buffers need to be adjusted. + + _metadata.adjustColumnSizeToFit(pos); + + MYSQL_BIND* row = _metadata.row(); + if (!_stmt.fetchColumn(pos, &row[pos])) + return false; + + return true; +} + +bool Extractor::extractJSON(std::size_t pos) +{ + // JSON columns are fetched with a zero-length + // buffer to avoid allocating huge amounts of memory. + // Therefore, when extracting the buffers need to be adjusted. + + _metadata.adjustColumnSizeToFit(pos); + + MYSQL_BIND* row = _metadata.row(); + row->buffer_type = MYSQL_TYPE_JSON; + if (!_stmt.fetchColumn(pos, &row[pos])) + return false; + + return true; +} ////////////// // Not implemented diff --git a/Data/MySQL/src/ResultMetadata.cpp b/Data/MySQL/src/ResultMetadata.cpp index bcc45c3d8..3d631c0d3 100644 --- a/Data/MySQL/src/ResultMetadata.cpp +++ b/Data/MySQL/src/ResultMetadata.cpp @@ -71,6 +71,7 @@ namespace case MYSQL_TYPE_MEDIUM_BLOB: case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: + case MYSQL_TYPE_JSON: return field.length; default: @@ -128,6 +129,8 @@ namespace case MYSQL_TYPE_LONG_BLOB: case MYSQL_TYPE_BLOB: return Poco::Data::MetaColumn::FDT_BLOB; + case MYSQL_TYPE_JSON: + return Poco::Data::MetaColumn::FDT_JSON; default: return Poco::Data::MetaColumn::FDT_UNKNOWN; } @@ -140,6 +143,13 @@ namespace Data { namespace MySQL { +ResultMetadata::~ResultMetadata() +{ + for (std::vector::iterator it = _buffer.begin(); it != _buffer.end(); ++it) + std::free(*it); +} + + void ResultMetadata::reset() { _columns.resize(0); @@ -165,7 +175,6 @@ void ResultMetadata::init(MYSQL_STMT* stmt) std::size_t count = mysql_num_fields(h); MYSQL_FIELD* fields = mysql_fetch_fields(h); - std::size_t commonSize = 0; _columns.reserve(count); for (std::size_t i = 0; i < count; i++) @@ -181,29 +190,24 @@ void ResultMetadata::init(MYSQL_STMT* stmt) 0, // TODO: precision !IS_NOT_NULL(fields[i].flags) // nullable )); - - commonSize += _columns[i].length(); } - _buffer.resize(commonSize); + _buffer.resize(count); _row.resize(count); _lengths.resize(count); _isNull.resize(count); - std::size_t offset = 0; - for (std::size_t i = 0; i < count; i++) { std::memset(&_row[i], 0, sizeof(MYSQL_BIND)); unsigned int len = static_cast(_columns[i].length()); + _buffer[i] = (char*) std::calloc(len, sizeof(char)); _row[i].buffer_type = fields[i].type; _row[i].buffer_length = len; - _row[i].buffer = (len > 0) ? (&_buffer[0] + offset) : 0; + _row[i].buffer = _buffer[i]; _row[i].length = &_lengths[i]; _row[i].is_null = reinterpret_cast(&_isNull[i]); // workaround to make it work with both MySQL 8 and earlier _row[i].is_unsigned = (fields[i].flags & UNSIGNED_FLAG) > 0; - - offset += _row[i].buffer_length; } } @@ -244,4 +248,13 @@ bool ResultMetadata::isNull(std::size_t pos) const } +void ResultMetadata::adjustColumnSizeToFit(std::size_t pos) +{ + std::free(_buffer[pos]); + _buffer[pos] = (char*) std::calloc(_lengths[pos], sizeof(char)); + _row[pos].buffer = _buffer[pos]; + _row[pos].buffer_length = _lengths[pos]; +} + + } } } // namespace Poco::Data::MySQL diff --git a/Data/MySQL/testsuite/run-db-setup.sh b/Data/MySQL/testsuite/run-db-setup.sh new file mode 100755 index 000000000..c675b58c8 --- /dev/null +++ b/Data/MySQL/testsuite/run-db-setup.sh @@ -0,0 +1,15 @@ +#!/bin/bash +#apt-get install mariadb-server +USER=pocotest +PASS=pocotest + +mysql -uroot <longBlob(); +} + +void MySQLTest::testJSON() +{ + if (!_pSession) fail("Test not available."); + + recreatePersonJSONTable(); + _pExecutor->json(); +} + + void MySQLTest::testUnsignedInts() { if (!_pSession) fail ("Test not available."); @@ -752,6 +769,24 @@ void MySQLTest::recreatePersonTimeTable() } +void MySQLTest::recreatePersonLongBLOBTable() +{ + dropTable("Person"); + try { *_pSession << "CREATE TABLE Person (LastName VARCHAR(30), FirstName VARCHAR(30), Address VARCHAR(30), Biography LONGTEXT)", now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail ("recreatePersonLongBLOBTable()"); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail ("recreatePersonLongBLOBTable()"); } +} + + +void MySQLTest::recreatePersonJSONTable() +{ + dropTable("Person"); + try { *_pSession << "CREATE TABLE Person (LastName VARCHAR(30), FirstName VARCHAR(30), Address VARCHAR(30), Biography JSON)", now; } + catch (ConnectionException& ce) { std::cout << ce.displayText() << std::endl; fail("recreatePersonLongBLOBTable()"); } + catch (StatementException& se) { std::cout << se.displayText() << std::endl; fail("recreatePersonLongBLOBTable()"); } +} + + void MySQLTest::recreateIntsTable() { dropTable("Strings"); @@ -919,6 +954,8 @@ CppUnit::Test* MySQLTest::suite() CppUnit_addTest(pSuite, MySQLTest, testDateTime); //CppUnit_addTest(pSuite, MySQLTest, testBLOB); CppUnit_addTest(pSuite, MySQLTest, testBLOBStmt); + CppUnit_addTest(pSuite, MySQLTest, testLongBLOB); + CppUnit_addTest(pSuite, MySQLTest, testJSON); CppUnit_addTest(pSuite, MySQLTest, testUnsignedInts); CppUnit_addTest(pSuite, MySQLTest, testFloat); CppUnit_addTest(pSuite, MySQLTest, testDouble); diff --git a/Data/MySQL/testsuite/src/MySQLTest.h b/Data/MySQL/testsuite/src/MySQLTest.h index 150401c41..3d75d7f45 100644 --- a/Data/MySQL/testsuite/src/MySQLTest.h +++ b/Data/MySQL/testsuite/src/MySQLTest.h @@ -79,6 +79,8 @@ public: void testDateTime(); void testBLOB(); void testBLOBStmt(); + void testLongBLOB(); + void testJSON(); void testUnsignedInts(); void testFloat(); @@ -117,6 +119,8 @@ private: void recreatePersonDateTimeTable(); void recreatePersonDateTable(); void recreatePersonTimeTable(); + void recreatePersonLongBLOBTable(); + void recreatePersonJSONTable(); void recreateStringsTable(); void recreateIntsTable(); void recreateUnsignedIntsTable(); diff --git a/Data/MySQL/testsuite/src/SQLExecutor.cpp b/Data/MySQL/testsuite/src/SQLExecutor.cpp index 27beb6f44..e8a22b7c1 100644 --- a/Data/MySQL/testsuite/src/SQLExecutor.cpp +++ b/Data/MySQL/testsuite/src/SQLExecutor.cpp @@ -1435,6 +1435,57 @@ void SQLExecutor::blobStmt() } +void SQLExecutor::longBlob() +{ + std::string funct = "longBlob()"; + std::string lastName("lastname"); + std::string firstName("firstname"); + std::string address("Address"); + Poco::Data::CLOB biography("Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.", 123); + + int count = 0; + Statement ins = (*_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(biography)); + ins.execute(); + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + assertTrue (count == 1); + + Poco::Data::CLOB res; + poco_assert (res.size() == 0); + Statement stmt = (*_pSession << "SELECT Biography FROM Person", into(res)); + try { stmt.execute(); } + catch(ConnectionException& ce){ std::cout << ce.displayText() << std::endl; fail (funct); } + catch(StatementException& se){ std::cout << se.displayText() << std::endl; fail (funct); } + poco_assert (res == biography); +} + +void SQLExecutor::json() +{ + std::string funct = "json()"; + std::string lastName("lastname"); + std::string firstName("firstname"); + std::string address("Address"); + std::string biography(R"({"biography": {"count": 42, "title": "Lorem Ipsum", "released": true}})"); + + int count = 0; + Statement ins = (*_pSession << "INSERT INTO Person VALUES (?,?,?,?)", use(lastName), use(firstName), use(address), use(biography)); + ins.execute(); + try { *_pSession << "SELECT COUNT(*) FROM Person", into(count), now; } + catch (ConnectionException& ce) { std::cout << ce.displayText() << std::endl; fail(funct); } + catch (StatementException& se) { std::cout << se.displayText() << std::endl; fail(funct); } + assertTrue(count == 1); + + Poco::Data::JSON res; + poco_assert(res.size() == 0); + Statement stmt = (*_pSession << "SELECT Biography FROM Person", into(res)); + try { stmt.execute(); } + catch (ConnectionException& ce) { std::cout << ce.displayText() << std::endl; fail(funct); } + catch (StatementException& se) { std::cout << se.displayText() << std::endl; fail(funct); } + poco_assert(res == biography); +} + + void SQLExecutor::tuples() { typedef Tuple TupleType; diff --git a/Data/MySQL/testsuite/src/SQLExecutor.h b/Data/MySQL/testsuite/src/SQLExecutor.h index 03e4ffe24..e11811853 100644 --- a/Data/MySQL/testsuite/src/SQLExecutor.h +++ b/Data/MySQL/testsuite/src/SQLExecutor.h @@ -83,6 +83,8 @@ public: void dateTime(); void date(); void time(); + void longBlob(); + void json(); void unsignedInts(); void floats(); void doubles(); diff --git a/Data/ODBC/CMakeLists.txt b/Data/ODBC/CMakeLists.txt index 98cd133b8..764f6a501 100644 --- a/Data/ODBC/CMakeLists.txt +++ b/Data/ODBC/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(DataODBC PUBLIC Poco::Data ODBC::ODBC) target_include_directories(DataODBC PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) target_compile_definitions(DataODBC PUBLIC THREADSAFE) diff --git a/Data/PostgreSQL/CMakeLists.txt b/Data/PostgreSQL/CMakeLists.txt index f727fc0c7..d02c2f106 100644 --- a/Data/PostgreSQL/CMakeLists.txt +++ b/Data/PostgreSQL/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(DataPostgreSQL PUBLIC Poco::Data PostgreSQL::client) target_include_directories(DataPostgreSQL PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/Data/SQLite/CMakeLists.txt b/Data/SQLite/CMakeLists.txt index b52e2c5c1..3a176d744 100644 --- a/Data/SQLite/CMakeLists.txt +++ b/Data/SQLite/CMakeLists.txt @@ -38,7 +38,7 @@ target_link_libraries(DataSQLite PUBLIC Poco::Data) target_include_directories(DataSQLite PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/Data/SQLite/testsuite/src/SQLiteTest.cpp b/Data/SQLite/testsuite/src/SQLiteTest.cpp old mode 100644 new mode 100755 index deac5c2d4..9806d8f08 --- a/Data/SQLite/testsuite/src/SQLiteTest.cpp +++ b/Data/SQLite/testsuite/src/SQLiteTest.cpp @@ -55,6 +55,7 @@ using Poco::Data::SQLChannel; using Poco::Data::LimitException; using Poco::Data::ConnectionFailedException; using Poco::Data::CLOB; +using Poco::Data::BLOB; using Poco::Data::Date; using Poco::Data::Time; using Poco::Data::Transaction; @@ -1419,6 +1420,47 @@ void SQLiteTest::testCLOB() } +void SQLiteTest::testBLOB() +{ + std::string lastName("lastname"); + std::string firstName("firstname"); + std::string address("Address"); + Session tmp(Poco::Data::SQLite::Connector::KEY, "dummy.db"); + tmp << "DROP TABLE IF EXISTS Person", now; + tmp << "CREATE TABLE IF NOT EXISTS Person (LastName VARCHAR(30), FirstName VARCHAR, Address VARCHAR, Image BLOB)", now; + typedef struct + { + int i = 0; + Poco::Int64 i64 = 1; + float f = 2.5; + double d = 3.5; + char c[16] = {0}; + } DataStruct; + DataStruct ds; + strcpy(ds.c, "123456789ABCDEF"); + BLOB img(reinterpret_cast(&ds), sizeof(ds)); + assertTrue(img.size() == sizeof(ds)); + int count = 0; + tmp << "INSERT INTO PERSON VALUES(:ln, :fn, :ad, :img)", use(lastName), use(firstName), use(address), use(img), now; + tmp << "SELECT COUNT(*) FROM PERSON", into(count), now; + assertTrue(count == 1); + BLOB res; + assertTrue(res.size() == 0); + + tmp << "SELECT Image FROM Person WHERE LastName == :ln", bind("lastname"), into(res), now; + assertTrue(res.size() == img.size()); + assertTrue(0 == std::memcmp(res.rawContent(), img.rawContent(), sizeof(img))); + assertTrue(0 == std::memcmp(res.rawContent(), &ds, sizeof(ds))); + DataStruct dsCopy; + std::memcpy(&dsCopy, res.rawContent(), sizeof(dsCopy)); + assertTrue(ds.i == dsCopy.i); + assertTrue(ds.i64 == dsCopy.i64); + assertTrue(ds.f == dsCopy.f); + assertTrue(ds.d == dsCopy.d); + assertTrue(std::string(ds.c) == std::string(dsCopy.c)); +} + + void SQLiteTest::testTuple10() { Session tmp (Poco::Data::SQLite::Connector::KEY, "dummy.db"); @@ -3448,6 +3490,7 @@ CppUnit::Test* SQLiteTest::suite() CppUnit_addTest(pSuite, SQLiteTest, testSingleSelect); CppUnit_addTest(pSuite, SQLiteTest, testEmptyDB); CppUnit_addTest(pSuite, SQLiteTest, testCLOB); + CppUnit_addTest(pSuite, SQLiteTest, testBLOB); CppUnit_addTest(pSuite, SQLiteTest, testTuple10); CppUnit_addTest(pSuite, SQLiteTest, testTupleVector10); CppUnit_addTest(pSuite, SQLiteTest, testTuple9); diff --git a/Data/SQLite/testsuite/src/SQLiteTest.h b/Data/SQLite/testsuite/src/SQLiteTest.h old mode 100644 new mode 100755 index a19fd85fe..76864945e --- a/Data/SQLite/testsuite/src/SQLiteTest.h +++ b/Data/SQLite/testsuite/src/SQLiteTest.h @@ -76,6 +76,7 @@ public: void testEmptyDB(); void testCLOB(); + void testBLOB(); void testTuple1(); void testTupleVector1(); diff --git a/Data/include/Poco/Data/AbstractBinder.h b/Data/include/Poco/Data/AbstractBinder.h index c1f0d0dbc..2ff3c9816 100644 --- a/Data/include/Poco/Data/AbstractBinder.h +++ b/Data/include/Poco/Data/AbstractBinder.h @@ -363,9 +363,12 @@ protected: bool transcodeRequired() const; void transcode(const std::string& from, std::string& to); void reverseTranscode(const std::string& from, std::string& to); + const std::string& toString(const UUID& uuid); private: + using StringList = std::vector; std::unique_ptr _pTranscoder; + std::unique_ptr _pStrings; }; diff --git a/Data/include/Poco/Data/LOB.h b/Data/include/Poco/Data/LOB.h index 3ca80463d..ea8e842f3 100644 --- a/Data/include/Poco/Data/LOB.h +++ b/Data/include/Poco/Data/LOB.h @@ -221,7 +221,7 @@ private: using BLOB = LOB; using CLOB = LOB; - +using JSON = std::string; // // inlines diff --git a/Data/include/Poco/Data/MetaColumn.h b/Data/include/Poco/Data/MetaColumn.h index 2952ed83d..0351766f1 100644 --- a/Data/include/Poco/Data/MetaColumn.h +++ b/Data/include/Poco/Data/MetaColumn.h @@ -51,6 +51,7 @@ public: FDT_TIME, FDT_TIMESTAMP, FDT_UUID, + FDT_JSON, FDT_UNKNOWN }; diff --git a/Data/src/AbstractBinder.cpp b/Data/src/AbstractBinder.cpp index 9752d88f2..d8397ea36 100644 --- a/Data/src/AbstractBinder.cpp +++ b/Data/src/AbstractBinder.cpp @@ -36,6 +36,11 @@ AbstractBinder::AbstractBinder(Poco::TextEncoding::Ptr pFromEncoding, AbstractBinder::~AbstractBinder() { + if (_pStrings) + { + for (auto& s : *_pStrings) + delete s; + } } @@ -53,6 +58,14 @@ void AbstractBinder::reverseTranscode(const std::string& from, std::string& to) } +const std::string& AbstractBinder::toString(const UUID& uuid) +{ + if (!_pStrings) _pStrings.reset(new StringList); + _pStrings->push_back(new std::string(uuid.toString())); + return *_pStrings->back(); +} + + void AbstractBinder::bind(std::size_t pos, const std::vector& val, Direction dir) { throw NotImplementedException("std::vector binder must be implemented."); diff --git a/Data/src/RecordSet.cpp b/Data/src/RecordSet.cpp index a84c35fdf..20e978b0a 100644 --- a/Data/src/RecordSet.cpp +++ b/Data/src/RecordSet.cpp @@ -160,6 +160,7 @@ Poco::Dynamic::Var RecordSet::value(std::size_t col, std::size_t row, bool useFi case MetaColumn::FDT_TIME: return value diff --git a/Foundation/Foundation_vs150.vcxproj b/Foundation/Foundation_vs150.vcxproj index 30fd738c8..708a99c21 100644 --- a/Foundation/Foundation_vs150.vcxproj +++ b/Foundation/Foundation_vs150.vcxproj @@ -55,7 +55,6 @@ {B01196CC-B693-4548-8464-2FF60499E73F} Foundation Win32Proj - 8.1 diff --git a/Foundation/include/Poco/AbstractObserver.h b/Foundation/include/Poco/AbstractObserver.h old mode 100644 new mode 100755 diff --git a/Foundation/include/Poco/Config.h b/Foundation/include/Poco/Config.h index 51a0571a2..5be362978 100644 --- a/Foundation/include/Poco/Config.h +++ b/Foundation/include/Poco/Config.h @@ -142,6 +142,21 @@ // #define POCO_NET_NO_IPv6 +// No UNIX socket support +// Define to disable unix sockets +// #define POCO_NET_NO_UNIX_SOCKET + + +// Define to nonzero to enable move +// semantics on classes where it +// introduces a new state. +// For explanation, see +// https://github.com/pocoproject/poco/wiki/Move-Semantics-in-POCO +#ifndef POCO_NEW_STATE_ON_MOVE +#define POCO_NEW_STATE_ON_MOVE 1 +#endif + + // Windows CE has no locale support #if defined(_WIN32_WCE) #define POCO_NO_LOCALE diff --git a/Foundation/include/Poco/Hash.h b/Foundation/include/Poco/Hash.h index 7aafb66a6..03ce66b39 100644 --- a/Foundation/include/Poco/Hash.h +++ b/Foundation/include/Poco/Hash.h @@ -10,6 +10,9 @@ // Copyright (c) 2006, Applied Informatics Software Engineering GmbH. // and Contributors. // +// hashRange Copyright 2005-2014 Daniel James. +// (Extracted from Boost 1.75.0 lib and adapted for poco on 2021-03-31) +// // SPDX-License-Identifier: BSL-1.0 // @@ -19,9 +22,17 @@ #include "Poco/Foundation.h" +#include "Poco/Types.h" #include +#if defined(_MSC_VER) +# define POCO_HASH_ROTL32(x, r) _rotl(x,r) +#else +# define POCO_HASH_ROTL32(x, r) (x << r) | (x >> (32 - r)) +#endif + + namespace Poco { @@ -99,6 +110,90 @@ inline std::size_t hash(UInt64 n) } +namespace Impl { + + +template +inline void hashCombine(SizeT& seed, SizeT value) +{ + seed ^= value + 0x9e3779b9 + (seed<<6) + (seed>>2); +} + + +inline void hashCombine(Poco::UInt32& h1, Poco::UInt32 k1) +{ + const uint32_t c1 = 0xcc9e2d51; + const uint32_t c2 = 0x1b873593; + + k1 *= c1; + k1 = POCO_HASH_ROTL32(k1,15); + k1 *= c2; + + h1 ^= k1; + h1 = POCO_HASH_ROTL32(h1,13); + h1 = h1*5+0xe6546b64; +} + + +#if defined(POCO_PTR_IS_64_BIT) && !(defined(__GNUC__) && ULONG_MAX == 0xffffffff) + + +inline void hashCombine(Poco::UInt64& h, Poco::UInt64 k) +{ + const Poco::UInt64 m = UINT64_C(0xc6a4a7935bd1e995); + const int r = 47; + + k *= m; + k ^= k >> r; + k *= m; + + h ^= k; + h *= m; + + // Completely arbitrary number, to + // prevent zeros from hashing to 0. + h += 0xe6546b64; +} + + +#endif // POCO_PTR_IS_64_BIT + + +} // namespace Impl + + +template +inline void hashCombine(std::size_t& seed, T const& v) +{ + Hash hasher; + Impl::hashCombine(seed, hasher(v)); +} + + +template +inline std::size_t hashRange(It first, It last) +{ + std::size_t seed = 0; + + for(; first != last; ++first) + { + hashCombine::value_type>(seed, *first); + } + + return seed; +} + + +template +inline void hashRange(std::size_t& seed, It first, It last) +{ + for(; first != last; ++first) + { + hashCombine::value_type>(seed, *first); + } +} + + } // namespace Poco diff --git a/Foundation/include/Poco/NObserver.h b/Foundation/include/Poco/NObserver.h old mode 100644 new mode 100755 diff --git a/Foundation/include/Poco/NotificationQueue.h b/Foundation/include/Poco/NotificationQueue.h index f299ed336..7db23a343 100644 --- a/Foundation/include/Poco/NotificationQueue.h +++ b/Foundation/include/Poco/NotificationQueue.h @@ -113,10 +113,10 @@ public: void wakeUpAll(); /// Wakes up all threads that wait for a notification. - + bool empty() const; /// Returns true iff the queue is empty. - + int size() const; /// Returns the number of notifications in the queue. @@ -127,17 +127,17 @@ public: /// Removes a notification from the queue. /// Returns true if remove succeeded, false otherwise - bool hasIdleThreads() const; + bool hasIdleThreads() const; /// Returns true if the queue has at least one thread waiting /// for a notification. - + static NotificationQueue& defaultQueue(); /// Returns a reference to the default /// NotificationQueue. protected: Notification::Ptr dequeueOne(); - + private: typedef std::deque NfQueue; struct WaitInfo diff --git a/Foundation/include/Poco/Observer.h b/Foundation/include/Poco/Observer.h old mode 100644 new mode 100755 diff --git a/Foundation/include/Poco/Platform_WIN32.h b/Foundation/include/Poco/Platform_WIN32.h index 65cdf46f5..3a8994b66 100644 --- a/Foundation/include/Poco/Platform_WIN32.h +++ b/Foundation/include/Poco/Platform_WIN32.h @@ -35,7 +35,7 @@ #error Inconsistent build settings (check for /MD[d]) #endif - +// https://en.wikipedia.org/wiki/Microsoft_Visual_C++ #if (_MSC_VER >= 1300) && (_MSC_VER < 1400) // Visual Studio 2003, MSVC++ 7.1 #define POCO_MSVS_VERSION 2003 #define POCO_MSVC_VERSION 71 @@ -57,9 +57,15 @@ #elif (_MSC_VER >= 1900) && (_MSC_VER < 1910) // Visual Studio 2015, MSVC++ 14.0 #define POCO_MSVS_VERSION 2015 #define POCO_MSVC_VERSION 140 -#elif (_MSC_VER >= 1910) && (_MSC_VER < 2000) // Visual Studio 2017, MSVC++ 14.1 +#elif (_MSC_VER >= 1910) && (_MSC_VER < 1920) // Visual Studio 2017, MSVC++ 14.1 #define POCO_MSVS_VERSION 2017 #define POCO_MSVC_VERSION 141 +#elif (_MSC_VER >= 1920) && (_MSC_VER < 1930) // Visual Studio 2019, MSVC++ 14.2 + #define POCO_MSVS_VERSION 2019 + #define POCO_MSVC_VERSION 142 +#elif (_MSC_VER >= 1930) // Visual Studio 2022, MSVC++ 14.3 + #define POCO_MSVS_VERSION 2022 + #define POCO_MSVC_VERSION 143 #endif diff --git a/Foundation/testsuite/TestApp_vs140.vcxproj b/Foundation/testsuite/TestApp_vs140.vcxproj index f25632eb9..1013fbdcd 100644 --- a/Foundation/testsuite/TestApp_vs140.vcxproj +++ b/Foundation/testsuite/TestApp_vs140.vcxproj @@ -55,7 +55,6 @@ {6C41E55D-C0FC-4E01-AA8D-B7DA40E31D3A} TestApp Win32Proj - 8.1 diff --git a/Foundation/testsuite/TestApp_vs150.vcxproj b/Foundation/testsuite/TestApp_vs150.vcxproj index 0f4e5213d..47ffba94f 100644 --- a/Foundation/testsuite/TestApp_vs150.vcxproj +++ b/Foundation/testsuite/TestApp_vs150.vcxproj @@ -55,7 +55,6 @@ {6C41E55D-C0FC-4E01-AA8D-B7DA40E31D3A} TestApp Win32Proj - 8.1 diff --git a/Foundation/testsuite/TestLibrary_vs140.vcxproj b/Foundation/testsuite/TestLibrary_vs140.vcxproj index 41c5f379c..ae3bcf7ae 100644 --- a/Foundation/testsuite/TestLibrary_vs140.vcxproj +++ b/Foundation/testsuite/TestLibrary_vs140.vcxproj @@ -22,7 +22,6 @@ TestLibrary {0955EB03-544B-4BD4-9C10-89CF38078F5F} Win32Proj - 8.1 diff --git a/Foundation/testsuite/TestLibrary_vs150.vcxproj b/Foundation/testsuite/TestLibrary_vs150.vcxproj index d2bd48fc4..a59f4f369 100644 --- a/Foundation/testsuite/TestLibrary_vs150.vcxproj +++ b/Foundation/testsuite/TestLibrary_vs150.vcxproj @@ -22,7 +22,6 @@ TestLibrary {0955EB03-544B-4BD4-9C10-89CF38078F5F} Win32Proj - 8.1 diff --git a/Foundation/testsuite/TestSuite_vs140.vcxproj b/Foundation/testsuite/TestSuite_vs140.vcxproj index 13a6d8757..089e45fc9 100644 --- a/Foundation/testsuite/TestSuite_vs140.vcxproj +++ b/Foundation/testsuite/TestSuite_vs140.vcxproj @@ -55,7 +55,6 @@ {F1EE93DF-347F-4CB3-B191-C4E63F38E972} TestSuite Win32Proj - 8.1 diff --git a/Foundation/testsuite/TestSuite_vs150.vcxproj b/Foundation/testsuite/TestSuite_vs150.vcxproj index 5c851afd0..c67f7c8bf 100644 --- a/Foundation/testsuite/TestSuite_vs150.vcxproj +++ b/Foundation/testsuite/TestSuite_vs150.vcxproj @@ -55,7 +55,6 @@ {F1EE93DF-347F-4CB3-B191-C4E63F38E972} TestSuite Win32Proj - 8.1 diff --git a/JSON/CMakeLists.txt b/JSON/CMakeLists.txt index bcbae095d..f8f478a4b 100644 --- a/JSON/CMakeLists.txt +++ b/JSON/CMakeLists.txt @@ -26,7 +26,7 @@ target_link_libraries(JSON PUBLIC Poco::Foundation) target_include_directories(JSON PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/JWT/CMakeLists.txt b/JWT/CMakeLists.txt index 524de978e..e4d756297 100644 --- a/JWT/CMakeLists.txt +++ b/JWT/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(JWT PUBLIC Poco::JSON Poco::Crypto) target_include_directories(JWT PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/MongoDB/CMakeLists.txt b/MongoDB/CMakeLists.txt index 7d9f9cd75..2d4cb08a8 100644 --- a/MongoDB/CMakeLists.txt +++ b/MongoDB/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(MongoDB PUBLIC Poco::Net) target_include_directories(MongoDB PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/Net/CMakeLists.txt b/Net/CMakeLists.txt index 758d5c58e..eb56e7118 100644 --- a/Net/CMakeLists.txt +++ b/Net/CMakeLists.txt @@ -35,7 +35,7 @@ endif(WIN32) target_include_directories(Net PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/Net/Makefile b/Net/Makefile index ca7b0d958..2d3291750 100644 --- a/Net/Makefile +++ b/Net/Makefile @@ -24,7 +24,7 @@ objects = \ HTTPRequestHandlerFactory HTTPStreamFactory ServerSocketImpl TCPServerParams \ QuotedPrintableEncoder QuotedPrintableDecoder StringPartSource \ FTPClientSession FTPStreamFactory PartHandler PartSource PartStore NullPartHandler \ - SocketReactor SocketNotifier SocketNotification AbstractHTTPRequestHandler \ + SocketReactor SocketProactor SocketNotifier SocketNotification AbstractHTTPRequestHandler \ MailRecipient MailMessage MailStream SMTPClientSession POP3ClientSession \ RawSocket RawSocketImpl ICMPClient ICMPEventArgs ICMPPacket ICMPPacketImpl \ ICMPSocket ICMPSocketImpl ICMPv4PacketImpl \ diff --git a/Net/Net_vs160.vcxproj b/Net/Net_vs160.vcxproj index 79f5fbe2e..8e78b4f7f 100644 --- a/Net/Net_vs160.vcxproj +++ b/Net/Net_vs160.vcxproj @@ -1,4 +1,4 @@ - + @@ -56,7 +56,7 @@ Net Win32Proj - + StaticLibrary MultiByte @@ -117,45 +117,45 @@ MultiByte v142 - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + <_ProjectFileVersion>15.0.28307.799 PocoNetd @@ -235,7 +235,7 @@ true true true - + Level3 ProgramDatabase Default @@ -268,9 +268,9 @@ true true true - + Level3 - + Default true @@ -299,7 +299,7 @@ true true true - + ..\lib\PocoNetmtd.pdb Level3 ProgramDatabase @@ -325,9 +325,9 @@ true true true - + Level3 - + Default true @@ -347,7 +347,7 @@ true true true - + ..\lib\PocoNetmdd.pdb Level3 ProgramDatabase @@ -373,10 +373,10 @@ true true true - + ..\lib\PocoNetmd.pdb Level3 - + Default true @@ -397,7 +397,7 @@ true true true - + Level3 ProgramDatabase Default @@ -430,9 +430,9 @@ true true true - + Level3 - + Default true @@ -461,7 +461,7 @@ true true true - + ..\lib64\PocoNetmtd.pdb Level3 ProgramDatabase @@ -487,9 +487,9 @@ true true true - + Level3 - + Default true @@ -509,7 +509,7 @@ true true true - + ..\lib64\PocoNetmdd.pdb Level3 ProgramDatabase @@ -535,9 +535,9 @@ true true true - + Level3 - + Default true @@ -546,122 +546,123 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -934,6 +935,7 @@ true + true @@ -992,6 +994,6 @@ true - - - + + + \ No newline at end of file diff --git a/Net/Net_vs160.vcxproj.filters b/Net/Net_vs160.vcxproj.filters index 25a9692b6..31a77e1f3 100644 --- a/Net/Net_vs160.vcxproj.filters +++ b/Net/Net_vs160.vcxproj.filters @@ -513,6 +513,9 @@ NTLM\Header Files + + Sockets\Header Files + @@ -830,6 +833,9 @@ NTLM\Source Files + + Sockets\Source Files + diff --git a/Net/include/Poco/Net/DatagramSocket.h b/Net/include/Poco/Net/DatagramSocket.h index ce64414b6..cd5fa113e 100644 --- a/Net/include/Poco/Net/DatagramSocket.h +++ b/Net/include/Poco/Net/DatagramSocket.h @@ -48,19 +48,14 @@ public: /// The socket will be created for the /// given address family. - DatagramSocket(const SocketAddress& address, bool reuseAddress = false); - /// Creates a datagram socket and binds it - /// to the given address. - /// - /// Depending on the address family, the socket - /// will be either an IPv4 or an IPv6 socket. - - DatagramSocket(const SocketAddress& address, bool reuseAddress, bool reusePort); + DatagramSocket(const SocketAddress& address, bool reuseAddress, bool reusePort = false, bool ipV6Only = false); /// Creates a datagram socket and binds it /// to the given address. /// /// Depending on the address family, the socket /// will be either an IPv4 or an IPv6 socket. + /// If ipV6Only is true, socket will be bound + /// to the IPv6 address only. DatagramSocket(const Socket& socket); /// Creates the DatagramSocket with the SocketImpl @@ -68,6 +63,10 @@ public: /// a DatagramSocketImpl, otherwise an InvalidArgumentException /// will be thrown. + DatagramSocket(const DatagramSocket& socket); + /// Creates the DatagramSocket with the SocketImpl + /// from another socket. + ~DatagramSocket(); /// Destroys the DatagramSocket. @@ -78,6 +77,43 @@ public: /// attaches the SocketImpl from the other socket and /// increments the reference count of the SocketImpl. +#if POCO_NEW_STATE_ON_MOVE + + DatagramSocket(Socket&& socket); + /// Creates the DatagramSocket with the SocketImpl + /// from another socket and zeroes the other socket's + /// SocketImpl.The SocketImpl must be + /// a DatagramSocketImpl, otherwise an InvalidArgumentException + /// will be thrown. + + DatagramSocket(DatagramSocket&& socket); + /// Creates the DatagramSocket with the SocketImpl + /// from another socket and zeroes the other socket's + /// SocketImpl. + + DatagramSocket& operator = (Socket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + + DatagramSocket& operator = (DatagramSocket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + +#endif // POCO_NEW_STATE_ON_MOVE + + DatagramSocket& operator = (const DatagramSocket& socket); + /// Assignment operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// increments the reference count of the SocketImpl. + void connect(const SocketAddress& address); /// Restricts incoming and outgoing /// packets to the specified address. @@ -109,6 +145,23 @@ public: /// /// Calls to connect cannot() come before calls to bind(). + void bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only = false); + /// Bind a local address to the socket. + /// + /// This is usually only done when establishing a server + /// socket. + /// + /// If reuseAddress is true, sets the SO_REUSEADDR + /// socket option. + /// + /// If reusePort is true, sets the SO_REUSEPORT + /// socket option. + /// + /// Sets the IPV6_V6ONLY socket option in accordance with + /// the supplied ipV6Only value. + /// + /// Calls to connect cannot() come before calls to bind(). + int sendBytes(const void* buffer, int length, int flags = 0); /// Sends the contents of the given buffer through /// the socket. @@ -231,7 +284,7 @@ protected: /// Creates the Socket and attaches the given SocketImpl. /// The socket takes ownership of the SocketImpl. /// - /// The SocketImpl must be a StreamSocketImpl, otherwise + /// The SocketImpl must be a DatagramSocketImpl, otherwise /// an InvalidArgumentException will be thrown. }; diff --git a/Net/include/Poco/Net/IPAddress.h b/Net/include/Poco/Net/IPAddress.h index 4ed446161..e993f42c3 100644 --- a/Net/include/Poco/Net/IPAddress.h +++ b/Net/include/Poco/Net/IPAddress.h @@ -24,6 +24,7 @@ #include "Poco/AutoPtr.h" #include "Poco/Exception.h" #include +#include #include @@ -56,6 +57,13 @@ class Net_API IPAddress public: using List = std::vector; + using RawIP = std::vector; + + static const unsigned IPv4Size = sizeof(in_addr); + static const unsigned IPv6Size = sizeof(in6_addr); + using RawIPv4 = std::array; + using RawIPv6 = std::array; + // The following declarations keep the Family type // backwards compatible with the previously used // enum declaration. @@ -71,6 +79,9 @@ public: IPAddress(const IPAddress& addr); /// Creates an IPAddress by copying another one. + IPAddress(IPAddress&& addr); + /// Creates an IPAddress by moving another one. + explicit IPAddress(Family family); /// Creates a wildcard (zero) IPAddress for the /// given address family. @@ -121,7 +132,16 @@ public: IPAddress& operator = (const IPAddress& addr); /// Assigns an IPAddress. - + + IPAddress& operator = (IPAddress&& addr); + /// Move-assigns an IPAddress. + + bool isV4() const; + bool isV6() const; + RawIPv4 toV4Bytes() const; + RawIPv6 toV6Bytes() const; + RawIP toBytes() const; + Family family() const; /// Returns the address family (IPv4 or IPv6) of the address. @@ -376,6 +396,8 @@ private: void newIPv6(const void* hostAddr); void newIPv6(const void* hostAddr, Poco::UInt32 scope); void newIPv6(unsigned prefix); + static std::string& compressV6(std::string& v6addr); + static std::string trimIPv6(const std::string v6Addr); #endif Ptr _pImpl; }; @@ -384,6 +406,19 @@ private: // // inlines // + +inline bool IPAddress::isV4() const +{ + return family() == IPv4; +} + + +inline bool IPAddress::isV6() const +{ + return family() == IPv6; +} + + inline IPAddress::Ptr IPAddress::pImpl() const { if (_pImpl) return _pImpl; diff --git a/Net/include/Poco/Net/MultiSocketPoller.h b/Net/include/Poco/Net/MultiSocketPoller.h index 518fe111c..807a2e752 100644 --- a/Net/include/Poco/Net/MultiSocketPoller.h +++ b/Net/include/Poco/Net/MultiSocketPoller.h @@ -30,7 +30,7 @@ namespace Net { template class MultiSocketPoller /// MultiSocketPoller, as its name indicates, repeatedly polls a set of - /// sockets for readability and/or eror. If socket is readable or in error + /// sockets for readability and/or error. If socket is readable or in error /// state, the reading/error handling actions are delegated to the reader. { public: diff --git a/Net/include/Poco/Net/Net.h b/Net/include/Poco/Net/Net.h index 2e9735ab2..29d795188 100644 --- a/Net/include/Poco/Net/Net.h +++ b/Net/include/Poco/Net/Net.h @@ -67,6 +67,14 @@ #endif // POCO_NET_NO_IPv6, POCO_HAVE_IPv6 +// Default to enabled local socket support if not explicitly disabled +#if !defined(POCO_NET_NO_UNIX_SOCKET) && !defined (POCO_HAVE_UNIX_SOCKET) + #define POCO_HAVE_UNIX_SOCKET +#elif defined(POCO_NET_NO_UNIX_SOCKET) && defined (POCO_HAVE_UNIX_SOCKET) + #undef POCO_HAVE_UNIX_SOCKET +#endif // POCO_NET_NO_UNIX_SOCKET, POCO_HAVE_UNIX_SOCKET + + namespace Poco { namespace Net { @@ -124,4 +132,24 @@ POCO_NET_FORCE_SYMBOL(pocoNetworkInitializer) #endif +#if defined(POCO_OS_FAMILY_BSD) + #ifndef POCO_HAVE_FD_POLL + #define POCO_HAVE_FD_POLL 1 + #endif +#endif + + +#if defined(POCO_OS_FAMILY_WINDOWS) + #ifndef POCO_HAVE_FD_POLL + // WSAPoll wants POLLOUT flag in order to return POLERR when there is no + // server on the other side. Windows default is multi-fd_set select, WSAPoll + // is disabled and not considered a production-grade code. + // see https://docs.microsoft.com/en-us/windows/win32/api/winsock2/nf-winsock2-wsapoll + // This is the version where the WSAPoll was (allegedly) fixed. + #if defined(WDK_NTDDI_VERSION) && (WDK_NTDDI_VERSION >= NTDDI_WIN10_VB) + //#define POCO_HAVE_FD_POLL 1 + #endif + #endif +#endif + #endif // Net_Net_INCLUDED diff --git a/Net/include/Poco/Net/PollSet.h b/Net/include/Poco/Net/PollSet.h index 198769683..75764afd8 100644 --- a/Net/include/Poco/Net/PollSet.h +++ b/Net/include/Poco/Net/PollSet.h @@ -78,6 +78,13 @@ public: /// Returns a PollMap containing the sockets that have had /// their state changed. + int count() const; + /// Returns the numberof sockets monitored. + + void wakeUp(); + /// Wakes up a waiting PollSet. + /// On platforms/implementations where this functionality + /// is not available, it does nothing. private: PollSetImpl* _pImpl; diff --git a/Net/include/Poco/Net/RawSocket.h b/Net/include/Poco/Net/RawSocket.h index 3638b20e4..3d0d018b2 100644 --- a/Net/include/Poco/Net/RawSocket.h +++ b/Net/include/Poco/Net/RawSocket.h @@ -53,6 +53,10 @@ public: /// a RawSocketImpl, otherwise an InvalidArgumentException /// will be thrown. + RawSocket(const RawSocket& socket); + /// Creates the RawSocket with the SocketImpl + /// from another socket. + ~RawSocket(); /// Destroys the RawSocket. @@ -61,7 +65,44 @@ public: /// /// Releases the socket's SocketImpl and /// attaches the SocketImpl from the other socket and - /// increments the reference count of the SocketImpl. + /// increments the reference count of the SocketImpl. + + RawSocket& operator = (const RawSocket& socket); + /// Assignment operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// increments the reference count of the SocketImpl. + +#if POCO_NEW_STATE_ON_MOVE + + RawSocket(Socket&& socket); + /// Creates the RawSocket with the SocketImpl + /// from another socket and zeroes the other socket's + /// SocketImpl.The SocketImpl must be + /// a RawSocketImpl, otherwise an InvalidArgumentException + /// will be thrown. + + RawSocket(RawSocket&& socket); + /// Creates the RawSocket with the SocketImpl + /// from another socket and zeroes the other socket's + /// SocketImpl. + + RawSocket& operator = (Socket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + + RawSocket& operator = (RawSocket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + +#endif //POCO_NEW_STATE_ON_MOVE void connect(const SocketAddress& address); /// Restricts incoming and outgoing @@ -135,7 +176,7 @@ protected: /// Creates the Socket and attaches the given SocketImpl. /// The socket takes ownership of the SocketImpl. /// - /// The SocketImpl must be a StreamSocketImpl, otherwise + /// The SocketImpl must be a RawSocketImpl, otherwise /// an InvalidArgumentException will be thrown. }; diff --git a/Net/include/Poco/Net/SingleSocketPoller.h b/Net/include/Poco/Net/SingleSocketPoller.h index 9b1b6b0f2..a767f368a 100644 --- a/Net/include/Poco/Net/SingleSocketPoller.h +++ b/Net/include/Poco/Net/SingleSocketPoller.h @@ -30,7 +30,7 @@ namespace Net { template class SingleSocketPoller - /// SinlgeSocketPoller, as its name indicates, repeatedly polls a single + /// SingleSocketPoller, as its name indicates, repeatedly polls a single /// socket for readability; if the socket is readable, the reading action /// is delegated to the reader. { diff --git a/Net/include/Poco/Net/Socket.h b/Net/include/Poco/Net/Socket.h index b6cf7a7cf..3eb962453 100644 --- a/Net/include/Poco/Net/Socket.h +++ b/Net/include/Poco/Net/Socket.h @@ -36,6 +36,8 @@ class Net_API Socket { public: using BufVec = SocketBufVec; + using Type = SocketImpl::Type; + using SocketList = std::vector; enum SelectMode /// The mode argument to poll() and select(). @@ -44,8 +46,6 @@ public: SELECT_WRITE = 2, SELECT_ERROR = 4 }; - - using SocketList = std::vector; Socket(); /// Creates an uninitialized socket. @@ -55,14 +55,31 @@ public: /// /// Attaches the SocketImpl from the other socket and /// increments the reference count of the SocketImpl. - + Socket& operator = (const Socket& socket); /// Assignment operator. /// /// Releases the socket's SocketImpl and /// attaches the SocketImpl from the other socket and /// increments the reference count of the SocketImpl. - + +#if POCO_NEW_STATE_ON_MOVE + + Socket(Socket&& socket); + /// Move constructor. + /// + /// Attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + + Socket& operator = (Socket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl, + /// attaches the SocketImpl from the other socket, + /// and zeroes the other socket's SocketImpl. + +#endif // POCO_NEW_STATE_ON_MOVE + virtual ~Socket(); /// Destroys the Socket and releases the /// SocketImpl. @@ -86,7 +103,25 @@ public: bool operator >= (const Socket& socket) const; /// Compares the SocketImpl pointers. - + + bool isNull() const; + /// Returns true if pointer to implementation is null. + + Type type() const; + /// Returns the socket type. + + bool isStream() const; + /// Returns true if socket is a stream socket, + /// false otherwise. + + bool isDatagram() const; + /// Returns true if socket is a datagram socket, + /// false otherwise. + + bool isRaw() const; + /// Returns true if socket is a raw socket, + /// false otherwise. + void close(); /// Closes the socket. @@ -134,6 +169,9 @@ public: /// Returns the number of bytes available that can be read /// without causing the socket to block. + int getError() const; + /// Returns the socket error. + void setSendBufferSize(int size); /// Sets the size of the send buffer. @@ -334,6 +372,12 @@ public: /// of buffers used for writing (ie. reading from socket /// into buffers). + static int lastError(); + /// Returns the last error code. + + static void error(); + /// Throws an appropriate exception for the last error. + protected: Socket(SocketImpl* pImpl); /// Creates the Socket and attaches the given SocketImpl. @@ -403,212 +447,318 @@ inline bool Socket::operator >= (const Socket& socket) const } +inline Socket::Type Socket::type() const +{ + return _pImpl->type(); +} + + +inline bool Socket::isStream() const +{ + return type() == Type::SOCKET_TYPE_STREAM; +} + + +inline bool Socket::isDatagram() const +{ + return type() == Type::SOCKET_TYPE_DATAGRAM; +} + + +inline bool Socket::isRaw() const +{ + return type() == Type::SOCKET_TYPE_RAW; +} + + +inline bool Socket::isNull() const +{ + return _pImpl == nullptr; +} + + inline void Socket::close() { - _pImpl->close(); + if (_pImpl) _pImpl->close(); } inline bool Socket::poll(const Poco::Timespan& timeout, int mode) const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->poll(timeout, mode); } inline int Socket::available() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->available(); } +inline int Socket::getError() const +{ + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + + return _pImpl->getError(); +} + + inline void Socket::setSendBufferSize(int size) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setSendBufferSize(size); } inline int Socket::getSendBufferSize() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getSendBufferSize(); } inline void Socket::setReceiveBufferSize(int size) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setReceiveBufferSize(size); } inline int Socket::getReceiveBufferSize() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getReceiveBufferSize(); } inline void Socket::setSendTimeout(const Poco::Timespan& timeout) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setSendTimeout(timeout); } inline Poco::Timespan Socket::getSendTimeout() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getSendTimeout(); } inline void Socket::setReceiveTimeout(const Poco::Timespan& timeout) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setReceiveTimeout(timeout); } inline Poco::Timespan Socket::getReceiveTimeout() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getReceiveTimeout(); } inline void Socket::setOption(int level, int option, int value) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setOption(level, option, value); } inline void Socket::setOption(int level, int option, unsigned value) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setOption(level, option, value); } inline void Socket::setOption(int level, int option, unsigned char value) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setOption(level, option, value); } inline void Socket::setOption(int level, int option, const Poco::Timespan& value) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setOption(level, option, value); } - + inline void Socket::setOption(int level, int option, const IPAddress& value) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setOption(level, option, value); } inline void Socket::getOption(int level, int option, int& value) const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->getOption(level, option, value); } inline void Socket::getOption(int level, int option, unsigned& value) const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->getOption(level, option, value); } inline void Socket::getOption(int level, int option, unsigned char& value) const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->getOption(level, option, value); } inline void Socket::getOption(int level, int option, Poco::Timespan& value) const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->getOption(level, option, value); } inline void Socket::getOption(int level, int option, IPAddress& value) const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->getOption(level, option, value); } inline void Socket::setLinger(bool on, int seconds) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setLinger(on, seconds); } inline void Socket::getLinger(bool& on, int& seconds) const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->getLinger(on, seconds); } inline void Socket::setNoDelay(bool flag) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setNoDelay(flag); } inline bool Socket::getNoDelay() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getNoDelay(); } inline void Socket::setKeepAlive(bool flag) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setKeepAlive(flag); } inline bool Socket::getKeepAlive() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getKeepAlive(); } inline void Socket::setReuseAddress(bool flag) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setReuseAddress(flag); } inline bool Socket::getReuseAddress() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getReuseAddress(); } inline void Socket::setReusePort(bool flag) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setReusePort(flag); } inline bool Socket::getReusePort() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getReusePort(); } inline void Socket::setOOBInline(bool flag) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setOOBInline(flag); } inline bool Socket::getOOBInline() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getOOBInline(); } inline void Socket::setBlocking(bool flag) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->setBlocking(flag); } inline bool Socket::getBlocking() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->getBlocking(); } @@ -621,24 +771,32 @@ inline SocketImpl* Socket::impl() const inline poco_socket_t Socket::sockfd() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->sockfd(); } inline SocketAddress Socket::address() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->address(); } - + inline SocketAddress Socket::peerAddress() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->peerAddress(); } inline bool Socket::secure() const { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + return _pImpl->secure(); } @@ -661,6 +819,8 @@ inline bool Socket::supportsIPv6() inline void Socket::init(int af) { + poco_assert_dbg(POCO_NEW_STATE_ON_MOVE && _pImpl); + _pImpl->init(af); } diff --git a/Net/include/Poco/Net/SocketAddress.h b/Net/include/Poco/Net/SocketAddress.h index 524d45c7e..c6d3069ef 100644 --- a/Net/include/Poco/Net/SocketAddress.h +++ b/Net/include/Poco/Net/SocketAddress.h @@ -138,6 +138,9 @@ public: SocketAddress(const SocketAddress& addr); /// Creates a SocketAddress by copying another one. + SocketAddress(SocketAddress&& addr); + /// Creates a SocketAddress by moving another one. + SocketAddress(const struct sockaddr* addr, poco_socklen_t length); /// Creates a SocketAddress from a native socket address. @@ -147,6 +150,9 @@ public: SocketAddress& operator = (const SocketAddress& socketAddress); /// Assigns another SocketAddress. + SocketAddress& operator = (SocketAddress&& socketAddress); + /// Move-assigns another SocketAddress. + IPAddress host() const; /// Returns the host IP address. diff --git a/Net/include/Poco/Net/SocketConnector.h b/Net/include/Poco/Net/SocketConnector.h index 72be21136..e03e44650 100644 --- a/Net/include/Poco/Net/SocketConnector.h +++ b/Net/include/Poco/Net/SocketConnector.h @@ -82,7 +82,7 @@ public: SocketConnector(SocketAddress& address, SocketReactor& reactor, bool doRegister = true) : _pReactor(0) - /// Creates an acceptor, using the given ServerSocket. + /// Creates an connector, using the given ServerSocket. /// The SocketConnector registers itself with the given SocketReactor. { _socket.connectNB(address); @@ -152,21 +152,22 @@ public: pNotification->release(); onConnect(); } - + void onConnect() { _socket.setBlocking(true); createServiceHandler(); unregisterConnector(); } - + void onError(ErrorNotification* pNotification) { pNotification->release(); onError(_socket.impl()->socketError()); unregisterConnector(); } - + + protected: virtual ServiceHandler* createServiceHandler() /// Create and initialize a new ServiceHandler instance. @@ -191,7 +192,7 @@ protected: { return _pReactor; } - + StreamSocket& socket() /// Returns a reference to the SocketConnector's socket. { diff --git a/Net/include/Poco/Net/SocketDefs.h b/Net/include/Poco/Net/SocketDefs.h index bdc9b0be4..d024a24b1 100644 --- a/Net/include/Poco/Net/SocketDefs.h +++ b/Net/include/Poco/Net/SocketDefs.h @@ -26,6 +26,7 @@ #if defined(POCO_OS_FAMILY_WINDOWS) #include "Poco/UnWindows.h" + #define FD_SETSIZE 1024 // increase as needed #include #include #include @@ -371,16 +372,18 @@ struct AddressFamily enum Family /// Possible address families for socket addresses. { - IPv4, + UNKNOWN = AF_UNSPEC, + /// Unspecified family + #if defined(POCO_OS_FAMILY_UNIX) + UNIX_LOCAL = AF_UNIX, + /// UNIX domain socket address family. Available on UNIX/POSIX platforms only. + #endif + IPv4 = AF_INET, /// IPv4 address family. #if defined(POCO_HAVE_IPv6) - IPv6, + IPv6 = AF_INET6 /// IPv6 address family. #endif - #if defined(POCO_OS_FAMILY_UNIX) - UNIX_LOCAL - /// UNIX domain socket address family. Available on UNIX/POSIX platforms only. - #endif }; }; diff --git a/Net/include/Poco/Net/SocketImpl.h b/Net/include/Poco/Net/SocketImpl.h index 1c7e5e442..5d75e7c6d 100644 --- a/Net/include/Poco/Net/SocketImpl.h +++ b/Net/include/Poco/Net/SocketImpl.h @@ -39,6 +39,13 @@ class Net_API SocketImpl: public Poco::RefCountedObject /// You should not create any instances of this class. { public: + enum Type + { + SOCKET_TYPE_STREAM = SOCK_STREAM, + SOCKET_TYPE_DATAGRAM = SOCK_DGRAM, + SOCKET_TYPE_RAW = SOCK_RAW + }; + enum SelectMode { SELECT_READ = 1, @@ -271,6 +278,12 @@ public: /// Returns true if the next operation corresponding to /// mode will not block, false otherwise. + Type type(); + /// Returns the socket type. + + virtual int getError(); + /// Returns the socket error. + virtual void setSendBufferSize(int size); /// Sets the size of the send buffer. @@ -526,6 +539,17 @@ private: // // inlines // +inline SocketImpl::Type SocketImpl::type() +{ + int type; + getOption(SOL_SOCKET, SO_TYPE, type); + poco_assert_dbg(type == SOCK_STREAM || + type == SOCK_DGRAM || + type == SOCK_RAW); + return static_cast(type); +} + + inline poco_socket_t SocketImpl::sockfd() const { return _sockfd; diff --git a/Net/include/Poco/Net/SocketProactor.h b/Net/include/Poco/Net/SocketProactor.h new file mode 100644 index 000000000..2d3258f6a --- /dev/null +++ b/Net/include/Poco/Net/SocketProactor.h @@ -0,0 +1,501 @@ +// +// SocketProactor.h +// +// Library: Net +// Package: Sockets +// Module: SocketProactor +// +// Definition of the SocketProactor class. +// +// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef Net_SocketProactor_INCLUDED +#define Net_SocketProactor_INCLUDED + + +#include "Poco/Net/Net.h" +#include "Poco/Net/Socket.h" +#include "Poco/Net/PollSet.h" +#include "Poco/Runnable.h" +#include "Poco/Timespan.h" +#include "Poco/Timestamp.h" +#include "Poco/AutoPtr.h" +#include "Poco/Mutex.h" +#include "Poco/Activity.h" +#include "Poco/NotificationQueue.h" +#include "Poco/ErrorHandler.h" +#include +#include +#include +#include +#include +#include +#include + + +namespace Poco { + + +class Thread; + + +namespace Net { + + +class Socket; +class Worker; + + +class Net_API SocketProactor final: public Poco::Runnable + /// This class implements the proactor pattern. + /// It may also contain a simple work executor (enabled by default), + /// which executes submitted workload. +{ +public: + using Buffer = std::vector; + using Work = std::function; + using Callback = std::function; + + static const int POLL_READ = PollSet::POLL_READ; + static const int POLL_WRITE = PollSet::POLL_WRITE; + static const int POLL_ERROR = PollSet::POLL_ERROR; + + static const Timestamp::TimeDiff PERMANENT_COMPLETION_HANDLER; + + explicit SocketProactor(bool worker = true); + /// Creates the SocketProactor. + + explicit SocketProactor(const Poco::Timespan& timeout, bool worker = true); + /// Creates the SocketProactor, using the given timeout. + + SocketProactor(const SocketProactor&) = delete; + SocketProactor(SocketProactor&&) = delete; + SocketProactor& operator=(const SocketProactor&) = delete; + SocketProactor& operator=(SocketProactor&&) = delete; + + ~SocketProactor(); + /// Destroys the SocketProactor. + + void addWork(const Work& ch, Timestamp::TimeDiff ms = PERMANENT_COMPLETION_HANDLER); + /// Adds work to be executed after the next poll() completion. + /// Function will be called until the specified expiration, + /// which defaults to immediately, ie. expiration after the + /// first invocation. + + void addWork(Work&& ch, Timestamp::TimeDiff ms = PERMANENT_COMPLETION_HANDLER, int pos = -1); + /// Adds work to be executed after the next poll() completion. + /// Function will be called until the specified expiration, + /// which defaults to immediately, ie. expiration after the + /// first invocation. + + void removeWork(); + /// Removes all scheduled work. + + int scheduledWork(); + /// Returns the number of scheduled functions. + + int removeScheduledWork(int count = -1); + /// Removes the count scheduled functions + /// from the front of the schedule queue. + /// Default is removal of all scheduled functions. + + int permanentWork(); + /// Returns the number of permanent functions. + + int removePermanentWork(int count = -1); + /// Removes the count permanent functions + /// from the front of the schedule queue. + /// Default is removal of all functions. + + int poll(int* pHandled = 0); + /// Polls all registered sockets and calls their respective handlers. + /// If pHandled is not null, after the call it contains the total number + /// of read/write/error socket handlers called. + /// Returns the number of completion handlers invoked. + + int runOne(); + /// Runs one handler, scheduled or permanent. + /// If there are no available handlers, it blocks + /// until the first handler is encountered and executed. + /// Returns 1 on successful handler invocation, 0 on + /// exception. + + void run(); + /// Runs the SocketProactor. The reactor will run + /// until stop() is called (in a separate thread). + + void stop(); + /// Stops the SocketProactor. + /// + /// The proactor will be stopped when the next event + /// (including a timeout event) occurs. + + void wakeUp(); + /// Wakes up idle reactor. + + void wait(); + /// Blocks and waits for the scheduled I/O completion + /// handlers loop to end. + + void setTimeout(const Poco::Timespan& timeout); + /// Sets the timeout. + /// + /// If no other event occurs for the given timeout + /// interval, a timeout event is sent to all event listeners. + /// + /// The default timeout is 250 milliseconds; + /// + /// The timeout is passed to the Socket::select() + /// method. + + Poco::Timespan getTimeout() const; + /// Returns the timeout. + + void addSocket(Socket sock, int mode); + /// Adds the socket to the poll set. + + void updateSocket(Socket sock, int mode); + /// Updates the socket mode in the poll set. + + void removeSocket(Socket sock); + /// Removes the socket from the poll set. + + void addReceiveFrom(Socket sock, Buffer& buf, SocketAddress& addr, Callback&& onCompletion); + /// Adds the datagram socket and the completion handler to the I/O receive queue. + + void addSendTo(Socket sock, const Buffer& message, const SocketAddress& addr, Callback&& onCompletion); + /// Adds the datagram socket and the completion handler to the I/O send queue. + + void addSendTo(Socket sock, Buffer&& message, const SocketAddress&& addr, Callback&& onCompletion); + /// Adds the datagram socket and the completion handler to the I/O send queue. + + void addSend(Socket sock, Buffer* pMessage, SocketAddress* pAddr, Callback&& onCompletion, bool own = false); + /// Adds the socket and the completion handler to the I/O send queue. + /// For stream socket, pAddr can be nullptr. + /// If `own` is true, message and address are deleted after the I/O completion. + + void addReceive(Socket sock, Buffer& buf, Callback&& onCompletion); + /// Adds the stream socket and the completion handler to the I/O receive queue. + + void addSend(Socket sock, const Buffer& message, Callback&& onCompletion); + /// Adds the stream socket and the completion handler to the I/O send queue. + + void addSend(Socket sock, Buffer&& message, Callback&& onCompletion); + /// Adds the stream socket and the completion handler to the I/O send queue. + + bool hasSocketHandlers() const; + /// Returns true if proactor had at least one I/O completion handler. + + bool has(const Socket& sock) const; + /// Returns true if socket is registered with this proactor. + + bool isRunning() const; + /// Returns true if this proactor is running + + bool ioCompletionInProgress() const; + /// Returns true if there are not executed handlers from last IO.. + +private: + void onShutdown(); + /// Called when the SocketProactor is about to terminate. + + int doWork(bool handleOne = false, bool expiredOnly = false); + /// Runs the scheduled work. + /// If handleOne is true, only the next scheduled function + /// is called. + /// If expiredOnly is true, only expired temporary functions + /// are called. + + typedef Poco::Mutex MutexType; + typedef MutexType::ScopedLock ScopedLock; + + static const long DEFAULT_MAX_TIMEOUT_MS = 250; + + struct Handler + /// Handler struct holds the scheduled I/O. + /// At the actual I/O, Buffer and SocketAddress + /// are used appropriately, and deleted if owned. + /// Callback is passed to the IOCompletion queue. + { + Buffer* _pBuf = nullptr; + SocketAddress* _pAddr = nullptr; + Callback _onCompletion = nullptr; + bool _owner = false; + }; + + class IONotification: public Notification + /// IONotification object is used to transfer + /// the I/O completion handlers into the + /// completion handlers queue. + { + public: + IONotification() = delete; + + IONotification(Callback&& onCompletion, int bytes, const std::error_code& errorCode): + _onCompletion(std::move(onCompletion)), + _bytes(bytes), + _errorCode(errorCode) + /// Creates the IONotification. + { + } + + ~IONotification() = default; + + void call() + /// Calls the completion handler. + { + _onCompletion(_errorCode, _bytes); + }; + + private: + Callback _onCompletion; + int _bytes; + std::error_code _errorCode; + }; + + class IOCompletion + /// IOCompletion utility class accompanies the + /// SocketProactor and serves to execute I/O + /// completion handlers in its own thread. + { + public: + IOCompletion() = delete; + + explicit IOCompletion(int maxTimeout): + _activity(this, &IOCompletion::run) + /// Creates IOCompletion. + { + start(); + } + + ~IOCompletion() + { + wakeUp(); + } + + void start() + /// Starts the I/O completion execution. + { + _activity.start(); + } + + void stop() + /// Stops the I/O completion execution. + { + _activity.stop(); + _nq.wakeUpAll(); + } + + void wait() + /// Blocks until I/O execution completely stops. + { + _activity.wait(); + } + + void enqueue(Notification::Ptr pNotification) + /// Enqueues I/O completion. + { + _nq.enqueueNotification(std::move(pNotification)); + } + + void wakeUp() + /// Wakes up the I/O completion execution loop. + { + _nq.wakeUpAll(); + } + + int queueSize() const + { + return _nq.size(); + } + + private: + bool runOne() + /// Runs the next I/O completion handler in the queue. + { + IONotification* pNf = dynamic_cast(_nq.waitDequeueNotification()); + if (pNf) + { + try + { + pNf->call(); + pNf->release(); + return true; + } + catch (Exception& exc) + { + ErrorHandler::handle(exc); + } + catch (std::exception& exc) + { + ErrorHandler::handle(exc); + } + catch (...) + { + ErrorHandler::handle(); + } + } + return false; + } + + void run() + /// Continuously runs enqueued completion handlers. + { + while(!_activity.isStopped()) runOne(); + } + + Activity _activity; + NotificationQueue _nq; + }; + + using IOHandlerList = std::deque>; + using IOHandlerIt = IOHandlerList::iterator; + using SubscriberMap = std::unordered_map>>; + + void sleep(bool isAtWork); + /// Sleep policy implementation. + /// If there is currently any work being done, + /// timeout is kept at zero (ie. no timeout), + /// otherwise, the timeout is incremented and + /// - trySleep() is called if proactor runs + /// in a Poco::Thread, which is necessary + /// for trySleep call to be interruptable + /// or + /// - sleep() is called (not interruptable) + /// + /// The value of _timeout can grow up to + /// _maxTimeout value. + + int error(Socket& sock); + /// Enqueues the completion handlers and removes + /// them from the handlers list after the operation + /// successfully completes. + + bool hasHandlers(SubscriberMap& handlers, int sockfd); + void deleteHandler(IOHandlerList& handlers, IOHandlerList::iterator& it); + + template + int errorImpl(Socket& sock, T& handlerMap, Poco::Mutex& mutex) + { + Poco::Mutex::ScopedLock l(mutex); + auto hIt = handlerMap.find(sock.impl()->sockfd()); + if (hIt == handlerMap.end()) return 0; + unsigned err = 0; + sock.getOption(SOL_SOCKET, SO_ERROR, err); + IOHandlerList& handlers = hIt->second; + int handled = static_cast(handlers.size()); + auto it = handlers.begin(); + auto end = handlers.end(); + while (it != end) + { + enqueueIONotification(std::move((*it)->_onCompletion), 0, err); + deleteHandler(handlers, it); + // end iterator is invalidated when the last member + // is removed, so make sure we don't check for it + if (handlers.empty()) break; + } + handled -= static_cast(handlers.size()); + if (handled) _ioCompletion.wakeUp(); + return handled; + } + + int send(Socket& sock); + /// Calls the appropriate output function; enqueues + /// the accompanying completion handler and removes + /// it from the handlers list after the operation + /// successfully completes. + + int receive(Socket& sock); + /// Calls the appropriate input function; enqueues + /// the accompanying completion handler and removes + /// it from the handlers list after the operation + /// successfully completes. + + void sendTo(SocketImpl& sock, IOHandlerIt& it); + /// Sends data to the datagram socket and enqueues the + /// accompanying completion handler. + + void send(SocketImpl& sock, IOHandlerIt& it); + /// Sends data to the stream socket and enqueues the + /// accompanying completion handler. + + void receiveFrom(SocketImpl& sock, IOHandlerIt& it, int available); + /// Reads data from the datagram socket and enqueues the + /// accompanying completion handler. + + void receive(SocketImpl& sock, IOHandlerIt& it, int available); + /// Reads data from the stream socket and enqueues the + /// accompanying completion handler. + + void enqueueIONotification(Callback&& onCompletion, int n, int err); + /// Enqueues the completion handler into the I/O + /// completion handler. + + Worker& worker(); + + std::atomic _isRunning; + std::atomic _isStopped; + std::atomic _stop; + long _timeout; + long _maxTimeout; + PollSet _pollSet; + Poco::Thread* _pThread; + + SubscriberMap _readHandlers; + SubscriberMap _writeHandlers; + IOCompletion _ioCompletion; + Poco::Mutex _writeMutex; + Poco::Mutex _readMutex; + + std::unique_ptr _pWorker; + friend class Worker; +}; + +// +// inlines +// + +inline void SocketProactor::addSocket(Socket sock, int mode) +{ + _pollSet.add(sock, mode | PollSet::POLL_ERROR); +} + + +inline void SocketProactor::updateSocket(Socket sock, int mode) +{ + _pollSet.update(sock, mode); +} + + +inline void SocketProactor::removeSocket(Socket sock) +{ + _pollSet.remove(sock); +} + + +inline void SocketProactor::enqueueIONotification(Callback&& onCompletion, int n, int err) +{ + if (onCompletion) + { + _ioCompletion.enqueue(new IONotification( + std::move(onCompletion), n, + std::error_code(err, std::generic_category()))); + } +} + + +inline bool SocketProactor::isRunning() const +{ + return _isRunning; +} + + +} } // namespace Poco::Net + + +#endif // Net_SocketProactor_INCLUDED diff --git a/Net/include/Poco/Net/SocketReactor.h b/Net/include/Poco/Net/SocketReactor.h index 08ca48568..2644f946f 100644 --- a/Net/include/Poco/Net/SocketReactor.h +++ b/Net/include/Poco/Net/SocketReactor.h @@ -209,7 +209,7 @@ protected: private: typedef Poco::AutoPtr NotifierPtr; typedef Poco::AutoPtr NotificationPtr; - typedef std::map EventHandlerMap; + typedef std::map EventHandlerMap; typedef Poco::FastMutex MutexType; typedef MutexType::ScopedLock ScopedLock; diff --git a/Net/include/Poco/Net/StreamSocket.h b/Net/include/Poco/Net/StreamSocket.h index 1960362b7..54ed55994 100644 --- a/Net/include/Poco/Net/StreamSocket.h +++ b/Net/include/Poco/Net/StreamSocket.h @@ -60,6 +60,10 @@ public: /// a StreamSocketImpl, otherwise an InvalidArgumentException /// will be thrown. + StreamSocket(const StreamSocket& socket); + /// Creates the StreamSocket with the SocketImpl + /// from another socket. + virtual ~StreamSocket(); /// Destroys the StreamSocket. @@ -70,6 +74,43 @@ public: /// attaches the SocketImpl from the other socket and /// increments the reference count of the SocketImpl. + StreamSocket& operator = (const StreamSocket& socket); + /// Assignment operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// increments the reference count of the SocketImpl. + +#if POCO_NEW_STATE_ON_MOVE + + StreamSocket(Socket&& socket); + /// Creates the StreamSocket with the SocketImpl + /// from another socket and zeroes the other socket's + /// SocketImpl.The SocketImpl must be + /// a StreamSocketImpl, otherwise an InvalidArgumentException + /// will be thrown. + + StreamSocket(StreamSocket&& socket); + /// Creates the StreamSocket with the SocketImpl + /// from another socket and zeroes the other socket's + /// SocketImpl. + + StreamSocket& operator = (Socket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + + StreamSocket& operator = (StreamSocket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + +#endif //POCO_NEW_STATE_ON_MOVE + void connect(const SocketAddress& address); /// Initializes the socket and establishes a connection to /// the TCP server at the given address. diff --git a/Net/include/Poco/Net/UDPHandler.h b/Net/include/Poco/Net/UDPHandler.h index 6aefb111b..02795e4a5 100644 --- a/Net/include/Poco/Net/UDPHandler.h +++ b/Net/include/Poco/Net/UDPHandler.h @@ -42,7 +42,7 @@ typedef int UDPMsgSizeT; template class UDPHandlerImpl: public Runnable, public RefCountedObject - /// UDP handler handles the data that arives to the UDP server. + /// UDP handler handles the data that arrives to the UDP server. /// The class is thread-safe and runs in its own thread, so many handlers /// can be used in parallel.Handler manages and provides the storage /// (fixed-size memory blocks of S size) to the reader, which signals back diff --git a/Net/include/Poco/Net/WebSocket.h b/Net/include/Poco/Net/WebSocket.h index 477a23fb3..183daf6d2 100644 --- a/Net/include/Poco/Net/WebSocket.h +++ b/Net/include/Poco/Net/WebSocket.h @@ -176,8 +176,11 @@ public: /// Creates a WebSocket from another Socket, which must be a WebSocket, /// otherwise a Poco::InvalidArgumentException will be thrown. + WebSocket(const WebSocket& socket); + /// Creates a WebSocket from another WebSocket. + virtual ~WebSocket(); - /// Destroys the StreamSocket. + /// Destroys the WebSocket. WebSocket& operator = (const Socket& socket); /// Assignment operator. @@ -185,6 +188,39 @@ public: /// The other socket must be a WebSocket, otherwise a Poco::InvalidArgumentException /// will be thrown. + WebSocket& operator = (const WebSocket& socket); + /// Assignment operator. + +#if POCO_NEW_STATE_ON_MOVE + + WebSocket(Socket&& socket); + /// Creates the WebSocket with the SocketImpl + /// from another socket and zeroes the other socket's + /// SocketImpl.The SocketImpl must be + /// a WebSocketImpl, otherwise an InvalidArgumentException + /// will be thrown. + + WebSocket(WebSocket&& socket); + /// Creates the WebSocket with the SocketImpl + /// from another socket and zeroes the other socket's + /// SocketImpl. + + WebSocket& operator = (Socket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + + WebSocket& operator = (WebSocket&& socket); + /// Assignment move operator. + /// + /// Releases the socket's SocketImpl and + /// attaches the SocketImpl from the other socket and + /// zeroes the other socket's SocketImpl. + +#endif //POCO_NEW_STATE_ON_MOVE + void shutdown(); /// Sends a Close control frame to the server end of /// the connection to initiate an orderly shutdown diff --git a/Net/src/DatagramSocket.cpp b/Net/src/DatagramSocket.cpp index 89085a1b3..1cb71e6e8 100644 --- a/Net/src/DatagramSocket.cpp +++ b/Net/src/DatagramSocket.cpp @@ -34,15 +34,12 @@ DatagramSocket::DatagramSocket(SocketAddress::Family family): Socket(new Datagra } -DatagramSocket::DatagramSocket(const SocketAddress& address, bool reuseAddress): Socket(new DatagramSocketImpl(address.family())) +DatagramSocket::DatagramSocket(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only): + Socket(new DatagramSocketImpl(address.family())) { - bind(address, reuseAddress); -} - - -DatagramSocket::DatagramSocket(const SocketAddress& address, bool reuseAddress, bool reusePort): Socket(new DatagramSocketImpl(address.family())) -{ - bind(address, reuseAddress, reusePort); + if (address.family() == SocketAddress::IPv6) + bind6(address, reuseAddress, reusePort, ipV6Only); + else bind(address, reuseAddress, reusePort); } @@ -53,6 +50,11 @@ DatagramSocket::DatagramSocket(const Socket& socket): Socket(socket) } +DatagramSocket::DatagramSocket(const DatagramSocket& socket): Socket(socket) +{ +} + + DatagramSocket::DatagramSocket(SocketImpl* pImpl): Socket(pImpl) { if (!dynamic_cast(impl())) @@ -74,6 +76,44 @@ DatagramSocket& DatagramSocket::operator = (const Socket& socket) return *this; } +#if POCO_NEW_STATE_ON_MOVE + +DatagramSocket::DatagramSocket(DatagramSocket&& socket): Socket(std::move(socket)) +{ +} + + +DatagramSocket::DatagramSocket(Socket&& socket): Socket(std::move(socket)) +{ + if (!dynamic_cast(impl())) + throw InvalidArgumentException("Cannot assign incompatible socket"); +} + + +DatagramSocket& DatagramSocket::operator = (Socket&& socket) +{ + if (dynamic_cast(socket.impl())) + Socket::operator = (std::move(socket)); + else + throw InvalidArgumentException("Cannot assign incompatible socket"); + return *this; +} + + +DatagramSocket& DatagramSocket::operator = (DatagramSocket&& socket) +{ + Socket::operator = (std::move(socket)); + return *this; +} + +#endif // POCO_NEW_STATE_ON_MOVE + +DatagramSocket& DatagramSocket::operator = (const DatagramSocket& socket) +{ + Socket::operator = (socket); + return *this; +} + void DatagramSocket::connect(const SocketAddress& address) { @@ -93,6 +133,12 @@ void DatagramSocket::bind(const SocketAddress& address, bool reuseAddress, bool } +void DatagramSocket::bind6(const SocketAddress& address, bool reuseAddress, bool reusePort, bool ipV6Only) +{ + impl()->bind6(address, reuseAddress, reusePort, ipV6Only); +} + + int DatagramSocket::sendBytes(const void* buffer, int length, int flags) { return impl()->sendBytes(buffer, length, flags); diff --git a/Net/src/ICMPSocketImpl.cpp b/Net/src/ICMPSocketImpl.cpp index ca161861e..5e27651fd 100644 --- a/Net/src/ICMPSocketImpl.cpp +++ b/Net/src/ICMPSocketImpl.cpp @@ -66,7 +66,7 @@ int ICMPSocketImpl::receiveFrom(void*, int, SocketAddress& address, int flags) { int maxPacketSize = _icmpPacket.maxPacketSize(); Poco::Buffer buffer(maxPacketSize); - int expected = _icmpPacket.packetSize(); + int leftover = _icmpPacket.packetSize(); int type = 0, code = 0; try @@ -83,8 +83,8 @@ int ICMPSocketImpl::receiveFrom(void*, int, SocketAddress& address, int flags) if (rc == 0) break; if (respAddr == address) { - expected -= rc; - if (expected <= 0) + leftover -= rc; + if (leftover <= 0) { if (_icmpPacket.validReplyID(buffer.begin(), maxPacketSize)) break; std::string err = _icmpPacket.errorDescription(buffer.begin(), maxPacketSize, type, code); @@ -95,7 +95,7 @@ int ICMPSocketImpl::receiveFrom(void*, int, SocketAddress& address, int flags) } else continue; } - while (expected > 0 && !_icmpPacket.validReplyID(buffer.begin(), maxPacketSize)); + while (leftover > 0 && !_icmpPacket.validReplyID(buffer.begin(), maxPacketSize)); } catch (ICMPException&) { @@ -113,10 +113,11 @@ int ICMPSocketImpl::receiveFrom(void*, int, SocketAddress& address, int flags) else throw; } - if (expected > 0) + if (leftover > 0) { - throw ICMPException(Poco::format("No response: expected %d, received: %d", _icmpPacket.packetSize(), - _icmpPacket.packetSize() - expected)); + std::string err = leftover < _icmpPacket.packetSize() ? "Incomplete" : "No"; + throw ICMPException(Poco::format("%s response: expected %d, received: %d", err, _icmpPacket.packetSize(), + _icmpPacket.packetSize() - leftover)); } struct timeval then = _icmpPacket.time(buffer.begin(), maxPacketSize); diff --git a/Net/src/IPAddress.cpp b/Net/src/IPAddress.cpp index e34a02181..164386c4b 100644 --- a/Net/src/IPAddress.cpp +++ b/Net/src/IPAddress.cpp @@ -19,6 +19,7 @@ #include "Poco/BinaryReader.h" #include "Poco/BinaryWriter.h" #include "Poco/String.h" +#include "Poco/Format.h" #include "Poco/Types.h" @@ -69,6 +70,11 @@ IPAddress::IPAddress(const IPAddress& addr) } +IPAddress::IPAddress(IPAddress&& addr): _pImpl(std::move(addr._pImpl)) +{ +} + + IPAddress::IPAddress(Family family) { if (family == IPv4) @@ -99,7 +105,7 @@ IPAddress::IPAddress(const std::string& addr) #if defined(POCO_HAVE_IPv6) IPv6AddressImpl empty6 = IPv6AddressImpl(); - if (addr.empty() || trim(addr) == "::") + if (addr.empty() || trimIPv6(addr) == "::") { newIPv6(empty6.addr()); return; @@ -237,6 +243,13 @@ IPAddress& IPAddress::operator = (const IPAddress& addr) } +IPAddress& IPAddress::operator = (IPAddress&& addr) +{ + _pImpl = std::move(addr._pImpl); + return *this; +} + + IPAddress::Family IPAddress::family() const { return pImpl()->family(); @@ -519,6 +532,47 @@ unsigned IPAddress::prefixLength() const } +std::string& IPAddress::compressV6(std::string& v6addr) +{ + // get rid of leading zeros at the beginning + while (v6addr.size() && v6addr[0] == '0') v6addr.erase(v6addr.begin()); + + // get rid of leading zeros in the middle + while (v6addr.find(":0") != std::string::npos) + Poco::replaceInPlace(v6addr, ":0", ":"); + + // get rid of extraneous colons + while (v6addr.find(":::") != std::string::npos) + Poco::replaceInPlace(v6addr, ":::", "::"); + + return v6addr; +} + + +std::string IPAddress::trimIPv6(const std::string v6Addr) +{ + std::string v6addr(v6Addr); + std::string::size_type len = v6addr.length(); + int dblColOcc = 0; + auto pos = v6addr.find("::"); + while ((pos <= len-2) && (pos != std::string::npos)) + { + ++dblColOcc; + pos = v6addr.find("::", pos + 2); + } + + if ((dblColOcc > 1) || + (std::count(v6addr.begin(), v6addr.end(), ':') > 8) || + (v6addr.find(":::") != std::string::npos) || + ((len >= 2) && ((v6addr[len-1] == ':') && v6addr[len-2] != ':'))) + { + return v6addr; + } + + return compressV6(v6addr); +} + + IPAddress IPAddress::parse(const std::string& addr) { return IPAddress(addr); @@ -535,7 +589,7 @@ bool IPAddress::tryParse(const std::string& addr, IPAddress& result) } #if defined(POCO_HAVE_IPv6) IPv6AddressImpl impl6(IPv6AddressImpl::parse(addr)); - if (impl6 != IPv6AddressImpl()) + if (impl6 != IPv6AddressImpl() || trimIPv6(addr) == "::") { result.newIPv6(impl6.addr(), impl6.scope()); return true; @@ -572,6 +626,54 @@ IPAddress IPAddress::broadcast() } +IPAddress::RawIPv4 IPAddress::toV4Bytes() const +{ + if (family() != IPv4) + throw Poco::InvalidAccessException(Poco::format("IPAddress::toV4Bytes(%d)", (int)family())); + + RawIPv4 bytes; + std::memcpy(&bytes[0], addr(), IPv4Size); + return bytes; +} + + +IPAddress::RawIPv6 IPAddress::toV6Bytes() const +{ + if (family() != IPv6) + throw Poco::InvalidAccessException(Poco::format("IPAddress::toV6Bytes(%d)", (int)family())); + + RawIPv6 bytes; + std::memcpy(&bytes[0], addr(), IPv6Size); + return bytes; +} + + +std::vector IPAddress::toBytes() const +{ + std::size_t sz = 0; + std::vector bytes; + const void* ptr = 0; + switch (family()) + { + case IPv4: + sz = sizeof(in_addr); + ptr = addr(); + break; +#if defined(POCO_HAVE_IPv6) + case IPv6: + sz = sizeof(in6_addr); + ptr = addr(); + break; +#endif + default: + throw Poco::IllegalStateException(Poco::format("IPAddress::toBytes(%d)", (int)family())); + } + bytes.resize(sz); + std::memcpy(&bytes[0], ptr, sz); + return bytes; +} + + } } // namespace Poco::Net diff --git a/Net/src/PollSet.cpp b/Net/src/PollSet.cpp index f0319120d..40c98d182 100644 --- a/Net/src/PollSet.cpp +++ b/Net/src/PollSet.cpp @@ -18,23 +18,13 @@ #include -#if defined(_WIN32) && _WIN32_WINNT >= 0x0600 -#ifndef POCO_HAVE_FD_POLL -#define POCO_HAVE_FD_POLL 1 -#endif -#elif defined(POCO_OS_FAMILY_BSD) -#ifndef POCO_HAVE_FD_POLL -#define POCO_HAVE_FD_POLL 1 -#endif -#endif - - #if defined(POCO_HAVE_FD_EPOLL) -#include + #include + #include #elif defined(POCO_HAVE_FD_POLL) -#ifndef _WIN32 -#include -#endif + #ifndef _WIN32 + #include + #endif #endif @@ -51,12 +41,12 @@ namespace Net { class PollSetImpl { public: - PollSetImpl(): - _epollfd(-1), - _events(1024) + PollSetImpl(): _epollfd(epoll_create(1)), + _events(1024), + _eventfd(eventfd(0, 0)) { - _epollfd = epoll_create(1); - if (_epollfd < 0) + int err = addImpl(_eventfd, PollSet::POLL_READ, 0); + if ((err) || (_epollfd < 0)) { SocketImpl::error(); } @@ -64,8 +54,8 @@ public: ~PollSetImpl() { - if (_epollfd >= 0) - ::close(_epollfd); + if (_epollfd >= 0) ::close(_epollfd); + if (_eventfd >= 0) ::close(_eventfd); } void add(const Socket& socket, int mode) @@ -73,17 +63,8 @@ public: Poco::FastMutex::ScopedLock lock(_mutex); SocketImpl* sockImpl = socket.impl(); - poco_socket_t fd = sockImpl->sockfd(); - struct epoll_event ev; - ev.events = 0; - if (mode & PollSet::POLL_READ) - ev.events |= EPOLLIN; - if (mode & PollSet::POLL_WRITE) - ev.events |= EPOLLOUT; - if (mode & PollSet::POLL_ERROR) - ev.events |= EPOLLERR; - ev.data.ptr = socket.impl(); - int err = epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev); + + int err = addImpl(sockImpl->sockfd(), mode, sockImpl); if (err) { @@ -158,18 +139,13 @@ public: PollSet::SocketModeMap poll(const Poco::Timespan& timeout) { PollSet::SocketModeMap result; - - { - Poco::FastMutex::ScopedLock lock(_mutex); - if(_socketMap.empty()) return result; - } - Poco::Timespan remainingTime(timeout); int rc; do { Poco::Timestamp start; rc = epoll_wait(_epollfd, &_events[0], _events.size(), remainingTime.totalMilliseconds()); + if (rc == 0) return result; if (rc < 0 && SocketImpl::lastError() == POCO_EINTR) { Poco::Timestamp end; @@ -187,26 +163,57 @@ public: for (int i = 0; i < rc; i++) { - std::map::iterator it = _socketMap.find(_events[i].data.ptr); - if (it != _socketMap.end()) + if (_events[i].data.ptr) // skip eventfd { - if (_events[i].events & EPOLLIN) - result[it->second] |= PollSet::POLL_READ; - if (_events[i].events & EPOLLOUT) - result[it->second] |= PollSet::POLL_WRITE; - if (_events[i].events & EPOLLERR) - result[it->second] |= PollSet::POLL_ERROR; + std::map::iterator it = _socketMap.find(_events[i].data.ptr); + if (it != _socketMap.end()) + { + if (_events[i].events & EPOLLIN) + result[it->second] |= PollSet::POLL_READ; + if (_events[i].events & EPOLLOUT) + result[it->second] |= PollSet::POLL_WRITE; + if (_events[i].events & EPOLLERR) + result[it->second] |= PollSet::POLL_ERROR; + } } } return result; } + void wakeUp() + { + uint64_t val = 1; + int n = ::write(_eventfd, &val, sizeof(val)); + if (n < 0) Socket::error(); + } + + int count() const + { + Poco::FastMutex::ScopedLock lock(_mutex); + return static_cast(_socketMap.size()); + } + private: + int addImpl(int fd, int mode, void* ptr) + { + struct epoll_event ev; + ev.events = 0; + if (mode & PollSet::POLL_READ) + ev.events |= EPOLLIN; + if (mode & PollSet::POLL_WRITE) + ev.events |= EPOLLOUT; + if (mode & PollSet::POLL_ERROR) + ev.events |= EPOLLERR; + ev.data.ptr = ptr; + return epoll_ctl(_epollfd, EPOLL_CTL_ADD, fd, &ev); + } + mutable Poco::FastMutex _mutex; int _epollfd; std::map _socketMap; std::vector _events; + int _eventfd; }; @@ -222,7 +229,6 @@ public: void add(const Socket& socket, int mode) { Poco::FastMutex::ScopedLock lock(_mutex); - poco_socket_t fd = socket.impl()->sockfd(); _addMap[fd] = mode; _removeSet.erase(fd); @@ -232,7 +238,6 @@ public: void remove(const Socket& socket) { Poco::FastMutex::ScopedLock lock(_mutex); - poco_socket_t fd = socket.impl()->sockfd(); _removeSet.insert(fd); _addMap.erase(fd); @@ -256,7 +261,6 @@ public: void update(const Socket& socket, int mode) { Poco::FastMutex::ScopedLock lock(_mutex); - poco_socket_t fd = socket.impl()->sockfd(); for (auto it = _pollfds.begin(); it != _pollfds.end(); ++it) { @@ -264,7 +268,7 @@ public: { it->events = 0; it->revents = 0; - setMode(it->fd, it->events, mode); + setMode(it->events, mode); } } } @@ -305,7 +309,7 @@ public: pfd.fd = it->first; pfd.events = 0; pfd.revents = 0; - setMode(pfd.fd, pfd.events, it->second); + setMode(pfd.events, it->second); _pollfds.push_back(pfd); } _addMap.clear(); @@ -320,12 +324,6 @@ public: Poco::Timestamp start; #ifdef _WIN32 rc = WSAPoll(&_pollfds[0], static_cast(_pollfds.size()), static_cast(remainingTime.totalMilliseconds())); - // see https://github.com/pocoproject/poco/issues/3248 - if ((remainingTime > 0) && (rc > 0) && !hasSignaledFDs()) - { - rc = -1; - WSASetLastError(WSAEINTR); - } #else rc = ::poll(&_pollfds[0], _pollfds.size(), remainingTime.totalMilliseconds()); #endif @@ -354,17 +352,13 @@ public: { if ((it->revents & POLLIN) #ifdef _WIN32 - || (it->revents & POLLHUP) + || (it->revents & POLLHUP) #endif ) result[its->second] |= PollSet::POLL_READ; - if ((it->revents & POLLOUT) -#ifdef _WIN32 - && (_wantPOLLOUT.find(it->fd) != _wantPOLLOUT.end()) -#endif - ) + if (it->revents & POLLOUT) result[its->second] |= PollSet::POLL_WRITE; - if (it->revents & POLLERR) + if (it->revents & POLLERR || (it->revents & POLLHUP)) result[its->second] |= PollSet::POLL_ERROR; } it->revents = 0; @@ -375,38 +369,20 @@ public: return result; } + void wakeUp() + { + // TODO + } + + int count() const + { + Poco::FastMutex::ScopedLock lock(_mutex); + return static_cast(_socketMap.size()); + } + private: -#ifdef _WIN32 - - void setMode(poco_socket_t fd, short& target, int mode) - { - if (mode & PollSet::POLL_READ) - target |= POLLIN; - - if (mode & PollSet::POLL_WRITE) - _wantPOLLOUT.insert(fd); - else - _wantPOLLOUT.erase(fd); - target |= POLLOUT; - } - - bool hasSignaledFDs() - { - for (const auto& pollfd : _pollfds) - { - if ((pollfd.revents | POLLOUT) && - (_wantPOLLOUT.find(pollfd.fd) != _wantPOLLOUT.end())) - { - return true; - } - } - return false; - } - -#else - - void setMode(poco_socket_t fd, short& target, int mode) + void setMode(short& target, int mode) { if (mode & PollSet::POLL_READ) target |= POLLIN; @@ -415,13 +391,8 @@ private: target |= POLLOUT; } -#endif - mutable Poco::FastMutex _mutex; std::map _socketMap; -#ifdef _WIN32 - std::set _wantPOLLOUT; -#endif std::map _addMap; std::set _removeSet; std::vector _pollfds; @@ -431,6 +402,300 @@ private: #else +#ifdef POCO_OS_FAMILY_WINDOWS + + +// +// Windows-specific implementation using select() +// The size of select set is determined at compile +// time (see FD_SETSIZE in SocketDefs.h). +// +// This implementation works around that limit by +// having multiple socket descriptor sets and, +// when needed, calling select() multiple times. +// To avoid multiple sets situtation, the FD_SETSIZE +// can be increased, however then Poco::Net library +// must be recompiled in order for the new setting +// to be in effect. +// + + +class PollSetImpl +{ +public: + PollSetImpl() : _fdRead(1, {0, {0}}), + _fdWrite(1, {0, {0}}), + _fdExcept(1, {0, {0}}), + _pFDRead(std::make_unique()), + _pFDWrite(std::make_unique()), + _pFDExcept(std::make_unique()), + _nfd(0) + { + } + + void add(const Socket& socket, int mode) + { + Poco::Net::SocketImpl* pImpl = socket.impl(); + poco_check_ptr(pImpl); + Poco::FastMutex::ScopedLock lock(_mutex); + _map[socket] = mode; + setMode(pImpl->sockfd(), mode); + } + + void remove(const Socket& socket) + { + Poco::Net::SocketImpl* pImpl = socket.impl(); + poco_check_ptr(pImpl); + Poco::FastMutex::ScopedLock lock(_mutex); + remove(pImpl->sockfd()); + _map.erase(socket); + } + + bool has(const Socket& socket) const + { + Poco::FastMutex::ScopedLock lock(_mutex); + return _map.find(socket) != _map.end(); + } + + bool empty() const + { + Poco::FastMutex::ScopedLock lock(_mutex); + return _map.empty(); + } + + void update(const Socket& socket, int mode) + { + Poco::Net::SocketImpl* pImpl = socket.impl(); + poco_check_ptr(pImpl); + SOCKET fd = pImpl->sockfd(); + Poco::FastMutex::ScopedLock lock(_mutex); + _map[socket] = mode; + setMode(fd, mode); + if (!(mode & PollSet::POLL_READ)) remove(fd, _fdRead); + if (!(mode & PollSet::POLL_WRITE)) remove(fd, _fdWrite); + if (!(mode & PollSet::POLL_ERROR)) remove(fd, _fdExcept); + } + + void clear() + { + Poco::FastMutex::ScopedLock lock(_mutex); + _map.clear(); + for (auto& fd : _fdRead) std::memset(&fd, 0, sizeof(fd)); + for (auto& fd : _fdWrite) std::memset(&fd, 0, sizeof(fd)); + for (auto& fd : _fdExcept) std::memset(&fd, 0, sizeof(fd)); + _nfd = 0; + } + + PollSet::SocketModeMap poll(const Poco::Timespan& timeout) + { + Poco::Timestamp start; + poco_assert_dbg(_fdRead.size() == _fdWrite.size()); + poco_assert_dbg(_fdWrite.size() == _fdExcept.size()); + + PollSet::SocketModeMap result; + if (_nfd == 0) return result; + + Poco::Timespan remainingTime(timeout); + struct timeval tv {0, 1000}; + + Poco::FastMutex::ScopedLock lock(_mutex); + + auto readIt = _fdRead.begin(); + auto writeIt = _fdWrite.begin(); + auto exceptIt = _fdExcept.begin(); + do + { + std::memcpy(_pFDRead.get(), &*readIt, sizeof(fd_set)); + std::memcpy(_pFDWrite.get(), &*writeIt, sizeof(fd_set)); + std::memcpy(_pFDExcept.get(), &*exceptIt, sizeof(fd_set)); + + int rc; + do + { + rc = ::select((int)_nfd + 1, _pFDRead.get(), _pFDWrite.get(), _pFDExcept.get(), &tv); + } while (rc < 0 && SocketImpl::lastError() == POCO_EINTR); + if (rc < 0) SocketImpl::error(); + else if (rc > 0) + { + for (auto it = _map.begin(); it != _map.end(); ++it) + { + poco_socket_t fd = it->first.impl()->sockfd(); + if (fd != POCO_INVALID_SOCKET) + { + if (FD_ISSET(fd, _pFDRead.get())) + { + result[it->first] |= PollSet::POLL_READ; + } + if (FD_ISSET(fd, _pFDWrite.get())) + { + result[it->first] |= PollSet::POLL_WRITE; + } + if (FD_ISSET(fd, _pFDExcept.get())) + { + result[it->first] |= PollSet::POLL_ERROR; + } + } + } + } + + Timespan elapsed = Timestamp() - start; + if (++readIt == _fdRead.end()) + { + if ((rc > 0) || (elapsed.totalMilliseconds() > timeout.totalMilliseconds())) + break; + readIt = _fdRead.begin(); + writeIt = _fdWrite.begin(); + exceptIt = _fdExcept.begin(); + } + else + { + ++writeIt; + ++exceptIt; + } + + Poco::UInt64 tOut = (((Poco::UInt64)tv.tv_sec * 1000000) + tv.tv_usec) * 2; + Poco::Timespan left = timeout - elapsed; + if (tOut > left.totalMicroseconds()) + tOut = left.totalMicroseconds(); + + tv.tv_sec = static_cast(tOut / 1000000); + tv.tv_usec = tOut % 1000000; + } while (true); + + return result; + } + + int count() const + { + Poco::FastMutex::ScopedLock lock(_mutex); + return static_cast(_map.size()); + } + + void wakeUp() + { + // TODO + } + +private: + + void setMode(std::vector& fdSet, SOCKET fd) + { + SOCKET* pFD = 0; + for (auto& fdr : fdSet) + { + SOCKET* begin = fdr.fd_array; + SOCKET* end = fdr.fd_array + fdr.fd_count; + pFD = std::find(begin, end, fd); + if (end != pFD) + { + FD_SET(fd, &fdr); + if (fd > _nfd) _nfd = fd; + return; + } + } + // not found, insert at first free location + for (auto& fdr : fdSet) + { + if (fdr.fd_count < FD_SETSIZE) + { + fdr.fd_count++; + fdr.fd_array[fdr.fd_count-1] = fd; + if (fd > _nfd) _nfd = fd; + return; + } + } + // all fd sets are full; insert another one + fdSet.push_back({0, {0}}); + fd_set& fds = fdSet.back(); + fds.fd_count = 1; + fds.fd_array[0] = fd; + if (fd > _nfd) _nfd = fd; + } + + void setMode(SOCKET fd, int mode) + { + if (mode & PollSet::POLL_READ) setMode(_fdRead, fd); + if (mode & PollSet::POLL_WRITE) setMode(_fdWrite, fd); + if (mode & PollSet::POLL_ERROR) setMode(_fdExcept, fd); + } + + void remove(SOCKET fd, std::vector& fdSets) + { + bool newNFD = false; + for (auto& fdSet : fdSets) + { + if (fdSet.fd_count) + { + newNFD = (fd == _nfd); + int i = 0; + for (; i < fdSet.fd_count; ++i) + { + if (fdSet.fd_array[i] == fd) + { + if (i == (fdSet.fd_count-1)) + { + fdSet.fd_array[i] = 0; + } + else + { + for (; i < fdSet.fd_count-1; ++i) + { + fdSet.fd_array[i] = fdSet.fd_array[i+1]; + if (newNFD && fdSet.fd_array[i] > _nfd) + _nfd = fdSet.fd_array[i]; + } + } + fdSet.fd_array[fdSet.fd_count-1] = 0; + fdSet.fd_count--; + break; + } + if (newNFD && fdSet.fd_array[i] > _nfd) + _nfd = fdSet.fd_array[i]; + } + } + } + if (newNFD) + { + findNFD(_fdRead); + findNFD(_fdWrite); + findNFD(_fdExcept); + } + } + + void findNFD(std::vector& fdSets) + { + for (auto& fdSet : fdSets) + { + for (int i = 0; i < fdSet.fd_count; ++i) + { + if (fdSet.fd_array[i] > _nfd) + _nfd = fdSet.fd_array[i]; + } + } + } + + void remove(SOCKET fd) + { + remove(fd, _fdRead); + remove(fd, _fdWrite); + remove(fd, _fdExcept); + } + + mutable Poco::FastMutex _mutex; + PollSet::SocketModeMap _map; + SOCKET _nfd; + std::vector _fdRead; + std::vector _fdWrite; + std::vector _fdExcept; + std::unique_ptr _pFDRead; + std::unique_ptr _pFDWrite; + std::unique_ptr _pFDExcept; +}; + + +#else + + // // Fallback implementation using select() // @@ -562,12 +827,26 @@ public: return result; } + void wakeUp() + { + // TODO + } + + int count() const + { + Poco::FastMutex::ScopedLock lock(_mutex); + return static_cast(_map.size()); + } + private: mutable Poco::FastMutex _mutex; PollSet::SocketModeMap _map; }; +#endif // POCO_OS_FAMILY_WINDOWS + + #endif @@ -625,4 +904,16 @@ PollSet::SocketModeMap PollSet::poll(const Poco::Timespan& timeout) } +int PollSet::count() const +{ + return _pImpl->count(); +} + + +void PollSet::wakeUp() +{ + _pImpl->wakeUp(); +} + + } } // namespace Poco::Net diff --git a/Net/src/RawSocket.cpp b/Net/src/RawSocket.cpp index 7605dcac3..7966a3fd2 100644 --- a/Net/src/RawSocket.cpp +++ b/Net/src/RawSocket.cpp @@ -50,6 +50,16 @@ RawSocket::RawSocket(const Socket& socket): Socket(socket) } +RawSocket::RawSocket(const RawSocket& socket): Socket(socket) +{ +} + + +RawSocket::RawSocket(RawSocket&& socket): Socket(std::move(socket)) +{ +} + + RawSocket::RawSocket(SocketImpl* pImpl): Socket(pImpl) { if (!dynamic_cast(impl())) @@ -72,6 +82,30 @@ RawSocket& RawSocket::operator = (const Socket& socket) } +RawSocket& RawSocket::operator = (Socket&& socket) +{ + if (dynamic_cast(socket.impl())) + Socket::operator = (std::move(socket)); + else + throw InvalidArgumentException("Cannot assign incompatible socket"); + return *this; +} + + +RawSocket& RawSocket::operator = (const RawSocket& socket) +{ + Socket::operator = (socket); + return *this; +} + + +RawSocket& RawSocket::operator = (RawSocket&& socket) +{ + Socket::operator = (std::move(socket)); + return *this; +} + + void RawSocket::connect(const SocketAddress& address) { impl()->connect(address); diff --git a/Net/src/Socket.cpp b/Net/src/Socket.cpp index 7aeb7948d..3e66e2220 100644 --- a/Net/src/Socket.cpp +++ b/Net/src/Socket.cpp @@ -18,10 +18,12 @@ #include #include // FD_SET needs memset on some platforms, so we can't use #if defined(POCO_HAVE_FD_EPOLL) -#include + #include #elif defined(POCO_HAVE_FD_POLL) -#include "Poco/SharedPtr.h" -#include + #include "Poco/SharedPtr.h" + #ifndef _WIN32 + #include + #endif #endif @@ -50,6 +52,30 @@ Socket::Socket(const Socket& socket): _pImpl->duplicate(); } +#if POCO_NEW_STATE_ON_MOVE + +Socket::Socket(Socket&& socket): + _pImpl(socket._pImpl) +{ + poco_check_ptr (_pImpl); + + socket._pImpl = nullptr; +} + + +Socket& Socket::operator = (Socket&& socket) +{ + if (&socket != this) + { + if (_pImpl) _pImpl->release(); + _pImpl = socket._pImpl; + socket._pImpl = nullptr; + } + return *this; +} + +#endif // POCO_NEW_STATE_ON_MOVE + Socket& Socket::operator = (const Socket& socket) { @@ -62,10 +88,9 @@ Socket& Socket::operator = (const Socket& socket) return *this; } - Socket::~Socket() { - _pImpl->release(); + if (_pImpl) _pImpl->release(); } @@ -207,8 +232,11 @@ int Socket::select(SocketList& readList, SocketList& writeList, SocketList& exce #elif defined(POCO_HAVE_FD_POLL) typedef Poco::SharedPtr> SharedPollArray; +#ifdef _WIN32 + typedef ULONG nfds_t; +#endif + nfds_t nfd = static_cast(readList.size() + writeList.size() + exceptList.size()); - nfds_t nfd = readList.size() + writeList.size() + exceptList.size(); if (0 == nfd) return 0; SharedPollArray pPollArr = new pollfd[nfd](); @@ -256,7 +284,11 @@ int Socket::select(SocketList& readList, SocketList& writeList, SocketList& exce do { Poco::Timestamp start; +#ifdef _WIN32 + rc = WSAPoll(pPollArr, nfd, static_cast(remainingTime.totalMilliseconds())); +#else rc = ::poll(pPollArr, nfd, remainingTime.totalMilliseconds()); +#endif if (rc < 0 && SocketImpl::lastError() == POCO_EINTR) { Poco::Timestamp end; @@ -276,17 +308,17 @@ int Socket::select(SocketList& readList, SocketList& writeList, SocketList& exce SocketList::iterator endE = exceptList.end(); for (int idx = 0; idx < nfd; ++idx) { - SocketList::iterator slIt = std::find_if(begR, endR, Socket::FDCompare(pPollArr[idx].fd)); + SocketList::iterator slIt = std::find_if(begR, endR, Socket::FDCompare(static_cast(pPollArr[idx].fd))); if (POLLIN & pPollArr[idx].revents && slIt != endR) readyReadList.push_back(*slIt); - slIt = std::find_if(begW, endW, Socket::FDCompare(pPollArr[idx].fd)); + slIt = std::find_if(begW, endW, Socket::FDCompare(static_cast(pPollArr[idx].fd))); if (POLLOUT & pPollArr[idx].revents && slIt != endW) readyWriteList.push_back(*slIt); - slIt = std::find_if(begE, endE, Socket::FDCompare(pPollArr[idx].fd)); + slIt = std::find_if(begE, endE, Socket::FDCompare(static_cast(pPollArr[idx].fd))); if (POLLERR & pPollArr[idx].revents && slIt != endE) readyExceptList.push_back(*slIt); } std::swap(readList, readyReadList); std::swap(writeList, readyWriteList); std::swap(exceptList, readyExceptList); - return readList.size() + writeList.size() + exceptList.size(); + return static_cast(readList.size() + writeList.size() + exceptList.size()); #else @@ -464,4 +496,16 @@ SocketBufVec Socket::makeBufVec(const std::vector& vec) } +int Socket::lastError() +{ + return SocketImpl::lastError(); +} + + +void Socket::error() +{ + SocketImpl::error(); +} + + } } // namespace Poco::Net diff --git a/Net/src/SocketAddress.cpp b/Net/src/SocketAddress.cpp index 1e24d222b..487f5ae07 100644 --- a/Net/src/SocketAddress.cpp +++ b/Net/src/SocketAddress.cpp @@ -150,6 +150,12 @@ SocketAddress::SocketAddress(const SocketAddress& socketAddress) } +SocketAddress::SocketAddress(SocketAddress&& socketAddress): + _pImpl(std::move(socketAddress._pImpl)) +{ +} + + SocketAddress::SocketAddress(const struct sockaddr* sockAddr, poco_socklen_t length) { if (length == sizeof(struct sockaddr_in) && sockAddr->sa_family == AF_INET) @@ -203,6 +209,13 @@ SocketAddress& SocketAddress::operator = (const SocketAddress& socketAddress) } +SocketAddress& SocketAddress::operator = (SocketAddress&& socketAddress) +{ + _pImpl = std::move(socketAddress._pImpl); + return *this; +} + + IPAddress SocketAddress::host() const { return pImpl()->host(); diff --git a/Net/src/SocketImpl.cpp b/Net/src/SocketImpl.cpp index 000de2dd5..3bbe95cfb 100644 --- a/Net/src/SocketImpl.cpp +++ b/Net/src/SocketImpl.cpp @@ -20,23 +20,12 @@ #include // FD_SET needs memset on some platforms, so we can't use -#if defined(_WIN32) && _WIN32_WINNT >= 0x0600 -#ifndef POCO_HAVE_FD_POLL -#define POCO_HAVE_FD_POLL 1 -#endif -#elif defined(POCO_OS_FAMILY_BSD) -#ifndef POCO_HAVE_FD_POLL -#define POCO_HAVE_FD_POLL 1 -#endif -#endif - - #if defined(POCO_HAVE_FD_EPOLL) -#include + #include #elif defined(POCO_HAVE_FD_POLL) -#ifndef _WIN32 -#include -#endif + #ifndef _WIN32 + #include + #endif #endif @@ -219,10 +208,8 @@ void SocketImpl::bind(const SocketAddress& address, bool reuseAddress, bool reus { init(address.af()); } - if (reuseAddress) - setReuseAddress(true); - if (reusePort) - setReusePort(true); + setReuseAddress(reuseAddress); + setReusePort(reusePort); #if defined(POCO_VXWORKS) int rc = ::bind(_sockfd, (sockaddr*) address.addr(), address.length()); #else @@ -253,10 +240,8 @@ void SocketImpl::bind6(const SocketAddress& address, bool reuseAddress, bool reu #else if (ipV6Only) throw Poco::NotImplementedException("IPV6_V6ONLY not defined."); #endif - if (reuseAddress) - setReuseAddress(true); - if (reusePort) - setReusePort(true); + setReuseAddress(reuseAddress); + setReusePort(reusePort); int rc = ::bind(_sockfd, address.addr(), address.length()); if (rc != 0) error(address.toString()); #else @@ -614,6 +599,13 @@ int SocketImpl::available() { int result = 0; ioctl(FIONREAD, result); +#if (POCO_OS != POCO_OS_LINUX) + if (type() == SOCKET_TYPE_DATAGRAM) + { + std::vector buf(result); + result = recvfrom(sockfd(), &buf[0], result, MSG_PEEK, NULL, NULL); + } +#endif return result; } @@ -759,6 +751,14 @@ bool SocketImpl::poll(const Poco::Timespan& timeout, int mode) } +int SocketImpl::getError() +{ + int result; + getOption(SOL_SOCKET, SO_ERROR, result); + return result; +} + + void SocketImpl::setSendBufferSize(int size) { setOption(SOL_SOCKET, SO_SNDBUF, size); @@ -1027,14 +1027,25 @@ void SocketImpl::setReuseAddress(bool flag) { int value = flag ? 1 : 0; setOption(SOL_SOCKET, SO_REUSEADDR, value); +#ifdef POCO_OS_FAMILY_WINDOWS + value = flag ? 0 : 1; + setOption(SOL_SOCKET, SO_EXCLUSIVEADDRUSE, value); +#endif } bool SocketImpl::getReuseAddress() { + bool ret = false; int value(0); getOption(SOL_SOCKET, SO_REUSEADDR, value); - return value != 0; + ret = (value != 0); +#ifdef POCO_OS_FAMILY_WINDOWS + value = 0; + getOption(SOL_SOCKET, SO_EXCLUSIVEADDRUSE, value); + ret = ret && (value == 0); +#endif + return ret; } diff --git a/Net/src/SocketNotification.cpp b/Net/src/SocketNotification.cpp index da3c922da..db92323e2 100644 --- a/Net/src/SocketNotification.cpp +++ b/Net/src/SocketNotification.cpp @@ -29,7 +29,7 @@ SocketNotification::~SocketNotification() { } - + void SocketNotification::setSocket(const Socket& socket) { _socket = socket; diff --git a/Net/src/SocketProactor.cpp b/Net/src/SocketProactor.cpp new file mode 100644 index 000000000..51e809dbb --- /dev/null +++ b/Net/src/SocketProactor.cpp @@ -0,0 +1,808 @@ +// +// SocketProactor.cpp +// +// Library: Net +// Package: Sockets +// Module: SocketProactor +// +// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "Poco/Net/SocketProactor.h" +#include "Poco/Net/DatagramSocket.h" +#include "Poco/Net/DatagramSocketImpl.h" +#include "Poco/Thread.h" +#include "Poco/Exception.h" +#ifdef POCO_OS_FAMILY_WINDOWS +#ifdef max +#undef max +#endif // max +#endif // POCO_OS_FAMILY_WINDOWS +#include + + +using Poco::Exception; +using Poco::ErrorHandler; + + +namespace Poco { +namespace Net { + + +// +// Worker +// + +class Worker + /// Worker is a utility class that executes work (functions). + /// Workload can be permanent (executed on every doWork() call), + /// or "one-shot" (scheduled for a single execution at a point + /// in the future). +{ +public: + using MutexType = SocketProactor::MutexType; + using ScopedLock = SocketProactor::ScopedLock; + using Work = SocketProactor::Work; + using WorkEntry = std::pair; + using WorkList = std::deque; + + void addWork(const Work& ch, Timestamp::TimeDiff ms = SocketProactor::PERMANENT_COMPLETION_HANDLER) + { + addWork(Work(ch), ms); + } + + void addWork(Work&& ch, Timestamp::TimeDiff ms, int pos = -1) + { + auto pch = SocketProactor::PERMANENT_COMPLETION_HANDLER; + Poco::Timestamp expires = (ms != pch) ? Timestamp() + (ms * 1000) : Timestamp(pch); + if (pos == -1 || (pos + 1) > _funcList.size()) + { + ScopedLock lock(_mutex); + _funcList.push_back({std::move(ch), expires}); + } + else + { + if (pos < 0) + throw Poco::InvalidArgumentException("SocketProactor::addWork()"); + ScopedLock lock(_mutex); + _funcList.insert(_funcList.begin() + pos, {std::move(ch), expires}); + } + + } + + void removeWork() + { + ScopedLock lock(_mutex); + _funcList.clear(); + } + + int scheduledWork() + { + int cnt = 0; + ScopedLock lock(_mutex); + WorkList::iterator it = _funcList.begin(); + for (; it != _funcList.end(); ++it) + { + if (!isPermanent(it->second)) ++cnt; + } + return cnt; + } + + int removeScheduledWork(int count) + { + auto isScheduled = [](const Timestamp &ts) + { return !isPermanent(ts); }; + return removeWork(isScheduled, count); + } + + int permanentWork() + { + int cnt = 0; + ScopedLock lock(_mutex); + WorkList::iterator it = _funcList.begin(); + for (; it != _funcList.end(); ++it) + { + if (isPermanent(it->second)) + ++cnt; + } + return cnt; + } + + + int removePermanentWork(int count) + { + auto perm = [](const Timestamp &ts) + { return isPermanent(ts); }; + return removeWork(perm, count); + } + + static bool isPermanent(const Timestamp &entry) + { + return entry == Timestamp(SocketProactor::PERMANENT_COMPLETION_HANDLER); + } + + int doWork(bool handleOne, bool expiredOnly) + { + std::unique_ptr pCH; + int handled = 0; + { + ScopedLock lock(_mutex); + WorkList::iterator it = _funcList.begin(); + try + { + while (it != _funcList.end()) + { + std::size_t prevSize = 0; + bool alwaysRun = isPermanent(it->second) && !expiredOnly; + bool isExpired = !alwaysRun && (Timestamp() >= it->second); + if (isExpired) + { + pCH.reset(new Work(std::move(it->first))); + it = _funcList.erase(it); + } + else if (alwaysRun) + { + pCH.reset(new Work(it->first)); + ++it; + } + else ++it; + prevSize = _funcList.size(); + + if (pCH) + { + (*pCH)(); + pCH.reset(); + ++handled; + if (handleOne) break; + } + // handler call may add or remove handlers; + // if so, we must start from the beginning + if (prevSize != _funcList.size()) + it = _funcList.begin(); + } + } + catch (Exception& exc) + { + ErrorHandler::handle(exc); + } + catch (std::exception& exc) + { + ErrorHandler::handle(exc); + } + catch (...) + { + ErrorHandler::handle(); + } + } + return handled; + } + + int runOne() + { + try + { + while (0 == doWork(true, false)); + return 1; + } + catch(...) {} + return 0; + } + +private: + template + int removeWork(F isType, int count) + /// Removes `count` functions of the specified type; + /// if count is -1, removes all the functions of the + /// specified type. + { + int removed = 0; + ScopedLock lock(_mutex); + int left = count > -1 ? count : static_cast(_funcList.size()); + WorkList::iterator it = _funcList.begin(); + while (left && it != _funcList.end()) + { + if (isType(it->second)) + { + ++removed; + it = _funcList.erase((it)); + --left; + } + else ++it; + } + return removed; + } + + WorkList _funcList; + MutexType _mutex; +}; + + +// +// SocketProactor +// + +const Timestamp::TimeDiff SocketProactor::PERMANENT_COMPLETION_HANDLER = + std::numeric_limits::max(); + + +SocketProactor::SocketProactor(bool worker): + _isRunning(false), + _isStopped(false), + _stop(false), + _timeout(0), + _maxTimeout(DEFAULT_MAX_TIMEOUT_MS), + _pThread(nullptr), + _ioCompletion(_maxTimeout), + _pWorker(worker ? new Worker : nullptr) +{ +} + + +SocketProactor::SocketProactor(const Poco::Timespan& timeout, bool worker): + _isRunning(false), + _isStopped(false), + _stop(false), + _timeout(0), + _maxTimeout(static_cast(timeout.totalMilliseconds())), + _pThread(nullptr), + _ioCompletion(_maxTimeout), + _pWorker(worker ? new Worker : nullptr) +{ +} + + +SocketProactor::~SocketProactor() +{ + _ioCompletion.stop(); + wait(); + for (auto& pS : _writeHandlers) + { + for (auto& pH : pS.second) + { + if (pH->_pBuf && pH->_owner) + delete pH->_pBuf; + } + } +} + + +void SocketProactor::wait() +{ + _ioCompletion.wakeUp(); + _ioCompletion.wait(); +} + + +bool SocketProactor::hasHandlers(SubscriberMap& handlers, int sockfd) +{ + Poco::Mutex::ScopedLock l(_writeMutex); + if (handlers.end() == handlers.find(sockfd)) + return false; + return true; +} + + +int SocketProactor::poll(int* pHandled) +{ + int handled = 0; + int worked = 0; + PollSet::SocketModeMap sm = _pollSet.poll(_timeout); + if (sm.size() > 0) + { + auto it = sm.begin(); + auto end = sm.end(); + for (; it != end; ++it) + { + if (it->second & PollSet::POLL_READ) + { + Socket sock = it->first; + if (hasHandlers(_readHandlers, static_cast(sock.impl()->sockfd()))) + handled += receive(sock); + } + if (it->second & PollSet::POLL_WRITE) + { + Socket sock = it->first; + if (hasHandlers(_writeHandlers, static_cast(sock.impl()->sockfd()))) + handled += send(sock); + } + if (it->second & PollSet::POLL_ERROR) + { + Socket sock = it->first; + handled += error(sock); + } + } + } + + if (_pWorker) + { + if (hasSocketHandlers() && handled) worked = doWork(); + else worked = doWork(false, true); + } + + if (pHandled) *pHandled = handled; + return worked; +} + + +void SocketProactor::addReceiveFrom(Socket sock, Buffer& buf, Poco::Net::SocketAddress& addr, Callback&& onCompletion) +{ + if (!sock.isDatagram()) + throw Poco::InvalidArgumentException("SocketProactor::addSend(): UDP socket required"); + std::unique_ptr pHandler(new Handler); + pHandler->_pAddr = std::addressof(addr); + pHandler->_pBuf = std::addressof(buf); + pHandler->_onCompletion = std::move(onCompletion); + + Poco::Mutex::ScopedLock l(_readMutex); + _readHandlers[sock.impl()->sockfd()].push_back(std::move(pHandler)); +} + + +void SocketProactor::addSendTo(Socket sock, const Buffer& message, const SocketAddress& addr, Callback&& onCompletion) +{ + if (!sock.isDatagram()) + throw Poco::InvalidArgumentException("SocketProactor::addSend(): UDP socket required"); + Buffer* pMessage = nullptr; + SocketAddress* pAddr = nullptr; + try + { + pMessage = new Buffer(message); + pAddr = new SocketAddress(addr); + } + catch(...) + { + delete pMessage; + delete pAddr; + throw; + } + addSend(sock, pMessage, pAddr, std::move(onCompletion), true); +} + + +void SocketProactor::addSendTo(Socket sock, Buffer&& message, const SocketAddress&& addr, Callback&& onCompletion) +{ + if (!sock.isDatagram()) + throw Poco::InvalidArgumentException("SocketProactor::addSend(): UDP socket required"); + Buffer* pMessage = nullptr; + SocketAddress* pAddr = nullptr; + try + { + pMessage = new Buffer(std::move(message)); + pAddr = new SocketAddress(std::move(addr)); + } + catch(...) + { + delete pMessage; + delete pAddr; + throw; + } + addSend(sock, pMessage, pAddr, std::move(onCompletion), true); +} + + +void SocketProactor::addReceive(Socket sock, Buffer& buf, Callback&& onCompletion) +{ + if (!sock.isStream()) + throw Poco::InvalidArgumentException("SocketProactor::addSend(): TCP socket required"); + std::unique_ptr pHandler(new Handler); + pHandler->_pAddr = nullptr; + pHandler->_pBuf = std::addressof(buf); + pHandler->_onCompletion = std::move(onCompletion); + + Poco::Mutex::ScopedLock l(_readMutex); + _readHandlers[sock.impl()->sockfd()].push_back(std::move(pHandler)); + if (!has(sock)) addSocket(sock, PollSet::POLL_READ); +} + + +void SocketProactor::addSend(Socket sock, const Buffer& message, Callback&& onCompletion) +{ + if (!sock.isStream()) + throw Poco::InvalidArgumentException("SocketProactor::addSend(): TCP socket required"); + Buffer* pMessage = nullptr; + try + { + pMessage = new Buffer(message); + } + catch(...) + { + delete pMessage; + throw; + } + addSend(sock, pMessage, nullptr, std::move(onCompletion), true); +} + + +void SocketProactor::addSend(Socket sock, Buffer&& message, Callback&& onCompletion) +{ + if (!sock.isStream()) + throw Poco::InvalidArgumentException("SocketProactor::addSend(): TCP socket required"); + Buffer* pMessage = nullptr; + try + { + pMessage = new Buffer(std::move(message)); + } + catch(...) + { + delete pMessage; + throw; + } + addSend(sock, pMessage, nullptr, std::move(onCompletion), true); +} + + +void SocketProactor::addSend(Socket sock, Buffer* pMessage, SocketAddress* pAddr, Callback&& onCompletion, bool own) +{ + std::unique_ptr pHandler(new Handler); + pHandler->_pAddr = pAddr; + pHandler->_pBuf = pMessage; + pHandler->_onCompletion = std::move(onCompletion); + pHandler->_owner = own; + + Poco::Mutex::ScopedLock l(_writeMutex); + _writeHandlers[sock.impl()->sockfd()].push_back(std::move(pHandler)); + if (!has(sock)) addSocket(sock, PollSet::POLL_WRITE); +} + + +int SocketProactor::error(Socket& sock) +{ + int cnt = errorImpl(sock, _readHandlers, _readMutex); + cnt += errorImpl(sock, _writeHandlers, _writeMutex); + return cnt; +} + + +int SocketProactor::send(Socket& sock) +{ + Poco::Mutex::ScopedLock l(_writeMutex); + auto hIt = _writeHandlers.find(sock.impl()->sockfd()); + if (hIt == _writeHandlers.end()) return 0; + IOHandlerList& handlers = hIt->second; + int handled = static_cast(handlers.size()); + auto it = handlers.begin(); + auto end = handlers.end(); + while (it != end) + { + if (sock.isDatagram()) + sendTo(*sock.impl(), it); + else if (sock.isStream()) + send(*sock.impl(), it); + else + { + deleteHandler(handlers, it); + throw Poco::InvalidArgumentException("Unknown socket type."); + } + deleteHandler(handlers, it); + + // end iterator is invalidated when the last member + // is removed, so make sure we don't check for it + if (handlers.empty()) break; + } + handled -= static_cast(handlers.size()); + if (handled) _ioCompletion.wakeUp(); + return handled; +} + + +void SocketProactor::sendTo(SocketImpl& sock, IOHandlerIt& it) +{ + Buffer* pBuf = (*it)->_pBuf; + if (pBuf && pBuf->size()) + { + SocketAddress *pAddr = (*it)->_pAddr; + int n = 0, err = 0; + try + { + n = sock.sendTo(&(*pBuf)[0], static_cast(pBuf->size()), *pAddr); + } + catch(std::exception&) + { + err = Socket::lastError(); + } + enqueueIONotification(std::move((*it)->_onCompletion), n, err); + } + else + { + if (!pBuf) + throw Poco::NullPointerException("SocketProactor::sendTo(): null buffer"); + else if (pBuf->empty()) + throw Poco::InvalidArgumentException("SocketProactor::sendTo(): empty buffer"); + else // we shouldn't be here + throw Poco::InvalidAccessException("SocketProactor::sendTo(): unexpected error"); + } +} + + +void SocketProactor::send(SocketImpl& sock, IOHandlerIt& it) +{ + Buffer* pBuf = (*it)->_pBuf; + if (pBuf && pBuf->size()) + { + int n = 0, err = 0; + try + { + n = sock.sendBytes(&(*pBuf)[0], static_cast(pBuf->size())); + } + catch(std::exception&) + { + err = Socket::lastError(); + } + enqueueIONotification(std::move((*it)->_onCompletion), n, err); + } + else + { + if (!pBuf) + throw Poco::NullPointerException("SocketProactor::sendTo(): null buffer"); + else if (pBuf->empty()) + throw Poco::InvalidArgumentException("SocketProactor::sendTo(): empty buffer"); + else // we shouldn't be here + throw Poco::InvalidAccessException("SocketProactor::sendTo(): unexpected error"); + } +} + + +int SocketProactor::receive(Socket& sock) +{ + Poco::Mutex::ScopedLock l(_readMutex); + auto hIt = _readHandlers.find(sock.impl()->sockfd()); + if (hIt == _readHandlers.end()) return 0; + IOHandlerList& handlers = hIt->second; + int handled = static_cast(handlers.size()); + int avail = 0; + auto it = handlers.begin(); + auto end = handlers.end(); + for (; it != end;) + { + if ((avail = sock.available())) + { + if (sock.isDatagram()) + receiveFrom(*sock.impl(), it, avail); + else if (sock.isStream()) + receive(*sock.impl(), it, avail); + else + throw Poco::InvalidArgumentException("Unknown socket type."); + + ++it; + handlers.pop_front(); + // end iterator is invalidated when the last member + // is removed, so make sure we don't check for it + if (handlers.size() == 0) break; + } + else break; + } + handled -= static_cast(handlers.size()); + if (handled) _ioCompletion.wakeUp(); + return handled; +} + + +void SocketProactor::receiveFrom(SocketImpl& sock, IOHandlerIt& it, int available) +{ + Buffer *pBuf = (*it)->_pBuf; + SocketAddress *pAddr = (*it)->_pAddr; + SocketAddress addr = *pAddr; + poco_check_ptr(pBuf); + if (pBuf->size() < available) pBuf->resize(available); + int n = 0, err = 0; + try + { + n = sock.receiveFrom(&(*pBuf)[0], available, *pAddr); + } + catch(std::exception&) + { + err = Socket::lastError(); + } + enqueueIONotification(std::move((*it)->_onCompletion), n, err); +} + + +void SocketProactor::receive(SocketImpl& sock, IOHandlerIt& it, int available) +{ + Buffer *pBuf = (*it)->_pBuf; + poco_check_ptr(pBuf); + if (pBuf->size() < available) pBuf->resize(available); + int n = 0, err = 0; + try + { + n = sock.receiveBytes(&(*pBuf)[0], available); + } + catch(std::exception&) + { + err = Socket::lastError(); + } + enqueueIONotification(std::move((*it)->_onCompletion), n, err); +} + + +int SocketProactor::doWork(bool handleOne, bool expiredOnly) +{ + return worker().doWork(handleOne, expiredOnly); +} + + +int SocketProactor::runOne() +{ + return worker().runOne(); +} + + +void SocketProactor::sleep(bool isAtWork) +{ + try + { + if (isAtWork) + { + _timeout = 0; + return; + } + else + { + if (_timeout < _maxTimeout) ++_timeout; + } + if (_pThread) _pThread->trySleep(_timeout); + else Thread::sleep(_timeout); + } + catch (Exception& exc) + { + ErrorHandler::handle(exc); + } + catch (std::exception& exc) + { + ErrorHandler::handle(exc); + } + catch (...) + { + ErrorHandler::handle(); + } +} + + +void SocketProactor::run() +{ + _pThread = Thread::current(); + _ioCompletion.start(); + int handled = 0; + if (!_isStopped) _stop = false; + _isStopped = false; + while (!_stop) + { + this->sleep(poll(&handled) || handled); + _isRunning = true; + } + _isRunning = false; + onShutdown(); +} + + +bool SocketProactor::hasSocketHandlers() const +{ + if (_readHandlers.size() || _writeHandlers.size()) + return true; + return false; +} + + +void SocketProactor::stop() +{ + // the reason for two flags is to prevent a race + // when stop() is called before run() (which sets + // stop to false before entering the polling loop + // in order to allow multiple run()/stop() cycles) + _stop = true; + _isStopped = true; +} + + +void SocketProactor::wakeUp() +{ + if (_pThread) _pThread->wakeUp(); +} + + +void SocketProactor::setTimeout(const Poco::Timespan& timeout) +{ + _timeout = static_cast(timeout.totalMilliseconds()); +} + + +Poco::Timespan SocketProactor::getTimeout() const +{ + return _maxTimeout; +} + + +Worker& SocketProactor::worker() +{ + poco_check_ptr(_pWorker); + return *_pWorker; +} + + +void SocketProactor::addWork(const Work& ch, Timestamp::TimeDiff ms) +{ + worker().addWork(Work(ch), ms); +} + + +void SocketProactor::addWork(Work&& ch, Timestamp::TimeDiff ms, int pos) +{ + worker().addWork(std::move(ch), ms, pos); +} + + +void SocketProactor::removeWork() +{ + worker().removeWork(); +} + + +int SocketProactor::scheduledWork() +{ + return worker().scheduledWork(); +} + + +int SocketProactor::removeScheduledWork(int count) +{ + return worker().removeScheduledWork(count); +} + + +int SocketProactor::permanentWork() +{ + return worker().permanentWork(); +} + + +int SocketProactor::removePermanentWork(int count) +{ + return worker().removePermanentWork(count); +} + + +bool SocketProactor::has(const Socket& sock) const +{ + return _pollSet.has(sock); +} + + +bool SocketProactor::ioCompletionInProgress() const +{ + return _ioCompletion.queueSize(); +} + + +void SocketProactor::onShutdown() +{ + _pollSet.wakeUp(); + _ioCompletion.stop(); + _ioCompletion.wait(); +} + + +void SocketProactor::deleteHandler(IOHandlerList& handlers, IOHandlerList::iterator& it) +{ + if ((*it)->_owner) + { + if ((*it)->_pBuf) + { + delete (*it)->_pBuf; + (*it)->_pBuf = nullptr; + } + if ((*it)->_pAddr) + { + delete (*it)->_pAddr; + (*it)->_pAddr = nullptr; + } + } + ++it; + handlers.pop_front(); +} + + +} } // namespace Poco::Net diff --git a/Net/src/SocketReactor.cpp b/Net/src/SocketReactor.cpp index fc30946f9..f79c8a840 100644 --- a/Net/src/SocketReactor.cpp +++ b/Net/src/SocketReactor.cpp @@ -179,11 +179,14 @@ bool SocketReactor::hasEventHandler(const Socket& socket, const Poco::AbstractOb SocketReactor::NotifierPtr SocketReactor::getNotifier(const Socket& socket, bool makeNew) { + const SocketImpl* pImpl = socket.impl(); + if (pImpl == nullptr) return 0; + poco_socket_t sockfd = pImpl->sockfd(); ScopedLock lock(_mutex); - EventHandlerMap::iterator it = _handlers.find(socket); + EventHandlerMap::iterator it = _handlers.find(sockfd); if (it != _handlers.end()) return it->second; - else if (makeNew) return (_handlers[socket] = new SocketNotifier(socket)); + else if (makeNew) return (_handlers[sockfd] = new SocketNotifier(socket)); return 0; } @@ -191,6 +194,8 @@ SocketReactor::NotifierPtr SocketReactor::getNotifier(const Socket& socket, bool void SocketReactor::removeEventHandler(const Socket& socket, const Poco::AbstractObserver& observer) { + const SocketImpl* pImpl = socket.impl(); + if (pImpl == nullptr) return; NotifierPtr pNotifier = getNotifier(socket); if (pNotifier && pNotifier->hasObserver(observer)) { @@ -198,7 +203,7 @@ void SocketReactor::removeEventHandler(const Socket& socket, const Poco::Abstrac { { ScopedLock lock(_mutex); - _handlers.erase(socket); + _handlers.erase(pImpl->sockfd()); } _pollSet.remove(socket); } diff --git a/Net/src/StreamSocket.cpp b/Net/src/StreamSocket.cpp index 46136da6c..afbdc9333 100644 --- a/Net/src/StreamSocket.cpp +++ b/Net/src/StreamSocket.cpp @@ -51,6 +51,11 @@ StreamSocket::StreamSocket(const Socket& socket): Socket(socket) } +StreamSocket::StreamSocket(const StreamSocket& socket): Socket(socket) +{ +} + + StreamSocket::StreamSocket(SocketImpl* pImpl): Socket(pImpl) { if (!dynamic_cast(impl())) @@ -73,6 +78,40 @@ StreamSocket& StreamSocket::operator = (const Socket& socket) } +StreamSocket& StreamSocket::operator = (const StreamSocket& socket) +{ + Socket::operator = (socket); + return *this; +} + +#if POCO_NEW_STATE_ON_MOVE + +StreamSocket::StreamSocket(Socket&& socket): Socket(std::move(socket)) +{ + if (!dynamic_cast(impl())) + throw InvalidArgumentException("Cannot assign incompatible socket"); +} + + +StreamSocket::StreamSocket(StreamSocket&& socket): Socket(std::move(socket)) +{ +} + +StreamSocket& StreamSocket::operator = (Socket&& socket) +{ + Socket::operator = (std::move(socket)); + return *this; +} + + +StreamSocket& StreamSocket::operator = (StreamSocket&& socket) +{ + Socket::operator = (std::move(socket)); + return *this; +} + +#endif // POCO_NEW_STATE_ON_MOVE + void StreamSocket::connect(const SocketAddress& address) { impl()->connect(address); diff --git a/Net/src/WebSocket.cpp b/Net/src/WebSocket.cpp index 19031a55e..46f2dacb6 100644 --- a/Net/src/WebSocket.cpp +++ b/Net/src/WebSocket.cpp @@ -65,6 +65,26 @@ WebSocket::WebSocket(const Socket& socket): } +WebSocket::WebSocket(Socket&& socket): + StreamSocket(std::move(socket)) +{ + if (!dynamic_cast(impl())) + throw InvalidArgumentException("Cannot assign incompatible socket"); +} + + +WebSocket::WebSocket(const WebSocket& socket): + StreamSocket(socket) +{ +} + + +WebSocket::WebSocket(WebSocket&& socket): + StreamSocket(std::move(socket)) +{ +} + + WebSocket::~WebSocket() { } @@ -80,6 +100,30 @@ WebSocket& WebSocket::operator = (const Socket& socket) } +WebSocket& WebSocket::operator = (Socket&& socket) +{ + if (dynamic_cast(socket.impl())) + Socket::operator = (std::move(socket)); + else + throw InvalidArgumentException("Cannot assign incompatible socket"); + return *this; +} + + +WebSocket& WebSocket::operator = (const WebSocket& socket) +{ + Socket::operator = (socket); + return *this; +} + + +WebSocket& WebSocket::operator = (WebSocket&& socket) +{ + Socket::operator = (std::move(socket)); + return *this; +} + + void WebSocket::shutdown() { shutdown(WS_NORMAL_CLOSE); diff --git a/Net/testsuite/Makefile b/Net/testsuite/Makefile index d55ae9050..0723c668c 100644 --- a/Net/testsuite/Makefile +++ b/Net/testsuite/Makefile @@ -19,7 +19,7 @@ objects = \ MediaTypeTest QuotedPrintableTest DialogSocketTest \ HTTPClientTestSuite FTPClientTestSuite FTPClientSessionTest \ FTPStreamFactoryTest DialogServer \ - SocketReactorTest ReactorTestSuite \ + SocketReactorTest ReactorTestSuite SocketProactorTest \ MailTestSuite MailMessageTest MailStreamTest \ SMTPClientSessionTest POP3ClientSessionTest \ RawSocketTest ICMPClientTest ICMPSocketTest ICMPClientTestSuite \ diff --git a/Net/testsuite/TestSuite_vs160.vcxproj b/Net/testsuite/TestSuite_vs160.vcxproj index 1d26a5a2f..8e913a5eb 100644 --- a/Net/testsuite/TestSuite_vs160.vcxproj +++ b/Net/testsuite/TestSuite_vs160.vcxproj @@ -1,4 +1,4 @@ - + @@ -56,7 +56,7 @@ TestSuite Win32Proj - + Application MultiByte @@ -117,45 +117,45 @@ MultiByte v142 - - + + - + - + - + - + - + - + - + - + - + - + - + - + - + <_ProjectFileVersion>15.0.28307.799 TestSuited @@ -243,7 +243,7 @@ true true true - + Level3 ProgramDatabase Default @@ -275,9 +275,9 @@ true true true - + Level3 - + Default true @@ -304,7 +304,7 @@ true true true - + Level3 ProgramDatabase Default @@ -336,9 +336,9 @@ true true true - + Level3 - + Default true @@ -365,7 +365,7 @@ true true true - + Level3 ProgramDatabase Default @@ -397,9 +397,9 @@ true true true - + Level3 - + Default true @@ -426,7 +426,7 @@ true true true - + Level3 ProgramDatabase Default @@ -458,9 +458,9 @@ true true true - + Level3 - + Default true @@ -487,7 +487,7 @@ true true true - + Level3 ProgramDatabase Default @@ -519,9 +519,9 @@ true true true - + Level3 - + Default true @@ -548,7 +548,7 @@ true true true - + Level3 ProgramDatabase Default @@ -580,9 +580,9 @@ true true true - + Level3 - + Default true @@ -598,70 +598,71 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -823,6 +824,7 @@ true + true @@ -860,6 +862,6 @@ true - - - + + + \ No newline at end of file diff --git a/Net/testsuite/TestSuite_vs160.vcxproj.filters b/Net/testsuite/TestSuite_vs160.vcxproj.filters index 6ace8dc10..5d348b9cf 100644 --- a/Net/testsuite/TestSuite_vs160.vcxproj.filters +++ b/Net/testsuite/TestSuite_vs160.vcxproj.filters @@ -363,6 +363,9 @@ UDP\Header Files + + Sockets\Header Files + @@ -560,5 +563,8 @@ UDP\Source Files + + Sockets\Source Files + \ No newline at end of file diff --git a/Net/testsuite/src/DatagramSocketTest.cpp b/Net/testsuite/src/DatagramSocketTest.cpp index 336f94956..e765de29c 100644 --- a/Net/testsuite/src/DatagramSocketTest.cpp +++ b/Net/testsuite/src/DatagramSocketTest.cpp @@ -13,12 +13,12 @@ #include "CppUnit/TestSuite.h" #include "UDPEchoServer.h" #include "Poco/Net/DatagramSocket.h" -#include "Poco/Net/SocketAddress.h" #include "Poco/Net/NetworkInterface.h" #include "Poco/Net/NetException.h" #include "Poco/Timespan.h" #include "Poco/Buffer.h" #include "Poco/Stopwatch.h" +#include "Poco/Thread.h" #include @@ -35,6 +35,7 @@ using Poco::Stopwatch; using Poco::TimeoutException; using Poco::InvalidArgumentException; using Poco::IOException; +using Poco::Thread; DatagramSocketTest::DatagramSocketTest(const std::string& name): CppUnit::TestCase(name) @@ -62,6 +63,46 @@ void DatagramSocketTest::testEcho() } +void DatagramSocketTest::testMoveDatagramSocket() +{ + UDPEchoServer echoServer; + DatagramSocket ss0 = DatagramSocket(); + char buffer[256]; + ss0.connect(SocketAddress("127.0.0.1", echoServer.port())); + DatagramSocket ss(std::move(ss0)); +#if POCO_NEW_STATE_ON_MOVE + assertTrue (ss0.isNull()); +#else + assertFalse (ss0.isNull()); +#endif + int n = ss.sendBytes("hello", 5); + assertTrue (n == 5); + n = ss.receiveBytes(buffer, sizeof(buffer)); + assertTrue (n == 5); + assertTrue (std::string(buffer, n) == "hello"); + + std::memset(buffer, 0, sizeof(buffer)); + ss0 = ss; + assertTrue (ss0.impl()); + assertTrue (ss.impl()); + assertTrue (ss0.impl() == ss.impl()); + ss = std::move(ss0); +#if POCO_NEW_STATE_ON_MOVE + assertTrue (ss0.isNull()); +#else + assertFalse (ss0.isNull()); +#endif + assertTrue (ss.impl()); + n = ss.sendBytes("hello", 5); + assertTrue (n == 5); + n = ss.receiveBytes(buffer, sizeof(buffer)); + assertTrue (n == 5); + assertTrue (std::string(buffer, n) == "hello"); + ss.close(); + ss0.close(); +} + + void DatagramSocketTest::testEchoBuffer() { UDPEchoServer echoServer; @@ -81,6 +122,25 @@ void DatagramSocketTest::testEchoBuffer() } +void DatagramSocketTest::testReceiveFromAvailable() +{ + UDPEchoServer echoServer(SocketAddress("127.0.0.1", 0)); + DatagramSocket ss(SocketAddress::IPv4); + int n = ss.sendTo("hello", 5, SocketAddress("127.0.0.1", echoServer.port())); + assertTrue (n == 5); + Thread::sleep(100); + char buffer[256]; + SocketAddress sa; + assertTrue (ss.available() == 5); + n = ss.receiveFrom(buffer, sizeof(buffer), sa); + assertTrue (sa.host() == echoServer.address().host()); + assertTrue (sa.port() == echoServer.port()); + assertTrue (n == 5); + assertTrue (std::string(buffer, n) == "hello"); + ss.close(); +} + + void DatagramSocketTest::testSendToReceiveFrom() { UDPEchoServer echoServer(SocketAddress("127.0.0.1", 0)); @@ -113,6 +173,158 @@ void DatagramSocketTest::testUnbound() } +Poco::UInt16 DatagramSocketTest::getFreePort(SocketAddress::Family family, Poco::UInt16 port) +{ + bool failed = false; + poco_assert_dbg(port > 0); + --port; + DatagramSocket sock(family); + do + { + failed = false; + SocketAddress sa(family, ++port); + try + { + sock.bind(sa, false); + } + catch (Poco::Net::NetException&) + { + failed = true; + } + } while (failed && sock.getError() == POCO_EADDRINUSE); + return port; +} + + +void DatagramSocketTest::testReuseAddressPortWildcard() +{ + Poco::UInt16 port = getFreePort(SocketAddress::IPv4, 1234); + Poco::UInt16 port6 = getFreePort(SocketAddress::IPv6, 1234); + assertTrue(port >= 1234); + assertTrue(port6 >= 1234); + + // reuse + { + DatagramSocket ds1(SocketAddress::IPv4); + ds1.bind(SocketAddress(port), true); + assertTrue(ds1.getReuseAddress()); + DatagramSocket ds2; + ds2.bind(SocketAddress(port), true); + assertTrue(ds2.getReuseAddress()); +#ifdef POCO_HAVE_IPv6 + DatagramSocket ds3(SocketAddress::IPv6); + ds3.bind6(SocketAddress(SocketAddress::IPv6, port6), true, true, false); + assertTrue(ds3.getReuseAddress()); +#endif + } + +#ifdef POCO_HAVE_IPv6 + { + DatagramSocket ds1(SocketAddress::IPv6); + ds1.bind6(SocketAddress(SocketAddress::IPv6, port6), true, true, false); + assertTrue(ds1.getReuseAddress()); + DatagramSocket ds2; + ds2.bind6(SocketAddress(SocketAddress::IPv6, port6), true, true, false); + assertTrue(ds2.getReuseAddress()); + DatagramSocket ds3; + ds3.bind(SocketAddress(port), true, true); + assertTrue(ds3.getReuseAddress()); + } +#endif + +#ifdef POCO_HAVE_IPv6 + { + DatagramSocket ds1(SocketAddress::IPv6); + ds1.bind6(SocketAddress(SocketAddress::IPv6, port), true, true, true); + assertTrue(ds1.getReuseAddress()); + DatagramSocket ds2; + ds2.bind6(SocketAddress(SocketAddress::IPv6, port), true, true, true); + assertTrue(ds2.getReuseAddress()); + } +#endif + + // not reuse + { + DatagramSocket ds1(SocketAddress::IPv4); + ds1.bind(SocketAddress(port), false); + assertTrue(!ds1.getReuseAddress()); + DatagramSocket ds2; + try + { + ds2.bind(SocketAddress(port), false); + fail("binding to non-reuse address must throw"); + } + catch (Poco::IOException&) {} + +#ifdef POCO_HAVE_IPv6 + { + DatagramSocket ds1(SocketAddress::IPv6); + ds1.bind6(SocketAddress(SocketAddress::IPv6, port), false, false, true); + assertTrue(!ds1.getReuseAddress()); + DatagramSocket ds2(SocketAddress::IPv6); + try + { + ds2.bind6(SocketAddress(SocketAddress::IPv6, port), false, false, true); + fail("binding to non-reuse address must throw"); + } + catch (Poco::IOException&) {} + } +#endif + } +} + + +void DatagramSocketTest::testReuseAddressPortSpecific() +{ + Poco::UInt16 port = getFreePort(SocketAddress::IPv4, 1234); + assertTrue(port >= 1234); + + // reuse + { + DatagramSocket ds1(SocketAddress::IPv4); + ds1.bind(SocketAddress(port), true); + assertTrue(ds1.getReuseAddress()); + DatagramSocket ds2; + ds2.bind(SocketAddress("127.0.0.1", port), true); + assertTrue(ds2.getReuseAddress()); +#ifdef POCO_HAVE_IPv6 + DatagramSocket ds3(SocketAddress::IPv6); + ds3.bind6(SocketAddress("::1", port), true, true, false); + assertTrue(ds3.getReuseAddress()); +#endif + } + + // not reuse + { + DatagramSocket ds1(SocketAddress::IPv4); + ds1.bind(SocketAddress("0.0.0.0", port), false); + assertTrue(!ds1.getReuseAddress()); + DatagramSocket ds2; + try + { + ds2.bind(SocketAddress("127.0.0.1", port), false); + fail("binding to non-reuse IPv4 address must throw"); + } + catch (Poco::IOException&) {} + +#ifdef POCO_HAVE_IPv6 + { + DatagramSocket ds1(SocketAddress::IPv6); + ds1.bind6(SocketAddress("::", port), false, false, true); + assertTrue(!ds1.getReuseAddress()); + DatagramSocket ds2(SocketAddress::IPv6); + try + { + ds2.bind6(SocketAddress("::1", port), false, false, true); + fail("binding to non-reuse IPv6 address must throw"); + } + catch (Poco::IOException&) {} + } +#endif + } +} + + void DatagramSocketTest::testBroadcast() { UDPEchoServer echoServer; @@ -607,9 +819,13 @@ CppUnit::Test* DatagramSocketTest::suite() CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("DatagramSocketTest"); CppUnit_addTest(pSuite, DatagramSocketTest, testEcho); + CppUnit_addTest(pSuite, DatagramSocketTest, testMoveDatagramSocket); CppUnit_addTest(pSuite, DatagramSocketTest, testEchoBuffer); + CppUnit_addTest(pSuite, DatagramSocketTest, testReceiveFromAvailable); CppUnit_addTest(pSuite, DatagramSocketTest, testSendToReceiveFrom); CppUnit_addTest(pSuite, DatagramSocketTest, testUnbound); + CppUnit_addTest(pSuite, DatagramSocketTest, testReuseAddressPortWildcard); + CppUnit_addTest(pSuite, DatagramSocketTest, testReuseAddressPortSpecific); #if (POCO_OS != POCO_OS_FREE_BSD) // works only with local net bcast and very randomly CppUnit_addTest(pSuite, DatagramSocketTest, testBroadcast); #endif diff --git a/Net/testsuite/src/DatagramSocketTest.h b/Net/testsuite/src/DatagramSocketTest.h index 3fad5880c..10bb5e2d8 100644 --- a/Net/testsuite/src/DatagramSocketTest.h +++ b/Net/testsuite/src/DatagramSocketTest.h @@ -15,6 +15,7 @@ #include "Poco/Net/Net.h" +#include "Poco/Net/SocketAddress.h" #include "CppUnit/TestCase.h" @@ -25,9 +26,13 @@ public: ~DatagramSocketTest(); void testEcho(); + void testMoveDatagramSocket(); void testEchoBuffer(); + void testReceiveFromAvailable(); void testSendToReceiveFrom(); void testUnbound(); + void testReuseAddressPortWildcard(); + void testReuseAddressPortSpecific(); void testBroadcast(); void testGatherScatterFixed(); void testGatherScatterVariable(); @@ -38,6 +43,8 @@ public: static CppUnit::Test* suite(); private: + static Poco::UInt16 getFreePort(Poco::Net::SocketAddress::Family family, std::uint16_t port); + // "STRF" are sendto/recvfrom versions of the same functionality void testGatherScatterFixedWin(); void testGatherScatterSTRFFixedWin(); diff --git a/Net/testsuite/src/ICMPClientTest.cpp b/Net/testsuite/src/ICMPClientTest.cpp index f7b629b3b..391339a8c 100644 --- a/Net/testsuite/src/ICMPClientTest.cpp +++ b/Net/testsuite/src/ICMPClientTest.cpp @@ -59,7 +59,7 @@ void ICMPClientTest::testPing() assertTrue (icmpClient.ping("10.0.2.15", 4) > 0); assertTrue (icmpClient.ping("10.0.2.2", 4) > 0); #else - assertTrue (icmpClient.ping("www.appinf.com", 4) > 0); + assertTrue (icmpClient.ping("github.com", 4) > 0); // warning: may fail depending on the existence of the addresses at test site // if so, adjust accordingly (i.e. specify non-existent or unreachable IP addresses) @@ -86,7 +86,7 @@ void ICMPClientTest::testBigPing() assertTrue (icmpClient.ping("10.0.2.15", 4) > 0); assertTrue (icmpClient.ping("10.0.2.2", 4) > 0); #else - assertTrue (icmpClient.ping("www.appinf.com", 4) > 0); + assertTrue (icmpClient.ping("github.com", 4) > 0); // warning: may fail depending on the existence of the addresses at test site // if so, adjust accordingly (i.e. specify non-existent or unreachable IP addresses) diff --git a/Net/testsuite/src/ICMPSocketTest.cpp b/Net/testsuite/src/ICMPSocketTest.cpp index 617b9b2da..195b58f2a 100644 --- a/Net/testsuite/src/ICMPSocketTest.cpp +++ b/Net/testsuite/src/ICMPSocketTest.cpp @@ -56,7 +56,7 @@ void ICMPSocketTest::testSendToReceiveFrom() { ICMPSocket ss(IPAddress::IPv4); - SocketAddress sa("www.appinf.com", 0); + SocketAddress sa("github.com", 0); SocketAddress sr(sa); try @@ -90,7 +90,7 @@ void ICMPSocketTest::testMTU() std::cout << addr.toString() << " : MTU=" << mtu << std::endl; assertTrue (mtu != 0); sz = 1500; - addr = SocketAddress("www.appinf.com:0"); + addr = SocketAddress("github.com:0"); mtu = ICMPSocket::mtu(addr, sz); std::cout << addr.toString() << " : MTU=" << mtu << std::endl; assertTrue (mtu != 0 && mtu <= sz); diff --git a/Net/testsuite/src/IPAddressTest.cpp b/Net/testsuite/src/IPAddressTest.cpp index 225e5b629..2f317a9cc 100644 --- a/Net/testsuite/src/IPAddressTest.cpp +++ b/Net/testsuite/src/IPAddressTest.cpp @@ -31,11 +31,13 @@ IPAddressTest::~IPAddressTest() void IPAddressTest::testStringConv() { - IPAddress ia1("127.0.0.1"); + IPAddress ia01 = IPAddress("127.0.0.1"); + IPAddress ia1(std::move(ia01)); assertTrue (ia1.family() == IPAddress::IPv4); assertTrue (ia1.toString() == "127.0.0.1"); - IPAddress ia2("192.168.1.120"); + IPAddress ia02 = IPAddress("192.168.1.120"); + IPAddress ia2(std::move(ia02)); assertTrue (ia2.family() == IPAddress::IPv4); assertTrue (ia2.toString() == "192.168.1.120"); @@ -56,15 +58,18 @@ void IPAddressTest::testStringConv() void IPAddressTest::testStringConv6() { #ifdef POCO_HAVE_IPv6 - IPAddress ia0("::1"); + IPAddress ia00 = IPAddress("::1"); + IPAddress ia0(std::move(ia00)); assertTrue (ia0.family() == IPAddress::IPv6); assertTrue (ia0.toString() == "::1"); - IPAddress ia1("1080:0:0:0:8:600:200a:425c"); + IPAddress ia01 = IPAddress("1080:0:0:0:8:600:200a:425c"); + IPAddress ia1(std::move(ia01)); assertTrue (ia1.family() == IPAddress::IPv6); assertTrue (ia1.toString() == "1080::8:600:200a:425c"); - IPAddress ia2("1080::8:600:200A:425C"); + IPAddress ia02 = IPAddress("1080::8:600:200A:425C"); + IPAddress ia2(std::move(ia02)); assertTrue (ia2.family() == IPAddress::IPv6); assertTrue (ia2.toString() == "1080::8:600:200a:425c"); @@ -94,8 +99,9 @@ void IPAddressTest::testStringConv6() void IPAddressTest::testParse() { IPAddress ip; + assertTrue (IPAddress::tryParse("0.0.0.0", ip)); + assertTrue (IPAddress::tryParse("255.255.255.255", ip)); assertTrue (IPAddress::tryParse("192.168.1.120", ip)); - assertTrue (!IPAddress::tryParse("192.168.1.280", ip)); ip = IPAddress::parse("192.168.1.120"); @@ -103,10 +109,37 @@ void IPAddressTest::testParse() { ip = IPAddress::parse("192.168.1.280"); fail("bad address - must throw"); - } + } catch (InvalidAddressException&) { } + +#ifdef POCO_HAVE_IPv6 + assertTrue (IPAddress::tryParse("::", ip)); + assertFalse (IPAddress::tryParse(":::", ip)); + assertTrue (IPAddress::tryParse("0::", ip)); + assertTrue (IPAddress::tryParse("0:0::", ip)); + assertTrue (IPAddress::tryParse("0:0:0::", ip)); + assertTrue (IPAddress::tryParse("0:0:0:0::", ip)); + assertTrue (IPAddress::tryParse("0:0:0:0:0::", ip)); + assertTrue (IPAddress::tryParse("0:0:0:0:0:0::", ip)); + assertTrue (IPAddress::tryParse("0:0:0:0:0:0:0::", ip)); + assertTrue (IPAddress::tryParse("0:0:0:0:0:0:0:0", ip)); + assertFalse (IPAddress::tryParse("0:0:0:0:0:0:0:0:", ip)); + assertFalse (IPAddress::tryParse("::0:0::", ip)); + assertFalse (IPAddress::tryParse("::0::0::", ip)); + + assertTrue (IPAddress::tryParse("::1", ip)); + assertTrue (IPAddress::tryParse("1080:0:0:0:8:600:200a:425c", ip)); + assertTrue (IPAddress::tryParse("1080::8:600:200a:425c", ip)); + assertTrue (IPAddress::tryParse("1080::8:600:200A:425C", ip)); + assertTrue (IPAddress::tryParse("1080::8:600:200a:425c", ip)); + assertTrue (IPAddress::tryParse("::192.168.1.120", ip)); + assertTrue (IPAddress::tryParse("::ffff:192.168.1.120", ip)); + assertTrue (IPAddress::tryParse("::ffff:192.168.1.120", ip)); + assertTrue (IPAddress::tryParse("ffff:ffff:ffff:ffff::", ip)); + assertTrue (IPAddress::tryParse("ffff:ffff::", ip)); +#endif } diff --git a/Net/testsuite/src/PollSetTest.cpp b/Net/testsuite/src/PollSetTest.cpp index 5ce60b059..9c2c41e7a 100644 --- a/Net/testsuite/src/PollSetTest.cpp +++ b/Net/testsuite/src/PollSetTest.cpp @@ -18,6 +18,7 @@ #include "Poco/Net/NetException.h" #include "Poco/Net/PollSet.h" #include "Poco/Stopwatch.h" +#include using Poco::Net::Socket; @@ -31,6 +32,38 @@ using Poco::Stopwatch; using Poco::Thread; +namespace { + +class Poller : public Poco::Runnable +{ +public: + Poller(PollSet& pollSet, const Timespan& timeout): _pollSet(pollSet), + _timeout(timeout) + { + } + + void run() + { + _running = true; + _pollSet.poll(_timeout); + _running = false; + } + + bool isRunning() + { + return _running; + } + +private: + PollSet& _pollSet; + Timespan _timeout; + bool _running = false; +}; + + +} + + PollSetTest::PollSetTest(const std::string& name): CppUnit::TestCase(name) { } @@ -41,6 +74,69 @@ PollSetTest::~PollSetTest() } +void PollSetTest::testTimeout() +{ + EchoServer echoServer; + StreamSocket ss; + ss.connect(SocketAddress("127.0.0.1", echoServer.port())); + PollSet ps; + ps.add(ss, PollSet::POLL_READ); + Timespan timeout(1000000); + Stopwatch sw; sw.start(); + PollSet::SocketModeMap sm = ps.poll(timeout); + sw.stop(); + assertTrue(sm.empty()); + assertTrue(sw.elapsed() >= 900000); + + ss.sendBytes("hello", 5); + sw.restart(); + sm = ps.poll(timeout); + sw.stop(); + assertTrue(ps.poll(timeout).size() == 1); + + // just here to prevent server exception on connection reset + char buffer[5]; + ss.receiveBytes(buffer, sizeof(buffer)); +} + + +void PollSetTest::testPollNB() +{ + EchoServer echoServer1; + StreamSocket ss1; + + ss1.connectNB(SocketAddress("127.0.0.1", echoServer1.port())); + + PollSet ps; + assertTrue(ps.empty()); + ps.add(ss1, PollSet::POLL_READ); + ps.add(ss1, PollSet::POLL_WRITE); + assertTrue(!ps.empty()); + assertTrue(ps.has(ss1)); + + while (!ss1.poll(Timespan(0, 10000), Socket::SELECT_WRITE)) + Poco::Thread::sleep(10); + + Timespan timeout(1000000); + PollSet::SocketModeMap sm; + while (sm.empty()) sm = ps.poll(timeout); + assertTrue(sm.find(ss1) != sm.end()); + assertTrue(sm.find(ss1)->second | PollSet::POLL_WRITE); + + ss1.setBlocking(true); + ss1.sendBytes("hello", 5); + char buffer[256]; + + sm = ps.poll(timeout); + assertTrue(sm.find(ss1) != sm.end()); + assertTrue(sm.find(ss1)->second | PollSet::POLL_READ); + + int n = ss1.receiveBytes(buffer, sizeof(buffer)); + assertTrue(n == 5); + assertTrue(std::string(buffer, n) == "hello"); +} + + void PollSetTest::testPoll() { EchoServer echoServer1; @@ -76,31 +172,32 @@ void PollSetTest::testPoll() PollSet::SocketModeMap sm = ps.poll(timeout); assertTrue (sm.find(ss1) != sm.end()); assertTrue (sm.find(ss2) == sm.end()); - assertTrue (sm.find(ss1)->second == PollSet::POLL_WRITE); + assertTrue (sm.find(ss1)->second | PollSet::POLL_WRITE); assertTrue (sw.elapsed() < 1100000); ps.update(ss1, PollSet::POLL_READ); + ss1.setBlocking(true); ss1.sendBytes("hello", 5); char buffer[256]; sw.restart(); sm = ps.poll(timeout); assertTrue (sm.find(ss1) != sm.end()); assertTrue (sm.find(ss2) == sm.end()); - assertTrue (sm.find(ss1)->second == PollSet::POLL_READ); + assertTrue (sm.find(ss1)->second | PollSet::POLL_READ); assertTrue (sw.elapsed() < 1100000); int n = ss1.receiveBytes(buffer, sizeof(buffer)); assertTrue (n == 5); assertTrue (std::string(buffer, n) == "hello"); - + ss2.setBlocking(true); ss2.sendBytes("HELLO", 5); sw.restart(); sm = ps.poll(timeout); assertTrue (sm.find(ss1) == sm.end()); assertTrue (sm.find(ss2) != sm.end()); - assertTrue (sm.find(ss2)->second == PollSet::POLL_READ); + assertTrue (sm.find(ss2)->second | PollSet::POLL_READ); assertTrue (sw.elapsed() < 1100000); n = ss2.receiveBytes(buffer, sizeof(buffer)); @@ -128,16 +225,14 @@ void PollSetTest::testPoll() void PollSetTest::testPollNoServer() { -#ifndef POCO_OS_FAMILY_WINDOWS StreamSocket ss1; StreamSocket ss2; - ss1.connectNB(SocketAddress("127.0.0.1", 0xFEFE)); ss2.connectNB(SocketAddress("127.0.0.1", 0xFEFF)); PollSet ps; assertTrue(ps.empty()); - ps.add(ss1, PollSet::POLL_READ); - ps.add(ss2, PollSet::POLL_READ); + ps.add(ss1, PollSet::POLL_READ | PollSet::POLL_WRITE | PollSet::POLL_ERROR); + ps.add(ss2, PollSet::POLL_READ | PollSet::POLL_WRITE | PollSet::POLL_ERROR); assertTrue(!ps.empty()); assertTrue(ps.has(ss1)); assertTrue(ps.has(ss2)); @@ -151,13 +246,11 @@ void PollSetTest::testPollNoServer() assertTrue(sm.size() == 2); for (auto s : sm) assertTrue(0 != (s.second | PollSet::POLL_ERROR)); -#endif // POCO_OS_FAMILY_WINDOWS } void PollSetTest::testPollClosedServer() { -#ifndef POCO_OS_FAMILY_WINDOWS EchoServer echoServer1; EchoServer echoServer2; StreamSocket ss1; @@ -165,7 +258,6 @@ void PollSetTest::testPollClosedServer() ss1.connect(SocketAddress("127.0.0.1", echoServer1.port())); ss2.connect(SocketAddress("127.0.0.1", echoServer2.port())); - PollSet ps; assertTrue(ps.empty()); ps.add(ss1, PollSet::POLL_READ); @@ -190,7 +282,28 @@ void PollSetTest::testPollClosedServer() assertTrue(sm.size() == 2); assertTrue(0 == ss1.receiveBytes(0, 0)); assertTrue(0 == ss2.receiveBytes(0, 0)); -#endif // POCO_OS_FAMILY_WINDOWS +} + + +void PollSetTest::testPollSetWakeUp() +{ +#if defined(POCO_HAVE_FD_EPOLL) + PollSet ps; + Timespan timeout(100000000); // 100 seconds + Poller poller(ps, timeout); + Thread t; + Stopwatch sw; + sw.start(); + t.start(poller); + while (!poller.isRunning()) Thread::sleep(100); + ps.wakeUp(); + t.join(); + sw.stop(); + assertFalse (poller.isRunning()); + assertTrue(sw.elapsedSeconds() < 1); +#else // TODO: other implementations + std::cout << "not implemented"; +#endif // POCO_HAVE_FD_EPOLL } @@ -208,9 +321,12 @@ CppUnit::Test* PollSetTest::suite() { CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("PollSetTest"); + CppUnit_addTest(pSuite, PollSetTest, testTimeout); + CppUnit_addTest(pSuite, PollSetTest, testPollNB); CppUnit_addTest(pSuite, PollSetTest, testPoll); CppUnit_addTest(pSuite, PollSetTest, testPollNoServer); CppUnit_addTest(pSuite, PollSetTest, testPollClosedServer); + CppUnit_addTest(pSuite, PollSetTest, testPollSetWakeUp); return pSuite; } diff --git a/Net/testsuite/src/PollSetTest.h b/Net/testsuite/src/PollSetTest.h index 9a8e70e64..cd8fe2478 100644 --- a/Net/testsuite/src/PollSetTest.h +++ b/Net/testsuite/src/PollSetTest.h @@ -24,9 +24,12 @@ public: PollSetTest(const std::string& name); ~PollSetTest(); + void testTimeout(); + void testPollNB(); void testPoll(); void testPollNoServer(); void testPollClosedServer(); + void testPollSetWakeUp(); void setUp(); void tearDown(); diff --git a/Net/testsuite/src/RawSocketTest.cpp b/Net/testsuite/src/RawSocketTest.cpp index 1f634b5ab..4ac2fb86f 100644 --- a/Net/testsuite/src/RawSocketTest.cpp +++ b/Net/testsuite/src/RawSocketTest.cpp @@ -84,6 +84,31 @@ void RawSocketTest::testSendToReceiveFromIPv4() } +void RawSocketTest::testEchoIPv4Move() +{ + SocketAddress sa("127.0.0.1", 0); + RawSocket rs0 = RawSocket(IPAddress::IPv4); + rs0.connect(sa); + + RawSocket rs(std::move(rs0)); + assertTrue (rs0.impl() == nullptr); + int n = rs.sendBytes("hello", 5); + assertTrue (5 == n); + + char buffer[256] = ""; + unsigned char* ptr = (unsigned char*) buffer; + + n = rs.receiveBytes(buffer, sizeof(buffer)); + int shift = ((buffer[0] & 0x0F) * 4); + ptr += shift; + + assertTrue (5 == (n - shift)); + assertTrue ("hello" == std::string((char*)ptr, 5)); + + rs.close(); +} + + void RawSocketTest::setUp() { } @@ -100,6 +125,7 @@ CppUnit::Test* RawSocketTest::suite() CppUnit_addTest(pSuite, RawSocketTest, testEchoIPv4); CppUnit_addTest(pSuite, RawSocketTest, testSendToReceiveFromIPv4); + CppUnit_addTest(pSuite, RawSocketTest, testEchoIPv4Move); return pSuite; } diff --git a/Net/testsuite/src/RawSocketTest.h b/Net/testsuite/src/RawSocketTest.h index 140c518fd..a38035e50 100644 --- a/Net/testsuite/src/RawSocketTest.h +++ b/Net/testsuite/src/RawSocketTest.h @@ -26,6 +26,7 @@ public: void testEchoIPv4(); void testSendToReceiveFromIPv4(); + void testEchoIPv4Move(); void setUp(); void tearDown(); diff --git a/Net/testsuite/src/SocketAddressTest.cpp b/Net/testsuite/src/SocketAddressTest.cpp index 0383d31fb..744e00d2c 100644 --- a/Net/testsuite/src/SocketAddressTest.cpp +++ b/Net/testsuite/src/SocketAddressTest.cpp @@ -13,6 +13,7 @@ #include "CppUnit/TestSuite.h" #include "Poco/Net/SocketAddress.h" #include "Poco/Net/NetException.h" +#include using Poco::Net::SocketAddress; @@ -41,19 +42,22 @@ void SocketAddressTest::testSocketAddress() assertTrue (wild.host().isWildcard()); assertTrue (wild.port() == 0); - SocketAddress sa1("192.168.1.100", 100); + SocketAddress sa01 = SocketAddress("192.168.1.100", 100); + SocketAddress sa1(std::move(sa01)); assertTrue (sa1.af() == AF_INET); assertTrue (sa1.family() == SocketAddress::IPv4); assertTrue (sa1.host().toString() == "192.168.1.100"); assertTrue (sa1.port() == 100); assertTrue (sa1.toString() == "192.168.1.100:100"); - SocketAddress sa2("192.168.1.100", "100"); + SocketAddress sa02 = SocketAddress("192.168.1.100", "100"); + SocketAddress sa2(std::move(sa02)); assertTrue (sa2.host().toString() == "192.168.1.100"); assertTrue (sa2.port() == 100); #if !defined(_WIN32_WCE) - SocketAddress sa3("192.168.1.100", "ftp"); + SocketAddress sa03 = SocketAddress("192.168.1.100", "ftp"); + SocketAddress sa3(std::move(sa03)); assertTrue (sa3.host().toString() == "192.168.1.100"); assertTrue (sa3.port() == 21); #endif @@ -67,7 +71,8 @@ void SocketAddressTest::testSocketAddress() { } - SocketAddress sa4("pocoproject.org", 80); + SocketAddress sa04 = SocketAddress("pocoproject.org", 80); + SocketAddress sa4(std::move(sa04)); assertTrue (sa4.host().toString() == "54.93.62.90"); assertTrue (sa4.port() == 80); @@ -92,11 +97,13 @@ void SocketAddressTest::testSocketAddress() { } - SocketAddress sa7("192.168.2.120:88"); + SocketAddress sa07 = SocketAddress("192.168.2.120:88"); + SocketAddress sa7(std::move(sa07)); assertTrue (sa7.host().toString() == "192.168.2.120"); assertTrue (sa7.port() == 88); - SocketAddress sa8("[192.168.2.120]:88"); + SocketAddress sa08 = SocketAddress("[192.168.2.120]:88"); + SocketAddress sa8(std::move(sa08)); assertTrue (sa8.host().toString() == "192.168.2.120"); assertTrue (sa8.port() == 88); @@ -121,7 +128,8 @@ void SocketAddressTest::testSocketAddress() SocketAddress sa10("www6.pocoproject.org", 80); assertTrue (sa10.host().toString() == "54.93.62.90" || sa10.host().toString() == "2001:4801:7828:101:be76:4eff:fe10:1455"); - SocketAddress sa11(SocketAddress::IPv4, "www6.pocoproject.org", 80); + SocketAddress sa011 = SocketAddress(SocketAddress::IPv4, "www6.pocoproject.org", 80); + SocketAddress sa11(std::move(sa011)); assertTrue (sa11.host().toString() == "54.93.62.90"); #ifdef POCO_HAVE_IPv6 @@ -168,6 +176,8 @@ void SocketAddressTest::testSocketAddress6() assertTrue (sa2.host().toString() == "fe80::e6ce:8fff:fe4a:edd0"); assertTrue (sa2.port() == 100); assertTrue (sa2.toString() == "[fe80::e6ce:8fff:fe4a:edd0]:100"); +#else + std::cout << "[IPv6 DISABLED]" << std::endl; #endif } @@ -191,6 +201,8 @@ void SocketAddressTest::testSocketAddressUnixLocal() SocketAddress sa4("/tmp/sock1"); assertTrue (sa1 == sa4); assertTrue (sa4.toString() == "/tmp/sock1"); +#else + std::cout << "[UNIX LOCAL DISABLED]" << std::endl; #endif } diff --git a/Net/testsuite/src/SocketProactorTest.cpp b/Net/testsuite/src/SocketProactorTest.cpp new file mode 100644 index 000000000..1c611dfe9 --- /dev/null +++ b/Net/testsuite/src/SocketProactorTest.cpp @@ -0,0 +1,314 @@ +// +// SocketProactorTest.cpp +// +// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#include "SocketProactorTest.h" +#include "UDPEchoServer.h" +#include "CppUnit/TestCaller.h" +#include "CppUnit/TestSuite.h" +#include "Poco/Net/StreamSocket.h" +#include "Poco/Net/DatagramSocket.h" +#include "Poco/Net/ServerSocket.h" +#include "Poco/Timestamp.h" +#include + +using Poco::Net::SocketProactor; +using Poco::Net::StreamSocket; +using Poco::Net::DatagramSocket; +using Poco::Net::ServerSocket; +using Poco::Net::SocketAddress; +using Poco::Thread; +using Poco::Timestamp; + + +SocketProactorTest::SocketProactorTest(const std::string& name): CppUnit::TestCase(name) +{ +} + + +SocketProactorTest::~SocketProactorTest() +{ +} + + +void SocketProactorTest::testTCPSocketProactor() +{ + EchoServer echoServer; + SocketProactor proactor(false); + StreamSocket s; + s.connect(SocketAddress("127.0.0.1", echoServer.port())); + int mode = SocketProactor::POLL_READ | SocketProactor::POLL_WRITE | SocketProactor::POLL_ERROR; + proactor.addSocket(s, mode); + std::string hello = "hello proactor world"; + bool sent = false, sendPassed = false; + auto onSendCompletion = [&](std::error_code err, int bytes) + { + sendPassed = (err.value() == 0) && (bytes == hello.length()); + sent = true; + }; + proactor.addSend(s, SocketProactor::Buffer(hello.begin(), hello.end()), onSendCompletion); + SocketProactor::Buffer buf(hello.size(), 0); + bool received = false, receivePassed = false; + auto onRecvCompletion = [&](std::error_code err, int bytes) + { + receivePassed = (err.value() == 0) && + (bytes == hello.length()) && + (std::string(buf.begin(), buf.end()) == hello); + received = true; + }; + proactor.addReceive(s, buf, onRecvCompletion); + while (!received) proactor.poll(); + + assertTrue (sent); + assertTrue (sendPassed); + assertTrue (received); + assertTrue (receivePassed); + + buf.clear(); + buf.resize(hello.size()); + assertFalse(std::string(buf.begin(), buf.end()) == hello); + + sent = false; + sendPassed = false; + received = false; + receivePassed = false; + + proactor.addSend(s, SocketProactor::Buffer(hello.begin(), hello.end()), nullptr); + proactor.addReceive(s, buf, nullptr); + int handled = 0, handledTot = 0; + do + { + proactor.poll(&handled); + handledTot += handled; + } + while (handledTot < 2); + + assertTrue(std::string(buf.begin(), buf.end()) == hello); + assertFalse (sent); + assertFalse (sendPassed); + assertFalse (received); + assertFalse (receivePassed); + + bool error = false; + bool errorPassed = false; + auto onError = [&](std::error_code err, int bytes) + { + errorPassed = (err.value() != 0) && (bytes == 0); + error = true; + }; + + StreamSocket errSock(SocketAddress::IPv4); + errSock.connectNB(SocketAddress("127.0.0.1", 0xFFEE)); + proactor.addSend(errSock, SocketProactor::Buffer(hello.begin(), hello.end()), onError); + Thread::sleep(100); + while (!error) proactor.poll(); + assertTrue (error); + assertTrue(errorPassed); +} + + +void SocketProactorTest::testUDPSocketProactor() +{ + UDPEchoServer echoServer; + DatagramSocket s(SocketAddress::IPv4); + SocketProactor proactor(false); + int mode = SocketProactor::POLL_READ | SocketProactor::POLL_WRITE; + proactor.addSocket(s, mode); + std::string hello = "hello proactor world"; + bool sent = false, sendPassed = false; + auto onSendCompletion = [&](std::error_code err, int bytes) + { + sendPassed = (err.value() == 0) && + (bytes == hello.length()); + sent = true; + }; + proactor.addSendTo(s, + SocketProactor::Buffer(hello.begin(), hello.end()), + SocketAddress("127.0.0.1", echoServer.port()), + onSendCompletion); + Poco::Net::SocketProactor::Buffer buf(hello.size(), 0); + bool received = false, receivePassed = false; + SocketAddress sa; + auto onRecvCompletion = [&](std::error_code err, int bytes) + { + receivePassed = (err.value() == 0) && + (bytes == hello.length()) && + (sa.host().toString() == "127.0.0.1") && + (sa.port() == echoServer.port()) && + (std::string(buf.begin(), buf.end()) == hello); + received = true; + }; + proactor.addReceiveFrom(s, buf, sa, onRecvCompletion); + while (!received) proactor.poll(); + + assertTrue (sent); + assertTrue (sendPassed); + assertTrue (received); + assertTrue (receivePassed); + + buf.clear(); + buf.resize(hello.size()); + assertFalse(std::string(buf.begin(), buf.end()) == hello); + + sent = false; + sendPassed = false; + received = false; + receivePassed = false; + + proactor.addSendTo(s, + SocketProactor::Buffer(hello.begin(), hello.end()), + SocketAddress("127.0.0.1", echoServer.port()), + nullptr); + proactor.addReceiveFrom(s, buf, sa, nullptr); + int handled = 0, handledTot = 0; + do + { + proactor.poll(&handled); + handledTot += handled; + } while (handledTot < 2); + + assertTrue(std::string(buf.begin(), buf.end()) == hello); + assertFalse (sent); + assertFalse (sendPassed); + assertFalse (received); + assertFalse (receivePassed); +} + + +void SocketProactorTest::testSocketProactorStartStop() +{ + UDPEchoServer echoServer; + DatagramSocket s(SocketAddress::IPv4); + SocketProactor proactor(false); + int mode = SocketProactor::POLL_READ | SocketProactor::POLL_WRITE; + proactor.addSocket(s, mode); + std::string hello = "hello proactor world"; + bool sent = false, sendPassed = false; + auto onSendCompletion = [&](std::error_code err, int bytes) + { + sendPassed = (err.value() == 0) && + (bytes == hello.length()); + sent = true; + }; + proactor.addSendTo(s, + SocketProactor::Buffer(hello.begin(), hello.end()), + SocketAddress("127.0.0.1", echoServer.port()), + onSendCompletion); + Poco::Net::SocketProactor::Buffer buf(hello.size(), 0); + bool received = false, receivePassed = false; + SocketAddress sa; + auto onRecvCompletion = [&](std::error_code err, int bytes) + { + receivePassed = (err.value() == 0) && + (bytes == hello.length()) && + (sa.host().toString() == "127.0.0.1") && + (sa.port() == echoServer.port()) && + (std::string(buf.begin(), buf.end()) == hello); + received = true; + proactor.stop(); + }; + proactor.addReceiveFrom(s, buf, sa, onRecvCompletion); + + while (!received) proactor.poll(); + + assertTrue (sent); + assertTrue (sendPassed); + assertTrue (received); + assertTrue (receivePassed); + + buf.clear(); + buf.resize(hello.size()); + assertFalse(std::string(buf.begin(), buf.end()) == hello); + + sent = false; + sendPassed = false; + received = false; + receivePassed = false; + + assertFalse (sent); + assertFalse (sendPassed); + assertFalse (received); + assertFalse (receivePassed); + + proactor.addSendTo(s, + SocketProactor::Buffer(hello.begin(), hello.end()), + SocketAddress("127.0.0.1", echoServer.port()), + onSendCompletion); + proactor.addReceiveFrom(s, buf, sa, onRecvCompletion); + while (!received) proactor.poll(); + + assertTrue(std::string(buf.begin(), buf.end()) == hello); + assertTrue (sent); + assertTrue (sendPassed); + assertTrue (received); + assertTrue (receivePassed); +} + + +void SocketProactorTest::testWork() +{ + SocketProactor proactor; + int executed = 0; + SocketProactor::Work work = [&]() { ++executed; }; + proactor.addWork(work); + assertTrue (proactor.runOne() == 1); + assertEquals (executed, 1); + assertTrue (proactor.poll() == 0); + assertEquals (executed, 1); + + UDPEchoServer echoServer; + DatagramSocket s(SocketAddress::IPv4); + int mode = SocketProactor::POLL_READ | SocketProactor::POLL_WRITE; + proactor.addSocket(s, mode); + proactor.addSendTo(s, + SocketProactor::Buffer(1, 0), + SocketAddress("127.0.0.1", echoServer.port()), + nullptr); + assertTrue (proactor.poll() == 1); + assertEquals (executed, 2); +} + + +void SocketProactorTest::testTimedWork() +{ + SocketProactor proactor; + int executed = 0; + SocketProactor::Work work = [&]() { ++executed; }; + proactor.addWork(work, 0); + proactor.addWork(work, 1000); + assertTrue (proactor.poll() == 1); + assertEquals (executed, 1); + Thread::sleep(1000); + assertTrue (proactor.poll() == 1); + assertEquals (executed, 2); +} + + +void SocketProactorTest::setUp() +{ +} + + +void SocketProactorTest::tearDown() +{ +} + + +CppUnit::Test* SocketProactorTest::suite() +{ + CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SocketProactorTest"); + + CppUnit_addTest(pSuite, SocketProactorTest, testTCPSocketProactor); + CppUnit_addTest(pSuite, SocketProactorTest, testUDPSocketProactor); + CppUnit_addTest(pSuite, SocketProactorTest, testSocketProactorStartStop); + CppUnit_addTest(pSuite, SocketProactorTest, testWork); + CppUnit_addTest(pSuite, SocketProactorTest, testTimedWork); + + return pSuite; +} diff --git a/Net/testsuite/src/SocketProactorTest.h b/Net/testsuite/src/SocketProactorTest.h new file mode 100644 index 000000000..b3901154d --- /dev/null +++ b/Net/testsuite/src/SocketProactorTest.h @@ -0,0 +1,49 @@ +// +// SocketProactorTest.h +// +// Definition of the SocketProactorTest class. +// +// Copyright (c) 2005-2006, Applied Informatics Software Engineering GmbH. +// and Contributors. +// +// SPDX-License-Identifier: BSL-1.0 +// + + +#ifndef SocketProactorTest_INCLUDED +#define SocketProactorTest_INCLUDED + + +#include "Poco/Net/Net.h" +#include "Poco/Thread.h" +#include "Poco/Observer.h" +#include "Poco/Net/SocketAddress.h" +#include "Poco/Net/SocketProactor.h" +#include "Poco/Net/SocketNotification.h" +#include "CppUnit/TestCase.h" +#include "EchoServer.h" + + +class SocketProactorTest: public CppUnit::TestCase +{ +public: + SocketProactorTest(const std::string& name); + ~SocketProactorTest(); + + void testTCPSocketProactor(); + void testUDPSocketProactor(); + void testSocketProactorStartStop(); + + void testWork(); + void testTimedWork(); + + void setUp(); + void tearDown(); + + static CppUnit::Test* suite(); + +private: +}; + + +#endif // SocketProactorTest_INCLUDED diff --git a/Net/testsuite/src/SocketReactorTest.cpp b/Net/testsuite/src/SocketReactorTest.cpp index b9e2efa61..b47a71a69 100644 --- a/Net/testsuite/src/SocketReactorTest.cpp +++ b/Net/testsuite/src/SocketReactorTest.cpp @@ -44,6 +44,10 @@ using Poco::Thread; namespace { + int DATA_SIZE = 1024; + int REACTORS_COUNT = 8; + int MAX_DATA_SIZE = DATA_SIZE * REACTORS_COUNT; + class EchoServiceHandler { public: @@ -130,7 +134,7 @@ namespace checkReadableObserverCount(1); _reactor.removeEventHandler(_socket, Observer(*this, &ClientServiceHandler::onReadable)); checkReadableObserverCount(0); - if (_once || _data.size() == 8192) + if (_once || _data.size() == MAX_DATA_SIZE) { _reactor.stop(); delete this; @@ -144,7 +148,7 @@ namespace checkWritableObserverCount(1); _reactor.removeEventHandler(_socket, Observer(*this, &ClientServiceHandler::onWritable)); checkWritableObserverCount(0); - std::string data(1024, 'x'); + std::string data(DATA_SIZE, 'x'); _socket.sendBytes(data.data(), (int) data.length()); _socket.shutdownSend(); } @@ -405,7 +409,7 @@ void SocketReactorTest::testSocketReactor() ClientServiceHandler::resetData(); reactor.run(); std::string data(ClientServiceHandler::data()); - assertTrue (data.size() == 1024); + assertTrue (data.size() == DATA_SIZE); assertTrue (!ClientServiceHandler::readableError()); assertTrue (!ClientServiceHandler::writableError()); assertTrue (!ClientServiceHandler::timeoutError()); @@ -425,7 +429,7 @@ void SocketReactorTest::testSetSocketReactor() ClientServiceHandler::resetData(); reactor.run(); std::string data(ClientServiceHandler::data()); - assertTrue (data.size() == 1024); + assertTrue (data.size() == DATA_SIZE); assertTrue (!ClientServiceHandler::readableError()); assertTrue (!ClientServiceHandler::writableError()); assertTrue (!ClientServiceHandler::timeoutError()); @@ -434,27 +438,26 @@ void SocketReactorTest::testSetSocketReactor() void SocketReactorTest::testParallelSocketReactor() { - SocketAddress ssa; + SocketAddress ssa("127.0.0.1:22087"); ServerSocket ss(ssa); SocketReactor reactor; - ParallelSocketAcceptor acceptor(ss, reactor); + ParallelSocketAcceptor acceptor(ss, reactor, REACTORS_COUNT); + SocketAddress sa("127.0.0.1", ss.address().port()); - SocketConnector connector1(sa, reactor); - SocketConnector connector2(sa, reactor); - SocketConnector connector3(sa, reactor); - SocketConnector connector4(sa, reactor); - SocketConnector connector5(sa, reactor); - SocketConnector connector6(sa, reactor); - SocketConnector connector7(sa, reactor); - SocketConnector connector8(sa, reactor); + std::vector*> connectors; + for (int i = 0; i < REACTORS_COUNT; ++i) + connectors.push_back(new SocketConnector(sa, reactor)); + ClientServiceHandler::setOnce(false); ClientServiceHandler::resetData(); reactor.run(); + std::string data(ClientServiceHandler::data()); - assertTrue (data.size() == 8192); + assertTrue (data.size() == MAX_DATA_SIZE); assertTrue (!ClientServiceHandler::readableError()); assertTrue (!ClientServiceHandler::writableError()); assertTrue (!ClientServiceHandler::timeoutError()); + for (auto& c : connectors) delete c; } diff --git a/Net/testsuite/src/SocketTest.cpp b/Net/testsuite/src/SocketTest.cpp index e42bcb666..2d668095d 100644 --- a/Net/testsuite/src/SocketTest.cpp +++ b/Net/testsuite/src/SocketTest.cpp @@ -64,6 +64,41 @@ void SocketTest::testEcho() } +void SocketTest::testMoveStreamSocket() +{ + EchoServer echoServer; + StreamSocket ss0 = StreamSocket(); + ss0.connect(SocketAddress("127.0.0.1", echoServer.port())); + StreamSocket ss(std::move(ss0)); +#if POCO_NEW_STATE_ON_MOVE + assertTrue (ss0.isNull()); +#else + assertFalse (ss0.isNull()); +#endif + + char buffer[256]; + std::memset(buffer, 0, sizeof(buffer)); + ss0 = ss; + assertTrue (ss0.impl()); + assertTrue (ss.impl()); + assertTrue (ss0.impl() == ss.impl()); + ss = std::move(ss0); +#if POCO_NEW_STATE_ON_MOVE + assertTrue (ss0.isNull()); +#else + assertFalse (ss0.isNull()); +#endif + assertTrue (ss.impl()); + int n = ss.sendBytes("hello", 5); + assertTrue (n == 5); + n = ss.receiveBytes(buffer, sizeof(buffer)); + assertTrue (n == 5); + assertTrue (std::string(buffer, n) == "hello"); + ss.close(); + ss0.close(); +} + + void SocketTest::testPoll() { EchoServer echoServer; @@ -559,6 +594,7 @@ CppUnit::Test* SocketTest::suite() CppUnit::TestSuite* pSuite = new CppUnit::TestSuite("SocketTest"); CppUnit_addTest(pSuite, SocketTest, testEcho); + CppUnit_addTest(pSuite, SocketTest, testMoveStreamSocket); CppUnit_addTest(pSuite, SocketTest, testPoll); CppUnit_addTest(pSuite, SocketTest, testAvailable); CppUnit_addTest(pSuite, SocketTest, testFIFOBuffer); diff --git a/Net/testsuite/src/SocketTest.h b/Net/testsuite/src/SocketTest.h index 775b882d2..85d5f4950 100644 --- a/Net/testsuite/src/SocketTest.h +++ b/Net/testsuite/src/SocketTest.h @@ -25,6 +25,7 @@ public: ~SocketTest(); void testEcho(); + void testMoveStreamSocket(); void testPoll(); void testAvailable(); void testFIFOBuffer(); diff --git a/Net/testsuite/src/SocketsTestSuite.cpp b/Net/testsuite/src/SocketsTestSuite.cpp index cce6fc756..e21e3ef5e 100644 --- a/Net/testsuite/src/SocketsTestSuite.cpp +++ b/Net/testsuite/src/SocketsTestSuite.cpp @@ -16,6 +16,7 @@ #include "DialogSocketTest.h" #include "RawSocketTest.h" #include "PollSetTest.h" +#include "SocketProactorTest.h" CppUnit::Test* SocketsTestSuite::suite() @@ -31,5 +32,6 @@ CppUnit::Test* SocketsTestSuite::suite() pSuite->addTest(MulticastSocketTest::suite()); #endif pSuite->addTest(PollSetTest::suite()); + pSuite->addTest(SocketProactorTest::suite()); return pSuite; } diff --git a/Net/testsuite/src/WebSocketTest.cpp b/Net/testsuite/src/WebSocketTest.cpp index 53790867c..116bb266e 100644 --- a/Net/testsuite/src/WebSocketTest.cpp +++ b/Net/testsuite/src/WebSocketTest.cpp @@ -121,7 +121,9 @@ void WebSocketTest::testWebSocket() HTTPClientSession cs("127.0.0.1", ss.address().port()); HTTPRequest request(HTTPRequest::HTTP_GET, "/ws", HTTPRequest::HTTP_1_1); HTTPResponse response; - WebSocket ws(cs, request, response); + WebSocket ws0 = WebSocket(cs, request, response); + WebSocket ws(std::move(ws0)); + assertTrue(ws0.impl() == nullptr); std::string payload("x"); ws.sendFrame(payload.data(), (int) payload.size()); diff --git a/NetSSL_OpenSSL/CMakeLists.txt b/NetSSL_OpenSSL/CMakeLists.txt index fe2d4a796..d5e61b88c 100644 --- a/NetSSL_OpenSSL/CMakeLists.txt +++ b/NetSSL_OpenSSL/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(NetSSL PUBLIC Poco::Crypto Poco::Util Poco::Net) target_include_directories(NetSSL PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/NetSSL_Win/CMakeLists.txt b/NetSSL_Win/CMakeLists.txt index 3e2b5fff9..c6325eb4f 100644 --- a/NetSSL_Win/CMakeLists.txt +++ b/NetSSL_Win/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(NetSSLWin PUBLIC Poco::Net Poco::Util Crypt32.lib) target_include_directories(NetSSLWin PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/PDF/CMakeLists.txt b/PDF/CMakeLists.txt index 1188726b8..2c565ad17 100644 --- a/PDF/CMakeLists.txt +++ b/PDF/CMakeLists.txt @@ -139,7 +139,7 @@ target_link_libraries(PDF PUBLIC Poco::XML Poco::Util) target_include_directories(PDF PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/PocoDoc/src/PocoDoc.cpp b/PocoDoc/src/PocoDoc.cpp index 53133f24e..05fe0959d 100644 --- a/PocoDoc/src/PocoDoc.cpp +++ b/PocoDoc/src/PocoDoc.cpp @@ -321,6 +321,11 @@ protected: logger().log(exc); ++errors; } + catch (std::exception& exc) + { + logger().error(std::string(exc.what())); + ++errors; + } } return errors; } diff --git a/Redis/CMakeLists.txt b/Redis/CMakeLists.txt index a93bfd960..875f49ab9 100644 --- a/Redis/CMakeLists.txt +++ b/Redis/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(Redis PUBLIC Poco::Net) target_include_directories(Redis PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/SevenZip/CMakeLists.txt b/SevenZip/CMakeLists.txt index 96ca10721..118ded71b 100644 --- a/SevenZip/CMakeLists.txt +++ b/SevenZip/CMakeLists.txt @@ -66,7 +66,7 @@ target_link_libraries(SevenZip PUBLIC Poco::Foundation) target_include_directories(SevenZip PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/Util/CMakeLists.txt b/Util/CMakeLists.txt index c667d79e5..5f3f7c1fd 100644 --- a/Util/CMakeLists.txt +++ b/Util/CMakeLists.txt @@ -42,7 +42,7 @@ endif() target_include_directories(Util PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/XML/CMakeLists.txt b/XML/CMakeLists.txt index 4fbf06f96..123657a0c 100644 --- a/XML/CMakeLists.txt +++ b/XML/CMakeLists.txt @@ -45,7 +45,7 @@ target_link_libraries(XML PUBLIC Poco::Foundation) target_include_directories(XML PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/Zip/CMakeLists.txt b/Zip/CMakeLists.txt index c3a25fb41..abe577653 100644 --- a/Zip/CMakeLists.txt +++ b/Zip/CMakeLists.txt @@ -25,7 +25,7 @@ target_link_libraries(Zip PUBLIC Poco::Foundation) target_include_directories(Zip PUBLIC $ - $ + $ PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src ) diff --git a/cmake/PocoMacros.cmake b/cmake/PocoMacros.cmake index 6443b827b..0ef354e9e 100644 --- a/cmake/PocoMacros.cmake +++ b/cmake/PocoMacros.cmake @@ -233,7 +233,7 @@ configure_file("cmake/Poco${target_name}Config.cmake" if(WIN32) set(PocoConfigPackageLocation "cmake") else() - set(PocoConfigPackageLocation "lib${LIB_SUFFIX}/cmake/${PROJECT_NAME}") + set(PocoConfigPackageLocation "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") endif() install(