From afdb9d2cddb55f6c34fa20e5d502175b44a112d7 Mon Sep 17 00:00:00 2001 From: Mikhail Filimonov Date: Thu, 20 Jan 2022 15:29:21 +0100 Subject: [PATCH] Add support for Microsoft Visual C++ --- .github/workflows/windows_msvc.yml | 60 +++++++++++++++++++++++ clickhouse/base/platform.h | 2 + clickhouse/base/socket.cpp | 77 +++++++++++++++++++++--------- clickhouse/base/socket.h | 15 +++++- tests/simple/CMakeLists.txt | 2 +- ut/CMakeLists.txt | 2 +- ut/tcp_server.cpp | 29 +++++++++-- ut/utils.h | 5 +- 8 files changed, 158 insertions(+), 34 deletions(-) create mode 100644 .github/workflows/windows_msvc.yml diff --git a/.github/workflows/windows_msvc.yml b/.github/workflows/windows_msvc.yml new file mode 100644 index 00000000..5ac5178b --- /dev/null +++ b/.github/workflows/windows_msvc.yml @@ -0,0 +1,60 @@ +name: Windows + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +env: + BUILD_TYPE: Release + GTEST_FILTER: --gtest_filter=-"*" + CLICKHOUSE_USER: clickhouse_cpp_cicd + CLICKHOUSE_PASSWORD: clickhouse_cpp_cicd + # + # CLICKHOUSE_HOST: localhost + # CLICKHOUSE_PORT: 9000 + # CLICKHOUSE_USER: default + # CLICKHOUSE_PASSWORD: + # CLICKHOUSE_DB: default + # + # CLICKHOUSE_SECURE_HOST: github.demo.trial.altinity.cloud + # CLICKHOUSE_SECURE_PORT: 9440 + # CLICKHOUSE_SECURE_USER: demo + # CLICKHOUSE_SECURE_PASSWORD: demo + # CLICKHOUSE_SECURE_DB: default + # + # CLICKHOUSE_SECURE2_HOST: gh-api.clickhouse.tech + # CLICKHOUSE_SECURE2_PORT: 9440 + # CLICKHOUSE_SECURE2_USER: explorer + # CLICKHOUSE_SECURE2_PASSWORD: + # CLICKHOUSE_SECURE2_DB: default + +jobs: + build: + runs-on: windows-latest + + steps: + - uses: actions/checkout@v2 + - uses: ilammy/msvc-dev-cmd@v1 + + - name: Configure CMake + run: cmake -B ${{github.workspace}}/build -DCMAKE_BUILD_TYPE=${{env.BUILD_TYPE}} -DBUILD_TESTS=ON + + - name: Build + run: cmake --build ${{github.workspace}}/build --config ${{env.BUILD_TYPE}} + + - name: Start tls offoader proxy + shell: bash + # that mimics non-secure clickhouse running on localhost + # by tunneling queries to remote tls server + # (needed because we can't start real clickhouse instance on windows) + run: | + choco install wget + wget https://github.com/filimonov/go-tlsoffloader/releases/download/v0.1.2/go-tlsoffloader_0.1.2_Windows_x86_64.tar.gz + tar -xvzf go-tlsoffloader_0.1.2_Windows_x86_64.tar.gz + ./go-tlsoffloader.exe -l localhost:9000 -b github.demo.trial.altinity.cloud:9440 & + + - name: Test + working-directory: ${{github.workspace}}/build/ut + run: Release\clickhouse-cpp-ut.exe "${{env.GTEST_FILTER}}" diff --git a/clickhouse/base/platform.h b/clickhouse/base/platform.h index 9f53da1a..c9c04699 100644 --- a/clickhouse/base/platform.h +++ b/clickhouse/base/platform.h @@ -13,6 +13,8 @@ #if defined(_win32_) || defined(_win64_) # define _win_ +# define _WIN32_WINNT 0x0600 // The WSAPoll function is defined on Windows Vista and later. +# define WIN32_LEAN_AND_MEAN 1 // don't include too much header automatically #endif #if defined(_linux_) || defined (_darwin_) diff --git a/clickhouse/base/socket.cpp b/clickhouse/base/socket.cpp index b00e3207..e014bdb2 100644 --- a/clickhouse/base/socket.cpp +++ b/clickhouse/base/socket.cpp @@ -14,11 +14,33 @@ # include # include # include -# include #endif namespace clickhouse { +#if defined(_win_) +char const* windowsErrorCategory::name() const noexcept { + return "WindowsSocketError"; +} + +std::string windowsErrorCategory::message(int c) const { + char error[UINT8_MAX]; + auto len = FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, static_cast(c), 0, error, sizeof(error), nullptr); + if (len == 0) { + return "unknown"; + } + while (len && (error[len - 1] == '\r' || error[len - 1] == '\n')) { + --len; + } + return std::string(error, len); +} + +windowsErrorCategory const& windowsErrorCategory::category() { + static windowsErrorCategory c; + return c; +} +#endif + namespace { class LocalNames : public std::unordered_set { @@ -37,6 +59,22 @@ class LocalNames : public std::unordered_set { } }; +inline int getSocketErrorCode() { +#if defined(_win_) + return WSAGetLastError(); +#else + return errno; +#endif +} + +const std::error_category& getErrorCategory() noexcept { +#if defined(_win_) + return windowsErrorCategory::category(); +#else + return std::system_category(); +#endif +} + void SetNonBlock(SOCKET fd, bool value) { #if defined(_unix_) int flags; @@ -55,8 +93,7 @@ void SetNonBlock(SOCKET fd, bool value) { return ioctl(fd, FIOBIO, &flags); #endif if (ret == -1) { - throw std::system_error( - errno, std::system_category(), "fail to set nonblocking mode"); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to set nonblocking mode"); } #elif defined(_win_) unsigned long inbuf = value; @@ -68,8 +105,7 @@ void SetNonBlock(SOCKET fd, bool value) { } if (WSAIoctl(fd, FIONBIO, &inbuf, sizeof(inbuf), &outbuf, sizeof(outbuf), &written, 0, 0) == SOCKET_ERROR) { - throw std::system_error( - errno, std::system_category(), "fail to set nonblocking mode"); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to set nonblocking mode"); } #endif } @@ -94,8 +130,13 @@ SOCKET SocketConnect(const NetworkAddress& addr) { SetNonBlock(s, true); if (connect(s, res->ai_addr, (int)res->ai_addrlen) != 0) { - int err = errno; - if (err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK) { + int err = getSocketErrorCode(); + if ( + err == EINPROGRESS || err == EAGAIN || err == EWOULDBLOCK +#if defined(_win_) + || err == WSAEWOULDBLOCK || err == WSAEINPROGRESS +#endif + ) { pollfd fd; fd.fd = s; fd.events = POLLOUT; @@ -103,7 +144,7 @@ SOCKET SocketConnect(const NetworkAddress& addr) { ssize_t rval = Poll(&fd, 1, 5000); if (rval == -1) { - throw std::system_error(errno, std::system_category(), "fail to connect"); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to connect"); } if (rval > 0) { socklen_t len = sizeof(err); @@ -122,11 +163,9 @@ SOCKET SocketConnect(const NetworkAddress& addr) { } } if (last_err > 0) { - throw std::system_error(last_err, std::system_category(), "fail to connect"); + throw std::system_error(last_err, getErrorCategory(), "fail to connect"); } - throw std::system_error( - errno, std::system_category(), "fail to connect" - ); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to connect"); } } // namespace @@ -156,7 +195,7 @@ NetworkAddress::NetworkAddress(const std::string& host, const std::string& port) const int error = getaddrinfo(host.c_str(), port.c_str(), &hints, &info_); if (error) { - throw std::system_error(errno, std::system_category()); + throw std::system_error(getSocketErrorCode(), getErrorCategory()); } } @@ -262,14 +301,10 @@ size_t SocketInput::DoRead(void* buf, size_t len) { } if (ret == 0) { - throw std::system_error( - errno, std::system_category(), "closed" - ); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "closed"); } - throw std::system_error( - errno, std::system_category(), "can't receive string data" - ); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "can't receive string data"); } bool SocketInput::Skip(size_t /*bytes*/) { @@ -292,9 +327,7 @@ size_t SocketOutput::DoWrite(const void* data, size_t len) { #endif if (::send(s_, (const char*)data, (int)len, flags) != (int)len) { - throw std::system_error( - errno, std::system_category(), "fail to send " + std::to_string(len) + " bytes of data" - ); + throw std::system_error(getSocketErrorCode(), getErrorCategory(), "fail to send " + std::to_string(len) + " bytes of data"); } return len; diff --git a/clickhouse/base/socket.h b/clickhouse/base/socket.h index 00bda042..38c7d5c4 100644 --- a/clickhouse/base/socket.h +++ b/clickhouse/base/socket.h @@ -1,8 +1,8 @@ #pragma once +#include "platform.h" #include "input.h" #include "output.h" -#include "platform.h" #include #include @@ -24,7 +24,7 @@ #endif #include - +#include struct addrinfo; @@ -47,6 +47,17 @@ class NetworkAddress { struct addrinfo* info_; }; +#if defined(_win_) + +class windowsErrorCategory : public std::error_category { +public: + char const* name() const noexcept override final; + std::string message(int c) const override final; + + static windowsErrorCategory const& category(); +}; + +#endif class Socket { public: diff --git a/tests/simple/CMakeLists.txt b/tests/simple/CMakeLists.txt index ad5d44e4..9f223630 100644 --- a/tests/simple/CMakeLists.txt +++ b/tests/simple/CMakeLists.txt @@ -3,7 +3,7 @@ ADD_EXECUTABLE (simple-test ) TARGET_LINK_LIBRARIES (simple-test - clickhouse-cpp-lib + clickhouse-cpp-lib-static ) IF (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") diff --git a/ut/CMakeLists.txt b/ut/CMakeLists.txt index 5cc125b1..bdb9bc98 100644 --- a/ut/CMakeLists.txt +++ b/ut/CMakeLists.txt @@ -25,7 +25,7 @@ ADD_EXECUTABLE (clickhouse-cpp-ut ) TARGET_LINK_LIBRARIES (clickhouse-cpp-ut - clickhouse-cpp-lib + clickhouse-cpp-lib-static gtest-lib ) IF (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") diff --git a/ut/tcp_server.cpp b/ut/tcp_server.cpp index cd479446..24794e09 100644 --- a/ut/tcp_server.cpp +++ b/ut/tcp_server.cpp @@ -1,13 +1,19 @@ #include "tcp_server.h" #include -#include #include #include #include -#include + +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN64) +# include +#else +# include +# include +# include +#endif + #include -#include namespace clickhouse { @@ -23,7 +29,7 @@ LocalTcpServer::~LocalTcpServer() { void LocalTcpServer::start() { //setup a socket sockaddr_in servAddr; - bzero((char*)&servAddr, sizeof(servAddr)); + memset((char*)&servAddr, 0, sizeof(servAddr)); servAddr.sin_family = AF_INET; servAddr.sin_addr.s_addr = htonl(INADDR_ANY); servAddr.sin_port = htons(port_); @@ -33,7 +39,14 @@ void LocalTcpServer::start() { throw std::runtime_error("Error establishing server socket"); } int enable = 1; - if (setsockopt(serverSd_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)) < 0) { + +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN64) + auto res = setsockopt(serverSd_, SOL_SOCKET, SO_REUSEADDR, (const char*)&enable, sizeof(enable)); +#else + auto res = setsockopt(serverSd_, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(enable)); +#endif + + if (res < 0) { std::cerr << "setsockopt(SO_REUSEADDR) failed" << std::endl; } //bind the socket to its local address @@ -47,8 +60,14 @@ void LocalTcpServer::start() { void LocalTcpServer::stop() { if(serverSd_ > 0) { + +#if defined(__WIN32__) || defined(_WIN32) || defined(_WIN64) + shutdown(serverSd_, SD_BOTH); + closesocket(serverSd_); +#else shutdown(serverSd_, SHUT_RDWR); close(serverSd_); +#endif serverSd_ = -1; } } diff --git a/ut/utils.h b/ut/utils.h index 0bde2c73..430dcec2 100644 --- a/ut/utils.h +++ b/ut/utils.h @@ -39,9 +39,8 @@ struct Timer private: static auto Now() { - struct timespec ts; - clock_gettime(CLOCK_THREAD_CPUTIME_ID, &ts); - return std::chrono::nanoseconds(ts.tv_sec * 1000000000LL + ts.tv_nsec); + std::chrono::nanoseconds ns = std::chrono::high_resolution_clock::now().time_since_epoch(); + return ns; } private: