From 25368814525aea5cd06b1b9ef0b1689bb6568cfd Mon Sep 17 00:00:00 2001 From: zvon Date: Wed, 15 Jan 2020 16:30:30 +0100 Subject: [PATCH] Network: refactor --- network.cpp | 120 -------------------------------------------- network.hpp | 24 +++++---- unix/network.cpp | 81 ++++++++++++++++++++++++++++++ windows/network.cpp | 85 +++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+), 129 deletions(-) delete mode 100644 network.cpp create mode 100644 unix/network.cpp create mode 100644 windows/network.cpp diff --git a/network.cpp b/network.cpp deleted file mode 100644 index 772b406..0000000 --- a/network.cpp +++ /dev/null @@ -1,120 +0,0 @@ -#include "network.hpp" -#include - -#ifdef _WIN32 - -// shamelessly stolen from http://www.cplusplus.com/forum/windows/109799/ - -Curl::Curl() { - connect = InternetOpen( L"WinInet/1.0", INTERNET_OPEN_TYPE_PRECONFIG, - nullptr, nullptr, 0 ); - if ( !connect ) - std::wcerr << "Something went wrong while connecting" << std::endl; -} - -Curl::~Curl() { - if ( connect ) - InternetCloseHandle( connect ); -} - -std::string Curl::execute( const std::wstring &url ) { - std::string source{}; - source.reserve( 10000 ); - - HINTERNET openAddress = InternetOpenUrl( - connect, url.c_str(), nullptr, 0, - INTERNET_FLAG_PRAGMA_NOCACHE | INTERNET_FLAG_KEEP_CONNECTION, 0 ); - - if ( !openAddress ) { - unsigned long errorNum = GetLastError(); - std::wcout << "Failed to open URL" << std::endl - << "Error No: " << errorNum << std::endl; - return ""; - } - - char dataReceived[4096]; - unsigned long numberOfBytesRead = 0; - while ( InternetReadFile( openAddress, dataReceived, 4096, - &numberOfBytesRead ) && - numberOfBytesRead ) { - source.append( dataReceived, numberOfBytesRead ); - } - - InternetCloseHandle( openAddress ); - return source; -} - -#else - -size_t writeCallback( void *contents, size_t size, size_t nmemb, - void *target ) { - *static_cast< std::string * >( target ) += - std::string( static_cast< char * >( contents ), size * nmemb ); - return size * nmemb; -} - -Curl::Curl() { - curl_global_init( CURL_GLOBAL_ALL ); - curl_handle = curl_easy_init(); - curl_easy_setopt( curl_handle, CURLOPT_WRITEFUNCTION, writeCallback ); - curl_easy_setopt( curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0" ); -} - -Curl::~Curl() { - curl_easy_cleanup( curl_handle ); - curl_slist_free_all( chunk ); - curl_global_cleanup(); -} - -void cleanUp( CURL *curl_handle ) { - curl_easy_setopt( curl_handle, CURLOPT_POST, 0 ); - curl_easy_setopt( curl_handle, CURLOPT_POSTFIELDS, "" ); -} - -std::string Curl::get( const std::string &url ) { - // get rid of garbage - cleanUp( curl_handle ); - std::string source; - source.reserve( 100000 ); - curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, - static_cast< void * >( &source ) ); - curl_easy_setopt( curl_handle, CURLOPT_URL, url.c_str() ); - curl_easy_setopt( curl_handle, CURLOPT_HTTPGET, 1 ); - auto res = curl_easy_perform( curl_handle ); - if ( res != CURLE_OK ) { - std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror( res ) - << std::endl; - return ""; - } - return source; -} - -void Curl::addHeader( const std::string &header ) { - chunk = curl_slist_append( chunk, header.c_str() ); - curl_easy_setopt( curl_handle, CURLOPT_HTTPHEADER, chunk ); -} - -void Curl::clearHeader() { - curl_slist_free_all( chunk ); - chunk = NULL; - curl_easy_setopt( curl_handle, CURLOPT_HTTPHEADER, chunk ); -} - -std::string Curl::post( const std::string &url, const std::string &data ) { - std::string source; - source.reserve( 100000 ); - curl_easy_setopt( curl_handle, CURLOPT_URL, url.c_str() ); - curl_easy_setopt( curl_handle, CURLOPT_POST, 1 ); - curl_easy_setopt( curl_handle, CURLOPT_POSTFIELDS, data.c_str() ); - curl_easy_setopt( curl_handle, CURLOPT_WRITEDATA, - static_cast< void * >( &source ) ); - auto res = curl_easy_perform( curl_handle ); - if ( res != CURLE_OK ) { - std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror( res ) - << std::endl; - return ""; - } - return source; -} - -#endif diff --git a/network.hpp b/network.hpp index dd0ce4c..35ddb58 100644 --- a/network.hpp +++ b/network.hpp @@ -3,8 +3,9 @@ #ifdef _WIN32 -#include #include +#include +#include using string = std::wstring; @@ -17,21 +18,26 @@ using string = std::string; #endif -class Curl { +class Request { public: - Curl(); - ~Curl(); + Request() = delete; + Request( const string &server ); + ~Request(); std::string get( const string &url ); - std::string post( const std::string &url, const std::string &data ); - void addHeader( const std::string &header ); + std::string post( const string &url, const std::string &data ); + void addHeader( const string &header ); void clearHeader(); + bool initSuccessful(); private: #ifdef _WIN32 - HINTERNET connect = nullptr; + HINTERNET _hInternet = nullptr; + HINTERNET _hConnect = nullptr; + std::wstring _headers = L""; #else - CURL *curl_handle; - struct curl_slist *chunk = NULL; + CURL *_curl_handle = nullptr; + struct curl_slist *_chunk = nullptr; + const string _server; #endif }; diff --git a/unix/network.cpp b/unix/network.cpp new file mode 100644 index 0000000..63eb8dc --- /dev/null +++ b/unix/network.cpp @@ -0,0 +1,81 @@ +#include "../network.hpp" +#include +#include + +size_t writeCallback( void *contents, size_t size, size_t nmemb, + void *target ) { + *static_cast< std::string * >( target ) += + std::string( static_cast< char * >( contents ), size * nmemb ); + return size * nmemb; +} + +Request::Request( const string &server ) : _server( server ) { + curl_global_init( CURL_GLOBAL_ALL ); + _curl_handle = curl_easy_init(); + if ( _curl_handle == NULL ) { + std::cerr << "ERROR curl_easy_init" << std::endl; + } + curl_easy_setopt( _curl_handle, CURLOPT_WRITEFUNCTION, writeCallback ); + curl_easy_setopt( _curl_handle, CURLOPT_USERAGENT, "libcurl-agent/1.0" ); +} + +Request::~Request() { + curl_easy_cleanup( _curl_handle ); + curl_slist_free_all( _chunk ); + curl_global_cleanup(); +} + +void cleanUp( CURL *curl_handle ) { + curl_easy_setopt( curl_handle, CURLOPT_POST, 0 ); + curl_easy_setopt( curl_handle, CURLOPT_POSTFIELDS, "" ); +} + +std::string Request::get( const std::string &url ) { + // get rid of garbage + cleanUp( _curl_handle ); + std::string response; + response.reserve( 100000 ); + curl_easy_setopt( _curl_handle, CURLOPT_WRITEDATA, + static_cast< void * >( &response ) ); + curl_easy_setopt( _curl_handle, CURLOPT_URL, ( _server + url ).c_str() ); + curl_easy_setopt( _curl_handle, CURLOPT_HTTPGET, 1 ); + auto res = curl_easy_perform( _curl_handle ); + if ( res != CURLE_OK ) { + std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror( res ) + << std::endl; + return ""; + } + return response; +} + +std::string Request::post( const std::string &url, const std::string &data ) { + std::string response; + response.reserve( 100000 ); + curl_easy_setopt( _curl_handle, CURLOPT_URL, ( _server + url ).c_str() ); + curl_easy_setopt( _curl_handle, CURLOPT_POST, 1 ); + curl_easy_setopt( _curl_handle, CURLOPT_POSTFIELDS, data.c_str() ); + curl_easy_setopt( _curl_handle, CURLOPT_WRITEDATA, + static_cast< void * >( &response ) ); + auto res = curl_easy_perform( _curl_handle ); + if ( res != CURLE_OK ) { + std::cerr << "curl_easy_perform() failed: " << curl_easy_strerror( res ) + << std::endl; + return ""; + } + return response; +} + +void Request::addHeader( const std::string &header ) { + _chunk = curl_slist_append( _chunk, header.c_str() ); + curl_easy_setopt( _curl_handle, CURLOPT_HTTPHEADER, _chunk ); +} + +void Request::clearHeader() { + curl_slist_free_all( _chunk ); + _chunk = nullptr; + curl_easy_setopt( _curl_handle, CURLOPT_HTTPHEADER, _chunk ); +} + +bool Request::initSuccessful() { + return _curl_handle != nullptr; +} diff --git a/windows/network.cpp b/windows/network.cpp new file mode 100644 index 0000000..7016b08 --- /dev/null +++ b/windows/network.cpp @@ -0,0 +1,85 @@ +#include "../network.hpp" +#include +#include + +const wchar_t *acceptTypes[] = { L"text/*", L"application/json", nullptr }; + +Request::Request( const string &server ) { + // Start connection + _hInternet = InternetOpen( L"WinInet/1.0", INTERNET_OPEN_TYPE_PRECONFIG, + nullptr, nullptr, 0 ); + if ( !_hInternet ) + std::wcerr << "ERROR InternetOpen: " << GetLastError() << std::endl; + // connect to server + _hConnect = InternetConnect( _hInternet, server.c_str(), + INTERNET_DEFAULT_HTTPS_PORT, nullptr, nullptr, + INTERNET_SERVICE_HTTP, 0, 0 ); + if ( !_hConnect ) + std::wcerr << "ERROR InternetConnect: " << GetLastError() << std::endl; +} + +Request::~Request() { + if ( _hConnect ) + InternetCloseHandle( _hConnect ); + if ( _hInternet ) + InternetCloseHandle( _hInternet ); +} + +std::string sendRequest( HINTERNET hRequest, const std::wstring &headers, + const std::string &optional ) { + std::string response{}; + response.reserve( 10000 ); + + // have to do a copy because c_str returns const + std::unique_ptr< char > opt( new char[optional.length()] ); + memcpy( opt.get(), optional.c_str(), optional.length() ); + + auto requestSent = HttpSendRequest( + hRequest, headers.c_str(), headers.length(), opt, optional.length() ); + + if ( !requestSent ) + std::wcerr << "ERROR HttpSendRequest: " << GetLastError() << std::endl; + + char dataReceived[4096]; + unsigned long numberOfBytesRead = 0; + // read while InternetReadFile returns true and reads more than 0 bytes + while ( + InternetReadFile( hRequest, dataReceived, 4096, &numberOfBytesRead ) && + numberOfBytesRead ) { + response.append( dataReceived, numberOfBytesRead ); + } + + return response; +} + +std::string Request::get( const std::wstring &url ) { + HINTERNET hRequest = + HttpOpenRequest( _hConnect, L"GET", url.c_str(), nullptr, nullptr, + acceptTypes, INTERNET_FLAG_SECURE, 0 ); + if ( !hRequest ) + std::wcerr << "ERROR HttpOpenRequest: " << GetLastError() << std::endl; + + return sendRequest( hRequest, _headers, "" ); +} + +std::string Request::post( const std::wstring &url, const std::string &data ) { + HINTERNET hRequest = + HttpOpenRequest( _hConnect, L"POST", url.c_str(), nullptr, nullptr, + acceptTypes, INTERNET_FLAG_SECURE, 0 ); + if ( !hRequest ) + std::wcerr << "ERROR HttpOpenRequest: " << GetLastError() << std::endl; + + return sendRequest( hRequest, _headers, data ); +} + +void Request::addHeader( const std::wstring &header ) { + _headers += header + L"\r\n"; +} + +void Request::clearHeader() { + _headers = L""; +} + +bool Request::initSuccessful() { + return _hInternet != nullptr && _hConnect != nullptr; +}