From 3b88ca10a10965d6afab936122f12347dee6e241 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Wed, 24 Apr 2019 23:53:25 -0300 Subject: [PATCH 01/18] Added: Http::Request::setProgressCallback to add a progress callback for the current request. Http::Request::cancel() to allow current request being cancelled. And some improvements in Http requests. --HG-- branch : dev --- include/eepp/network/http.hpp | 43 +++++-- src/eepp/network/http.cpp | 123 ++++++++++++++------- src/examples/http_request/http_request.cpp | 38 +++++-- 3 files changed, 145 insertions(+), 59 deletions(-) diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index ac834fd9f..69b58f96e 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -109,6 +109,27 @@ class EE_API Http : NonCopyable { /** Enables/Disables follow redirects */ void setFollowRedirect( bool follow ); + + /** Definition of the current progress callback + * @param http The http client + * @param request The http request + * @param totalBytes The total bytes of the document / files ( only available if Content-Length is returned, otherwise is 0 ) + * @param currentBytes Current received total bytes + * @return True if continue the request, false will cancel the current request. + */ + typedef std::function ProgressCallback; + + /** Sets a progress callback */ + void setProgressCallback( const ProgressCallback& progressCallback ); + + /** Get the progress callback */ + const ProgressCallback& getProgressCallback() const; + + /** Cancels the current request if being processed */ + void cancel(); + + /** @return True if the current request was cancelled */ + const bool& isCancelled() const; private: friend class Http; @@ -128,16 +149,18 @@ class EE_API Http : NonCopyable { typedef std::map FieldTable; // Member data - FieldTable mFields; ///< Fields of the header associated to their value - Method mMethod; ///< Method to use for the request - std::string mUri; ///< Target URI of the request - unsigned int mMajorVersion; ///< Major HTTP version - unsigned int mMinorVersion; ///< Minor HTTP version - std::string mBody; ///< Body of the request - bool mValidateCertificate; ///< Validates the SSL certificate in case of an HTTPS request - bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request - bool mFollowRedirect; ///< Follows redirect response codes - unsigned int mRedirectionCount; ///< Number of redirections followed by the request + FieldTable mFields; ///< Fields of the header associated to their value + Method mMethod; ///< Method to use for the request + std::string mUri; ///< Target URI of the request + unsigned int mMajorVersion; ///< Major HTTP version + unsigned int mMinorVersion; ///< Minor HTTP version + std::string mBody; ///< Body of the request + bool mValidateCertificate; ///< Validates the SSL certificate in case of an HTTPS request + bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request + bool mFollowRedirect; ///< Follows redirect response codes + mutable bool mCancel; ///< Cancel state of current request + ProgressCallback mProgressCallback; ///< Progress callback + mutable unsigned int mRedirectionCount; ///< Number of redirections followed by the request }; /** @brief Define a HTTP response */ diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index a2f6fea38..f22e1d638 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -17,6 +17,7 @@ Http::Request::Request(const std::string& uri, Method method, const std::string& mValidateCertificate( validateCertificate ), mValidateHostname( validateHostname ), mFollowRedirect( followRedirect ), + mCancel( false ), mRedirectionCount( 0 ) { setMethod(method); @@ -78,6 +79,22 @@ void Http::Request::setFollowRedirect(bool follow) { mFollowRedirect = follow; } +void Http::Request::setProgressCallback(const Http::Request::ProgressCallback& progressCallback) { + mProgressCallback = progressCallback; +} + +const Http::Request::ProgressCallback& Http::Request::getProgressCallback() const { + return mProgressCallback; +} + +void Http::Request::cancel() { + mCancel = true; +} + +const bool &Http::Request::isCancelled() const { + return mCancel; +} + std::string Http::Request::prepare() const { std::ostringstream out; @@ -365,7 +382,7 @@ Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && request.getFollowRedirect() ) { - const_cast( request ).mRedirectionCount++; + request.mRedirectionCount++; // Only continue redirecting if less than 10 redirections were done if ( request.mRedirectionCount < 10 ) { @@ -378,7 +395,7 @@ Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { } } - return received; + return std::move(received); } Http::Response Http::downloadRequest(const Http::Request& request, IOStream& writeTo, Time timeout) { @@ -391,27 +408,50 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri mConnection = Conn; } + // First make sure that the request is valid -- add missing mandatory fields Request toSend(prepareFields(request)); + + // Prepare the response Response received; + // Connect the socket to the host if (mConnection->connect(mHost, mPort, timeout) == Socket::Done) { + // Convert the request to string and send it through the connected socket std::string requestStr = toSend.prepare(); if (!requestStr.empty()) { + // Send it through the socket if (mConnection->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { + // Wait for the server's response int isnheader = 0; - size_t len = 0; + std::size_t currentTotalBytes = 0; + std::size_t len = 0; char * eol; // end of line char * bol; // beginning of line std::size_t size = 0; - const size_t bufferSize = 1024; + const std::size_t bufferSize = 1024; char buffer[bufferSize+1]; std::string header; - while (mConnection->receive(buffer, bufferSize, size) == Socket::Done) { - if ( isnheader != 0 ) + while (!request.isCancelled() && mConnection->receive(buffer, bufferSize, size) == Socket::Done) { + if ( isnheader != 0 ) { + currentTotalBytes += size; writeTo.write( buffer, size ); + if ( request.getProgressCallback() ) { + std::size_t length = 0; + + if ( !received.getField("content-length").empty() ) { + String::fromString( length, received.getField("content-length") ); + } + + if ( !request.getProgressCallback()( *this, request, length, currentTotalBytes ) ) { + request.mCancel = true; + break; + } + } + } + if ( isnheader == 0 ) { // calculate combined length of unprocessed data and new data len += size; @@ -421,15 +461,19 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // checks if the header break happened to be the first line of the buffer if ( !( strncmp( buffer, "\r\n", 2 ) ) ) { - if (len > 2) + if (len > 2) { + currentTotalBytes += (len-2); writeTo.write(buffer, (len-2)); + } continue; } if ( !( strncmp( buffer, "\n", 1 ) ) ) { - if ( len > 1 ) + if ( len > 1 ) { + currentTotalBytes += (len-1); writeTo.write(buffer, (len-1)); + } continue; } @@ -457,14 +501,43 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri len = len - ( bol - buffer ); // write remaining data to FILE stream - if ( len > 0 ) + if ( len > 0 ) { + currentTotalBytes += len; writeTo.write( bol, len ); + } header.append( buffer, ( bol - buffer ) ); // reset length of left over data to zero and continue processing // non-header information len = 0; + + if ( !header.empty() ) { + // Build the Response object from the received data + received.parse(header); + + // If a redirection is requested, and requests follows redirections, + // send a new request to the redirection location. + if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && + request.getFollowRedirect() ) { + + request.mRedirectionCount++; + + // Only continue redirecting if less than 10 redirections were done + if ( request.mRedirectionCount < 10 ) { + std::string location( received.getField("location") ); + URI uri( location ); + Http http( uri.getHost(), uri.getPort(), uri.getScheme() == "https" ? true : false ); + Http::Request newRequest( request ); + newRequest.setUri( uri.getPathEtc() ); + + // Close the connection + mConnection->disconnect(); + + return http.downloadRequest( request, writeTo, timeout ); + } + } + } } } @@ -473,32 +546,6 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } } } - - if ( !header.empty() ) { - received.parse(header); - - // If a redirection is requested, and requests follows redirections, - // send a new request to the redirection location. - if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && - request.getFollowRedirect() ) { - - const_cast( request ).mRedirectionCount++; - - // Only continue redirecting if less than 10 redirections were done - if ( request.mRedirectionCount < 10 ) { - std::string location( received.getField("location") ); - URI uri( location ); - Http http( uri.getHost(), uri.getPort(), uri.getScheme() == "https" ? true : false ); - Http::Request newRequest( request ); - newRequest.setUri( uri.getPathEtc() ); - - // Close the connection - mConnection->disconnect(); - - return http.downloadRequest( request, writeTo, timeout ); - } - } - } } } @@ -506,7 +553,7 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri mConnection->disconnect(); } - return received; + return std::move(received); } Http::Response Http::downloadRequest(const Http::Request & request, std::string writePath, Time timeout) { @@ -595,7 +642,7 @@ void Http::removeOldThreads() { } } -Http::Request Http::prepareFields(const Http::Request & request) { +Http::Request Http::prepareFields(const Http::Request& request) { Request toSend(request); if (!toSend.hasField("User-Agent")) { @@ -620,7 +667,7 @@ Http::Request Http::prepareFields(const Http::Request & request) { toSend.setField("Connection", "close"); } - return toSend; + return std::move(toSend); } void Http::sendAsyncRequest( AsyncResponseCallback cb, const Http::Request& request, Time timeout ) { diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index f821f9647..c20488ec7 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -1,12 +1,23 @@ #include #include +void printResponseHeaders( Http::Response& response ) { + Http::Response::FieldTable headers = response.getHeaders(); + + std::cout << "\r\nHeaders: " << std::endl; + + for ( auto&& head : headers ) { + std::cout << "\t" << head.first << ": " << head.second << std::endl; + } +} + EE_MAIN_FUNC int main (int argc, char * argv []) { args::ArgumentParser parser("HTTP request program example"); args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); args::ValueFlag output(parser, "file", "Write to file instead of stdout", {'o', "output"} ); + args::Flag head(parser, "head", "Show document info", {'I',"head"} ); + args::Flag progress(parser, "progress", "Show current progress of a download", {'p',"progress"} ); args::Positional url(parser, "url", "The url to request"); - args::Flag verbose(parser, "verbose", "Prints the request response headers", {'v',"verbose"} ); try { parser.ParseCLI(argc, argv); @@ -73,14 +84,8 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { Http::Response::Status status = response.getStatus(); if ( status == Http::Response::Ok ) { - if ( verbose ) { - Http::Response::FieldTable headers = response.getHeaders(); - - std::cout << "Headers: " << std::endl; - - for ( auto&& head : headers ) { - std::cout << "\t" << head.first << ": " << head.second << std::endl; - } + if ( head ) { + printResponseHeaders(response); std::cout << std::endl << "Body: " << std::endl; } @@ -90,12 +95,23 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { std::cout << "Error " << status << std::endl; } } else { - http.downloadRequest(request, output.Get(), Seconds(5)); + if ( progress ) { + request.setProgressCallback( []( const Http& http, const Http::Request& request, size_t totalBytes, size_t currentBytes ) { + std::cout << "\rDownloaded " << FileSystem::sizeToString( currentBytes ).c_str() << " of " << FileSystem::sizeToString( totalBytes ).c_str() << " "; + std::cout << std::flush; + return true; + }); + } + + Http::Response response = http.downloadRequest(request, output.Get(), Seconds(5)); + + if ( head ) + printResponseHeaders(response); } } } - if ( verbose ) + if ( head ) MemoryManager::showResults(); return EXIT_SUCCESS; From 46f6b40207dd3fbe3ad6c723e124c676b5e21501 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 25 Apr 2019 01:35:55 -0300 Subject: [PATCH 02/18] Http response parse minor fix for chunked transfer encoding. --HG-- branch : dev --- src/eepp/network/http.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index f22e1d638..0ee532528 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -225,7 +225,8 @@ void Http::Response::parse(const std::string& data) { // Copy the actual content data std::istreambuf_iterator it(in); - for (std::size_t i = 0; i < length; i++) + std::istreambuf_iterator itEnd; + for (std::size_t i = 0; ((i < length) && (it != itEnd)); i++) mBody.push_back(*it++); } From af033b2fd006dd8fc28bb0617dddbf69c8a51271 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 27 Apr 2019 22:56:45 -0300 Subject: [PATCH 03/18] Minor improvements on http requests and minor bug fix. --HG-- branch : dev --- include/eepp/math/base.hpp | 7 -- include/eepp/math/interpolation1d.hpp | 3 +- include/eepp/math/interpolation2d.hpp | 3 +- include/eepp/network/http.hpp | 26 +++-- projects/linux/ee.config | 2 - projects/linux/ee.files | 1 - projects/linux/ee.includes | 2 - src/eepp/network/http.cpp | 106 +++++++++++++++++---- src/examples/http_request/http_request.cpp | 40 +++++++- 9 files changed, 146 insertions(+), 44 deletions(-) delete mode 100644 include/eepp/math/base.hpp diff --git a/include/eepp/math/base.hpp b/include/eepp/math/base.hpp deleted file mode 100644 index e734cfde3..000000000 --- a/include/eepp/math/base.hpp +++ /dev/null @@ -1,7 +0,0 @@ -#ifndef EE_MATH_BASE -#define EE_MATH_BASE - -#include -#include - -#endif diff --git a/include/eepp/math/interpolation1d.hpp b/include/eepp/math/interpolation1d.hpp index 41d90a36b..fa5e7e9b4 100644 --- a/include/eepp/math/interpolation1d.hpp +++ b/include/eepp/math/interpolation1d.hpp @@ -1,7 +1,8 @@ #ifndef EE_MATHCINTERPOLATION_H #define EE_MATHCINTERPOLATION_H -#include +#include +#include #include #include diff --git a/include/eepp/math/interpolation2d.hpp b/include/eepp/math/interpolation2d.hpp index bb4034aa2..f3b34286f 100755 --- a/include/eepp/math/interpolation2d.hpp +++ b/include/eepp/math/interpolation2d.hpp @@ -1,7 +1,8 @@ #ifndef EE_MATHCWAYPOINTS_H #define EE_MATHCWAYPOINTS_H -#include +#include +#include #include #include #include diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index 69b58f96e..659ef69bb 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -40,6 +40,9 @@ class EE_API Http : NonCopyable { Patch ///< The PATCH method is used to apply partial modifications to a resource. }; + /** @return Method from a method name string. */ + static Method methodFromString( std::string methodString ); + /** @brief Default constructor ** This constructor creates a GET request, with the root ** URI ("/") and an empty body. @@ -62,6 +65,20 @@ class EE_API Http : NonCopyable { ** @param value Value of the field */ void setField(const std::string& field, const std::string& value); + /** @brief Check if the request defines a field + ** This function uses case-insensitive comparisons. + ** @param field Name of the field to test + ** @return True if the field exists, false otherwise */ + bool hasField(const std::string& field) const; + + /** @brief Get the value of a field + ** If the field @a field is not found in the response header, + ** the empty string is returned. This function uses + ** case-insensitive comparisons. + ** @param field Name of the field to get + ** @return Value of the field, or empty string if not found */ + const std::string& getField(const std::string& field) const; + /** @brief Set the request method ** See the Method enumeration for a complete list of all ** the availale methods. @@ -139,12 +156,6 @@ class EE_API Http : NonCopyable { ** @return String containing the request, ready to be sent */ std::string prepare() const; - /** @brief Check if the request defines a field - ** This function uses case-insensitive comparisons. - ** @param field Name of the field to test - ** @return True if the field exists, false otherwise */ - bool hasField(const std::string& field) const; - // Types typedef std::map FieldTable; @@ -228,6 +239,9 @@ class EE_API Http : NonCopyable { ** @return Status code of the response */ Status getStatus() const; + /** @brief Get the response status description */ + const char * getStatusDescription() const; + /** @brief Get the major HTTP version number of the response ** @return Major HTTP version number ** @see GetMinorHttpVersion */ diff --git a/projects/linux/ee.config b/projects/linux/ee.config index c69ad180d..8aaaa23a8 100644 --- a/projects/linux/ee.config +++ b/projects/linux/ee.config @@ -1,7 +1,6 @@ #define EE_SDL_VERSION_2 #define EE_X11_PLATFORM #define EE_DEBUG -#define EE_LIBSNDFILE_ENABLED #define EE_MEMORY_MANAGER #define EE_SHADERS_SUPPORTED #define EE_GLEW_AVAILABLE @@ -10,7 +9,6 @@ #define EE_BACKEND_SFML_ACTIVE #define EE_BACKEND_SDL2 #define EE_GL3_ENABLED -#define EE_SHADERS_SUPPORTED #define EE_BACKEND_SDL_ACTIVE #define EE_MBEDTLS #define DR_MP3_IMPLEMENTATION diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 1880d817f..75889ec8b 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -143,7 +143,6 @@ ../../include/eepp/maps/mapobjectlayer.hpp ../../include/eepp/maps/tilemap.hpp ../../include/eepp/maps/tilemaplayer.hpp -../../include/eepp/math/base.hpp ../../include/eepp/math/ease.hpp ../../include/eepp/math/easing.hpp ../../include/eepp/math.hpp diff --git a/projects/linux/ee.includes b/projects/linux/ee.includes index cdfe74fdb..d11e995a6 100644 --- a/projects/linux/ee.includes +++ b/projects/linux/ee.includes @@ -4,8 +4,6 @@ ../../include/eepp/thirdparty ../../src/thirdparty/efsw/include ../../src/thirdparty/libvorbis/include -../../src/eepp/audio -../../include/eepp/audio /usr/include/freetype2/ ../../include/eepp/ui diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 0ee532528..1b4e33820 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -13,6 +13,28 @@ using namespace EE::Network::SSL; namespace EE { namespace Network { +Http::Request::Method Http::Request::methodFromString( std::string methodString ) { + String::toLowerInPlace(methodString); + + if ( "get" == methodString ) { + return Method::Get; + } else if ( "head" == methodString ) { + return Method::Head; + } else if ( "post" == methodString ) { + return Method::Post; + } else if ( "put" == methodString ) { + return Method::Put; + } else if ( "delete" == methodString ) { + return Method::Delete; + } else if ( "options" == methodString ) { + return Method::Options; + } else if ( "patch" == methodString ) { + return Method::Patch; + } else { + return Method::Get; + } +} + Http::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname , bool followRedirect) : mValidateCertificate( validateCertificate ), mValidateHostname( validateHostname ), @@ -133,6 +155,16 @@ bool Http::Request::hasField(const std::string& field) const { return mFields.find(String::toLower(field)) != mFields.end(); } +const std::string& Http::Request::getField(const std::string& field) const { + FieldTable::const_iterator it = mFields.find(String::toLower(field)); + if (it != mFields.end()) { + return it->second; + } else { + static const std::string empty = ""; + return empty; + } +} + Http::Response::Response() : mStatus (ConnectionFailed), mMajorVersion(0), @@ -158,6 +190,44 @@ Http::Response::Status Http::Response::getStatus() const { return mStatus; } +const char * Http::Response::getStatusDescription() const { + switch ( mStatus ) { + // 2xx: success + case Ok: return "Successfull"; + case Created: return "The resource has successfully been created"; + case Accepted: return "The request has been accepted, but will be processed later by the server"; + case NoContent: return "The server didn't send any data in return"; + case ResetContent: return "The server informs the client that it should clear the view (form) that caused the request to be sent"; + case PartialContent: return "The server has sent a part of the resource, as a response to a partial GET request"; + + // 3xx: redirection + case MultipleChoices: return "The requested page can be accessed from several locations"; + case MovedPermanently: return "The requested page has permanently moved to a new location"; + case MovedTemporarily: return "The requested page has temporarily moved to a new location"; + case NotModified: return "For conditionnal requests, means the requested page hasn't changed and doesn't need to be refreshed"; + + // 4xx: client error + case BadRequest: return "The server couldn't understand the request (syntax error)"; + case Unauthorized: return "The requested page needs an authentification to be accessed"; + case Forbidden: return "The requested page cannot be accessed at all, even with authentification"; + case NotFound: return "The requested page doesn't exist"; + case RangeNotSatisfiable: return "The server can't satisfy the partial GET request (with a \"Range\" header field)"; + + // 5xx: server error + case InternalServerError: return "The server encountered an unexpected error"; + case NotImplemented: return "The server doesn't implement a requested feature"; + case BadGateway: return "The gateway server has received an error from the source server"; + case ServiceNotAvailable: return "The server is temporarily unavailable (overloaded, in maintenance, ...)"; + case GatewayTimeout: return "The gateway server couldn't receive a response from the source server"; + case VersionNotSupported: return "The server doesn't support the requested HTTP version"; + + // 10xx: Custom codes + case InvalidResponse: return "Response is not a valid HTTP one"; + case ConnectionFailed: return "Connection with server failed"; + default: return "Unknown response status"; + } +} + unsigned int Http::Response::getMajorHttpVersion() const { return mMajorVersion; } @@ -435,24 +505,6 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri std::string header; while (!request.isCancelled() && mConnection->receive(buffer, bufferSize, size) == Socket::Done) { - if ( isnheader != 0 ) { - currentTotalBytes += size; - writeTo.write( buffer, size ); - - if ( request.getProgressCallback() ) { - std::size_t length = 0; - - if ( !received.getField("content-length").empty() ) { - String::fromString( length, received.getField("content-length") ); - } - - if ( !request.getProgressCallback()( *this, request, length, currentTotalBytes ) ) { - request.mCancel = true; - break; - } - } - } - if ( isnheader == 0 ) { // calculate combined length of unprocessed data and new data len += size; @@ -499,7 +551,7 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri bol += 1; // calculate the amount of data remaining in the buffer - len = len - ( bol - buffer ); + len = size - ( bol - buffer ); // write remaining data to FILE stream if ( len > 0 ) { @@ -545,6 +597,22 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri if ( isnheader == 0 ) { header.append( buffer, ( bol - buffer ) ); } + } else { + currentTotalBytes += size; + writeTo.write( buffer, size ); + + if ( request.getProgressCallback() ) { + std::size_t length = 0; + + if ( !received.getField("content-length").empty() ) { + String::fromString( length, received.getField("content-length") ); + } + + if ( !request.getProgressCallback()( *this, request, length, currentTotalBytes ) ) { + request.mCancel = true; + break; + } + } } } } diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index c20488ec7..9a0561a20 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -14,9 +14,13 @@ void printResponseHeaders( Http::Response& response ) { EE_MAIN_FUNC int main (int argc, char * argv []) { args::ArgumentParser parser("HTTP request program example"); args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); - args::ValueFlag output(parser, "file", "Write to file instead of stdout", {'o', "output"} ); - args::Flag head(parser, "head", "Show document info", {'I',"head"} ); - args::Flag progress(parser, "progress", "Show current progress of a download", {'p',"progress"} ); + args::ValueFlag postData(parser, "data", "HTTP POST data", {'d', "data"}); + args::ValueFlagList headers(parser, "header", "Pass custom header(s) to server", {'H', "header"}); + args::Flag head(parser, "head", "Show document info", {'I',"head"}); + args::Flag insecure(parser, "insecure", "Allow insecure server connections when using SSL", {'k',"insecure"}); + args::ValueFlag output(parser, "file", "Write to file instead of stdout", {'o', "output"}); + args::Flag progress(parser, "progress", "Show current progress of a download", {'p',"progress"}); + args::ValueFlag requestMethod(parser, "request", "Specify request command to use", {'X', "request"}); args::Positional url(parser, "url", "The url to request"); try { @@ -70,12 +74,38 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { uri = URI( "http://" + url.Get() ); } + // Allow insecure connections if requested + if ( insecure ) { + request.setValidateCertificate(false); + request.setValidateHostname(false); + } + // Set the host and port from the URI http.setHost( uri.getHost(), uri.getPort() ); // Set the path and query parts for the request request.setUri( uri.getPathEtc() ); + // Set the headers + for ( const std::string& header : args::get(headers) ) { + std::string::size_type pos = header.find_first_of( ':' ); + if ( std::string::npos != pos ) { + std::string key( header.substr( 0, pos ) ); + std::string val( String::trim( header.substr( pos + 1 ) ) ); + request.setField(key, val); + } + } + + // Set the request method + if ( requestMethod ) { + request.setMethod( Http::Request::methodFromString( requestMethod.Get() ) ); + } + + // Set the post data / body + if ( postData ) { + request.setBody( postData.Get() ); + } + if ( !output ) { // Send the request Http::Response response = http.sendRequest(request); @@ -92,11 +122,11 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { std::cout << response.getBody() << std::endl; } else { - std::cout << "Error " << status << std::endl; + std::cout << "Error " << status << std::endl << response.getStatusDescription() << std::endl; } } else { if ( progress ) { - request.setProgressCallback( []( const Http& http, const Http::Request& request, size_t totalBytes, size_t currentBytes ) { + request.setProgressCallback( []( const Http&, const Http::Request&, size_t totalBytes, size_t currentBytes ) { std::cout << "\rDownloaded " << FileSystem::sizeToString( currentBytes ).c_str() << " of " << FileSystem::sizeToString( totalBytes ).c_str() << " "; std::cout << std::flush; return true; From ebc5f49b4a696c5d132b5dcabaf4f83863a9ffe9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 28 Apr 2019 03:00:59 -0300 Subject: [PATCH 04/18] HTTP improvements. --HG-- branch : dev --- include/eepp/network/http.hpp | 7 + include/eepp/network/uri.hpp | 3 + include/eepp/system/filesystem.hpp | 7 + src/eepp/network/http.cpp | 225 +++++++++++---------- src/eepp/network/uri.cpp | 11 + src/eepp/system/filesystem.cpp | 20 ++ src/examples/http_request/http_request.cpp | 52 ++++- 7 files changed, 208 insertions(+), 117 deletions(-) diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index 659ef69bb..3bcf0d061 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -127,6 +127,12 @@ class EE_API Http : NonCopyable { /** Enables/Disables follow redirects */ void setFollowRedirect( bool follow ); + /** @return The maximun number of redirects allowd if follow redirect is enabled. */ + const unsigned int& getMaxRedirects() const; + + /** Set the maximun number of redirects allowed if follow redirect is enabled. */ + void setMaxRedirects( unsigned int maxRedirects ); + /** Definition of the current progress callback * @param http The http client * @param request The http request @@ -171,6 +177,7 @@ class EE_API Http : NonCopyable { bool mFollowRedirect; ///< Follows redirect response codes mutable bool mCancel; ///< Cancel state of current request ProgressCallback mProgressCallback; ///< Progress callback + unsigned int mMaxRedirections; ///< Maximun number of redirections allowed mutable unsigned int mRedirectionCount; ///< Number of redirections followed by the request }; diff --git a/include/eepp/network/uri.hpp b/include/eepp/network/uri.hpp index 9cce38f06..33f04fac1 100644 --- a/include/eepp/network/uri.hpp +++ b/include/eepp/network/uri.hpp @@ -213,6 +213,9 @@ class EE_API URI { /** Places the single path segments (delimited by slashes) into the given vector. */ void getPathSegments(std::vector& segments); + /** @return The last path segment if any */ + std::string getLastPathSegment(); + /** URI-encodes the given string by escaping reserved and non-ASCII * characters. The encoded string is appended to encodedStr. */ static void encode(const std::string& str, const std::string& reserved, std::string& encodedStr); diff --git a/include/eepp/system/filesystem.hpp b/include/eepp/system/filesystem.hpp index 36f799d1b..5da11ecf4 100644 --- a/include/eepp/system/filesystem.hpp +++ b/include/eepp/system/filesystem.hpp @@ -110,6 +110,13 @@ class EE_API FileSystem { /** @return Returns free disk space for a given path in bytes */ static Int64 getDiskFreeSpace(const std::string& path); + + /** Creates a file name available for the directory path. + * @example For file name "file-name-" will search the first available name + * in the file system starting from file-name-1, file-name-2, and so on. + * @return The file name found, otherwise empty string if error. + */ + static std::string fileGetNumberedFileNameFromPath( std::string directoryPath, const std::string& fileName, const std::string& separator = ".", const std::string& fileExtension = "" ); }; }} diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 1b4e33820..7de52558b 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -13,6 +13,40 @@ using namespace EE::Network::SSL; namespace EE { namespace Network { +namespace { + +class IOFakeStreamString : public IOStream { + public: + ios_size read( char * data, ios_size size ) override { + return 0; + } + + ios_size write( const char * data, ios_size size ) override { + mStream.append( data, size ); + return std::move(size); + } + + ios_size seek( ios_size position ) override { + return 0; + } + + ios_size tell() override { + return getSize(); + } + + ios_size getSize() override { + return mStream.size(); + } + + bool isOpen() override { + return true; + } + + std::string mStream; +}; + +} + Http::Request::Method Http::Request::methodFromString( std::string methodString ) { String::toLowerInPlace(methodString); @@ -40,11 +74,12 @@ Http::Request::Request(const std::string& uri, Method method, const std::string& mValidateHostname( validateHostname ), mFollowRedirect( followRedirect ), mCancel( false ), + mMaxRedirections( 10 ), mRedirectionCount( 0 ) { setMethod(method); setUri(uri); - setHttpVersion(1, 0); + setHttpVersion(1, 1); setBody(body); } @@ -101,6 +136,14 @@ void Http::Request::setFollowRedirect(bool follow) { mFollowRedirect = follow; } +const unsigned int& Http::Request::getMaxRedirects() const { + return mMaxRedirections; +} + +void Http::Request::setMaxRedirects(unsigned int maxRedirects) { + mMaxRedirections = maxRedirects; +} + void Http::Request::setProgressCallback(const Http::Request::ProgressCallback& progressCallback) { mProgressCallback = progressCallback; } @@ -277,35 +320,7 @@ void Http::Response::parse(const std::string& data) { // Parse the other lines, which contain fields, one by one parseFields(in); - // Finally extract the body mBody.clear(); - - // Determine whether the transfer is chunked - if (String::toLower(getField("transfer-encoding")) != "chunked") { - // Not chunked - everything at once - std::copy(std::istreambuf_iterator(in), std::istreambuf_iterator(), std::back_inserter(mBody)); - } else { - // Chunked - have to read chunk by chunk - std::size_t length; - - // Read all chunks, identified by a chunk-size not being 0 - while (in >> std::hex >> length) { - // Drop the rest of the line (chunk-extension) - in.ignore(std::numeric_limits::max(), '\n'); - - // Copy the actual content data - std::istreambuf_iterator it(in); - std::istreambuf_iterator itEnd; - for (std::size_t i = 0; ((i < length) && (it != itEnd)); i++) - mBody.push_back(*it++); - } - - // Drop the rest of the line (chunk-extension) - in.ignore(std::numeric_limits::max(), '\n'); - - // Read all trailers (if present) - parseFields(in); - } } void Http::Response::parseFields(std::istream &in) { @@ -407,66 +422,10 @@ void Http::setHost(const std::string& host, unsigned short port, bool useSSL) { } Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { - if ( 0 == mHost.toInteger() ) { - return Response(); - } - - if ( NULL == mConnection ) { - TcpSocket * Conn = mIsSSL ? SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ) : TcpSocket::New(); - mConnection = Conn; - } - - // First make sure that the request is valid -- add missing mandatory fields - Request toSend(prepareFields(request)); - - // Prepare the response - Response received; - - // Connect the socket to the host - if (mConnection->connect(mHost, mPort, timeout) == Socket::Done) { - // Convert the request to string and send it through the connected socket - std::string requestStr = toSend.prepare(); - - if (!requestStr.empty()) { - // Send it through the socket - if (mConnection->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { - // Wait for the server's response - std::string receivedStr; - std::size_t size = 0; - char buffer[1024]; - - while (mConnection->receive(buffer, sizeof(buffer), size) == Socket::Done) { - receivedStr.append(buffer, buffer + size); - } - - // Build the Response object from the received data - received.parse(receivedStr); - } - } - - // Close the connection - mConnection->disconnect(); - } - - // If a redirection is requested, and requests follows redirections, - // send a new request to the redirection location. - if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && - request.getFollowRedirect() ) { - - request.mRedirectionCount++; - - // Only continue redirecting if less than 10 redirections were done - if ( request.mRedirectionCount < 10 ) { - std::string location( received.getField("location") ); - URI uri( location ); - Http http( uri.getHost(), uri.getPort(), uri.getScheme() == "https" ? true : false ); - Http::Request newRequest( request ); - newRequest.setUri( uri.getPathEtc() ); - return http.sendRequest( request, timeout ); - } - } - - return std::move(received); + IOFakeStreamString stream; + Response response = downloadRequest( request, stream, timeout ); + response.mBody = std::move(stream.mStream); + return std::move(response); } Http::Response Http::downloadRequest(const Http::Request& request, IOStream& writeTo, Time timeout) { @@ -494,38 +453,41 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // Send it through the socket if (mConnection->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { // Wait for the server's response - int isnheader = 0; + bool isnheader = false; std::size_t currentTotalBytes = 0; std::size_t len = 0; char * eol; // end of line char * bol; // beginning of line - std::size_t size = 0; + std::size_t readed = 0; const std::size_t bufferSize = 1024; char buffer[bufferSize+1]; std::string header; + std::string fileBuffer; + bool newFileBuffer = false; + bool chunked = false; - while (!request.isCancelled() && mConnection->receive(buffer, bufferSize, size) == Socket::Done) { - if ( isnheader == 0 ) { + while (!request.isCancelled() && mConnection->receive(buffer, bufferSize, readed) == Socket::Done) { + if ( !isnheader ) { // calculate combined length of unprocessed data and new data - len += size; + len += readed; // NULL terminate buffer for string functions buffer[len] = '\0'; // checks if the header break happened to be the first line of the buffer - if ( !( strncmp( buffer, "\r\n", 2 ) ) ) { + if ( 0 == strncmp( buffer, "\r\n", 2 ) ) { if (len > 2) { currentTotalBytes += (len-2); - writeTo.write(buffer, (len-2)); + fileBuffer.append(buffer, buffer + (len-2)); } continue; } - if ( !( strncmp( buffer, "\n", 1 ) ) ) { + if ( 0 == strncmp( buffer, "\n", 1 ) ) { if ( len > 1 ) { currentTotalBytes += (len-1); - writeTo.write(buffer, (len-1)); + fileBuffer.append(buffer, buffer + (len-1)); } continue; @@ -534,14 +496,14 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // process each line in buffer looking for header break bol = buffer; - while( ( eol = strchr( bol, '\n') ) != NULL ) { + while( !isnheader && ( eol = strchr( bol, '\n') ) != NULL ) { // update bol based upon the value of eol bol = eol + 1; // test if end of headers has been reached - if ( ( !( strncmp( bol, "\r\n", 2 ) ) ) || ( ! ( strncmp( bol, "\n", 1) ) ) ) { + if ( 0 == strncmp( bol, "\r\n", 2 ) || 0 == strncmp( bol, "\n", 1) ) { // note that end of headers has been reached - isnheader = 1; + isnheader = true; // update the value of bol to reflect the beginning of the line // immediately after the headers @@ -551,12 +513,12 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri bol += 1; // calculate the amount of data remaining in the buffer - len = size - ( bol - buffer ); + len = readed - ( bol - buffer ); // write remaining data to FILE stream if ( len > 0 ) { currentTotalBytes += len; - writeTo.write( bol, len ); + fileBuffer.append(bol, bol + len); } header.append( buffer, ( bol - buffer ) ); @@ -569,15 +531,22 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // Build the Response object from the received data received.parse(header); + // Check if the response is chunked + chunked = received.getField("transfer-encoding") == "chunked"; + + // If is not chunked just save the file buffer and clear it + if ( !chunked && !fileBuffer.empty() ) { + writeTo.write( &fileBuffer[0], fileBuffer.size() ); + fileBuffer.clear(); + } + // If a redirection is requested, and requests follows redirections, // send a new request to the redirection location. if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && request.getFollowRedirect() ) { - request.mRedirectionCount++; - // Only continue redirecting if less than 10 redirections were done - if ( request.mRedirectionCount < 10 ) { + if ( request.mRedirectionCount < request.getMaxRedirects() ) { std::string location( received.getField("location") ); URI uri( location ); Http http( uri.getHost(), uri.getPort(), uri.getScheme() == "https" ? true : false ); @@ -587,6 +556,8 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // Close the connection mConnection->disconnect(); + request.mRedirectionCount++; + return http.downloadRequest( request, writeTo, timeout ); } } @@ -594,12 +565,48 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } } - if ( isnheader == 0 ) { + if ( !isnheader ) { header.append( buffer, ( bol - buffer ) ); } } else { - currentTotalBytes += size; - writeTo.write( buffer, size ); + currentTotalBytes += readed; + + if ( chunked ) { + fileBuffer.append( buffer, buffer + readed ); + + if ( newFileBuffer ) { + if ( fileBuffer.substr( 0, 2 ) == "\r\n" ) { + fileBuffer = fileBuffer.substr( 2 ); + } + + newFileBuffer = false; + } + + std::string::size_type lenEnd = fileBuffer.find_first_of("\r\n"); + + if ( lenEnd != std::string::npos ) { + std::string::size_type firstCharPos = lenEnd + 2; + unsigned long length; + bool res = String::fromString( length, fileBuffer.substr(0, lenEnd), std::hex ); + + if ( res && length ) { + if ( fileBuffer.size() - firstCharPos >= length ) { + writeTo.write( &fileBuffer[firstCharPos], length ); + fileBuffer = fileBuffer.substr( firstCharPos + length ); + newFileBuffer = true; + + // Check if already have the \r\n of the next length in the buffer + if ( !fileBuffer.empty() && fileBuffer.substr( 0, 2 ) == "\r\n" ) { + // Remove it to be able to read the next length + fileBuffer = fileBuffer.substr( 2 ); + newFileBuffer = false; + } + } + } + } + } else { + writeTo.write( buffer, readed ); + } if ( request.getProgressCallback() ) { std::size_t length = 0; diff --git a/src/eepp/network/uri.cpp b/src/eepp/network/uri.cpp index f3c41c154..c1f734827 100644 --- a/src/eepp/network/uri.cpp +++ b/src/eepp/network/uri.cpp @@ -411,6 +411,17 @@ void URI::getPathSegments(std::vector& segments) { getPathSegments(mPath, segments); } +std::string URI::getLastPathSegment() { + std::vector segments; + getPathSegments( segments ); + + if ( !segments.empty() ) { + return segments[ segments.size() - 1 ]; + } + + return ""; +} + void URI::getPathSegments(const std::string& path, std::vector& segments) { std::string::const_iterator it = path.begin(); std::string::const_iterator end = path.end(); diff --git a/src/eepp/system/filesystem.cpp b/src/eepp/system/filesystem.cpp index 348207d74..142d873e0 100644 --- a/src/eepp/system/filesystem.cpp +++ b/src/eepp/system/filesystem.cpp @@ -580,4 +580,24 @@ Int64 FileSystem::getDiskFreeSpace(const std::string& path) { #endif } +std::string FileSystem::fileGetNumberedFileNameFromPath(std::string directoryPath, const std::string& fileName, const std::string& separator, const std::string& fileExtension) { + Uint32 fileNum = 1; + std::string fileNumName; + + if ( FileSystem::isDirectory( directoryPath ) ) { + dirPathAddSlashAtEnd( directoryPath ); + + while ( fileNum < 10000 ) { + fileNumName = String::format( std::string( "%s" + separator + "%d%s" ).c_str(), fileName.c_str(), fileNum, fileExtension.empty() ? "" : std::string( "." + fileExtension ).c_str() ); + + if ( !FileSystem::fileExists( directoryPath + fileNumName ) ) + return fileNumName; + + fileNum++; + } + } + + return ""; +} + }} diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index 9a0561a20..4ff57477b 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -1,6 +1,7 @@ #include #include +// Prints the response headers void printResponseHeaders( Http::Response& response ) { Http::Response::FieldTable headers = response.getHeaders(); @@ -18,6 +19,8 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { args::ValueFlagList headers(parser, "header", "Pass custom header(s) to server", {'H', "header"}); args::Flag head(parser, "head", "Show document info", {'I',"head"}); args::Flag insecure(parser, "insecure", "Allow insecure server connections when using SSL", {'k',"insecure"}); + args::Flag location(parser, "location", "Follow redirects", {'L',"location"}); + args::ValueFlag maxRedirs(parser, "max-redirs", "Maximum number of redirects allowed", {"max-redirs"}); args::ValueFlag output(parser, "file", "Write to file instead of stdout", {'o', "output"}); args::Flag progress(parser, "progress", "Show current progress of a download", {'p',"progress"}); args::ValueFlag requestMethod(parser, "request", "Specify request command to use", {'X', "request"}); @@ -67,9 +70,10 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { } }, asyncRequest, Seconds( 5 ) ); } else { - // If the user provided the URI, creates an instance of URI to parse it. + // If the user provided the URL, creates an instance of URI to parse it. URI uri( url.Get() ); + // If no scheme provided asume HTTP if ( uri.getScheme().empty() ) { uri = URI( "http://" + url.Get() ); } @@ -106,6 +110,23 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { request.setBody( postData.Get() ); } + // If progress requested print a progress on screen + if ( progress ) { + request.setProgressCallback( []( const Http&, const Http::Request&, size_t totalBytes, size_t currentBytes ) { + std::cout << "\rDownloaded " << FileSystem::sizeToString( currentBytes ).c_str() << " of " << FileSystem::sizeToString( totalBytes ).c_str() << " "; + std::cout << std::flush; + return true; + }); + } + + // Set follow redirect + request.setFollowRedirect(location.Get()); + + // Set the maximun number of redirects + if ( maxRedirs ) { + request.setMaxRedirects(maxRedirs.Get()); + } + if ( !output ) { // Send the request Http::Response response = http.sendRequest(request); @@ -125,15 +146,30 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { std::cout << "Error " << status << std::endl << response.getStatusDescription() << std::endl; } } else { - if ( progress ) { - request.setProgressCallback( []( const Http&, const Http::Request&, size_t totalBytes, size_t currentBytes ) { - std::cout << "\rDownloaded " << FileSystem::sizeToString( currentBytes ).c_str() << " of " << FileSystem::sizeToString( totalBytes ).c_str() << " "; - std::cout << std::flush; - return true; - }); + std::string path( output.Get() ); + + // If output path is a directory guess a file name + if ( FileSystem::isDirectory( path ) ) { + std::string lastPathSegment = uri.getLastPathSegment(); + + // If there's a path end segment + if ( !lastPathSegment.empty() ) { + FileSystem::dirPathAddSlashAtEnd( path ); + + // Save with the path end segment name + if ( !FileSystem::fileExists( path + lastPathSegment ) ) { + path += lastPathSegment; + } else { + path += FileSystem::fileGetNumberedFileNameFromPath( path, lastPathSegment ); + } + } else { + // Create a file name if no name found + path += FileSystem::fileGetNumberedFileNameFromPath( path, "eepp-network-file", "-" ); + } } - Http::Response response = http.downloadRequest(request, output.Get(), Seconds(5)); + // Download the request response into a file + Http::Response response = http.downloadRequest(request, path, Seconds(5)); if ( head ) printResponseHeaders(response); From 9e20da4bffd447a406a8f6c628e61d7d0bb91e75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 28 Apr 2019 16:57:35 -0300 Subject: [PATCH 05/18] HTTP Proxy without tunneling. --HG-- branch : dev-proxy --- include/eepp/network/http.hpp | 29 +++++- src/eepp/network/http.cpp | 107 +++++++++++++++++++-- src/examples/http_request/http_request.cpp | 31 +++--- 3 files changed, 147 insertions(+), 20 deletions(-) diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index 3bcf0d061..9bf2f74b8 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -37,7 +38,8 @@ class EE_API Http : NonCopyable { Put, ///< The PUT method replaces all current representations of the target resource with the request payload. Delete, ///< The DELETE method deletes the specified resource. Options, ///< The OPTIONS method is used to describe the communication options for the target resource. - Patch ///< The PATCH method is used to apply partial modifications to a resource. + Patch, ///< The PATCH method is used to apply partial modifications to a resource. + Connect ///< The CONNECT method starts two-way communications with the requested resource. It can be used to open a tunnel. }; /** @return Method from a method name string. */ @@ -133,6 +135,15 @@ class EE_API Http : NonCopyable { /** Set the maximun number of redirects allowed if follow redirect is enabled. */ void setMaxRedirects( unsigned int maxRedirects ); + /** Sets the request proxy */ + void setProxy( const URI& uri ); + + /** @return The request proxy */ + const URI& getProxy() const; + + /** @return Is a proxy is need to be used */ + bool isProxied() const; + /** Definition of the current progress callback * @param http The http client * @param request The http request @@ -160,7 +171,7 @@ class EE_API Http : NonCopyable { ** This is used internally by Http before sending the ** request to the web server. ** @return String containing the request, ready to be sent */ - std::string prepare() const; + std::string prepare(const Http& http) const; // Types typedef std::map FieldTable; @@ -179,6 +190,7 @@ class EE_API Http : NonCopyable { ProgressCallback mProgressCallback; ///< Progress callback unsigned int mMaxRedirections; ///< Maximun number of redirections allowed mutable unsigned int mRedirectionCount; ///< Number of redirections followed by the request + URI mProxy; ///< Proxy information }; /** @brief Define a HTTP response */ @@ -224,6 +236,9 @@ class EE_API Http : NonCopyable { ConnectionFailed = 1001 ///< Connection with server failed }; + /** @return The status string */ + static const char * statusToString( const Status& status ); + /** @brief Default constructor ** Constructs an empty response. */ Response(); @@ -388,6 +403,12 @@ class EE_API Http : NonCopyable { /** @return The host port */ const unsigned short& getPort() const; + + /** @return If the HTTP client uses SSL/TLS */ + const bool& isSSL() const; + + /** @return The URI from the schema + hostname + port */ + URI getURI() const; private: class AsyncRequest : public Thread { public: @@ -411,6 +432,7 @@ class EE_API Http : NonCopyable { bool mStreamOwned; IOStream * mStream; }; + friend class AsyncRequest; ThreadLocalPtr mConnection; ///< Connection to the host IpAddress mHost; ///< Web host address @@ -419,6 +441,9 @@ class EE_API Http : NonCopyable { std::list mThreads; Mutex mThreadsMutex; bool mIsSSL; + URI mProxy; + + Http(const std::string& host, unsigned short port, bool useSSL, URI proxy); void removeOldThreads(); diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 7de52558b..40d5277fd 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -64,6 +64,8 @@ Http::Request::Method Http::Request::methodFromString( std::string methodString return Method::Options; } else if ( "patch" == methodString ) { return Method::Patch; + } else if ( "connect" == methodString ) { + return Method::Connect; } else { return Method::Get; } @@ -144,6 +146,18 @@ void Http::Request::setMaxRedirects(unsigned int maxRedirects) { mMaxRedirections = maxRedirects; } +void Http::Request::setProxy(const URI& uri) { + mProxy = uri; +} + +const URI& Http::Request::getProxy() const { + return mProxy; +} + +bool Http::Request::isProxied() const { + return !mProxy.empty(); +} + void Http::Request::setProgressCallback(const Http::Request::ProgressCallback& progressCallback) { mProgressCallback = progressCallback; } @@ -160,7 +174,7 @@ const bool &Http::Request::isCancelled() const { return mCancel; } -std::string Http::Request::prepare() const { +std::string Http::Request::prepare(const Http& http) const { std::ostringstream out; // Convert the method to its string representation @@ -174,10 +188,18 @@ std::string Http::Request::prepare() const { case Delete: method = "DELETE"; break; case Options: method = "OPTIONS"; break; case Patch: method = "PATCH"; break; + case Connect: method = "CONNECT"; break; } // Write the first line containing the request type - out << method << " " << mUri << " "; + if ( mProxy.empty() ) { + out << method << " " << mUri << " "; + } else { + URI uri = http.getURI(); + uri.setPathEtc( mUri ); + out << method << " " << uri.toString() << " "; + } + out << "HTTP/" << mMajorVersion << "." << mMinorVersion << "\r\n"; // Write fields @@ -208,6 +230,44 @@ const std::string& Http::Request::getField(const std::string& field) const { } } +const char * Http::Response::statusToString( const Http::Response::Status& status ) { + switch ( status ) { + // 2xx: success + case Ok: return "OK"; + case Created: return "Created"; + case Accepted: return "Accepted"; + case NoContent: return "No Content"; + case ResetContent: return "Reset Content"; + case PartialContent: return "Partial Content"; + + // 3xx: redirection + case MultipleChoices: return "Multiple Choices"; + case MovedPermanently: return "Moved Permanently"; + case MovedTemporarily: return "Moved Temporarily"; + case NotModified: return "Not Modified"; + + // 4xx: client error + case BadRequest: return "BadRequest"; + case Unauthorized: return "Unauthorized"; + case Forbidden: return "Forbidden"; + case NotFound: return "Not Found"; + case RangeNotSatisfiable: return "Range Not Satisfiable"; + + // 5xx: server error + case InternalServerError: return "Internal Server Error"; + case NotImplemented: return "Not Implemented"; + case BadGateway: return "Bad Gateway"; + case ServiceNotAvailable: return "Service Not Available"; + case GatewayTimeout: return "Gateway Timeout"; + case VersionNotSupported: return "Version Not Supported"; + + // 10xx: Custom codes + case InvalidResponse: return "Invalid Response"; + case ConnectionFailed: return "Connection Failed"; + default: return ""; + } +} + Http::Response::Response() : mStatus (ConnectionFailed), mMajorVersion(0), @@ -358,6 +418,15 @@ Http::Http(const std::string& host, unsigned short port, bool useSSL) : setHost(host, port, useSSL); } +Http::Http(const std::string & host, unsigned short port, bool useSSL, URI proxy) : + mConnection( NULL ), + mHostName(host), + mPort(port), + mProxy(proxy) +{ + setHost(host, port, useSSL); +} + Http::~Http() { std::list::iterator itt; @@ -409,7 +478,12 @@ void Http::setHost(const std::string& host, unsigned short port, bool useSSL) { if (!mHostName.empty() && (*mHostName.rbegin() == '/')) mHostName.erase(mHostName.size() - 1); - mHost = IpAddress(mHostName); + if ( !mProxy.empty() ) { + mHost = IpAddress(mProxy.getHost()); + sameHost = false; + } else { + mHost = IpAddress(mHostName); + } // If the new host is different to the last set host // and there's an open connection to the host, we close @@ -429,12 +503,19 @@ Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { } Http::Response Http::downloadRequest(const Http::Request& request, IOStream& writeTo, Time timeout) { + if ( mProxy.empty() && request.isProxied() ) { + Http http( mHostName, mPort, mIsSSL, request.getProxy() ); + return http.downloadRequest( request, writeTo, timeout ); + } + if ( 0 == mHost.toInteger() ) { return Response(); } if ( NULL == mConnection ) { - TcpSocket * Conn = mIsSSL ? SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ) : TcpSocket::New(); + TcpSocket * Conn = ( mProxy.empty() ? mIsSSL : ( SSLSocket::isSupported() && mProxy.getScheme() == "https" ) ) ? + SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ) : + TcpSocket::New(); mConnection = Conn; } @@ -445,9 +526,9 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri Response received; // Connect the socket to the host - if (mConnection->connect(mHost, mPort, timeout) == Socket::Done) { + if (mConnection->connect(mHost, mProxy.empty() ? mPort : mProxy.getPort(), timeout) == Socket::Done) { // Convert the request to string and send it through the connected socket - std::string requestStr = toSend.prepare(); + std::string requestStr = toSend.prepare(*this); if (!requestStr.empty()) { // Send it through the socket @@ -743,6 +824,12 @@ Http::Request Http::prepareFields(const Http::Request& request) { toSend.setField("Connection", "close"); } + if (!mProxy.empty()) { + toSend.setField("Accept", "*/*"); + + toSend.setField("Proxy-connection", "close"); + } + return std::move(toSend); } @@ -797,4 +884,12 @@ const unsigned short& Http::getPort() const { return mPort; } +const bool& Http::isSSL() const { + return mIsSSL; +} + +URI Http::getURI() const { + return URI( String::format( "%s://%s:%d", mIsSSL ? "https" : "http", mHostName.c_str(), mPort ) ); +} + }} diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index 4ff57477b..6b5c5fe25 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -5,11 +5,13 @@ void printResponseHeaders( Http::Response& response ) { Http::Response::FieldTable headers = response.getHeaders(); - std::cout << "\r\nHeaders: " << std::endl; + std::cout << "HTTP/" << response.getMajorHttpVersion() << "." << response.getMinorHttpVersion() << " " << response.getStatus() << " " << Http::Response::statusToString( response.getStatus() ) << std::endl; for ( auto&& head : headers ) { - std::cout << "\t" << head.first << ": " << head.second << std::endl; + std::cout << head.first << ": " << head.second << std::endl; } + + std::cout << std::endl; } EE_MAIN_FUNC int main (int argc, char * argv []) { @@ -17,14 +19,16 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); args::ValueFlag postData(parser, "data", "HTTP POST data", {'d', "data"}); args::ValueFlagList headers(parser, "header", "Pass custom header(s) to server", {'H', "header"}); - args::Flag head(parser, "head", "Show document info", {'I',"head"}); + args::Flag includeHead(parser, "include", "Include protocol response headers in the output", {'i',"include"}); args::Flag insecure(parser, "insecure", "Allow insecure server connections when using SSL", {'k',"insecure"}); args::Flag location(parser, "location", "Follow redirects", {'L',"location"}); args::ValueFlag maxRedirs(parser, "max-redirs", "Maximum number of redirects allowed", {"max-redirs"}); args::ValueFlag output(parser, "file", "Write to file instead of stdout", {'o', "output"}); + args::ValueFlag proxy(parser, "proxy", "[protocol://]host[:port] Use this proxy", {'x', "proxy"}); args::Flag progress(parser, "progress", "Show current progress of a download", {'p',"progress"}); + args::Flag verbose(parser, "verbose", "Make the operation more talkative", {'v',"verbose"}); args::ValueFlag requestMethod(parser, "request", "Specify request command to use", {'X', "request"}); - args::Positional url(parser, "url", "The url to request"); + args::Positional url(parser, "URL", "The URL to request"); try { parser.ParseCLI(argc, argv); @@ -127,6 +131,11 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { request.setMaxRedirects(maxRedirs.Get()); } + // Set the proxy for the request + if ( proxy ) { + request.setProxy( URI( proxy.Get() ) ); + } + if ( !output ) { // Send the request Http::Response response = http.sendRequest(request); @@ -134,16 +143,14 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { // Check the status code and display the result Http::Response::Status status = response.getStatus(); + if ( includeHead ) + printResponseHeaders(response); + if ( status == Http::Response::Ok ) { - if ( head ) { - printResponseHeaders(response); - - std::cout << std::endl << "Body: " << std::endl; - } - std::cout << response.getBody() << std::endl; } else { std::cout << "Error " << status << std::endl << response.getStatusDescription() << std::endl; + std::cout << response.getBody() << std::endl; } } else { std::string path( output.Get() ); @@ -171,13 +178,13 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { // Download the request response into a file Http::Response response = http.downloadRequest(request, path, Seconds(5)); - if ( head ) + if ( includeHead ) printResponseHeaders(response); } } } - if ( head ) + if ( verbose ) MemoryManager::showResults(); return EXIT_SUCCESS; From f664f89d343518bd7fe0900012f3cc87322bf1ed Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 28 Apr 2019 19:25:56 -0300 Subject: [PATCH 06/18] HTTP Proxy Tunneling support. --HG-- branch : dev-proxy --- include/eepp/network/http.hpp | 80 ++++- include/eepp/network/ssl/sslsocket.hpp | 15 +- src/eepp/network/http.cpp | 295 ++++++++++++++---- .../ssl/backend/mbedtls/mbedtlssocket.cpp | 4 +- src/eepp/network/ssl/sslsocket.cpp | 29 +- src/examples/http_request/http_request.cpp | 2 +- 6 files changed, 338 insertions(+), 87 deletions(-) diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index 9bf2f74b8..fa81aacbb 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -45,6 +45,9 @@ class EE_API Http : NonCopyable { /** @return Method from a method name string. */ static Method methodFromString( std::string methodString ); + /** @return The method string from a method */ + static std::string methodToString( const Method& method ); + /** @brief Default constructor ** This constructor creates a GET request, with the root ** URI ("/") and an empty body. @@ -135,15 +138,6 @@ class EE_API Http : NonCopyable { /** Set the maximun number of redirects allowed if follow redirect is enabled. */ void setMaxRedirects( unsigned int maxRedirects ); - /** Sets the request proxy */ - void setProxy( const URI& uri ); - - /** @return The request proxy */ - const URI& getProxy() const; - - /** @return Is a proxy is need to be used */ - bool isProxied() const; - /** Definition of the current progress callback * @param http The http client * @param request The http request @@ -164,7 +158,8 @@ class EE_API Http : NonCopyable { /** @return True if the current request was cancelled */ const bool& isCancelled() const; - private: + + private: friend class Http; /** @brief Prepare the final request to send to the server @@ -173,6 +168,9 @@ class EE_API Http : NonCopyable { ** @return String containing the request, ready to be sent */ std::string prepare(const Http& http) const; + /** Prepares a http tunnel request */ + std::string prepareTunnel(const Http& http); + // Types typedef std::map FieldTable; @@ -317,8 +315,10 @@ class EE_API Http : NonCopyable { ** than the standard one, or use an unknown protocol. ** @param host Web server to connect to ** @param port Port to use for connection - ** @param useSSL force the SSL usage ( if compiled with the support of it ). If the host starts with https:// it will use it by default. */ - Http(const std::string& host, unsigned short port = 0, bool useSSL = false); + ** @param useSSL force the SSL usage ( if compiled with the support of it ). If the host starts with https:// it will use it by default. + ** @param proxy Set an http proxy for the host connection + */ + Http(const std::string& host, unsigned short port = 0, bool useSSL = false, URI proxy = URI()); ~Http(); @@ -332,8 +332,10 @@ class EE_API Http : NonCopyable { ** than the standard one, or use an unknown protocol. ** @param host Web server to connect to ** @param port Port to use for connection - ** @param useSSL force the SSL usage ( if compiled with the support of it ). If the host starts with https:// it will use it by default. */ - void setHost(const std::string& host, unsigned short port = 0, bool useSSL = false); + ** @param useSSL force the SSL usage ( if compiled with the support of it ). If the host starts with https:// it will use it by default. + ** @param proxy Set an http proxy for the host connection + */ + void setHost(const std::string& host, unsigned short port = 0, bool useSSL = false, URI proxy = URI()); /** @brief Send a HTTP request and return the server's response. ** You must have a valid host before sending a request (see setHost). @@ -409,6 +411,15 @@ class EE_API Http : NonCopyable { /** @return The URI from the schema + hostname + port */ URI getURI() const; + + /** Sets the request proxy */ + void setProxy( const URI& uri ); + + /** @return The request proxy */ + const URI& getProxy() const; + + /** @return Is a proxy is need to be used */ + bool isProxied() const; private: class AsyncRequest : public Thread { public: @@ -433,8 +444,45 @@ class EE_API Http : NonCopyable { IOStream * mStream; }; + class HttpConnection { + public: + HttpConnection(); + + HttpConnection( TcpSocket * socket ); + + ~HttpConnection(); + + void setSocket( TcpSocket * socket ); + + TcpSocket * getSocket() const; + + void disconnect(); + + const bool& isConnected() const; + + void setConnected( const bool& connected ); + + const bool& isTunneled() const; + + void setTunneled( const bool& tunneled ); + + const bool& isSSL() const; + + void setSSL( const bool& ssl ); + + const bool& isKeepAlive() const; + + void setKeepAlive( const bool& isKeepAlive ); + protected: + TcpSocket * mSocket; + bool mIsConnected; + bool mIsTunneled; + bool mIsSSL; + bool mIsKeepAlive; + }; + friend class AsyncRequest; - ThreadLocalPtr mConnection; ///< Connection to the host + ThreadLocalPtr mConnection; ///< Connection to the host IpAddress mHost; ///< Web host address std::string mHostName; ///< Web host name unsigned short mPort; ///< Port used for connection with host @@ -443,8 +491,6 @@ class EE_API Http : NonCopyable { bool mIsSSL; URI mProxy; - Http(const std::string& host, unsigned short port, bool useSSL, URI proxy); - void removeOldThreads(); Request prepareFields(const Http::Request& request); diff --git a/include/eepp/network/ssl/sslsocket.hpp b/include/eepp/network/ssl/sslsocket.hpp index 1161d65eb..0f1829a52 100644 --- a/include/eepp/network/ssl/sslsocket.hpp +++ b/include/eepp/network/ssl/sslsocket.hpp @@ -36,15 +36,22 @@ class EE_API SSLSocket : public TcpSocket { Status receive(Packet& packet); + Status sslConnect(const IpAddress& remoteAddress, unsigned short remotePort, Time timeout = Time::Zero); + + void sslDisconnect(); + + Status tcpConnect(const IpAddress& remoteAddress, unsigned short remotePort, Time timeout = Time::Zero); + + void tcpDisconnect(); + + Status tcpReceive(void* data, std::size_t size, std::size_t& received); + + Status tcpSend(const void* data, std::size_t size, std::size_t& sent); protected: friend class SSLSocketImpl; friend class OpenSSLSocket; friend class MbedTLSSocket; - Status tcp_receive(void* data, std::size_t size, std::size_t& received); - - Status tcp_send(const void* data, std::size_t size, std::size_t& sent); - SSLSocketImpl * mImpl; std::string mHostName; bool mValidateCertificate; diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 40d5277fd..abe746e36 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -71,6 +71,20 @@ Http::Request::Method Http::Request::methodFromString( std::string methodString } } +std::string Http::Request::methodToString(const Http::Request::Method& method) { + switch (method) { + default : + case Get: return "GET"; + case Head: return "HEAD"; + case Post: return "POST"; + case Put: return "PUT"; + case Delete: return "DELETE"; + case Options: return "OPTIONS"; + case Patch: return "PATCH"; + case Connect: return "CONNECT"; + } +} + Http::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname , bool followRedirect) : mValidateCertificate( validateCertificate ), mValidateHostname( validateHostname ), @@ -146,18 +160,6 @@ void Http::Request::setMaxRedirects(unsigned int maxRedirects) { mMaxRedirections = maxRedirects; } -void Http::Request::setProxy(const URI& uri) { - mProxy = uri; -} - -const URI& Http::Request::getProxy() const { - return mProxy; -} - -bool Http::Request::isProxied() const { - return !mProxy.empty(); -} - void Http::Request::setProgressCallback(const Http::Request::ProgressCallback& progressCallback) { mProgressCallback = progressCallback; } @@ -174,25 +176,36 @@ const bool &Http::Request::isCancelled() const { return mCancel; } +std::string Http::Request::prepareTunnel(const Http& http) { + std::ostringstream out; + + setMethod( Connect ); + + std::string method = methodToString( mMethod ); + + out << method << " " << http.getHostName() << ":" << http.getPort() << " "; + out << "HTTP/" << mMajorVersion << "." << mMinorVersion << "\r\n"; + + setField( "Host", String::format( "%s:%d", http.getHostName().c_str(), http.getPort() ) ); + setField( "Proxy-Connection", "Keep-Alive" ); + setField( "User-Agent", "eepp-network" ); + + for (FieldTable::const_iterator i = mFields.begin(); i != mFields.end(); ++i) + out << i->first << ": " << i->second << "\r\n"; + + out << "\r\n"; + + return out.str(); +} + std::string Http::Request::prepare(const Http& http) const { std::ostringstream out; // Convert the method to its string representation - std::string method; - switch (mMethod) { - default : - case Get: method = "GET"; break; - case Head: method = "HEAD"; break; - case Post: method = "POST"; break; - case Put: method = "PUT"; break; - case Delete: method = "DELETE"; break; - case Options: method = "OPTIONS"; break; - case Patch: method = "PATCH"; break; - case Connect: method = "CONNECT"; break; - } + std::string method = methodToString( mMethod ); // Write the first line containing the request type - if ( mProxy.empty() ) { + if ( http.getProxy().empty() ) { out << method << " " << mUri << " "; } else { URI uri = http.getURI(); @@ -411,20 +424,14 @@ Http::Http() : { } -Http::Http(const std::string& host, unsigned short port, bool useSSL) : - mConnection( NULL ), - mIsSSL( false ) -{ - setHost(host, port, useSSL); -} - Http::Http(const std::string & host, unsigned short port, bool useSSL, URI proxy) : mConnection( NULL ), mHostName(host), mPort(port), + mIsSSL( useSSL ), mProxy(proxy) { - setHost(host, port, useSSL); + setHost(host, port, useSSL, proxy); } Http::~Http() { @@ -440,12 +447,14 @@ Http::~Http() { } // Then we destroy the last open connection - TcpSocket * tcp = mConnection; + HttpConnection * connection = mConnection; - eeSAFE_DELETE( tcp ); + eeSAFE_DELETE( connection ); } -void Http::setHost(const std::string& host, unsigned short port, bool useSSL) { +void Http::setHost(const std::string& host, unsigned short port, bool useSSL, URI proxy) { + mProxy = proxy; + bool sameHost( host == mHostName && port == mPort && useSSL == mIsSSL ); // Check the protocol @@ -489,8 +498,8 @@ void Http::setHost(const std::string& host, unsigned short port, bool useSSL) { // and there's an open connection to the host, we close // the old connection to prepare a new one. if ( !sameHost && NULL != mConnection ) { - TcpSocket * tcp = mConnection; - eeSAFE_DELETE( tcp ); + HttpConnection * connection = mConnection; + eeSAFE_DELETE( connection ); mConnection = NULL; } } @@ -503,20 +512,32 @@ Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { } Http::Response Http::downloadRequest(const Http::Request& request, IOStream& writeTo, Time timeout) { - if ( mProxy.empty() && request.isProxied() ) { - Http http( mHostName, mPort, mIsSSL, request.getProxy() ); - return http.downloadRequest( request, writeTo, timeout ); - } - if ( 0 == mHost.toInteger() ) { return Response(); } if ( NULL == mConnection ) { - TcpSocket * Conn = ( mProxy.empty() ? mIsSSL : ( SSLSocket::isSupported() && mProxy.getScheme() == "https" ) ) ? - SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ) : - TcpSocket::New(); - mConnection = Conn; + HttpConnection * connection = eeNew( HttpConnection, () ); + TcpSocket * socket = NULL; + + // If the http client is proxied and the end host use SSL + // We need to create an HTTP Tunnel against the proxy server + if ( isProxied() && mIsSSL && SSLSocket::isSupported() ) { + socket = SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ); + + connection->setSSL( true ); + } else { + bool isSSL = !isProxied() ? mIsSSL : ( SSLSocket::isSupported() && mProxy.getScheme() == "https" ); + + socket = isSSL ? SSLSocket::New( mHostName, request.getValidateCertificate(), request.getValidateHostname() ) : + TcpSocket::New(); + + connection->setSSL( isSSL ); + } + + connection->setSocket( socket ); + + mConnection = connection; } // First make sure that the request is valid -- add missing mandatory fields @@ -525,14 +546,77 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // Prepare the response Response received; + // If not connected, try to connect to the server + if ( !mConnection->isConnected() ) { + // We need to create an HTTP Tunnel? + if ( isProxied() && mIsSSL && SSLSocket::isSupported() ) { + SSLSocket * sslSocket = reinterpret_cast( mConnection->getSocket() ); + + // For an HTTP Tunnel first we need to connect to the proxy server ( without TLS ) + if (sslSocket->tcpConnect( mHost, mProxy.getPort(), timeout ) != Socket::Done) { + return std::move(received); + } else { + mConnection->setConnected(true); + } + } else { + if (mConnection->getSocket()->connect(mHost, mProxy.empty() ? mPort : mProxy.getPort(), timeout) != Socket::Done) { + return std::move(received); + } else { + mConnection->setConnected(true); + } + } + } + // Connect the socket to the host - if (mConnection->connect(mHost, mProxy.empty() ? mPort : mProxy.getPort(), timeout) == Socket::Done) { + if (mConnection->isConnected()) { + // Create a HTTP Tunnel for SSL connections if not ready + if ( isProxied() && mIsSSL && !mConnection->isTunneled() ) { + // Create the HTTP Tunnel request + Request tunnelRequest; + std::string tunnelStr = tunnelRequest.prepareTunnel(*this); + + SSLSocket * sslSocket = reinterpret_cast( mConnection->getSocket() ); + std::size_t sent; + + // Send the request + if (sslSocket->tcpSend(tunnelStr.c_str(), tunnelStr.size(), sent) == Socket::Done) { + const std::size_t bufferSize = 1024; + char buffer[bufferSize+1]; + std::size_t readed = 0; + + // Get the proxy server response + if (sslSocket->tcpReceive(buffer, bufferSize, readed) == Socket::Done) { + // Parse the HTTP Tunnel request response + Response tunnelResponse; + std::string header; + header.append( buffer, readed ); + tunnelResponse.parse(header); + + if ( tunnelResponse.getStatus() == Response::Ok ) { + // Stablish the SSL connection if the response is positive + if (sslSocket->sslConnect( mHost, mProxy.getPort(), timeout ) != Socket::Done) { + return std::move(received); + } + } else { + return std::move(tunnelResponse); + } + } else { + return std::move(received); + } + + mConnection->setTunneled(true); + mConnection->setKeepAlive(true); + } + } + // Convert the request to string and send it through the connected socket std::string requestStr = toSend.prepare(*this); if (!requestStr.empty()) { + Socket::Status status; + // Send it through the socket - if (mConnection->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { + if (mConnection->getSocket()->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { // Wait for the server's response bool isnheader = false; std::size_t currentTotalBytes = 0; @@ -547,7 +631,7 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri bool newFileBuffer = false; bool chunked = false; - while (!request.isCancelled() && mConnection->receive(buffer, bufferSize, readed) == Socket::Done) { + while (!request.isCancelled() && ( status = mConnection->getSocket()->receive(buffer, bufferSize, readed) ) == Socket::Done) { if ( !isnheader ) { // calculate combined length of unprocessed data and new data len += readed; @@ -621,6 +705,11 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri fileBuffer.clear(); } + if ( received.getField("connection") == "closed" ) { + mConnection->setConnected(false); + mConnection->setTunneled(false); + } + // If a redirection is requested, and requests follows redirections, // send a new request to the redirection location. if ( ( received.getStatus() == Response::MovedPermanently || received.getStatus() == Response::MovedTemporarily ) && @@ -635,7 +724,8 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri newRequest.setUri( uri.getPathEtc() ); // Close the connection - mConnection->disconnect(); + if ( !mConnection->isKeepAlive() ) + mConnection->disconnect(); request.mRedirectionCount++; @@ -703,11 +793,20 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } } } + + if ( status == Socket::Status::Disconnected ) { + mConnection->setConnected(false); + mConnection->setTunneled(false); + } + } else { + mConnection->setConnected(false); + mConnection->setTunneled(false); } } // Close the connection - mConnection->disconnect(); + if ( !mConnection->isKeepAlive() ) + mConnection->disconnect(); } return std::move(received); @@ -769,8 +868,8 @@ void Http::AsyncRequest::run() { } // The Async Request destroys the socket used to create the request - TcpSocket * tcp = mHttp->mConnection; - eeSAFE_DELETE( tcp ); + HttpConnection * connection = mHttp->mConnection; + eeSAFE_DELETE( connection ); mHttp->mConnection = NULL; mRunning = false; @@ -827,12 +926,28 @@ Http::Request Http::prepareFields(const Http::Request& request) { if (!mProxy.empty()) { toSend.setField("Accept", "*/*"); - toSend.setField("Proxy-connection", "close"); + if ( mIsSSL ) { + toSend.setField("Proxy-connection", "keep-alive"); + } else { + toSend.setField("Proxy-connection", "close"); + } } return std::move(toSend); } +void Http::setProxy(const URI& uri) { + setHost( mHostName, mPort, mIsSSL, uri ); +} + +const URI& Http::getProxy() const { + return mProxy; +} + +bool Http::isProxied() const { + return !mProxy.empty(); +} + void Http::sendAsyncRequest( AsyncResponseCallback cb, const Http::Request& request, Time timeout ) { AsyncRequest * thread = eeNew( AsyncRequest, ( this, cb, request, timeout ) ); @@ -872,11 +987,11 @@ void Http::downloadAsyncRequest(Http::AsyncResponseCallback cb, const Http::Requ mThreads.push_back( thread ); } -const IpAddress &Http::getHost() const { +const IpAddress& Http::getHost() const { return mHost; } -const std::string &Http::getHostName() const { +const std::string& Http::getHostName() const { return mHostName; } @@ -892,4 +1007,70 @@ URI Http::getURI() const { return URI( String::format( "%s://%s:%d", mIsSSL ? "https" : "http", mHostName.c_str(), mPort ) ); } +Http::HttpConnection::HttpConnection() : + mSocket(NULL), + mIsConnected(false), + mIsTunneled(false), + mIsSSL(false), + mIsKeepAlive(false) +{} + +Http::HttpConnection::HttpConnection(TcpSocket * socket) : + mSocket( socket ), + mIsConnected( false ), + mIsTunneled( false ), + mIsSSL( false ) +{} + +Http::HttpConnection::~HttpConnection() { + eeSAFE_DELETE(mSocket); +} + +void Http::HttpConnection::setSocket(TcpSocket * socket) { + mSocket = socket; +} + +TcpSocket *Http::HttpConnection::getSocket() const { + return mSocket; +} + +void Http::HttpConnection::disconnect() { + if ( NULL != mSocket ) + mSocket->disconnect(); + + mIsConnected = false; +} + +const bool &Http::HttpConnection::isConnected() const { + return mIsConnected; +} + +void Http::HttpConnection::setConnected(const bool & connected) { + mIsConnected = connected; +} + +const bool &Http::HttpConnection::isTunneled() const { + return mIsTunneled; +} + +void Http::HttpConnection::setTunneled(const bool & tunneled) { + mIsTunneled = tunneled; +} + +const bool &Http::HttpConnection::isSSL() const { + return mIsSSL; +} + +void Http::HttpConnection::setSSL(const bool & ssl) { + mIsSSL = ssl; +} + +const bool &Http::HttpConnection::isKeepAlive() const { + return mIsKeepAlive; +} + +void Http::HttpConnection::setKeepAlive(const bool & isKeepAlive) { + mIsKeepAlive = isKeepAlive; +} + }} diff --git a/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp b/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp index cf341e472..2f32240a0 100644 --- a/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp +++ b/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp @@ -72,7 +72,7 @@ int MbedTLSSocket::bio_send(void *ctx, const unsigned char *buf, size_t len) { MbedTLSSocket *sp = (MbedTLSSocket *)ctx; size_t sent; - Socket::Status err = sp->mSSLSocket->tcp_send((const void*)buf, len, sent); + Socket::Status err = sp->mSSLSocket->tcpSend((const void*)buf, len, sent); if (err != Socket::Done) { return MBEDTLS_ERR_SSL_INTERNAL_ERROR; @@ -90,7 +90,7 @@ int MbedTLSSocket::bio_recv(void *ctx, unsigned char *buf, size_t len) { MbedTLSSocket *sp = (MbedTLSSocket *)ctx; size_t got; - Socket::Status err = sp->mSSLSocket->tcp_receive(buf, len, got); + Socket::Status err = sp->mSSLSocket->tcpReceive(buf, len, got); if (err != Socket::Done) { return MBEDTLS_ERR_SSL_INTERNAL_ERROR; diff --git a/src/eepp/network/ssl/sslsocket.cpp b/src/eepp/network/ssl/sslsocket.cpp index fff6a3fb2..5967b8f39 100644 --- a/src/eepp/network/ssl/sslsocket.cpp +++ b/src/eepp/network/ssl/sslsocket.cpp @@ -134,16 +134,17 @@ SSLSocket::~SSLSocket() { Socket::Status SSLSocket::connect( const IpAddress& remoteAddress, unsigned short remotePort, Time timeout ) { Status status = Socket::Disconnected; - if ( ( status = TcpSocket::connect( remoteAddress, remotePort, timeout ) ) == Socket::Done ) { - status = mImpl->connect( remoteAddress, remotePort, timeout ); + if ( ( status = tcpConnect( remoteAddress, remotePort, timeout ) ) == Socket::Done ) { + status = sslConnect( remoteAddress, remotePort, timeout ); } return status; } void SSLSocket::disconnect() { - mImpl->disconnect(); - TcpSocket::disconnect(); + sslDisconnect(); + + tcpDisconnect(); } Socket::Status SSLSocket::send(const void* data, std::size_t size) { @@ -162,11 +163,27 @@ Socket::Status SSLSocket::receive(Packet& packet) { return TcpSocket::receive( packet ); } -Socket::Status SSLSocket::tcp_receive(void * data, std::size_t size, std::size_t & received) { +Socket::Status SSLSocket::sslConnect(const IpAddress & remoteAddress, unsigned short remotePort, Time timeout) { + return mImpl->connect( remoteAddress, remotePort, timeout ); +} + +void SSLSocket::sslDisconnect() { + mImpl->disconnect(); +} + +Socket::Status SSLSocket::tcpConnect(const IpAddress & remoteAddress, unsigned short remotePort, Time timeout) { + return TcpSocket::connect( remoteAddress, remotePort, timeout ); +} + +void SSLSocket::tcpDisconnect() { + TcpSocket::disconnect(); +} + +Socket::Status SSLSocket::tcpReceive(void * data, std::size_t size, std::size_t & received) { return TcpSocket::receive( data, size, received ); } -Socket::Status SSLSocket::tcp_send(const void * data, std::size_t size, std::size_t & sent) { +Socket::Status SSLSocket::tcpSend(const void * data, std::size_t size, std::size_t & sent) { return TcpSocket::send( data, size, sent ); } diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index 6b5c5fe25..2dec6548e 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -133,7 +133,7 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { // Set the proxy for the request if ( proxy ) { - request.setProxy( URI( proxy.Get() ) ); + http.setProxy( URI( proxy.Get() ) ); } if ( !output ) { From 73c60719a42e7c975da384aa727ebec0740f35d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 28 Apr 2019 23:46:01 -0300 Subject: [PATCH 07/18] Increased packet buffer size for HTTP requests. --HG-- branch : dev --- src/eepp/network/http.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index abe746e36..fc0a6c681 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -13,6 +13,8 @@ using namespace EE::Network::SSL; namespace EE { namespace Network { +#define PACKET_BUFFER_SIZE (16384) + namespace { class IOFakeStreamString : public IOStream { @@ -580,12 +582,11 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // Send the request if (sslSocket->tcpSend(tunnelStr.c_str(), tunnelStr.size(), sent) == Socket::Done) { - const std::size_t bufferSize = 1024; - char buffer[bufferSize+1]; + char buffer[PACKET_BUFFER_SIZE+1]; std::size_t readed = 0; // Get the proxy server response - if (sslSocket->tcpReceive(buffer, bufferSize, readed) == Socket::Done) { + if (sslSocket->tcpReceive(buffer, PACKET_BUFFER_SIZE, readed) == Socket::Done) { // Parse the HTTP Tunnel request response Response tunnelResponse; std::string header; @@ -624,14 +625,13 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri char * eol; // end of line char * bol; // beginning of line std::size_t readed = 0; - const std::size_t bufferSize = 1024; - char buffer[bufferSize+1]; + char buffer[PACKET_BUFFER_SIZE+1]; std::string header; std::string fileBuffer; bool newFileBuffer = false; bool chunked = false; - while (!request.isCancelled() && ( status = mConnection->getSocket()->receive(buffer, bufferSize, readed) ) == Socket::Done) { + while (!request.isCancelled() && ( status = mConnection->getSocket()->receive(buffer, PACKET_BUFFER_SIZE, readed) ) == Socket::Done) { if ( !isnheader ) { // calculate combined length of unprocessed data and new data len += readed; From 70dd74149176606ca127e6e3e8a37dc09e20d6c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Wed, 1 May 2019 22:00:40 -0300 Subject: [PATCH 08/18] Added Compression class with gzip and deflate support. Added IOStreamInflate and IOStreamString. Added support for compressed response in HTTP requests. --HG-- branch : dev --- include/eepp/network/http.hpp | 13 +- include/eepp/system.hpp | 3 + include/eepp/system/compression.hpp | 55 ++++ include/eepp/system/iostreaminflate.hpp | 48 ++++ include/eepp/system/iostreamstring.hpp | 44 ++++ projects/linux/ee.files | 6 + src/eepp/network/http.cpp | 291 ++++++++++++--------- src/eepp/system/compression.cpp | 167 ++++++++++++ src/eepp/system/iostreaminflate.cpp | 166 ++++++++++++ src/eepp/system/iostreamstring.cpp | 68 +++++ src/eepp/window/platform/x11/cursorx11.cpp | 8 +- src/eepp/window/platform/x11/x11impl.cpp | 58 ++-- src/eepp/window/platform/x11/x11impl.hpp | 6 +- src/examples/http_request/http_request.cpp | 13 +- 14 files changed, 783 insertions(+), 163 deletions(-) create mode 100644 include/eepp/system/compression.hpp create mode 100644 include/eepp/system/iostreaminflate.hpp create mode 100644 include/eepp/system/iostreamstring.hpp create mode 100644 src/eepp/system/compression.cpp create mode 100644 src/eepp/system/iostreaminflate.cpp create mode 100644 src/eepp/system/iostreamstring.cpp diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index fa81aacbb..a65e02f5d 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -57,8 +57,9 @@ class EE_API Http : NonCopyable { ** @param validateCertificate Enables certificate validation for https request ** @param validateHostname Enables hostname validation for https request ** @param followRedirect Allow follor redirects to the request. + ** @param compressedResponse Set if the requested response should be compressed ( if available ) */ - Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = true, bool validateHostname = true, bool followRedirect = true); + Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = true, bool validateHostname = true, bool followRedirect = true, bool compressedResponse = false); /** @brief Set the value of a field ** The field is created if it doesn't exist. The name of @@ -159,6 +160,15 @@ class EE_API Http : NonCopyable { /** @return True if the current request was cancelled */ const bool& isCancelled() const; + /** @return If requests a compressed response */ + const bool& isCompressedResponse() const; + + /** Set to request a compressed response from the server + ** The returned response will be automatically decompressed + ** by the client. + */ + void setCompressedResponse(const bool& compressedResponse); + private: friend class Http; @@ -184,6 +194,7 @@ class EE_API Http : NonCopyable { bool mValidateCertificate; ///< Validates the SSL certificate in case of an HTTPS request bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request bool mFollowRedirect; ///< Follows redirect response codes + bool mCompressedResponse; ///< Request comrpessed response mutable bool mCancel; ///< Cancel state of current request ProgressCallback mProgressCallback; ///< Progress callback unsigned int mMaxRedirections; ///< Maximun number of redirections allowed diff --git a/include/eepp/system.hpp b/include/eepp/system.hpp index 47d94a3db..bcffdedf4 100644 --- a/include/eepp/system.hpp +++ b/include/eepp/system.hpp @@ -25,6 +25,7 @@ #include #include #include +#include #include #include #include @@ -32,6 +33,8 @@ #include #include #include +#include +#include #include #endif diff --git a/include/eepp/system/compression.hpp b/include/eepp/system/compression.hpp new file mode 100644 index 000000000..3f77e9eb7 --- /dev/null +++ b/include/eepp/system/compression.hpp @@ -0,0 +1,55 @@ +#ifndef EE_SYSTEM_COMPRESSION_HPP +#define EE_SYSTEM_COMPRESSION_HPP + +#include +#include + +namespace EE { namespace System { + +class Compression { + public: + enum Mode { + MODE_DEFLATE, + MODE_GZIP + }; + + enum Status { + OK = 0, + ERRNO = -1, + STREAM_ERROR = -2, + DATA_ERROR = -3, + MEM_ERROR = -4, + BUF_ERROR = -5, + VERSION_ERROR = -6 + }; + + struct ZlibConfig { + int level= -1; + }; + + struct GzipConfig { + int level = -1; + }; + + struct Config { + Config() {} + ZlibConfig zlib; + GzipConfig gzip; + }; + + static Status compress(Uint8* dst, Uint64 dstMaxSize, const Uint8* src, Uint64 srcSize, Mode mode = MODE_DEFLATE, const Config& config = Config()); + + static Status compress(IOStream& dst, IOStream& src, Mode mode = MODE_DEFLATE, const Config& config = Config()); + + static int getMaxCompressedBufferSize(Uint64 srcSize, Mode mode = MODE_DEFLATE, const Config& config = Config()); + + static Status decompress(Uint8* dst, Uint64 dstMaxSize, const Uint8* src, Uint64 srcSize, Mode mode = MODE_DEFLATE); + + static Status decompress(IOStream& dst, IOStream& src, Mode mode = MODE_DEFLATE); + + static std::size_t getModeDefaultChunkSize( const Mode& mode ); +}; + +}} + +#endif // EE_SYSTEM_COMPRESSION_HPP diff --git a/include/eepp/system/iostreaminflate.hpp b/include/eepp/system/iostreaminflate.hpp new file mode 100644 index 000000000..3ea1a1c45 --- /dev/null +++ b/include/eepp/system/iostreaminflate.hpp @@ -0,0 +1,48 @@ +#ifndef EE_SYSTEM_IOSTREAMINFLATE_HPP +#define EE_SYSTEM_IOSTREAMINFLATE_HPP + +#include +#include +#include + +namespace EE { namespace System { + +struct LocalStreamData; + +/** @brief Implementation of a inflating stream */ +class EE_API IOStreamInflate : public IOStream { + public: + static IOStreamInflate * New( IOStream& inOutStream, Compression::Mode mode ); + + /** @brief Use a stream as a input or output buffer + ** @param inOutStream Stream where the results will ve loaded or saved. + ** It must be used only for reading or writing, can't mix both calls. + ** @param mode Compression/Decompression method used + */ + IOStreamInflate( IOStream& inOutStream, Compression::Mode mode ); + + virtual ~IOStreamInflate(); + + ios_size read( char * data, ios_size size ); + + ios_size write( const char * data, ios_size size ); + + ios_size seek( ios_size position ); + + ios_size tell(); + + ios_size getSize(); + + bool isOpen(); + + const Compression::Mode& getMode() const; + protected: + IOStream& mStream; + Compression::Mode mMode; + SafeDataPointer mBuffer; + LocalStreamData * mLocalStream; +}; + +}} + +#endif // EE_SYSTEM_IOSTREAMINFLATE_HPP diff --git a/include/eepp/system/iostreamstring.hpp b/include/eepp/system/iostreamstring.hpp new file mode 100644 index 000000000..4f2b4ad84 --- /dev/null +++ b/include/eepp/system/iostreamstring.hpp @@ -0,0 +1,44 @@ +#ifndef EE_SYSTEM_IOSTREAMSTRING_HPP +#define EE_SYSTEM_IOSTREAMSTRING_HPP + +#include +#include + +namespace EE { namespace System { + +/** @brief Implementation of a memory stream file using an std::string as a container */ +class EE_API IOStreamString : public IOStream { + public: + IOStreamString(); + + ios_size read( char * data, ios_size size ); + + ios_size write( const char * data, ios_size size ); + + ios_size write( const std::string& string ); + + ios_size seek( ios_size position ); + + ios_size tell(); + + ios_size getSize(); + + bool isOpen(); + + void clear(); + + /** @return Pointer to the current position in the stream */ + const char * getPositionPointer(); + + /** @return The pointer to the beggining of the stream */ + const char * getStreamPointer() const; + + const std::string& getStream() const; + protected: + std::string mStream; + ios_size mPos; +}; + +}} + +#endif // EE_SYSTEM_IOSTREAMSTRING_HPP diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 75889ec8b..daaec457c 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -241,6 +241,7 @@ ../../include/eepp/system/bitop.hpp ../../include/eepp/system/clock.hpp ../../include/eepp/system/color.hpp +../../include/eepp/system/compression.hpp ../../include/eepp/system/condition.hpp ../../include/eepp/system/container.hpp ../../include/eepp/system/directorypack.hpp @@ -249,8 +250,10 @@ ../../include/eepp/system/inifile.hpp ../../include/eepp/system/iostreamfile.hpp ../../include/eepp/system/iostream.hpp +../../include/eepp/system/iostreaminflate.hpp ../../include/eepp/system/iostreammemory.hpp ../../include/eepp/system/iostreampak.hpp +../../include/eepp/system/iostreamstring.hpp ../../include/eepp/system/iostreamzip.hpp ../../include/eepp/system/lock.hpp ../../include/eepp/system/log.hpp @@ -666,13 +669,16 @@ ../../src/eepp/system/base64.cpp ../../src/eepp/system/clock.cpp ../../src/eepp/system/color.cpp +../../src/eepp/system/compression.cpp ../../src/eepp/system/condition.cpp ../../src/eepp/system/directorypack.cpp ../../src/eepp/system/filesystem.cpp ../../src/eepp/system/inifile.cpp ../../src/eepp/system/iostreamfile.cpp +../../src/eepp/system/iostreaminflate.cpp ../../src/eepp/system/iostreammemory.cpp ../../src/eepp/system/iostreampak.cpp +../../src/eepp/system/iostreamstring.cpp ../../src/eepp/system/iostreamzip.cpp ../../src/eepp/system/lock.cpp ../../src/eepp/system/log.cpp diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index fc0a6c681..c4b63e65a 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -3,6 +3,9 @@ #include #include #include +#include +#include +#include #include #include #include @@ -15,62 +18,17 @@ namespace EE { namespace Network { #define PACKET_BUFFER_SIZE (16384) -namespace { - -class IOFakeStreamString : public IOStream { - public: - ios_size read( char * data, ios_size size ) override { - return 0; - } - - ios_size write( const char * data, ios_size size ) override { - mStream.append( data, size ); - return std::move(size); - } - - ios_size seek( ios_size position ) override { - return 0; - } - - ios_size tell() override { - return getSize(); - } - - ios_size getSize() override { - return mStream.size(); - } - - bool isOpen() override { - return true; - } - - std::string mStream; -}; - -} - Http::Request::Method Http::Request::methodFromString( std::string methodString ) { String::toLowerInPlace(methodString); - - if ( "get" == methodString ) { - return Method::Get; - } else if ( "head" == methodString ) { - return Method::Head; - } else if ( "post" == methodString ) { - return Method::Post; - } else if ( "put" == methodString ) { - return Method::Put; - } else if ( "delete" == methodString ) { - return Method::Delete; - } else if ( "options" == methodString ) { - return Method::Options; - } else if ( "patch" == methodString ) { - return Method::Patch; - } else if ( "connect" == methodString ) { - return Method::Connect; - } else { - return Method::Get; - } + if ( "get" == methodString ) return Method::Get; + else if ( "head" == methodString ) return Method::Head; + else if ( "post" == methodString ) return Method::Post; + else if ( "put" == methodString ) return Method::Put; + else if ( "delete" == methodString ) return Method::Delete; + else if ( "options" == methodString ) return Method::Options; + else if ( "patch" == methodString ) return Method::Patch; + else if ( "connect" == methodString ) return Method::Connect; + else return Method::Get; } std::string Http::Request::methodToString(const Http::Request::Method& method) { @@ -87,10 +45,11 @@ std::string Http::Request::methodToString(const Http::Request::Method& method) { } } -Http::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname , bool followRedirect) : +Http::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname , bool followRedirect, bool compressedResponse) : mValidateCertificate( validateCertificate ), mValidateHostname( validateHostname ), mFollowRedirect( followRedirect ), + mCompressedResponse( compressedResponse ), mCancel( false ), mMaxRedirections( 10 ), mRedirectionCount( 0 ) @@ -200,6 +159,14 @@ std::string Http::Request::prepareTunnel(const Http& http) { return out.str(); } +const bool& Http::Request::isCompressedResponse() const { + return mCompressedResponse; +} + +void Http::Request::setCompressedResponse(const bool& compressedResponse) { + mCompressedResponse = compressedResponse; +} + std::string Http::Request::prepare(const Http& http) const { std::ostringstream out; @@ -440,12 +407,12 @@ Http::~Http() { std::list::iterator itt; // First we wait to finish any request pending - for ( itt = mThreads.begin(); itt != mThreads.end(); ++itt ) { - (*itt)->wait(); + for ( auto&& itt : mThreads ) { + itt->wait(); } - for ( itt = mThreads.begin(); itt != mThreads.end(); ++itt ) { - eeDelete( *itt ); + for ( auto&& itt : mThreads ) { + eeDelete( itt ); } // Then we destroy the last open connection @@ -507,10 +474,10 @@ void Http::setHost(const std::string& host, unsigned short port, bool useSSL, UR } Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { - IOFakeStreamString stream; + IOStreamString stream; Response response = downloadRequest( request, stream, timeout ); - response.mBody = std::move(stream.mStream); - return std::move(response); + response.mBody = stream.getStream(); + return response; } Http::Response Http::downloadRequest(const Http::Request& request, IOStream& writeTo, Time timeout) { @@ -556,13 +523,13 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // For an HTTP Tunnel first we need to connect to the proxy server ( without TLS ) if (sslSocket->tcpConnect( mHost, mProxy.getPort(), timeout ) != Socket::Done) { - return std::move(received); + return received; } else { mConnection->setConnected(true); } } else { if (mConnection->getSocket()->connect(mHost, mProxy.empty() ? mPort : mProxy.getPort(), timeout) != Socket::Done) { - return std::move(received); + return received; } else { mConnection->setConnected(true); } @@ -596,13 +563,13 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri if ( tunnelResponse.getStatus() == Response::Ok ) { // Stablish the SSL connection if the response is positive if (sslSocket->sslConnect( mHost, mProxy.getPort(), timeout ) != Socket::Done) { - return std::move(received); + return received; } } else { - return std::move(tunnelResponse); + return tunnelResponse; } } else { - return std::move(received); + return received; } mConnection->setTunneled(true); @@ -619,19 +586,26 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // Send it through the socket if (mConnection->getSocket()->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { // Wait for the server's response - bool isnheader = false; std::size_t currentTotalBytes = 0; std::size_t len = 0; + std::size_t readed = 0; char * eol; // end of line char * bol; // beginning of line - std::size_t readed = 0; char buffer[PACKET_BUFFER_SIZE+1]; - std::string header; - std::string fileBuffer; - bool newFileBuffer = false; + std::string headerBuffer; + std::string chunkBuffer; + IOStreamString fileBuffer; + bool isnheader = false; bool chunked = false; + bool chunkNewBuffer = false; + bool chunkEnded = false; + bool compressed = false; + IOStreamInflate * inflateStream = NULL; + ios_size inflateChunkSize = 0; + std::size_t contentLength = 0; while (!request.isCancelled() && ( status = mConnection->getSocket()->receive(buffer, PACKET_BUFFER_SIZE, readed) ) == Socket::Done) { + // If we didn't receive the header yet, we will try to find the end of the header if ( !isnheader ) { // calculate combined length of unprocessed data and new data len += readed; @@ -643,7 +617,7 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri if ( 0 == strncmp( buffer, "\r\n", 2 ) ) { if (len > 2) { currentTotalBytes += (len-2); - fileBuffer.append(buffer, buffer + (len-2)); + chunkBuffer.append(buffer, buffer + (len-2)); } continue; @@ -652,7 +626,7 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri if ( 0 == strncmp( buffer, "\n", 1 ) ) { if ( len > 1 ) { currentTotalBytes += (len-1); - fileBuffer.append(buffer, buffer + (len-1)); + chunkBuffer.append(buffer, buffer + (len-1)); } continue; @@ -683,26 +657,40 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // write remaining data to FILE stream if ( len > 0 ) { currentTotalBytes += len; - fileBuffer.append(bol, bol + len); + chunkBuffer.append(bol, bol + len); } - header.append( buffer, ( bol - buffer ) ); + headerBuffer.append( buffer, ( bol - buffer ) ); // reset length of left over data to zero and continue processing // non-header information len = 0; - if ( !header.empty() ) { + if ( !headerBuffer.empty() ) { // Build the Response object from the received data - received.parse(header); + received.parse(headerBuffer); + + headerBuffer.clear(); // Check if the response is chunked chunked = received.getField("transfer-encoding") == "chunked"; - // If is not chunked just save the file buffer and clear it - if ( !chunked && !fileBuffer.empty() ) { - writeTo.write( &fileBuffer[0], fileBuffer.size() ); - fileBuffer.clear(); + // Check if the content is compressed + std::string encoding( received.getField("content-encoding") ); + compressed = encoding == "gzip" || encoding == "deflate"; + + if ( compressed ) { + Compression::Mode compressionMode = "gzip" == encoding ? Compression::MODE_GZIP : Compression::MODE_DEFLATE; + + inflateChunkSize = Compression::getModeDefaultChunkSize( compressionMode ); + + inflateStream = IOStreamInflate::New( writeTo, compressionMode ); + } + + // Get the content length + if ( !received.getField("content-length").empty() ) { + if ( !String::fromString( contentLength, received.getField("content-length") ) ) + contentLength = 0; } if ( received.getField("connection") == "closed" ) { @@ -732,61 +720,108 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri return http.downloadRequest( request, writeTo, timeout ); } } + + // If is not chunked just save the file buffer and clear it + if ( !chunked && !chunkBuffer.empty() ) { + fileBuffer.write( &chunkBuffer[0], chunkBuffer.size() ); + chunkBuffer.clear(); + } } } } if ( !isnheader ) { - header.append( buffer, ( bol - buffer ) ); + headerBuffer.append( buffer, ( bol - buffer ) ); } } else { currentTotalBytes += readed; if ( chunked ) { - fileBuffer.append( buffer, buffer + readed ); + // If the chunk reading ended we just add the buffer received as a header + // Otherwise we process the buffer data as chunk + if ( !chunkEnded ) { + // Keep a chunk buffer until the end of chunk is found + chunkBuffer.append( buffer, buffer + readed ); - if ( newFileBuffer ) { - if ( fileBuffer.substr( 0, 2 ) == "\r\n" ) { - fileBuffer = fileBuffer.substr( 2 ); + // If the new chunk starts with \r\n and the last removed chunk + // did not contain the trailing \r\n, we remove it to detect + // correctly the next length data + if ( chunkNewBuffer ) { + if ( chunkBuffer.substr( 0, 2 ) == "\r\n" ) { + chunkBuffer = chunkBuffer.substr( 2 ); + } + + chunkNewBuffer = false; } - newFileBuffer = false; - } + // Check for the first \r\n to find the end of the length definition + std::string::size_type lenEnd = chunkBuffer.find_first_of("\r\n"); - std::string::size_type lenEnd = fileBuffer.find_first_of("\r\n"); + if ( lenEnd != std::string::npos ) { + std::string::size_type firstCharPos = lenEnd + 2; + unsigned long length; - if ( lenEnd != std::string::npos ) { - std::string::size_type firstCharPos = lenEnd + 2; - unsigned long length; - bool res = String::fromString( length, fileBuffer.substr(0, lenEnd), std::hex ); + // Get the length of the chunk + bool res = String::fromString( length, chunkBuffer.substr(0, lenEnd), std::hex ); - if ( res && length ) { - if ( fileBuffer.size() - firstCharPos >= length ) { - writeTo.write( &fileBuffer[firstCharPos], length ); - fileBuffer = fileBuffer.substr( firstCharPos + length ); - newFileBuffer = true; + // If the length is solved... + if ( res ) { + // And it's bigger than 0, means that there are more chunks + if ( length > 0 ) { + // Check if the chunk buffer size at least equals to the length reported + if ( chunkBuffer.size() - firstCharPos >= length ) { + // In that case write the chunk to the file buffer + fileBuffer.write( &chunkBuffer[firstCharPos], length ); - // Check if already have the \r\n of the next length in the buffer - if ( !fileBuffer.empty() && fileBuffer.substr( 0, 2 ) == "\r\n" ) { - // Remove it to be able to read the next length - fileBuffer = fileBuffer.substr( 2 ); - newFileBuffer = false; + // And keep the remaining not completed chunk + chunkBuffer = chunkBuffer.substr( firstCharPos + length ); + chunkNewBuffer = true; + + // Check if already have the \r\n of the next length in the buffer + if ( !chunkBuffer.empty() && chunkBuffer.substr( 0, 2 ) == "\r\n" ) { + // Remove it to be able to read the next length + chunkBuffer = chunkBuffer.substr( 2 ); + chunkNewBuffer = false; + } + } + } else { + // If the value is 0 means that the data ended + // But after this we can receive extra headers + chunkEnded = true; } } } + } else { + headerBuffer.append( buffer, buffer + readed ); } } else { - writeTo.write( buffer, readed ); + // If not chunked just write into the file buffer + fileBuffer.write( buffer, readed ); + } + + if ( compressed ) { + if ( fileBuffer.getSize() - inflateChunkSize >= 0 ) { + inflateStream->write( fileBuffer.getStreamPointer(), inflateChunkSize ); + + IOStreamString newFileBuffer; + + fileBuffer.seek(inflateChunkSize); + + std::size_t trailing = fileBuffer.getSize() - inflateChunkSize; + + if ( trailing > 0 ) + newFileBuffer.write( fileBuffer.getPositionPointer(), trailing ); + + fileBuffer = newFileBuffer; + } + } else { + fileBuffer.seek(0); + writeTo.write( fileBuffer.getPositionPointer(), fileBuffer.getSize() ); + fileBuffer.clear(); } if ( request.getProgressCallback() ) { - std::size_t length = 0; - - if ( !received.getField("content-length").empty() ) { - String::fromString( length, received.getField("content-length") ); - } - - if ( !request.getProgressCallback()( *this, request, length, currentTotalBytes ) ) { + if ( !request.getProgressCallback()( *this, request, contentLength, currentTotalBytes ) ) { request.mCancel = true; break; } @@ -794,10 +829,21 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } } + if ( !headerBuffer.empty() ) { + std::istringstream in(headerBuffer); + received.parseFields(in); + } + + if ( compressed && fileBuffer.getSize() > 0 ) { + inflateStream->write( fileBuffer.getStreamPointer(), fileBuffer.getSize() ); + } + if ( status == Socket::Status::Disconnected ) { mConnection->setConnected(false); mConnection->setTunneled(false); } + + eeSAFE_DELETE( inflateStream ); } else { mConnection->setConnected(false); mConnection->setTunneled(false); @@ -809,11 +855,11 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri mConnection->disconnect(); } - return std::move(received); + return received; } Http::Response Http::downloadRequest(const Http::Request & request, std::string writePath, Time timeout) { - IOStreamFile file( writePath, "wb" ); + IOStreamFile file( writePath, "wb+" ); return downloadRequest( request, file, timeout ); } @@ -901,13 +947,11 @@ void Http::removeOldThreads() { Http::Request Http::prepareFields(const Http::Request& request) { Request toSend(request); - if (!toSend.hasField("User-Agent")) { + if (!toSend.hasField("User-Agent")) toSend.setField("User-Agent", "eepp-network"); - } - if (!toSend.hasField("Host")) { + if (!toSend.hasField("Host")) toSend.setField("Host", mHostName); - } if (!toSend.hasField("Content-Length")) { std::ostringstream out; @@ -915,13 +959,11 @@ Http::Request Http::prepareFields(const Http::Request& request) { toSend.setField("Content-Length", out.str()); } - if ((toSend.mMethod == Request::Post) && !toSend.hasField("Content-Type")) { + if ((toSend.mMethod == Request::Post) && !toSend.hasField("Content-Type")) toSend.setField("Content-Type", "application/x-www-form-urlencoded"); - } - if ((toSend.mMajorVersion * 10 + toSend.mMinorVersion >= 11) && !toSend.hasField("Connection")) { + if ((toSend.mMajorVersion * 10 + toSend.mMinorVersion >= 11) && !toSend.hasField("Connection")) toSend.setField("Connection", "close"); - } if (!mProxy.empty()) { toSend.setField("Accept", "*/*"); @@ -933,7 +975,10 @@ Http::Request Http::prepareFields(const Http::Request& request) { } } - return std::move(toSend); + if ( request.isCompressedResponse() ) + toSend.setField("Accept-Encoding", "gzip, deflate"); + + return toSend; } void Http::setProxy(const URI& uri) { diff --git a/src/eepp/system/compression.cpp b/src/eepp/system/compression.cpp new file mode 100644 index 000000000..17db69928 --- /dev/null +++ b/src/eepp/system/compression.cpp @@ -0,0 +1,167 @@ +#include +#include +#include +#include + +#include + +#define DEFLATE_CHUNK_SIZE (16384) + +namespace EE { namespace System { + +Compression::Status Compression::compress(Uint8* dst, Uint64 dstMaxSize, const Uint8* src, Uint64 srcSize, Mode mode, const Config& config) { + IOStreamMemory srcMem( (const char*)src, srcSize ); + IOStreamMemory dstMem( (char*)dst, dstMaxSize ); + return compress( dstMem, srcMem, mode, config ); +} + +Compression::Status Compression::compress(IOStream& dst, IOStream& src, Compression::Mode mode, const Config& config) { + switch (mode) { + case MODE_DEFLATE: + case MODE_GZIP: + { + int ret, flush; + unsigned have; + z_stream strm = {}; + char in[DEFLATE_CHUNK_SIZE]; + char out[DEFLATE_CHUNK_SIZE]; + int level = mode == MODE_DEFLATE ? config.zlib.level : config.gzip.level; + int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + + ret = deflateInit2(&strm, level, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); + if (ret != Z_OK) + return (Status)ret; + + src.seek(0); + + do { + strm.avail_in = src.read(in, DEFLATE_CHUNK_SIZE); + if ( strm.avail_in == 0 ) { + deflateEnd(&strm); + return Status::ERRNO; + } + + flush = src.tell() == src.getSize() ? Z_FINISH : Z_NO_FLUSH; + strm.next_in = (unsigned char*)in; + + do { + strm.avail_out = DEFLATE_CHUNK_SIZE; + strm.next_out = (unsigned char*)out; + + ret = deflate(&strm, flush); + + if ( ret == Z_STREAM_ERROR ) + return Status::STREAM_ERROR; + + have = DEFLATE_CHUNK_SIZE - strm.avail_out; + + if ( dst.write(out, have) != have ) { + deflateEnd(&strm); + + return Status::ERRNO; + } + } while (strm.avail_out == 0); + + if (strm.avail_in != 0) + return Status::DATA_ERROR; + } while (flush != Z_FINISH); + } + } + + return Status::OK; +} + +int Compression::getMaxCompressedBufferSize(Uint64 srcSize, Mode mode, const Config&) { + switch (mode) { + case MODE_DEFLATE: + case MODE_GZIP: + { + int windowBits = mode == MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + + z_stream strm = {}; + int err = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); + if (err != Z_OK) + return -1; + int aout = deflateBound(&strm, srcSize); + deflateEnd(&strm); + return aout; + } + } + + return -1; +} + +Compression::Status Compression::decompress(Uint8* dst, Uint64 dstMaxSize, const Uint8 * src, Uint64 srcSize, Mode mode) { + IOStreamMemory srcMem( (const char*)src, srcSize ); + IOStreamMemory dstMem( (char*)dst, dstMaxSize ); + return decompress( dstMem, srcMem, mode ); +} + +Compression::Status Compression::decompress(IOStream& dst, IOStream& src, Mode mode) { + switch (mode) { + case MODE_DEFLATE: + case MODE_GZIP: + { + SafeDataPointer buffer( DEFLATE_CHUNK_SIZE ); + SafeDataPointer bufferDst( DEFLATE_CHUNK_SIZE ); + + src.seek( 0 ); + + int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + + z_stream strm = {}; + strm.next_in = buffer.data; + + int err = inflateInit2(&strm, windowBits); + if (err != Z_OK) + return (Status)err; + + int zlibStatus; + int bytesRead; + unsigned int have; + Uint32 totalSize = src.getSize(); + Uint32 totalRead = 0; + + while ( totalRead < totalSize ) { + bytesRead = src.read( (char*)buffer.data, buffer.size ); + + strm.avail_in = bytesRead; + strm.next_in = buffer.data; + + do { + strm.avail_out = bufferDst.size; + strm.next_out = bufferDst.data; + zlibStatus = inflate(&strm, Z_NO_FLUSH); + + switch (zlibStatus) { + case Z_OK: + case Z_STREAM_END: + case Z_BUF_ERROR: + break; + default: + inflateEnd(&strm); + return (Status)zlibStatus; + } + + have = bufferDst.size- strm.avail_out; + + dst.write( (const char*)bufferDst.data, have ); + } while (strm.avail_out == 0); + + totalRead += bytesRead; + } + + inflateEnd(&strm); + + return Status::OK; + } + } + + return Status::ERRNO; +} + +std::size_t Compression::getModeDefaultChunkSize(const Mode&) { + return DEFLATE_CHUNK_SIZE; +} + +}} diff --git a/src/eepp/system/iostreaminflate.cpp b/src/eepp/system/iostreaminflate.cpp new file mode 100644 index 000000000..4ed40641c --- /dev/null +++ b/src/eepp/system/iostreaminflate.cpp @@ -0,0 +1,166 @@ +#include + +#include + +namespace EE { namespace System { + +struct LocalStreamData { + z_stream strm; + int state; +}; + +IOStreamInflate * IOStreamInflate::New(IOStream& inOutStream, Compression::Mode mode) { + return eeNew( IOStreamInflate, ( inOutStream, mode ) ); +} + +IOStreamInflate::IOStreamInflate(IOStream& inOutStream, Compression::Mode mode) : + mStream(inOutStream), + mMode(mode), + mBuffer(Compression::getModeDefaultChunkSize(mode)), + mLocalStream(eeNew(LocalStreamData,())) +{ + int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + + mLocalStream->strm = z_stream{}; + + mLocalStream->state = inflateInit2(&mLocalStream->strm, windowBits); +} + +IOStreamInflate::~IOStreamInflate() { + inflateEnd( &mLocalStream->strm ); + + eeSAFE_DELETE( mLocalStream ); +} + +ios_size IOStreamInflate::read(char * buffer, ios_size length) { + if ( mLocalStream->state != Z_OK || !mStream.isOpen() ) + return 0; + + z_stream& zstr = mLocalStream->strm; + + if (zstr.avail_in == 0) { + ios_size n = 0; + + if ( mStream.isOpen()) { + n = mStream.read((char*)mBuffer.data, mBuffer.size); + } + + zstr.next_in = (unsigned char*) mBuffer.data; + zstr.avail_in = n; + } + + zstr.next_out = (unsigned char*) buffer; + zstr.avail_out = length; + + for (;;) { + int rc = inflate(&zstr, Z_NO_FLUSH); + + if (rc == Z_DATA_ERROR) { + if (zstr.avail_in == 0) { + if (mStream.isOpen()) + rc = Z_OK; + else + rc = Z_STREAM_END; + } + } + + if (rc == Z_STREAM_END) { + return length - zstr.avail_out; + } + + if (rc != Z_OK) + return 0; + + if (zstr.avail_out == 0) + return static_cast(length); + + if (zstr.avail_in == 0) { + ios_size n = 0; + + if (mStream.isOpen()) { + n = mStream.read((char*)mBuffer.data, mBuffer.size); + } + + if (n > 0) { + zstr.next_in = (unsigned char*) mBuffer.data; + zstr.avail_in = n; + } else { + return length - zstr.avail_out; + } + } + } +} + +ios_size IOStreamInflate::write(const char * buffer, ios_size length) { + if ( mLocalStream->state != Z_OK || !mStream.isOpen() || length == 0 ) + return 0; + + z_stream& zstr = mLocalStream->strm; + + zstr.next_in = (unsigned char*) buffer; + zstr.avail_in = length; + zstr.next_out = mBuffer.data; + zstr.avail_out = mBuffer.size; + + for (;;) { + int rc = inflate(&zstr, Z_NO_FLUSH); + + if (rc == Z_STREAM_END) { + ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size - zstr.avail_out); + + if (ret == 0) + return 0; + + break; + } + + if (rc != Z_OK) + return 0; + + if (zstr.avail_out == 0) { + ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size); + + if (ret == 0) + return 0; + + zstr.next_out = (unsigned char*) mBuffer.data; + zstr.avail_out = mBuffer.size; + } + + if (zstr.avail_in == 0) { + ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size - zstr.avail_out); + + if (ret == 0) + return 0; + + zstr.next_out = (unsigned char*) mBuffer.data; + zstr.avail_out = mBuffer.size; + + break; + } + } + + return length; +} + +ios_size IOStreamInflate::seek(ios_size position) { + return mStream.seek( position ); +} + +ios_size IOStreamInflate::tell() { + return mStream.tell(); +} + +ios_size IOStreamInflate::getSize() { + return mStream.getSize(); +} + +bool IOStreamInflate::isOpen() { + return mStream.isOpen(); +} + +const Compression::Mode& IOStreamInflate::getMode() const { + return mMode; +} + +}} diff --git a/src/eepp/system/iostreamstring.cpp b/src/eepp/system/iostreamstring.cpp new file mode 100644 index 000000000..daa5cc12a --- /dev/null +++ b/src/eepp/system/iostreamstring.cpp @@ -0,0 +1,68 @@ +#include +#include + +namespace EE { namespace System { + +IOStreamString::IOStreamString() : + mPos(0) +{} + +ios_size IOStreamString::read(char * data, ios_size size) { + Int64 endPosition = mPos + size; + Int64 count = endPosition <= getSize() ? size : getSize() - mPos; + + if ( count > 0 ) { + memcpy( data, &mStream[mPos], static_cast( count ) ); + mPos += count; + } + + return count; +} + +ios_size IOStreamString::write(const char * data, ios_size size) { + mStream.insert( mPos, data, size ); + + mPos += size; + + return size; +} + +ios_size IOStreamString::write(const std::string& string) { + return write( string.c_str(), string.size() ); +} + +ios_size IOStreamString::seek(ios_size position) { + mPos = ( position < getSize() ) ? position : getSize(); + return mPos; +} + +ios_size IOStreamString::tell() { + return getSize(); +} + +ios_size IOStreamString::getSize() { + return mStream.size(); +} + +bool IOStreamString::isOpen() { + return true; +} + +void IOStreamString::clear() { + mStream.clear(); + mPos = 0; +} + +const char* IOStreamString::getPositionPointer() { + return &mStream[mPos]; +} + +const char* IOStreamString::getStreamPointer() const { + return mStream.c_str(); +} + +const std::string& IOStreamString::getStream() const { + return mStream; +} + +}} diff --git a/src/eepp/window/platform/x11/cursorx11.cpp b/src/eepp/window/platform/x11/cursorx11.cpp index 142cf2853..735aef5bf 100644 --- a/src/eepp/window/platform/x11/cursorx11.cpp +++ b/src/eepp/window/platform/x11/cursorx11.cpp @@ -38,7 +38,7 @@ CursorX11::CursorX11( const std::string& path, const Vector2i& hotspot, const st CursorX11::~CursorX11() { if ( None != mCursor ) - XFreeCursor( getPlatform()->GetDisplay(), mCursor ); + XFreeCursor( getPlatform()->getDisplay(), mCursor ); } void CursorX11::create() { @@ -65,11 +65,11 @@ void CursorX11::create() { image->xhot = mHotSpot.x; image->yhot = mHotSpot.y; - getPlatform()->Lock(); + getPlatform()->lock(); - mCursor = XcursorImageLoadCursor( getPlatform()->GetDisplay(), image ); + mCursor = XcursorImageLoadCursor( getPlatform()->getDisplay(), image ); - getPlatform()->Unlock(); + getPlatform()->unlock(); XcursorImageDestroy( image ); } diff --git a/src/eepp/window/platform/x11/x11impl.cpp b/src/eepp/window/platform/x11/x11impl.cpp index a4a5b8db3..1ad9a199b 100644 --- a/src/eepp/window/platform/x11/x11impl.cpp +++ b/src/eepp/window/platform/x11/x11impl.cpp @@ -40,18 +40,18 @@ X11Impl::~X11Impl() { } void X11Impl::minimizeWindow() { - Lock(); + lock(); XIconifyWindow( mDisplay, mX11Window, 0 ); XFlush( mDisplay ); - Unlock(); + unlock(); } void X11Impl::maximizeWindow() { // coded by Rafał Maj, idea from Måns Rullgård http://tinyurl.com/68mvk3 - Lock(); + lock(); XEvent xev; Atom wm_state = XAtom( "_NET_WM_STATE" ); @@ -71,11 +71,11 @@ void X11Impl::maximizeWindow() { XFlush(mDisplay); - Unlock(); + unlock(); } bool X11Impl::isWindowMaximized() { - Lock(); + lock(); //bool minimized = false; bool maximizedhorz = false; @@ -117,7 +117,7 @@ bool X11Impl::isWindowMaximized() { XFlush(mDisplay); - Unlock(); + unlock(); if( maximizedhorz && maximizedvert ) { return true; @@ -127,45 +127,45 @@ bool X11Impl::isWindowMaximized() { } void X11Impl::hideWindow() { - Lock(); + lock(); XUnmapWindow( mDisplay, mX11Window ); - Unlock(); + unlock(); } void X11Impl::raiseWindow() { - Lock(); + lock(); XRaiseWindow( mDisplay, mX11Window ); - Unlock(); + unlock(); } void X11Impl::showWindow() { - Lock(); + lock(); XMapRaised( mDisplay, mX11Window ); - Unlock(); + unlock(); } void X11Impl::moveWindow( int left, int top ) { - Lock(); + lock(); XMoveWindow( mDisplay, mX11Window, left, top ); XFlush( mDisplay ); - Unlock(); + unlock(); } void X11Impl::setContext( eeWindowContex Context ) { - Lock(); + lock(); glXMakeCurrent( mDisplay, mX11Window, Context ); - Unlock(); + unlock(); } Vector2i X11Impl::getPosition() { @@ -181,20 +181,20 @@ void X11Impl::showMouseCursor() { if ( !mCursorHidden ) return; - Lock(); + lock(); XDefineCursor( mDisplay, mMainWindow, mCursorCurrent ); mCursorHidden = false; - Unlock(); + unlock(); } void X11Impl::hideMouseCursor() { if ( mCursorHidden ) return; - Lock(); + lock(); if ( mCursorInvisible == None ) { unsigned long gcmask; @@ -225,7 +225,7 @@ void X11Impl::hideMouseCursor() { mCursorHidden = true; - Unlock(); + unlock(); } Cursor * X11Impl::createMouseCursor( Texture * tex, const Vector2i& hotspot, const std::string& name ) { @@ -244,21 +244,21 @@ void X11Impl::setMouseCursor( Cursor * cursor ) { mCursorCurrent = reinterpret_cast( cursor )->GetCursor(); if ( !mCursorHidden ) { - Lock(); + lock(); XDefineCursor( mDisplay, mMainWindow, mCursorCurrent ); - Unlock(); + unlock(); } } void X11Impl::restoreCursor() { if ( !mCursorHidden ) { - Lock(); + lock(); XDefineCursor( mDisplay, mMainWindow, mCursorCurrent ); - Unlock(); + unlock(); } else { hideMouseCursor(); } @@ -287,7 +287,7 @@ void X11Impl::setSystemMouseCursor( Cursor::SysType syscursor ) { XFreeCursor( mDisplay, mCursorSystemLast ); } - Lock(); + lock(); mCursorCurrent = XCreateFontCursor( mDisplay, cursor_shape ); mCursorSystemLast = mCursorCurrent; @@ -296,19 +296,19 @@ void X11Impl::setSystemMouseCursor( Cursor::SysType syscursor ) { XDefineCursor( mDisplay, mMainWindow, mCursorCurrent ); } - Unlock(); + unlock(); } -eeWindowHandle X11Impl::GetDisplay() const { +eeWindowHandle X11Impl::getDisplay() const { return mDisplay; } -void X11Impl::Lock() { +void X11Impl::lock() { if ( NULL != mLock ) mLock(); } -void X11Impl::Unlock() { +void X11Impl::unlock() { if ( NULL != mUnlock ) mUnlock(); } diff --git a/src/eepp/window/platform/x11/x11impl.hpp b/src/eepp/window/platform/x11/x11impl.hpp index 529bc8cca..f1446c117 100644 --- a/src/eepp/window/platform/x11/x11impl.hpp +++ b/src/eepp/window/platform/x11/x11impl.hpp @@ -54,11 +54,11 @@ class X11Impl : public PlatformImpl { void restoreCursor(); - eeWindowHandle GetDisplay() const; + eeWindowHandle getDisplay() const; - void Lock(); + void lock(); - void Unlock(); + void unlock(); eeWindowContex getWindowContext(); protected: diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index 2dec6548e..3d3cdb6ec 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -17,6 +17,7 @@ void printResponseHeaders( Http::Response& response ) { EE_MAIN_FUNC int main (int argc, char * argv []) { args::ArgumentParser parser("HTTP request program example"); args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); + args::Flag compressed(parser, "compressed", "Request compressed response", {"compressed"}); args::ValueFlag postData(parser, "data", "HTTP POST data", {'d', "data"}); args::ValueFlagList headers(parser, "header", "Pass custom header(s) to server", {'H', "header"}); args::Flag includeHead(parser, "include", "Include protocol response headers in the output", {'i',"include"}); @@ -136,6 +137,11 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { http.setProxy( URI( proxy.Get() ) ); } + // Request a compressed response + if ( compressed ) { + request.setCompressedResponse( true ); + } + if ( !output ) { // Send the request Http::Response response = http.sendRequest(request); @@ -147,21 +153,22 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { printResponseHeaders(response); if ( status == Http::Response::Ok ) { - std::cout << response.getBody() << std::endl; + std::cout << response.getBody(); } else { std::cout << "Error " << status << std::endl << response.getStatusDescription() << std::endl; - std::cout << response.getBody() << std::endl; + std::cout << response.getBody(); } } else { std::string path( output.Get() ); // If output path is a directory guess a file name if ( FileSystem::isDirectory( path ) ) { + FileSystem::dirPathAddSlashAtEnd( path ); + std::string lastPathSegment = uri.getLastPathSegment(); // If there's a path end segment if ( !lastPathSegment.empty() ) { - FileSystem::dirPathAddSlashAtEnd( path ); // Save with the path end segment name if ( !FileSystem::fileExists( path + lastPathSegment ) ) { From b53c820c56c83eb9a8ac0278aa98391af936b91e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Wed, 1 May 2019 22:35:26 -0300 Subject: [PATCH 09/18] Fixed chunked encoding. --HG-- branch : dev --- src/eepp/network/http.cpp | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index c4b63e65a..2db6dd520 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -726,6 +726,9 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri fileBuffer.write( &chunkBuffer[0], chunkBuffer.size() ); chunkBuffer.clear(); } + + if ( chunked ) + readed = 0; } } } @@ -733,10 +736,12 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri if ( !isnheader ) { headerBuffer.append( buffer, ( bol - buffer ) ); } - } else { + } + + if ( isnheader ) { currentTotalBytes += readed; - if ( chunked ) { + if ( chunked && readed ) { // If the chunk reading ended we just add the buffer received as a header // Otherwise we process the buffer data as chunk if ( !chunkEnded ) { From 48a31680e4e4726dcd28e495e0025e702c94e6ca Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Thu, 2 May 2019 02:03:17 -0300 Subject: [PATCH 10/18] Fixed chunked transfer encoding (for real?). --HG-- branch : dev --- src/eepp/network/http.cpp | 189 ++++++++++++++++++++------------------ 1 file changed, 99 insertions(+), 90 deletions(-) diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 2db6dd520..af5449def 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -589,8 +589,8 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri std::size_t currentTotalBytes = 0; std::size_t len = 0; std::size_t readed = 0; - char * eol; // end of line - char * bol; // beginning of line + char * eol = NULL; // end of line + char * bol = NULL; // beginning of line char buffer[PACKET_BUFFER_SIZE+1]; std::string headerBuffer; std::string chunkBuffer; @@ -605,35 +605,18 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri std::size_t contentLength = 0; while (!request.isCancelled() && ( status = mConnection->getSocket()->receive(buffer, PACKET_BUFFER_SIZE, readed) ) == Socket::Done) { + char * readBuffer = buffer; + // If we didn't receive the header yet, we will try to find the end of the header if ( !isnheader ) { // calculate combined length of unprocessed data and new data len += readed; // NULL terminate buffer for string functions - buffer[len] = '\0'; - - // checks if the header break happened to be the first line of the buffer - if ( 0 == strncmp( buffer, "\r\n", 2 ) ) { - if (len > 2) { - currentTotalBytes += (len-2); - chunkBuffer.append(buffer, buffer + (len-2)); - } - - continue; - } - - if ( 0 == strncmp( buffer, "\n", 1 ) ) { - if ( len > 1 ) { - currentTotalBytes += (len-1); - chunkBuffer.append(buffer, buffer + (len-1)); - } - - continue; - } + readBuffer[len] = '\0'; // process each line in buffer looking for header break - bol = buffer; + bol = readBuffer; while( !isnheader && ( eol = strchr( bol, '\n') ) != NULL ) { // update bol based upon the value of eol @@ -652,26 +635,15 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri bol += 1; // calculate the amount of data remaining in the buffer - len = readed - ( bol - buffer ); + len = readed - ( bol - readBuffer ); - // write remaining data to FILE stream - if ( len > 0 ) { - currentTotalBytes += len; - chunkBuffer.append(bol, bol + len); - } - - headerBuffer.append( buffer, ( bol - buffer ) ); - - // reset length of left over data to zero and continue processing - // non-header information - len = 0; + // Fill the header buffer + headerBuffer.append( readBuffer, ( bol - readBuffer ) ); if ( !headerBuffer.empty() ) { // Build the Response object from the received data received.parse(headerBuffer); - headerBuffer.clear(); - // Check if the response is chunked chunked = received.getField("transfer-encoding") == "chunked"; @@ -721,87 +693,124 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } } - // If is not chunked just save the file buffer and clear it - if ( !chunked && !chunkBuffer.empty() ) { - fileBuffer.write( &chunkBuffer[0], chunkBuffer.size() ); - chunkBuffer.clear(); + // Move the readBuffer to the starting point + // of the file buffer + if ( len > 0 ) { + readBuffer = bol; + readed = len; + } else { + readed = 0; } - if ( chunked ) - readed = 0; + headerBuffer.clear(); } } } if ( !isnheader ) { - headerBuffer.append( buffer, ( bol - buffer ) ); + headerBuffer.append( readBuffer, ( bol - readBuffer ) ); } } if ( isnheader ) { currentTotalBytes += readed; - if ( chunked && readed ) { - // If the chunk reading ended we just add the buffer received as a header - // Otherwise we process the buffer data as chunk - if ( !chunkEnded ) { - // Keep a chunk buffer until the end of chunk is found - chunkBuffer.append( buffer, buffer + readed ); + if ( chunked ) { + if ( readed > 0 ) { + // If the chunk reading ended we just add the buffer received as a header + // Otherwise we process the buffer data as chunk + if ( !chunkEnded ) { + // Keep a chunk buffer until the end of chunk is found + chunkBuffer.append( readBuffer, readBuffer + readed ); - // If the new chunk starts with \r\n and the last removed chunk - // did not contain the trailing \r\n, we remove it to detect - // correctly the next length data - if ( chunkNewBuffer ) { - if ( chunkBuffer.substr( 0, 2 ) == "\r\n" ) { - chunkBuffer = chunkBuffer.substr( 2 ); + // If the new chunk starts with \r\n and the last removed chunk + // did not contain the trailing \r\n, we remove it to detect + // correctly the next length data + if ( chunkNewBuffer ) { + if ( chunkBuffer.substr( 0, 2 ) == "\r\n" ) { + chunkBuffer = chunkBuffer.substr( 2 ); + } + + chunkNewBuffer = false; } - chunkNewBuffer = false; - } + bool retry; - // Check for the first \r\n to find the end of the length definition - std::string::size_type lenEnd = chunkBuffer.find_first_of("\r\n"); + do { + retry = false; - if ( lenEnd != std::string::npos ) { - std::string::size_type firstCharPos = lenEnd + 2; - unsigned long length; + // Check for the first \r\n to find the end of the length definition + std::string::size_type lenEnd = chunkBuffer.find_first_of("\r\n"); - // Get the length of the chunk - bool res = String::fromString( length, chunkBuffer.substr(0, lenEnd), std::hex ); + if ( lenEnd != std::string::npos ) { + std::string::size_type firstCharPos = lenEnd + 2; + unsigned long length; - // If the length is solved... - if ( res ) { - // And it's bigger than 0, means that there are more chunks - if ( length > 0 ) { - // Check if the chunk buffer size at least equals to the length reported - if ( chunkBuffer.size() - firstCharPos >= length ) { - // In that case write the chunk to the file buffer - fileBuffer.write( &chunkBuffer[firstCharPos], length ); + // Get the length of the chunk + bool res = String::fromString( length, chunkBuffer.substr(0, lenEnd), std::hex ); - // And keep the remaining not completed chunk - chunkBuffer = chunkBuffer.substr( firstCharPos + length ); - chunkNewBuffer = true; + // If the length is solved... + if ( res ) { + // And it's bigger than 0, means that there are more chunks + if ( length > 0 ) { + // Check if the chunk buffer size at least equals to the length reported + if ( chunkBuffer.size() - firstCharPos >= length ) { + // In that case write the chunk to the file buffer + fileBuffer.write( &chunkBuffer[firstCharPos], length ); - // Check if already have the \r\n of the next length in the buffer - if ( !chunkBuffer.empty() && chunkBuffer.substr( 0, 2 ) == "\r\n" ) { - // Remove it to be able to read the next length - chunkBuffer = chunkBuffer.substr( 2 ); - chunkNewBuffer = false; + // And keep the remaining not completed chunk + chunkBuffer = chunkBuffer.substr( firstCharPos + length ); + + // Check if already have the \r\n of the next length in the buffer + if ( !chunkBuffer.empty() ) { + std::size_t pos = 0; + + // Remove al the \r\n remaining + while ( pos < chunkBuffer.size() && 0 == strncmp( &chunkBuffer[pos], "\r\n", 2 ) ) { + pos += 2; + } + + if ( pos > 0 ) { + // Remove it to be able to read the next length + chunkBuffer = chunkBuffer.substr( pos ); + } + + // If still the chunk is not empty it could be another chunk + // already received, so we check that retrying + if ( !chunkBuffer.empty() ) { + std::string::size_type lenEnd = chunkBuffer.find_first_of("\r\n"); + + if ( lenEnd != std::string::npos ) { + bool res = String::fromString( length, chunkBuffer.substr(0, lenEnd), std::hex ); + + if ( res && length > 0 ) { + retry = true; + } + } + } + } + + // If the next chunk received starts with \r\n it's because + // it's part of the chunk size information, so we need to flag it + // to remove it + chunkNewBuffer = true; + } + } else { + // If the value is 0 means that the data ended + // But after this we can receive extra headers + chunkEnded = true; + chunkBuffer.clear(); } } - } else { - // If the value is 0 means that the data ended - // But after this we can receive extra headers - chunkEnded = true; } - } + } while ( retry ); + } else { + headerBuffer.append( readBuffer, readBuffer + readed ); } - } else { - headerBuffer.append( buffer, buffer + readed ); } - } else { + } else if ( readed > 0 ) { // If not chunked just write into the file buffer - fileBuffer.write( buffer, readed ); + fileBuffer.write( readBuffer, readed ); } if ( compressed ) { From 5204e31228d2e7db443e2c18ea832f8aad2aa2ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sun, 5 May 2019 19:16:32 -0300 Subject: [PATCH 11/18] Minor changes. --HG-- branch : dev --- include/eepp/network/http.hpp | 1 - src/eepp/system/compression.cpp | 2 +- src/examples/http_request/http_request.cpp | 11 ++++------- 3 files changed, 5 insertions(+), 9 deletions(-) diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index a65e02f5d..76090a591 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -11,7 +11,6 @@ #include #include #include -#include #include #include #include diff --git a/src/eepp/system/compression.cpp b/src/eepp/system/compression.cpp index 17db69928..440bc0e0e 100644 --- a/src/eepp/system/compression.cpp +++ b/src/eepp/system/compression.cpp @@ -21,7 +21,7 @@ Compression::Status Compression::compress(IOStream& dst, IOStream& src, Compress case MODE_GZIP: { int ret, flush; - unsigned have; + ios_size have; z_stream strm = {}; char in[DEFLATE_CHUNK_SIZE]; char out[DEFLATE_CHUNK_SIZE]; diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index 3d3cdb6ec..e61c5b28a 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -35,15 +35,15 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { parser.ParseCLI(argc, argv); } catch (const args::Help&) { std::cout << parser; - return 0; + return EXIT_SUCCESS; } catch (const args::ParseError& e) { std::cerr << e.what() << std::endl; std::cerr << parser; - return 1; + return EXIT_FAILURE; } catch (args::ValidationError& e) { std::cerr << e.what() << std::endl; std::cerr << parser; - return 1; + return EXIT_FAILURE; } { @@ -59,10 +59,7 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { http.setHost("http://en.wikipedia.org"); } - // Prepare a request to get the wikipedia main page - request.setUri("/wiki/Main_Page"); - - // Creates an async http request + // Creates an async http request and set the path requested Http::Request asyncRequest( "/wiki/" + Version::getCodename() ); http.sendAsyncRequest([]( const Http& http, Http::Request& request, Http::Response& response ) { From 73f2c57a14f773f47c41736baeaf06ced18432ea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Mon, 6 May 2019 22:16:59 -0300 Subject: [PATCH 12/18] Added setSendTimeout and setReceiveTimeout to TcpSocket and UdpSocket. --HG-- branch : dev --- include/eepp/network/socket.hpp | 3 +-- include/eepp/network/tcpsocket.hpp | 8 ++++++++ include/eepp/network/udpsocket.hpp | 8 ++++++++ src/eepp/network/platform/unix/socketimpl.cpp | 14 ++++++++++++++ src/eepp/network/platform/unix/socketimpl.hpp | 9 +++++++++ src/eepp/network/platform/win/socketimpl.cpp | 10 ++++++++++ src/eepp/network/platform/win/socketimpl.hpp | 6 ++++++ src/eepp/network/tcpsocket.cpp | 12 ++++++++++++ src/eepp/network/udpsocket.cpp | 14 ++++++++++++-- 9 files changed, 80 insertions(+), 4 deletions(-) diff --git a/include/eepp/network/socket.hpp b/include/eepp/network/socket.hpp index 23b16c85f..884a95120 100644 --- a/include/eepp/network/socket.hpp +++ b/include/eepp/network/socket.hpp @@ -48,8 +48,7 @@ class EE_API Socket : NonCopyable { bool isBlocking() const; protected : /** @brief Types of protocols that the socket can use */ - enum Type - { + enum Type { Tcp, ///< TCP protocol Udp ///< UDP protocol }; diff --git a/include/eepp/network/tcpsocket.hpp b/include/eepp/network/tcpsocket.hpp index fc16f9f04..e201d9353 100644 --- a/include/eepp/network/tcpsocket.hpp +++ b/include/eepp/network/tcpsocket.hpp @@ -113,6 +113,14 @@ class EE_API TcpSocket : public Socket { ** @see Send */ virtual Status receive(Packet& packet); + /** Set the send timeout. Only callable after connect ( after the socket + ** has been initialized ). */ + void setSendTimeout(SocketHandle sock, const Time& timeout); + + /** Set the receive timeout Only callable after connect ( after the socket + ** has been initialized ). */ + void setReceiveTimeout(SocketHandle sock, const Time& timeout); + private: friend class TcpListener; diff --git a/include/eepp/network/udpsocket.hpp b/include/eepp/network/udpsocket.hpp index 5cf175ab0..27840baba 100644 --- a/include/eepp/network/udpsocket.hpp +++ b/include/eepp/network/udpsocket.hpp @@ -98,6 +98,14 @@ class EE_API UdpSocket : public Socket { ** @return Status code ** @see Send */ Status receive(Packet& packet, IpAddress& remoteAddress, unsigned short& remotePort); + + /** Set the send timeout. Only callable after bind ( after the socket + ** has been initialized ). */ + void setSendTimeout(SocketHandle sock, const Time& timeout); + + /** Set the receive timeout Only callable after bind ( after the socket + ** has been initialized ). */ + void setReceiveTimeout(SocketHandle sock, const Time& timeout); private: // Member data std::vector mBuffer; ///< Temporary buffer holding the received data in Receive(Packet) diff --git a/src/eepp/network/platform/unix/socketimpl.cpp b/src/eepp/network/platform/unix/socketimpl.cpp index 8b175ff1d..c003e0bff 100644 --- a/src/eepp/network/platform/unix/socketimpl.cpp +++ b/src/eepp/network/platform/unix/socketimpl.cpp @@ -57,6 +57,20 @@ Socket::Status SocketImpl::getErrorStatus() { } } +void SocketImpl::setSendTimeout(SocketHandle sock, const Time& timeout) { + struct timeval time; + time.tv_sec = static_cast(timeout.asMicroseconds() / 1000000); + time.tv_usec = static_cast(timeout.asMicroseconds() % 1000000); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&time, sizeof time); +} + +void SocketImpl::setReceiveTimeout(SocketHandle sock, const Time & timeout) { + struct timeval time; + time.tv_sec = static_cast(timeout.asMicroseconds() / 1000000); + time.tv_usec = static_cast(timeout.asMicroseconds() % 1000000); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&time, sizeof time); +} + }}} #endif diff --git a/src/eepp/network/platform/unix/socketimpl.hpp b/src/eepp/network/platform/unix/socketimpl.hpp index 0cc0e5f9f..3cf6dbfca 100644 --- a/src/eepp/network/platform/unix/socketimpl.hpp +++ b/src/eepp/network/platform/unix/socketimpl.hpp @@ -5,6 +5,7 @@ #if defined( EE_PLATFORM_POSIX ) +#include #include #include #include @@ -14,6 +15,8 @@ #include #include +using namespace EE::System; + namespace EE { namespace Network { namespace Private { /** @brief Helper class implementing all the non-portable socket stuff; this is the Unix version */ @@ -44,6 +47,12 @@ class SocketImpl { /** Get the last socket error status ** @return Status corresponding to the last socket error */ static Socket::Status getErrorStatus(); + + /** Set the send timeout */ + static void setSendTimeout(SocketHandle sock, const Time& timeout); + + /** Set the receive timeout */ + static void setReceiveTimeout(SocketHandle sock, const Time& timeout); }; }}} diff --git a/src/eepp/network/platform/win/socketimpl.cpp b/src/eepp/network/platform/win/socketimpl.cpp index a773244e3..433f853f0 100644 --- a/src/eepp/network/platform/win/socketimpl.cpp +++ b/src/eepp/network/platform/win/socketimpl.cpp @@ -42,6 +42,16 @@ Socket::Status SocketImpl::getErrorStatus() { } } +void SocketImpl::setSendTimeout(SocketHandle sock, const Time& timeout) { + DWORD time = timeout.asMilliseconds(); + setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, (const char*)&time, sizeof time); +} + +void SocketImpl::setReceiveTimeout(SocketHandle sock, const Time & timeout) { + DWORD time = timeout.asMilliseconds(); + setsockopt(sock, SOL_SOCKET, SO_RCVTIMEO, (const char*)&time, sizeof time); +} + /** Windows needs some initialization and cleanup to get ** sockets working properly... so let's create a class that will do it automatically */ struct SocketInitializer diff --git a/src/eepp/network/platform/win/socketimpl.hpp b/src/eepp/network/platform/win/socketimpl.hpp index ea241cbcd..2a2d2d4bb 100644 --- a/src/eepp/network/platform/win/socketimpl.hpp +++ b/src/eepp/network/platform/win/socketimpl.hpp @@ -47,6 +47,12 @@ class SocketImpl { /** Get the last socket error status ** @return Status corresponding to the last socket error */ static Socket::Status getErrorStatus(); + + /** Set the send timeout */ + static void setSendTimeout(SocketHandle sock, const Time& timeout); + + /** Set the receive timeout */ + static void setReceiveTimeout(SocketHandle sock, const Time& timeout); }; }}} diff --git a/src/eepp/network/tcpsocket.cpp b/src/eepp/network/tcpsocket.cpp index ed1e257b6..b0cfde2b8 100644 --- a/src/eepp/network/tcpsocket.cpp +++ b/src/eepp/network/tcpsocket.cpp @@ -316,6 +316,18 @@ Socket::Status TcpSocket::receive(Packet& packet) { return Done; } +void TcpSocket::setSendTimeout(SocketHandle sock, const Time& timeout) { + if (getHandle() != Private::SocketImpl::invalidSocket()) { + Private::SocketImpl::setSendTimeout(getHandle(), timeout); + } +} + +void TcpSocket::setReceiveTimeout(SocketHandle sock, const Time& timeout) { + if (getHandle() != Private::SocketImpl::invalidSocket()) { + Private::SocketImpl::setReceiveTimeout(getHandle(), timeout); + } +} + TcpSocket::PendingPacket::PendingPacket() : Size (0), SizeReceived(0), diff --git a/src/eepp/network/udpsocket.cpp b/src/eepp/network/udpsocket.cpp index 0dcf8a7ac..0f9883975 100644 --- a/src/eepp/network/udpsocket.cpp +++ b/src/eepp/network/udpsocket.cpp @@ -61,8 +61,7 @@ Socket::Status UdpSocket::send(const void* data, std::size_t size, const IpAddre create(); // Make sure that all the data will fit in one datagram - if (size > MaxDatagramSize) - { + if (size > MaxDatagramSize) { eePRINTL( "Cannot send data over the network (the number of bytes to send is greater than UdpSocket::MaxDatagramSize)" ); return Error; } @@ -143,5 +142,16 @@ Socket::Status UdpSocket::receive(Packet& packet, IpAddress& remoteAddress, unsi return status; } +void UdpSocket::setSendTimeout(SocketHandle sock, const Time& timeout) { + if (getHandle() != Private::SocketImpl::invalidSocket()) { + Private::SocketImpl::setSendTimeout(getHandle(), timeout); + } +} + +void UdpSocket::setReceiveTimeout(SocketHandle sock, const Time& timeout) { + if (getHandle() != Private::SocketImpl::invalidSocket()) { + Private::SocketImpl::setReceiveTimeout(getHandle(), timeout); + } +} }} From 9a42b7606a85da1788d675b87b8e88da5b6a225f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Tue, 7 May 2019 00:36:22 -0300 Subject: [PATCH 13/18] Added IOStreamDeflate. --HG-- branch : dev --- include/eepp/system.hpp | 1 + include/eepp/system/iostreamdeflate.hpp | 49 ++++++ include/eepp/system/iostreamfile.hpp | 2 + projects/linux/ee.files | 2 + projects/linux/ee.includes | 4 - src/eepp/system/iostreamdeflate.cpp | 193 ++++++++++++++++++++++++ src/eepp/system/iostreamfile.cpp | 12 +- 7 files changed, 256 insertions(+), 7 deletions(-) create mode 100644 include/eepp/system/iostreamdeflate.hpp create mode 100644 src/eepp/system/iostreamdeflate.cpp diff --git a/include/eepp/system.hpp b/include/eepp/system.hpp index bcffdedf4..cf7c5d490 100644 --- a/include/eepp/system.hpp +++ b/include/eepp/system.hpp @@ -35,6 +35,7 @@ #include #include #include +#include #include #endif diff --git a/include/eepp/system/iostreamdeflate.hpp b/include/eepp/system/iostreamdeflate.hpp new file mode 100644 index 000000000..e301ef330 --- /dev/null +++ b/include/eepp/system/iostreamdeflate.hpp @@ -0,0 +1,49 @@ +#ifndef EE_SYSTEM_IOSTREAMDEFLATE_HPP +#define EE_SYSTEM_IOSTREAMDEFLATE_HPP + +#include +#include +#include + +namespace EE { namespace System { + +struct LocalStreamData; + +/** @brief Implementation of a deflating stream */ +class EE_API IOStreamDeflate : public IOStream { + public: + static IOStreamDeflate * New( IOStream& inOutStream, Compression::Mode mode, const Compression::Config& config = Compression::Config() ); + + /** @brief Use a stream as a input or output buffer + ** @param inOutStream Stream where the results will ve loaded or saved. + ** It must be used only for reading or writing, can't mix both calls. + ** @param mode Compression method used + ** @param config Compression configuration + */ + IOStreamDeflate( IOStream& inOutStream, Compression::Mode mode, const Compression::Config& config = Compression::Config() ); + + virtual ~IOStreamDeflate(); + + ios_size read( char * data, ios_size size ); + + ios_size write( const char * data, ios_size size ); + + ios_size seek( ios_size position ); + + ios_size tell(); + + ios_size getSize(); + + bool isOpen(); + + const Compression::Mode& getMode() const; + protected: + IOStream& mStream; + Compression::Mode mMode; + SafeDataPointer mBuffer; + LocalStreamData * mLocalStream; +}; + +}} + +#endif // EE_SYSTEM_IOSTREAMDEFLATE_HPP diff --git a/include/eepp/system/iostreamfile.hpp b/include/eepp/system/iostreamfile.hpp index be0bf99ca..a7b96c371 100644 --- a/include/eepp/system/iostreamfile.hpp +++ b/include/eepp/system/iostreamfile.hpp @@ -33,6 +33,8 @@ class EE_API IOStreamFile : public IOStream { /** @brief Synchronizes the buffer associated with the stream to its controlled output sequence. ** This effectively means that all unwritten characters in the buffer are written to its controlled output sequence as soon as possible. */ void flush(); + + void close(); protected: std::FILE* mFS; ios_size mSize; diff --git a/projects/linux/ee.files b/projects/linux/ee.files index daaec457c..8e8c35f33 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -248,6 +248,7 @@ ../../include/eepp/system/filesystem.hpp ../../include/eepp/system.hpp ../../include/eepp/system/inifile.hpp +../../include/eepp/system/iostreamdeflate.hpp ../../include/eepp/system/iostreamfile.hpp ../../include/eepp/system/iostream.hpp ../../include/eepp/system/iostreaminflate.hpp @@ -674,6 +675,7 @@ ../../src/eepp/system/directorypack.cpp ../../src/eepp/system/filesystem.cpp ../../src/eepp/system/inifile.cpp +../../src/eepp/system/iostreamdeflate.cpp ../../src/eepp/system/iostreamfile.cpp ../../src/eepp/system/iostreaminflate.cpp ../../src/eepp/system/iostreammemory.cpp diff --git a/projects/linux/ee.includes b/projects/linux/ee.includes index d11e995a6..5d55259ce 100644 --- a/projects/linux/ee.includes +++ b/projects/linux/ee.includes @@ -5,7 +5,3 @@ ../../src/thirdparty/efsw/include ../../src/thirdparty/libvorbis/include /usr/include/freetype2/ - -../../include/eepp/ui -../../src/eepp/ui -../../bin/assets/layouts diff --git a/src/eepp/system/iostreamdeflate.cpp b/src/eepp/system/iostreamdeflate.cpp new file mode 100644 index 000000000..9862ab702 --- /dev/null +++ b/src/eepp/system/iostreamdeflate.cpp @@ -0,0 +1,193 @@ +#include + +#include + +namespace EE { namespace System { + +struct LocalStreamData { + z_stream strm; + int state; + bool writedStream; +}; + +IOStreamDeflate * IOStreamDeflate::New(IOStream& inOutStream, Compression::Mode mode, const Compression::Config& config) { + return eeNew( IOStreamDeflate, ( inOutStream, mode ) ); +} + +IOStreamDeflate::IOStreamDeflate(IOStream& inOutStream, Compression::Mode mode, const Compression::Config& config) : + mStream(inOutStream), + mMode(mode), + mBuffer(Compression::getModeDefaultChunkSize(mode)), + mLocalStream(eeNew(LocalStreamData,())) +{ + int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; + int level = mode == Compression::MODE_DEFLATE ? config.zlib.level : config.gzip.level; + + mLocalStream->strm = z_stream{}; + mLocalStream->writedStream = false; + + mLocalStream->state = deflateInit2(&mLocalStream->strm, level, Z_DEFLATED, windowBits, 8, Z_DEFAULT_STRATEGY); +} + +IOStreamDeflate::~IOStreamDeflate() { + if (mStream.isOpen() && mLocalStream->writedStream) { + z_stream& zstr = mLocalStream->strm; + + if (zstr.next_out) { + int rc = deflate(&zstr, Z_FINISH); + + if (rc != Z_OK && rc != Z_STREAM_END) return; + + mStream.write((char*)mBuffer.data, mBuffer.size - zstr.avail_out); + + if (!mStream.isOpen()) return; + + zstr.next_out = (unsigned char*) mBuffer.data; + zstr.avail_out = mBuffer.size; + + while (rc != Z_STREAM_END) { + rc = deflate(&zstr, Z_FINISH); + + if (rc != Z_OK && rc != Z_STREAM_END) return; + + mStream.write((char*)mBuffer.data, mBuffer.size - zstr.avail_out); + + if (!mStream.isOpen()) return; + + zstr.next_out = (unsigned char*) mBuffer.data; + zstr.avail_out = mBuffer.size; + } + } + } + + deflateEnd( &mLocalStream->strm ); + + eeSAFE_DELETE( mLocalStream ); +} + +ios_size IOStreamDeflate::read(char * buffer, ios_size length) { + if ( mLocalStream->state != Z_OK || !mStream.isOpen() ) + return 0; + + z_stream& zstr = mLocalStream->strm; + + bool eof = false; + + if (zstr.avail_in == 0) { + ios_size n = 0; + + if ( mStream.isOpen()) { + n = mStream.read((char*)mBuffer.data, mBuffer.size); + } + + if (n > 0) { + zstr.next_in = (unsigned char*) mBuffer.data; + zstr.avail_in = n; + } else { + zstr.next_in = NULL; + zstr.avail_in = 0; + eof = true; + } + } + + zstr.next_out = (unsigned char*) buffer; + zstr.avail_out = length; + + for (;;) { + int rc = deflate(&zstr, eof ? Z_FINISH : Z_NO_FLUSH); + + if (eof && rc == Z_STREAM_END) { + return length - zstr.avail_out; + } + + if (rc != Z_OK) + return 0; + + if (zstr.avail_out == 0) + return static_cast(length); + + if (zstr.avail_in == 0) { + ios_size n = 0; + + if (mStream.isOpen()) { + n = mStream.read((char*)mBuffer.data, mBuffer.size); + } + + if (n > 0) { + zstr.next_in = (unsigned char*) mBuffer.data; + zstr.avail_in = n; + } else { + zstr.next_in = NULL; + zstr.avail_in = 0; + eof = true; + } + } + } +} + +ios_size IOStreamDeflate::write(const char * buffer, ios_size length) { + mLocalStream->writedStream = true; + + if ( mLocalStream->state != Z_OK || !mStream.isOpen() || length == 0 ) + return 0; + + z_stream& zstr = mLocalStream->strm; + + zstr.next_in = (unsigned char*) buffer; + zstr.avail_in = length; + zstr.next_out = mBuffer.data; + zstr.avail_out = mBuffer.size; + + for (;;) { + int rc = deflate(&zstr, Z_NO_FLUSH); + + if (rc != Z_OK) + return 0; + + if (zstr.avail_out == 0) { + ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size); + + if (ret == 0) + return 0; + + zstr.next_out = (unsigned char*) mBuffer.data; + zstr.avail_out = mBuffer.size; + } + + if (zstr.avail_in == 0) { + ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size - zstr.avail_out); + + if (ret == 0) + return 0; + + zstr.next_out = (unsigned char*) mBuffer.data; + zstr.avail_out = mBuffer.size; + + break; + } + } + + return length; +} + +ios_size IOStreamDeflate::seek(ios_size position) { + return mStream.seek( position ); +} + +ios_size IOStreamDeflate::tell() { + return mStream.tell(); +} + +ios_size IOStreamDeflate::getSize() { + return mStream.getSize(); +} + +bool IOStreamDeflate::isOpen() { + return mStream.isOpen(); +} + +const Compression::Mode& IOStreamDeflate::getMode() const { + return mMode; +} + +}} diff --git a/src/eepp/system/iostreamfile.cpp b/src/eepp/system/iostreamfile.cpp index ee6f3ebe8..770f5b76b 100644 --- a/src/eepp/system/iostreamfile.cpp +++ b/src/eepp/system/iostreamfile.cpp @@ -15,9 +15,7 @@ IOStreamFile::IOStreamFile( const std::string& path, const char * modes ) : } IOStreamFile::~IOStreamFile() { - if ( isOpen() ) { - std::fclose(mFS); - } + close(); } ios_size IOStreamFile::read( char * data, ios_size size ) { @@ -80,4 +78,12 @@ void IOStreamFile::flush() { std::fflush( mFS ); } +void IOStreamFile::close() { + if ( isOpen() ) { + std::fclose(mFS); + + mFS = NULL; + } +} + }} From f028f7b2b97b7865ee29979d53a8d30c7e952b47 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Wed, 8 May 2019 02:13:07 -0300 Subject: [PATCH 14/18] Http clean up. --HG-- branch : dev --- include/eepp/system/iostreamdeflate.hpp | 12 +- include/eepp/system/iostreaminflate.hpp | 12 +- include/eepp/system/iostreamstring.hpp | 14 +- premake4.lua | 111 +++++++------- projects/linux/ee.files | 2 + src/eepp/network/http.cpp | 153 +++----------------- src/eepp/network/http/httpstreamchunked.cpp | 112 ++++++++++++++ src/eepp/network/http/httpstreamchunked.hpp | 28 ++++ 8 files changed, 239 insertions(+), 205 deletions(-) create mode 100644 src/eepp/network/http/httpstreamchunked.cpp create mode 100644 src/eepp/network/http/httpstreamchunked.hpp diff --git a/include/eepp/system/iostreamdeflate.hpp b/include/eepp/system/iostreamdeflate.hpp index e301ef330..d92e24801 100644 --- a/include/eepp/system/iostreamdeflate.hpp +++ b/include/eepp/system/iostreamdeflate.hpp @@ -24,17 +24,17 @@ class EE_API IOStreamDeflate : public IOStream { virtual ~IOStreamDeflate(); - ios_size read( char * data, ios_size size ); + virtual ios_size read( char * data, ios_size size ); - ios_size write( const char * data, ios_size size ); + virtual ios_size write( const char * data, ios_size size ); - ios_size seek( ios_size position ); + virtual ios_size seek( ios_size position ); - ios_size tell(); + virtual ios_size tell(); - ios_size getSize(); + virtual ios_size getSize(); - bool isOpen(); + virtual bool isOpen(); const Compression::Mode& getMode() const; protected: diff --git a/include/eepp/system/iostreaminflate.hpp b/include/eepp/system/iostreaminflate.hpp index 3ea1a1c45..5564ee96f 100644 --- a/include/eepp/system/iostreaminflate.hpp +++ b/include/eepp/system/iostreaminflate.hpp @@ -23,17 +23,17 @@ class EE_API IOStreamInflate : public IOStream { virtual ~IOStreamInflate(); - ios_size read( char * data, ios_size size ); + virtual ios_size read( char * data, ios_size size ); - ios_size write( const char * data, ios_size size ); + virtual ios_size write( const char * data, ios_size size ); - ios_size seek( ios_size position ); + virtual ios_size seek( ios_size position ); - ios_size tell(); + virtual ios_size tell(); - ios_size getSize(); + virtual ios_size getSize(); - bool isOpen(); + virtual bool isOpen(); const Compression::Mode& getMode() const; protected: diff --git a/include/eepp/system/iostreamstring.hpp b/include/eepp/system/iostreamstring.hpp index 4f2b4ad84..0866a33ef 100644 --- a/include/eepp/system/iostreamstring.hpp +++ b/include/eepp/system/iostreamstring.hpp @@ -11,19 +11,19 @@ class EE_API IOStreamString : public IOStream { public: IOStreamString(); - ios_size read( char * data, ios_size size ); + virtual ios_size read( char * data, ios_size size ); - ios_size write( const char * data, ios_size size ); + virtual ios_size write( const char * data, ios_size size ); - ios_size write( const std::string& string ); + virtual ios_size write( const std::string& string ); - ios_size seek( ios_size position ); + virtual ios_size seek( ios_size position ); - ios_size tell(); + virtual ios_size tell(); - ios_size getSize(); + virtual ios_size getSize(); - bool isOpen(); + virtual bool isOpen(); void clear(); diff --git a/premake4.lua b/premake4.lua index 76b77feca..0e7cc96c4 100644 --- a/premake4.lua +++ b/premake4.lua @@ -145,8 +145,8 @@ newoption { trigger = "with-static-backend", description = "It will try to compi newoption { trigger = "with-gles2", description = "Compile with GLES2 support" } newoption { trigger = "with-gles1", description = "Compile with GLES1 support" } newoption { trigger = "use-frameworks", description = "In Mac OS X it will try to link the external libraries from its frameworks. For example, instead of linking against SDL2 it will link agains SDL2.framework." } -newoption { - trigger = "with-backend", +newoption { + trigger = "with-backend", description = "Select the backend to use for window and input handling.\n\t\t\tIf no backend is selected or if the selected is not installed the script will search for a backend present in the system, and will use it.", allowed = { { "SDL2", "SDL2 (default and recommended)" }, @@ -167,21 +167,21 @@ function explode(div,str) end function os.get_real() - if _OPTIONS.platform == "ios-arm7" or + if _OPTIONS.platform == "ios-arm7" or _OPTIONS.platform == "ios-x86" or _OPTIONS.platform == "ios-cross-arm7" or _OPTIONS.platform == "ios-cross-x86" then return "ios" end - + if _OPTIONS.platform == "android-arm7" then return "android" end - + if _OPTIONS.platform == "mingw32" then return _OPTIONS.platform end - + if _OPTIONS.platform == "emscripten" then return _OPTIONS.platform end @@ -229,7 +229,7 @@ end function os_findlib( name ) if os.is_real("macosx") and ( is_xcode() or _OPTIONS["use-frameworks"] ) then local path = "/Library/Frameworks/" .. name .. ".framework" - + if os.isdir( path ) then return path end @@ -241,12 +241,12 @@ end function get_backend_link_name( name ) if os.is_real("macosx") and ( is_xcode() or _OPTIONS["use-frameworks"] ) then local fname = name .. ".framework" - + if os_findlib( name ) then -- Search for the framework return fname end end - + return name end @@ -254,7 +254,7 @@ function string.starts(String,Start) if ( _ACTION ) then return string.sub(String,1,string.len(Start))==Start end - + return false end @@ -315,7 +315,7 @@ function build_base_cpp_configuration( package_name ) if not os.is("windows") then buildoptions{ "-fPIC" } end - + set_ios_config() set_xcode_config() @@ -364,7 +364,7 @@ function build_link_configuration( package_name, use_ee_icon ) includedirs { "include" } local extension = ""; - + if package_name == "eepp" then defines { "EE_EXPORTS" } elseif package_name == "eepp-static" then @@ -384,13 +384,13 @@ function build_link_configuration( package_name, use_ee_icon ) add_static_links() links { link_list } end - - if os.is("windows") and not is_vs() then + + if os.is("windows") and not is_vs() then if ( true == use_ee_icon ) then linkoptions { "../../bin/assets/icon/ee.res" } end end - + if os.is_real("emscripten") then extension = ".html" @@ -403,11 +403,11 @@ function build_link_configuration( package_name, use_ee_icon ) linkoptions { "--preload-file assets/" } end end - + if _OPTIONS.platform == "ios-cross-arm7" then extension = ".ios" end - + if _OPTIONS.platform == "ios-cross-x86" then extension = ".x86.ios" end @@ -440,10 +440,10 @@ function build_link_configuration( package_name, use_ee_icon ) fix_shared_lib_linking_path( package_name, "libeepp" ) targetname ( package_name .. extension ) - + configuration "windows" add_cross_config_links() - + configuration "emscripten" linkoptions{ "-O2 -s TOTAL_MEMORY=67108864 -s ASM_JS=1 -s VERBOSE=1 -s DISABLE_EXCEPTION_CATCHING=0 -s USE_SDL=2 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s FULL_ES3=1 -s \"BINARYEN_TRAP_MODE='clamp'\"" } buildoptions { "-fno-strict-aliasing -O2 -s USE_SDL=2 -s PRECISE_F32=1" } @@ -463,7 +463,7 @@ end function generate_os_links() if os.is_real("linux") then multiple_insert( os_links, { "rt", "pthread", "X11", "openal", "GL", "Xcursor" } ) - + if _OPTIONS["with-static-eepp"] then table.insert( os_links, "dl" ) end @@ -488,28 +488,28 @@ function parse_args() if _OPTIONS["with-gles2"] then defines { "EE_GLES2", "SOIL_GLES2" } end - + if _OPTIONS["with-gles1"] then defines { "EE_GLES1", "SOIL_GLES1" } - end + end end function add_static_links() -- The linking order DOES matter -- Expose the symbols that need one static library AFTER adding that static lib - + -- Add static backends if next(static_backends) ~= nil then for _, value in pairs( static_backends ) do linkoptions { value } end end - + if _OPTIONS["with-static-freetype"] or not os_findlib("freetype") then print("Enabled static freetype") links { "freetype-static" } end - + links { "SOIL2-static", "chipmunk-static", "libzip-static", @@ -523,7 +523,7 @@ function add_static_links() if _OPTIONS["with-ssl"] and not _OPTIONS["with-openssl"] then links { "mbedtls-static" } end - + if not os.is_real("haiku") and not os.is_real("ios") and not os.is_real("android") and not os.is_real("emscripten") then links{ "glew-static" } end @@ -544,7 +544,7 @@ function add_sdl2() print("Using SDL2 backend"); files { "src/eepp/window/backend/SDL2/*.cpp" } defines { "EE_BACKEND_SDL_ACTIVE", "EE_SDL_VERSION_2" } - + if not can_add_static_backend("SDL2") then table.insert( link_list, get_backend_link_name( "SDL2" ) ) else @@ -556,7 +556,7 @@ function add_sfml() print("Using SFML backend"); files { "src/eepp/window/backend/SFML/*.cpp" } defines { "EE_BACKEND_SFML_ACTIVE" } - + if not can_add_static_backend("SFML") then table.insert( link_list, get_backend_link_name( "sfml-system" ) ) table.insert( link_list, get_backend_link_name( "sfml-window" ) ) @@ -577,13 +577,13 @@ end function set_ios_config() if _OPTIONS.platform == "ios-arm7" or _OPTIONS.platform == "ios-x86" then local err = false - + if nil == os.getenv("TOOLCHAINPATH") then print("You must set TOOLCHAINPATH enviroment variable.") print("\tExample: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/usr/bin/") err = true end - + if nil == os.getenv("SYSROOTPATH") then print("You must set SYSROOTPATH enviroment variable.") print("\tExample: /Applications/Xcode.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS5.0.sdk") @@ -595,7 +595,7 @@ function set_ios_config() print("\tExample: 5.0") err = true end - + if err then os.exit(1) end @@ -604,14 +604,14 @@ function set_ios_config() local framework_path = sysroot_path .. "/System/Library/Frameworks" local framework_libs_path = framework_path .. "/usr/lib" local sysroot_ver = " -miphoneos-version-min=" .. os.getenv("IOSVERSION") .. " -isysroot " .. sysroot_path - + buildoptions { sysroot_ver .. " -I" .. sysroot_path .. "/usr/include" } linkoptions { sysroot_ver } libdirs { framework_libs_path } linkoptions { " -F" .. framework_path .. " -L" .. framework_libs_path .. " -isysroot " .. sysroot_path } includedirs { "src/thirdparty/SDL2/include" } end - + if _OPTIONS.platform == "ios-cross-arm7" or _OPTIONS.platform == "ios-cross-x86" then includedirs { "src/thirdparty/SDL2/include" } end @@ -625,7 +625,7 @@ function backend_is( name, libname ) if next(backends) == nil then backends = string.explode(_OPTIONS["with-backend"],",") end - + local backend_sel = table.contains( backends, name ) local ret_val = os_findlib( libname ) and backend_sel @@ -641,7 +641,7 @@ function backend_is( name, libname ) return ret_val end -function select_backend() +function select_backend() if backend_is("SDL2", "SDL2") then print("Selected SDL2") add_sdl2() @@ -693,10 +693,10 @@ end function build_eepp( build_name ) includedirs { "include", "src", "src/thirdparty", "include/eepp/thirdparty", "src/thirdparty/freetype2/include", "src/thirdparty/zlib", "src/thirdparty/libogg/include", "src/thirdparty/libvorbis/include", "src/thirdparty/mbedtls/include" } - + set_ios_config() set_xcode_config() - + add_static_links() if is_vs() then @@ -725,6 +725,7 @@ function build_eepp( build_name ) "src/eepp/window/platform/null/*.cpp", "src/eepp/network/*.cpp", "src/eepp/network/ssl/*.cpp", + "src/eepp/network/http/*.cpp", "src/eepp/scene/*.cpp", "src/eepp/scene/actions/*.cpp", "src/eepp/ui/*.cpp", @@ -736,11 +737,11 @@ function build_eepp( build_name ) "src/eepp/maps/*.cpp", "src/eepp/maps/mapeditor/*.cpp" } - + check_ssl_support() - + select_backend() - + if not _OPTIONS["with-static-freetype"] and os_findlib("freetype") then table.insert( link_list, get_backend_link_name( "freetype" ) ) end @@ -748,19 +749,19 @@ function build_eepp( build_name ) multiple_insert( link_list, os_links ) links { link_list } - + build_link_configuration( build_name ) configuration "windows" files { "src/eepp/window/platform/win/*.cpp" } add_cross_config_links() - + configuration "linux" files { "src/eepp/window/platform/x11/*.cpp" } - + configuration "macosx" files { "src/eepp/window/platform/osx/*.cpp" } - + configuration "emscripten" if _OPTIONS["force-gles1"] then defines{ "EE_GLES1_DEFAULT" } @@ -776,7 +777,7 @@ function set_targetdir( dir ) end solution "eepp" - + targetdir("./bin/") configurations { "debug", "release" } @@ -865,7 +866,7 @@ solution "eepp" files { "src/thirdparty/freetype2/src/**.c" } includedirs { "src/thirdparty/freetype2/include" } build_base_configuration( "freetype" ) - + project "chipmunk-static" kind "StaticLib" @@ -900,15 +901,15 @@ solution "eepp" language "C++" set_targetdir("libs/" .. os.get_real() .. "/thirdparty/") includedirs { "src/thirdparty/efsw/include", "src/thirdparty/efsw/src" } - + if os.is("windows") then osfiles = "src/thirdparty/efsw/src/efsw/platform/win/*.cpp" else osfiles = "src/thirdparty/efsw/src/efsw/platform/posix/*.cpp" end - + files { "src/thirdparty/efsw/src/efsw/*.cpp", osfiles } - + if os.is("windows") then excludes { "src/thirdparty/efsw/src/efsw/WatcherKqueue.cpp", "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherKqueue.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" } elseif os.is("linux") then @@ -918,7 +919,7 @@ solution "eepp" elseif os.is("freebsd") then excludes { "src/thirdparty/efsw/src/efsw/WatcherInotify.cpp", "src/thirdparty/efsw/src/efsw/WatcherWin32.cpp", "src/thirdparty/efsw/src/efsw/WatcherFSEvents.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherInotify.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherWin32.cpp", "src/thirdparty/efsw/src/efsw/FileWatcherFSEvents.cpp" } end - + build_base_cpp_configuration( "efsw" ) project "eepp-main" @@ -932,7 +933,7 @@ solution "eepp" language "C++" set_targetdir("libs/" .. os.get_real() .. "/") build_eepp( "eepp-static" ) - + project "eepp-shared" kind "SharedLib" language "C++" @@ -1007,20 +1008,20 @@ solution "eepp" language "C++" files { "src/tools/mapeditor/*.cpp" } build_link_configuration( "eepp-MapEditor", true ) - + project "eepp-uieditor" set_kind() language "C++" includedirs { "src/thirdparty/efsw/include", "src/thirdparty" } - + if not os.is("windows") and not os.is("haiku") then links { "pthread" } end - + links { "efsw-static", "pugixml-static" } files { "src/tools/uieditor/*.cpp" } build_link_configuration( "eepp-UIEditor", true ) - + if os.isfile("external_projects.lua") then dofile("external_projects.lua") end diff --git a/projects/linux/ee.files b/projects/linux/ee.files index 8e8c35f33..e3841c305 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -591,6 +591,8 @@ ../../src/eepp/math/transform.cpp ../../src/eepp/network/ftp.cpp ../../src/eepp/network/http.cpp +../../src/eepp/network/http/httpstreamchunked.cpp +../../src/eepp/network/http/httpstreamchunked.hpp ../../src/eepp/network/ipaddress.cpp ../../src/eepp/network/packet.cpp ../../src/eepp/network/platform/platformimpl.hpp diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index af5449def..f93b0482b 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -13,6 +14,7 @@ #include using namespace EE::Network::SSL; +using namespace EE::Network::Private; namespace EE { namespace Network { @@ -592,17 +594,14 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri char * eol = NULL; // end of line char * bol = NULL; // beginning of line char buffer[PACKET_BUFFER_SIZE+1]; - std::string headerBuffer; - std::string chunkBuffer; - IOStreamString fileBuffer; bool isnheader = false; bool chunked = false; - bool chunkNewBuffer = false; - bool chunkEnded = false; bool compressed = false; - IOStreamInflate * inflateStream = NULL; - ios_size inflateChunkSize = 0; std::size_t contentLength = 0; + std::string headerBuffer; + HttpStreamChunked * chunkedStream = NULL; + IOStreamInflate * inflateStream = NULL; + IOStream * bufferStream = NULL; while (!request.isCancelled() && ( status = mConnection->getSocket()->receive(buffer, PACKET_BUFFER_SIZE, readed) ) == Socket::Done) { char * readBuffer = buffer; @@ -654,11 +653,16 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri if ( compressed ) { Compression::Mode compressionMode = "gzip" == encoding ? Compression::MODE_GZIP : Compression::MODE_DEFLATE; - inflateChunkSize = Compression::getModeDefaultChunkSize( compressionMode ); - inflateStream = IOStreamInflate::New( writeTo, compressionMode ); } + IOStream& writeToStream = compressed ? *inflateStream : writeTo; + + if ( chunked ) + chunkedStream = eeNew( HttpStreamChunked, ( writeToStream ) ); + + bufferStream = chunked ? chunkedStream : ( compressed ? inflateStream : &writeTo ); + // Get the content length if ( !received.getField("content-length").empty() ) { if ( !String::fromString( contentLength, received.getField("content-length") ) ) @@ -689,6 +693,8 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri request.mRedirectionCount++; + eeSAFE_DELETE( chunkedStream ); + eeSAFE_DELETE( inflateStream ); return http.downloadRequest( request, writeTo, timeout ); } } @@ -715,124 +721,8 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri if ( isnheader ) { currentTotalBytes += readed; - if ( chunked ) { - if ( readed > 0 ) { - // If the chunk reading ended we just add the buffer received as a header - // Otherwise we process the buffer data as chunk - if ( !chunkEnded ) { - // Keep a chunk buffer until the end of chunk is found - chunkBuffer.append( readBuffer, readBuffer + readed ); - - // If the new chunk starts with \r\n and the last removed chunk - // did not contain the trailing \r\n, we remove it to detect - // correctly the next length data - if ( chunkNewBuffer ) { - if ( chunkBuffer.substr( 0, 2 ) == "\r\n" ) { - chunkBuffer = chunkBuffer.substr( 2 ); - } - - chunkNewBuffer = false; - } - - bool retry; - - do { - retry = false; - - // Check for the first \r\n to find the end of the length definition - std::string::size_type lenEnd = chunkBuffer.find_first_of("\r\n"); - - if ( lenEnd != std::string::npos ) { - std::string::size_type firstCharPos = lenEnd + 2; - unsigned long length; - - // Get the length of the chunk - bool res = String::fromString( length, chunkBuffer.substr(0, lenEnd), std::hex ); - - // If the length is solved... - if ( res ) { - // And it's bigger than 0, means that there are more chunks - if ( length > 0 ) { - // Check if the chunk buffer size at least equals to the length reported - if ( chunkBuffer.size() - firstCharPos >= length ) { - // In that case write the chunk to the file buffer - fileBuffer.write( &chunkBuffer[firstCharPos], length ); - - // And keep the remaining not completed chunk - chunkBuffer = chunkBuffer.substr( firstCharPos + length ); - - // Check if already have the \r\n of the next length in the buffer - if ( !chunkBuffer.empty() ) { - std::size_t pos = 0; - - // Remove al the \r\n remaining - while ( pos < chunkBuffer.size() && 0 == strncmp( &chunkBuffer[pos], "\r\n", 2 ) ) { - pos += 2; - } - - if ( pos > 0 ) { - // Remove it to be able to read the next length - chunkBuffer = chunkBuffer.substr( pos ); - } - - // If still the chunk is not empty it could be another chunk - // already received, so we check that retrying - if ( !chunkBuffer.empty() ) { - std::string::size_type lenEnd = chunkBuffer.find_first_of("\r\n"); - - if ( lenEnd != std::string::npos ) { - bool res = String::fromString( length, chunkBuffer.substr(0, lenEnd), std::hex ); - - if ( res && length > 0 ) { - retry = true; - } - } - } - } - - // If the next chunk received starts with \r\n it's because - // it's part of the chunk size information, so we need to flag it - // to remove it - chunkNewBuffer = true; - } - } else { - // If the value is 0 means that the data ended - // But after this we can receive extra headers - chunkEnded = true; - chunkBuffer.clear(); - } - } - } - } while ( retry ); - } else { - headerBuffer.append( readBuffer, readBuffer + readed ); - } - } - } else if ( readed > 0 ) { - // If not chunked just write into the file buffer - fileBuffer.write( readBuffer, readed ); - } - - if ( compressed ) { - if ( fileBuffer.getSize() - inflateChunkSize >= 0 ) { - inflateStream->write( fileBuffer.getStreamPointer(), inflateChunkSize ); - - IOStreamString newFileBuffer; - - fileBuffer.seek(inflateChunkSize); - - std::size_t trailing = fileBuffer.getSize() - inflateChunkSize; - - if ( trailing > 0 ) - newFileBuffer.write( fileBuffer.getPositionPointer(), trailing ); - - fileBuffer = newFileBuffer; - } - } else { - fileBuffer.seek(0); - writeTo.write( fileBuffer.getPositionPointer(), fileBuffer.getSize() ); - fileBuffer.clear(); - } + if ( readed > 0 ) + bufferStream->write( readBuffer, readed ); if ( request.getProgressCallback() ) { if ( !request.getProgressCallback()( *this, request, contentLength, currentTotalBytes ) ) { @@ -843,20 +733,21 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } } + if ( chunked && NULL != chunkedStream && !chunkedStream->getHeaderBuffer().empty() ) { + headerBuffer.append( chunkedStream->getHeaderBuffer() ); + } + if ( !headerBuffer.empty() ) { std::istringstream in(headerBuffer); received.parseFields(in); } - if ( compressed && fileBuffer.getSize() > 0 ) { - inflateStream->write( fileBuffer.getStreamPointer(), fileBuffer.getSize() ); - } - if ( status == Socket::Status::Disconnected ) { mConnection->setConnected(false); mConnection->setTunneled(false); } + eeSAFE_DELETE( chunkedStream ); eeSAFE_DELETE( inflateStream ); } else { mConnection->setConnected(false); diff --git a/src/eepp/network/http/httpstreamchunked.cpp b/src/eepp/network/http/httpstreamchunked.cpp new file mode 100644 index 000000000..806fa055e --- /dev/null +++ b/src/eepp/network/http/httpstreamchunked.cpp @@ -0,0 +1,112 @@ + #include + +namespace EE { namespace Network { namespace Private { + +HttpStreamChunked::HttpStreamChunked(IOStream & mWriteTo) : + mWriteTo( mWriteTo ), + mChunkNewBuffer(false), + mChunkEnded(false) +{} + +ios_size HttpStreamChunked::write(const char * data, ios_size size) { + ios_size writeTotal = 0; + + // If the chunk reading ended we just add the buffer received as a header + // Otherwise we process the buffer data as chunk + if ( !mChunkEnded ) { + // Keep a chunk buffer until the end of chunk is found + mChunkBuffer.append( data, size ); + + // If the new chunk starts with \r\n and the last removed chunk + // did not contain the trailing \r\n, we remove it to detect + // correctly the next length data + if ( mChunkNewBuffer ) { + if ( mChunkBuffer.substr( 0, 2 ) == "\r\n" ) { + mChunkBuffer = mChunkBuffer.substr( 2 ); + } + + mChunkNewBuffer = false; + } + + bool retry; + + do { + retry = false; + + // Check for the first \r\n to find the end of the length definition + std::string::size_type lenEnd = mChunkBuffer.find_first_of("\r\n"); + + if ( lenEnd != std::string::npos ) { + std::string::size_type firstCharPos = lenEnd + 2; + unsigned long length; + + // Get the length of the chunk + bool res = String::fromString( length, mChunkBuffer.substr(0, lenEnd), std::hex ); + + // If the length is solved... + if ( res ) { + // And it's bigger than 0, means that there are more chunks + if ( length > 0 ) { + // Check if the chunk buffer size at least equals to the length reported + if ( mChunkBuffer.size() - firstCharPos >= length ) { + // In that case write the chunk to the file buffer + writeTotal = mWriteTo.write( &mChunkBuffer[firstCharPos], length ); + + // And keep the remaining not completed chunk + mChunkBuffer = mChunkBuffer.substr( firstCharPos + length ); + + // Check if already have the \r\n of the next length in the buffer + if ( !mChunkBuffer.empty() ) { + std::size_t pos = 0; + + // Remove al the \r\n remaining + while ( pos < mChunkBuffer.size() && 0 == strncmp( &mChunkBuffer[pos], "\r\n", 2 ) ) { + pos += 2; + } + + if ( pos > 0 ) { + // Remove it to be able to read the next length + mChunkBuffer = mChunkBuffer.substr( pos ); + } + + // If still the chunk is not empty it could be another chunk + // already received, so we check that retrying + if ( !mChunkBuffer.empty() ) { + std::string::size_type lenEnd = mChunkBuffer.find_first_of("\r\n"); + + if ( lenEnd != std::string::npos ) { + bool res = String::fromString( length, mChunkBuffer.substr(0, lenEnd), std::hex ); + + if ( res && length > 0 ) { + retry = true; + } + } + } + } + + // If the next chunk received starts with \r\n it's because + // it's part of the chunk size information, so we need to flag it + // to remove it + mChunkNewBuffer = true; + } + } else { + // If the value is 0 means that the data ended + // But after this we can receive extra headers + mChunkEnded = true; + mChunkBuffer.clear(); + } + } + } + } while ( retry ); + } else { + mHeaderBuffer.append( data, size ); + } + + return writeTotal; +} + +const std::string& HttpStreamChunked::getHeaderBuffer() const { + return mHeaderBuffer; +} + +}}} diff --git a/src/eepp/network/http/httpstreamchunked.hpp b/src/eepp/network/http/httpstreamchunked.hpp new file mode 100644 index 000000000..7f4f0d122 --- /dev/null +++ b/src/eepp/network/http/httpstreamchunked.hpp @@ -0,0 +1,28 @@ +#ifndef EE_NETWORK_HTTPSTREAMCHUNKED_HPP +#define EE_NETWORK_HTTPSTREAMCHUNKED_HPP + +#include +#include + +using namespace EE::System; + +namespace EE { namespace Network { namespace Private { + +class HttpStreamChunked : public IOStreamString { + public: + HttpStreamChunked( IOStream& mWriteTo ); + + ios_size write( const char * data, ios_size size ); + + const std::string& getHeaderBuffer() const; + protected: + IOStream& mWriteTo; + std::string mChunkBuffer; + std::string mHeaderBuffer; + bool mChunkNewBuffer = false; + bool mChunkEnded = false; +}; + +}}} + +#endif // EE_NETWORK_HTTPSTREAMCHUNKED_HPP From 39ec343096a7ea3d82e1caba96e780292c37b549 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Wed, 8 May 2019 22:06:08 -0300 Subject: [PATCH 15/18] Minor optimization in HTTP requests. --HG-- branch : dev --- src/eepp/network/http.cpp | 8 ++++++++ src/eepp/system/iostreaminflate.cpp | 7 +++---- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index f93b0482b..4ed75267a 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -730,6 +730,14 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri break; } } + + // If the response is compressed and the stream ended means that we received + // the message. So we can skip the socket receive call. + if ( ( compressed && NULL != inflateStream && !inflateStream->isOpen() ) || + ( contentLength > 0 && contentLength == currentTotalBytes ) + ) { + break; + } } } diff --git a/src/eepp/system/iostreaminflate.cpp b/src/eepp/system/iostreaminflate.cpp index 4ed40641c..269a1a4dc 100644 --- a/src/eepp/system/iostreaminflate.cpp +++ b/src/eepp/system/iostreaminflate.cpp @@ -106,10 +106,9 @@ ios_size IOStreamInflate::write(const char * buffer, ios_size length) { int rc = inflate(&zstr, Z_NO_FLUSH); if (rc == Z_STREAM_END) { - ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size - zstr.avail_out); + length = mStream.write( (const char*)mBuffer.data, mBuffer.size - zstr.avail_out); - if (ret == 0) - return 0; + mLocalStream->state = rc; break; } @@ -156,7 +155,7 @@ ios_size IOStreamInflate::getSize() { } bool IOStreamInflate::isOpen() { - return mStream.isOpen(); + return mStream.isOpen() && mLocalStream->state != Z_STREAM_END; } const Compression::Mode& IOStreamInflate::getMode() const { From cf281eaa12ea5c4e648e5f93e5ff269ca44c8266 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 10 May 2019 00:40:43 -0300 Subject: [PATCH 16/18] Added continue / resume download support for HTTP Requests. --HG-- branch : dev --- include/eepp/network/http.hpp | 10 +++++ src/eepp/network/http.cpp | 44 +++++++++++++++++++++- src/examples/http_request/http_request.cpp | 11 +++++- 3 files changed, 62 insertions(+), 3 deletions(-) diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index 76090a591..75cc2b1f5 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -168,6 +168,12 @@ class EE_API Http : NonCopyable { */ void setCompressedResponse(const bool& compressedResponse); + /** Resumes download if a file is already present */ + void setContinue(const bool& resume); + + /** @return If must continue a download previously started. */ + const bool& isContinue() const; + private: friend class Http; @@ -194,6 +200,7 @@ class EE_API Http : NonCopyable { bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request bool mFollowRedirect; ///< Follows redirect response codes bool mCompressedResponse; ///< Request comrpessed response + bool mContinue; ///< Resume download mutable bool mCancel; ///< Cancel state of current request ProgressCallback mProgressCallback; ///< Progress callback unsigned int mMaxRedirections; ///< Maximun number of redirections allowed @@ -261,6 +268,9 @@ class EE_API Http : NonCopyable { ** @return Value of the field, or empty string if not found */ const std::string& getField(const std::string& field) const; + /** @return If the field is found in the response headers. */ + bool hasField(const std::string& field) const; + /** @brief Get the response status code ** The status code should be the first thing to be checked ** after receiving a response, it defines whether it is a diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 4ed75267a..473d1f8df 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -52,6 +52,7 @@ Http::Request::Request(const std::string& uri, Method method, const std::string& mValidateHostname( validateHostname ), mFollowRedirect( followRedirect ), mCompressedResponse( compressedResponse ), + mContinue( false ), mCancel( false ), mMaxRedirections( 10 ), mRedirectionCount( 0 ) @@ -161,6 +162,14 @@ std::string Http::Request::prepareTunnel(const Http& http) { return out.str(); } +void Http::Request::setContinue(const bool& resume) { + mContinue = resume; +} + +const bool& Http::Request::isContinue() const { + return mContinue; +} + const bool& Http::Request::isCompressedResponse() const { return mCompressedResponse; } @@ -273,6 +282,10 @@ const std::string& Http::Response::getField(const std::string& field) const { } } +bool Http::Response::hasField(const std::string & field) const { + return mFields.find(String::toLower(field)) != mFields.end(); +} + Http::Response::Status Http::Response::getStatus() const { return mStatus; } @@ -579,6 +592,33 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } } + if ( request.isContinue() ) { + std::size_t continueLength = writeTo.getSize(); + + if ( continueLength > 0 ) { + IOStreamString responseHeadBody; + Request requestHead = request; + requestHead.setContinue( false ); + requestHead.setMethod( Request::Head ); + Response responseHead = downloadRequest( requestHead, responseHeadBody ); + std::size_t contentLength = 0; + + if ( responseHead.hasField("Accept-Ranges") && + responseHead.hasField("Content-Length") && + String::fromString( contentLength, responseHead.getField("Content-Length") ) && + contentLength > 0 && + continueLength < contentLength + ) + { + writeTo.seek( continueLength ); + Request newRequest( request ); + newRequest.setContinue( false ); + newRequest.setField( "Range", String::format( "bytes=%lu-%lu", continueLength, contentLength ) ); + return downloadRequest( newRequest, writeTo, timeout ); + } + } + } + // Convert the request to string and send it through the connected socket std::string requestStr = toSend.prepare(*this); @@ -771,8 +811,8 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri return received; } -Http::Response Http::downloadRequest(const Http::Request & request, std::string writePath, Time timeout) { - IOStreamFile file( writePath, "wb+" ); +Http::Response Http::downloadRequest(const Http::Request& request, std::string writePath, Time timeout) { + IOStreamFile file( writePath, request.isContinue() ? "ab+" : "wb+" ); return downloadRequest( request, file, timeout ); } diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index e61c5b28a..e2b697262 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -17,6 +17,7 @@ void printResponseHeaders( Http::Response& response ) { EE_MAIN_FUNC int main (int argc, char * argv []) { args::ArgumentParser parser("HTTP request program example"); args::HelpFlag help(parser, "help", "Display this help menu", {'h', "help"}); + args::Flag resume(parser, "continue", "Resume getting a partially-downloaded file", {'c',"continue"}); args::Flag compressed(parser, "compressed", "Request compressed response", {"compressed"}); args::ValueFlag postData(parser, "data", "HTTP POST data", {'d', "data"}); args::ValueFlagList headers(parser, "header", "Pass custom header(s) to server", {'H', "header"}); @@ -130,7 +131,10 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { } // Set the proxy for the request - if ( proxy ) { + char * http_proxy = getenv( "http_proxy" ); + if ( !proxy && NULL != http_proxy ) { + http.setProxy( URI( http_proxy ) ); + } else if ( proxy ) { http.setProxy( URI( proxy.Get() ) ); } @@ -139,6 +143,11 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { request.setCompressedResponse( true ); } + // Resume existing download + if ( resume ) { + request.setContinue( true ); + } + if ( !output ) { // Send the request Http::Response response = http.sendRequest(request); From 46af3b4e1fdb330b09d856c954eff6cfc84584d5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Fri, 10 May 2019 01:49:47 -0300 Subject: [PATCH 17/18] More detailed request progress information. Minor optimization. --HG-- branch : dev --- include/eepp/network/http.hpp | 373 +++++++++++---------- src/eepp/network/http.cpp | 29 +- src/examples/http_request/http_request.cpp | 8 +- 3 files changed, 219 insertions(+), 191 deletions(-) diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index 75cc2b1f5..9ffa6d7f9 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -26,188 +26,6 @@ namespace EE { namespace Network { /** @brief A HTTP client */ class EE_API Http : NonCopyable { public : - /** @brief Define a HTTP request */ - class EE_API Request { - public : - /** @brief Enumerate the available HTTP methods for a request */ - enum Method { - Get, ///< The GET method requests a representation of the specified resource. Requests using GET should only retrieve data. - Head, ///< Request a page's header only - Post, ///< The POST method is used to submit an entity to the specified resource, often causing a change in state or side effects on the server. - Put, ///< The PUT method replaces all current representations of the target resource with the request payload. - Delete, ///< The DELETE method deletes the specified resource. - Options, ///< The OPTIONS method is used to describe the communication options for the target resource. - Patch, ///< The PATCH method is used to apply partial modifications to a resource. - Connect ///< The CONNECT method starts two-way communications with the requested resource. It can be used to open a tunnel. - }; - - /** @return Method from a method name string. */ - static Method methodFromString( std::string methodString ); - - /** @return The method string from a method */ - static std::string methodToString( const Method& method ); - - /** @brief Default constructor - ** This constructor creates a GET request, with the root - ** URI ("/") and an empty body. - ** @param uri Target URI - ** @param method Method to use for the request - ** @param body Content of the request's body - ** @param validateCertificate Enables certificate validation for https request - ** @param validateHostname Enables hostname validation for https request - ** @param followRedirect Allow follor redirects to the request. - ** @param compressedResponse Set if the requested response should be compressed ( if available ) - */ - Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = true, bool validateHostname = true, bool followRedirect = true, bool compressedResponse = false); - - /** @brief Set the value of a field - ** The field is created if it doesn't exist. The name of - ** the field is case insensitive. - ** By default, a request doesn't contain any field (but the - ** mandatory fields are added later by the HTTP client when - ** sending the request). - ** @param field Name of the field to set - ** @param value Value of the field */ - void setField(const std::string& field, const std::string& value); - - /** @brief Check if the request defines a field - ** This function uses case-insensitive comparisons. - ** @param field Name of the field to test - ** @return True if the field exists, false otherwise */ - bool hasField(const std::string& field) const; - - /** @brief Get the value of a field - ** If the field @a field is not found in the response header, - ** the empty string is returned. This function uses - ** case-insensitive comparisons. - ** @param field Name of the field to get - ** @return Value of the field, or empty string if not found */ - const std::string& getField(const std::string& field) const; - - /** @brief Set the request method - ** See the Method enumeration for a complete list of all - ** the availale methods. - ** The method is Http::Request::Get by default. - ** @param method Method to use for the request */ - void setMethod(Method method); - - /** @brief Set the requested URI - ** The URI is the resource (usually a web page or a file) - ** that you want to get or post. - ** The URI is "/" (the root page) by default. - ** @param uri URI to request, relative to the host */ - void setUri(const std::string& uri); - - /** @brief Set the HTTP version for the request - ** The HTTP version is 1.0 by default. - ** @param major Major HTTP version number - ** @param minor Minor HTTP version number */ - void setHttpVersion(unsigned int major, unsigned int minor); - - /** @brief Set the body of the request - ** The body of a request is optional and only makes sense - ** for POST requests. It is ignored for all other methods. - ** The body is empty by default. - ** @param body Content of the body */ - void setBody(const std::string& body); - - /** @return The request Uri */ - const std::string& getUri() const; - - /** @return If SSL certificate validation is enabled */ - const bool& getValidateCertificate() const; - - /** Enable/disable SSL certificate validation */ - void setValidateCertificate( bool enable ); - - /** @return If SSL hostname validation is enabled */ - const bool& getValidateHostname() const; - - /** Enable/disable SSL hostname validation */ - void setValidateHostname( bool enable ); - - /** @return If requests follow redirects */ - const bool& getFollowRedirect() const; - - /** Enables/Disables follow redirects */ - void setFollowRedirect( bool follow ); - - /** @return The maximun number of redirects allowd if follow redirect is enabled. */ - const unsigned int& getMaxRedirects() const; - - /** Set the maximun number of redirects allowed if follow redirect is enabled. */ - void setMaxRedirects( unsigned int maxRedirects ); - - /** Definition of the current progress callback - * @param http The http client - * @param request The http request - * @param totalBytes The total bytes of the document / files ( only available if Content-Length is returned, otherwise is 0 ) - * @param currentBytes Current received total bytes - * @return True if continue the request, false will cancel the current request. - */ - typedef std::function ProgressCallback; - - /** Sets a progress callback */ - void setProgressCallback( const ProgressCallback& progressCallback ); - - /** Get the progress callback */ - const ProgressCallback& getProgressCallback() const; - - /** Cancels the current request if being processed */ - void cancel(); - - /** @return True if the current request was cancelled */ - const bool& isCancelled() const; - - /** @return If requests a compressed response */ - const bool& isCompressedResponse() const; - - /** Set to request a compressed response from the server - ** The returned response will be automatically decompressed - ** by the client. - */ - void setCompressedResponse(const bool& compressedResponse); - - /** Resumes download if a file is already present */ - void setContinue(const bool& resume); - - /** @return If must continue a download previously started. */ - const bool& isContinue() const; - - private: - friend class Http; - - /** @brief Prepare the final request to send to the server - ** This is used internally by Http before sending the - ** request to the web server. - ** @return String containing the request, ready to be sent */ - std::string prepare(const Http& http) const; - - /** Prepares a http tunnel request */ - std::string prepareTunnel(const Http& http); - - // Types - typedef std::map FieldTable; - - // Member data - FieldTable mFields; ///< Fields of the header associated to their value - Method mMethod; ///< Method to use for the request - std::string mUri; ///< Target URI of the request - unsigned int mMajorVersion; ///< Major HTTP version - unsigned int mMinorVersion; ///< Minor HTTP version - std::string mBody; ///< Body of the request - bool mValidateCertificate; ///< Validates the SSL certificate in case of an HTTPS request - bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request - bool mFollowRedirect; ///< Follows redirect response codes - bool mCompressedResponse; ///< Request comrpessed response - bool mContinue; ///< Resume download - mutable bool mCancel; ///< Cancel state of current request - ProgressCallback mProgressCallback; ///< Progress callback - unsigned int mMaxRedirections; ///< Maximun number of redirections allowed - mutable unsigned int mRedirectionCount; ///< Number of redirections followed by the request - URI mProxy; ///< Proxy information - }; - /** @brief Define a HTTP response */ class EE_API Response { public: @@ -323,6 +141,197 @@ class EE_API Http : NonCopyable { std::string mBody; ///< Body of the response }; + /** @brief Define a HTTP request */ + class EE_API Request { + public : + /** @brief Enumerate the available HTTP methods for a request */ + enum Method { + Get, ///< The GET method requests a representation of the specified resource. Requests using GET should only retrieve data. + Head, ///< Request a page's header only + Post, ///< The POST method is used to submit an entity to the specified resource, often causing a change in state or side effects on the server. + Put, ///< The PUT method replaces all current representations of the target resource with the request payload. + Delete, ///< The DELETE method deletes the specified resource. + Options, ///< The OPTIONS method is used to describe the communication options for the target resource. + Patch, ///< The PATCH method is used to apply partial modifications to a resource. + Connect ///< The CONNECT method starts two-way communications with the requested resource. It can be used to open a tunnel. + }; + + /** @brief Enumerate the available states for a request */ + enum Status { + Connected, ///< Connected to server. + Sent, ///< Request sent to the server. + HeaderReceived, ///< Header received. + ContentReceived ///< Content received. + }; + + /** @return Method from a method name string. */ + static Method methodFromString( std::string methodString ); + + /** @return The method string from a method */ + static std::string methodToString( const Method& method ); + + /** @brief Default constructor + ** This constructor creates a GET request, with the root + ** URI ("/") and an empty body. + ** @param uri Target URI + ** @param method Method to use for the request + ** @param body Content of the request's body + ** @param validateCertificate Enables certificate validation for https request + ** @param validateHostname Enables hostname validation for https request + ** @param followRedirect Allow follor redirects to the request. + ** @param compressedResponse Set if the requested response should be compressed ( if available ) + */ + Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = true, bool validateHostname = true, bool followRedirect = true, bool compressedResponse = false); + + /** @brief Set the value of a field + ** The field is created if it doesn't exist. The name of + ** the field is case insensitive. + ** By default, a request doesn't contain any field (but the + ** mandatory fields are added later by the HTTP client when + ** sending the request). + ** @param field Name of the field to set + ** @param value Value of the field */ + void setField(const std::string& field, const std::string& value); + + /** @brief Check if the request defines a field + ** This function uses case-insensitive comparisons. + ** @param field Name of the field to test + ** @return True if the field exists, false otherwise */ + bool hasField(const std::string& field) const; + + /** @brief Get the value of a field + ** If the field @a field is not found in the response header, + ** the empty string is returned. This function uses + ** case-insensitive comparisons. + ** @param field Name of the field to get + ** @return Value of the field, or empty string if not found */ + const std::string& getField(const std::string& field) const; + + /** @brief Set the request method + ** See the Method enumeration for a complete list of all + ** the availale methods. + ** The method is Http::Request::Get by default. + ** @param method Method to use for the request */ + void setMethod(Method method); + + /** @brief Set the requested URI + ** The URI is the resource (usually a web page or a file) + ** that you want to get or post. + ** The URI is "/" (the root page) by default. + ** @param uri URI to request, relative to the host */ + void setUri(const std::string& uri); + + /** @brief Set the HTTP version for the request + ** The HTTP version is 1.0 by default. + ** @param major Major HTTP version number + ** @param minor Minor HTTP version number */ + void setHttpVersion(unsigned int major, unsigned int minor); + + /** @brief Set the body of the request + ** The body of a request is optional and only makes sense + ** for POST requests. It is ignored for all other methods. + ** The body is empty by default. + ** @param body Content of the body */ + void setBody(const std::string& body); + + /** @return The request Uri */ + const std::string& getUri() const; + + /** @return If SSL certificate validation is enabled */ + const bool& getValidateCertificate() const; + + /** Enable/disable SSL certificate validation */ + void setValidateCertificate( bool enable ); + + /** @return If SSL hostname validation is enabled */ + const bool& getValidateHostname() const; + + /** Enable/disable SSL hostname validation */ + void setValidateHostname( bool enable ); + + /** @return If requests follow redirects */ + const bool& getFollowRedirect() const; + + /** Enables/Disables follow redirects */ + void setFollowRedirect( bool follow ); + + /** @return The maximun number of redirects allowd if follow redirect is enabled. */ + const unsigned int& getMaxRedirects() const; + + /** Set the maximun number of redirects allowed if follow redirect is enabled. */ + void setMaxRedirects( unsigned int maxRedirects ); + + /** Definition of the current progress callback + * @param http The http client + * @param request The http request + * @param status The status of the progress event + * @param totalBytes The total bytes of the document / files ( only available if Content-Length is returned, otherwise is 0 ) + * @param currentBytes Current received total bytes + * @return True if continue the request, false will cancel the current request. + */ + typedef std::function ProgressCallback; + + /** Sets a progress callback */ + void setProgressCallback( const ProgressCallback& progressCallback ); + + /** Get the progress callback */ + const ProgressCallback& getProgressCallback() const; + + /** Cancels the current request if being processed */ + void cancel(); + + /** @return True if the current request was cancelled */ + const bool& isCancelled() const; + + /** @return If requests a compressed response */ + const bool& isCompressedResponse() const; + + /** Set to request a compressed response from the server + ** The returned response will be automatically decompressed + ** by the client. + */ + void setCompressedResponse(const bool& compressedResponse); + + /** Resumes download if a file is already present */ + void setContinue(const bool& resume); + + /** @return If must continue a download previously started. */ + const bool& isContinue() const; + + private: + friend class Http; + + /** @brief Prepare the final request to send to the server + ** This is used internally by Http before sending the + ** request to the web server. + ** @return String containing the request, ready to be sent */ + std::string prepare(const Http& http) const; + + /** Prepares a http tunnel request */ + std::string prepareTunnel(const Http& http); + + // Types + typedef std::map FieldTable; + + // Member data + FieldTable mFields; ///< Fields of the header associated to their value + Method mMethod; ///< Method to use for the request + std::string mUri; ///< Target URI of the request + unsigned int mMajorVersion; ///< Major HTTP version + unsigned int mMinorVersion; ///< Minor HTTP version + std::string mBody; ///< Body of the request + bool mValidateCertificate; ///< Validates the SSL certificate in case of an HTTPS request + bool mValidateHostname; ///< Validates the hostname in case of an HTTPS request + bool mFollowRedirect; ///< Follows redirect response codes + bool mCompressedResponse; ///< Request comrpessed response + bool mContinue; ///< Resume download + mutable bool mCancel; ///< Cancel state of current request + ProgressCallback mProgressCallback; ///< Progress callback + unsigned int mMaxRedirections; ///< Maximun number of redirections allowed + mutable unsigned int mRedirectionCount; ///< Number of redirections followed by the request + URI mProxy; ///< Proxy information + }; + /** @brief Default constructor */ Http(); diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 473d1f8df..e39cef95d 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -491,10 +491,16 @@ void Http::setHost(const std::string& host, unsigned short port, bool useSSL, UR Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { IOStreamString stream; Response response = downloadRequest( request, stream, timeout ); - response.mBody = stream.getStream(); + response.mBody = std::move(stream.getStream()); return response; } +static bool sendProgress( const Http& http, const Http::Request& request, const Http::Response& response, const Http::Request::Status& status, const std::size_t& totalBytes, const std::size_t& currentBytes ) { + if ( request.getProgressCallback() ) + return request.getProgressCallback()( http, request, response, status, totalBytes, currentBytes ); + return true; +} + Http::Response Http::downloadRequest(const Http::Request& request, IOStream& writeTo, Time timeout) { if ( 0 == mHost.toInteger() ) { return Response(); @@ -549,6 +555,11 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri mConnection->setConnected(true); } } + + if ( mConnection->isConnected() && !sendProgress( *this, request, received, Request::Connected, 0, 0 ) ) { + mConnection->disconnect(); + return received; + } } // Connect the socket to the host @@ -627,6 +638,10 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri // Send it through the socket if (mConnection->getSocket()->send(requestStr.c_str(), requestStr.size()) == Socket::Done) { + if ( !sendProgress( *this, request, received, Request::Sent, 0, 0 ) ) { + request.mCancel = true; + } + // Wait for the server's response std::size_t currentTotalBytes = 0; std::size_t len = 0; @@ -739,6 +754,10 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri } } + if ( !sendProgress( *this, request, received, Request::HeaderReceived, contentLength, 0 ) ) { + request.mCancel = true; + } + // Move the readBuffer to the starting point // of the file buffer if ( len > 0 ) { @@ -764,11 +783,9 @@ Http::Response Http::downloadRequest(const Http::Request& request, IOStream& wri if ( readed > 0 ) bufferStream->write( readBuffer, readed ); - if ( request.getProgressCallback() ) { - if ( !request.getProgressCallback()( *this, request, contentLength, currentTotalBytes ) ) { - request.mCancel = true; - break; - } + if ( !sendProgress( *this, request, received, Request::ContentReceived, contentLength, currentTotalBytes ) ) { + request.mCancel = true; + break; } // If the response is compressed and the stream ended means that we received diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index e2b697262..1324d5291 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -115,9 +115,11 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { // If progress requested print a progress on screen if ( progress ) { - request.setProgressCallback( []( const Http&, const Http::Request&, size_t totalBytes, size_t currentBytes ) { - std::cout << "\rDownloaded " << FileSystem::sizeToString( currentBytes ).c_str() << " of " << FileSystem::sizeToString( totalBytes ).c_str() << " "; - std::cout << std::flush; + request.setProgressCallback( []( const Http&, const Http::Request&, const Http::Response&, const Http::Request::Status& status, size_t totalBytes, size_t currentBytes ) { + if ( status == Http::Request::ContentReceived ) { + std::cout << "\rDownloaded " << FileSystem::sizeToString( currentBytes ).c_str() << " of " << FileSystem::sizeToString( totalBytes ).c_str() << " "; + std::cout << std::flush; + } return true; }); } From a0711c15a4bf76ff11f93078d509b631cc710a45 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Sat, 11 May 2019 00:58:55 -0300 Subject: [PATCH 18/18] Renamed TSafeDataPointer to TScopedBuffer. --HG-- branch : dev --- include/eepp/audio/music.hpp | 4 +- include/eepp/graphics/fonttruetype.hpp | 2 +- include/eepp/system/directorypack.hpp | 2 +- include/eepp/system/filesystem.hpp | 8 +- include/eepp/system/iostreamdeflate.hpp | 4 +- include/eepp/system/iostreaminflate.hpp | 4 +- include/eepp/system/pack.hpp | 4 +- include/eepp/system/pak.hpp | 2 +- include/eepp/system/safedatapointer.hpp | 68 -------- include/eepp/system/scopedbuffer.hpp | 153 ++++++++++++++++++ include/eepp/system/zip.hpp | 2 +- projects/linux/ee.files | 2 +- src/eepp/audio/music.cpp | 2 +- src/eepp/audio/soundbuffer.cpp | 8 +- src/eepp/audio/soundfilereadermp3.cpp | 8 +- src/eepp/graphics/fonttruetype.cpp | 5 +- src/eepp/graphics/image.cpp | 48 +++--- src/eepp/graphics/shader.cpp | 18 +-- src/eepp/graphics/textureatlasloader.cpp | 6 +- src/eepp/graphics/textureloader.cpp | 8 +- src/eepp/maps/tilemap.cpp | 6 +- .../ssl/backend/mbedtls/mbedtlssocket.cpp | 12 +- .../ssl/backend/openssl/opensslsocket.cpp | 6 +- src/eepp/system/compression.cpp | 20 +-- src/eepp/system/directorypack.cpp | 2 +- src/eepp/system/filesystem.cpp | 17 +- src/eepp/system/inifile.cpp | 6 +- src/eepp/system/iostreamdeflate.cpp | 36 ++--- src/eepp/system/iostreaminflate.cpp | 26 +-- src/eepp/system/iostreamzip.cpp | 4 +- src/eepp/system/pak.cpp | 15 +- src/eepp/system/rc4.cpp | 6 +- src/eepp/system/translator.cpp | 12 +- src/eepp/system/zip.cpp | 15 +- src/eepp/ui/css/stylesheetparser.cpp | 6 +- src/eepp/ui/uimenu.cpp | 2 +- src/eepp/ui/uiscenenode.cpp | 12 +- 37 files changed, 320 insertions(+), 241 deletions(-) delete mode 100644 include/eepp/system/safedatapointer.hpp create mode 100644 include/eepp/system/scopedbuffer.hpp diff --git a/include/eepp/audio/music.hpp b/include/eepp/audio/music.hpp index 584f39538..e3970aacd 100644 --- a/include/eepp/audio/music.hpp +++ b/include/eepp/audio/music.hpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include @@ -239,7 +239,7 @@ class EE_API Music : public SoundStream std::vector mSamples; ///< Temporary buffer of samples Mutex mMutex; ///< Mutex protecting the data Span mLoopSpan; ///< Loop Range Specifier - SafeDataPointer mData; + ScopedBuffer mData; }; }} diff --git a/include/eepp/graphics/fonttruetype.hpp b/include/eepp/graphics/fonttruetype.hpp index 471c201c0..4d7690740 100644 --- a/include/eepp/graphics/fonttruetype.hpp +++ b/include/eepp/graphics/fonttruetype.hpp @@ -86,7 +86,7 @@ class EE_API FontTrueType : public Font { void* mStreamRec; ///< Pointer to the stream rec instance (it is typeless to avoid exposing implementation details) void* mStroker; ///< Pointer to the stroker (it is typeless to avoid exposing implementation details) int* mRefCount; ///< Reference counter used by implicit sharing - SafeDataPointer mMemCopy; + mutable ScopedBuffer mMemCopy; ///< If loaded from memory, this is the file copy in memory Font::Info mInfo; ///< Information about the font mutable PageTable mPages; ///< Table containing the glyphs pages by character size mutable std::vector mPixelBuffer; ///< Pixel buffer holding a glyph's pixels before being written to the texture diff --git a/include/eepp/system/directorypack.hpp b/include/eepp/system/directorypack.hpp index 55cc5ec2c..53a861957 100644 --- a/include/eepp/system/directorypack.hpp +++ b/include/eepp/system/directorypack.hpp @@ -52,7 +52,7 @@ class EE_API DirectoryPack : public Pack { bool extractFileToMemory( const std::string& path, std::vector& data ); /** Extract a file to memory from the pakFile */ - bool extractFileToMemory( const std::string& path, SafeDataPointer& data ); + bool extractFileToMemory( const std::string& path, ScopedBuffer& data ); /** Check if a file exists in the pack file and return the number of the file, otherwise return -1. */ Int32 exists( const std::string& path ); diff --git a/include/eepp/system/filesystem.hpp b/include/eepp/system/filesystem.hpp index 5da11ecf4..709817987 100644 --- a/include/eepp/system/filesystem.hpp +++ b/include/eepp/system/filesystem.hpp @@ -4,7 +4,7 @@ #include #include #include -#include +#include namespace EE { namespace System { @@ -25,10 +25,10 @@ class EE_API FileSystem { /** Copy a file to memory * @param path The file path - * @param data A SafeDataPointer to allocate the data to memory - * @return True if returned the file to the SafeDataPointer + * @param data A ScopedBuffer to allocate the data to memory + * @return True if returned the file to the ScopedBuffer */ - static bool fileGet( const std::string& path, SafeDataPointer& data ); + static bool fileGet( const std::string& path, ScopedBuffer& data ); /** Copy a file to location. * @param src Source File Path diff --git a/include/eepp/system/iostreamdeflate.hpp b/include/eepp/system/iostreamdeflate.hpp index d92e24801..eab4223bc 100644 --- a/include/eepp/system/iostreamdeflate.hpp +++ b/include/eepp/system/iostreamdeflate.hpp @@ -2,7 +2,7 @@ #define EE_SYSTEM_IOSTREAMDEFLATE_HPP #include -#include +#include #include namespace EE { namespace System { @@ -40,7 +40,7 @@ class EE_API IOStreamDeflate : public IOStream { protected: IOStream& mStream; Compression::Mode mMode; - SafeDataPointer mBuffer; + ScopedBuffer mBuffer; LocalStreamData * mLocalStream; }; diff --git a/include/eepp/system/iostreaminflate.hpp b/include/eepp/system/iostreaminflate.hpp index 5564ee96f..8eaacfae3 100644 --- a/include/eepp/system/iostreaminflate.hpp +++ b/include/eepp/system/iostreaminflate.hpp @@ -2,7 +2,7 @@ #define EE_SYSTEM_IOSTREAMINFLATE_HPP #include -#include +#include #include namespace EE { namespace System { @@ -39,7 +39,7 @@ class EE_API IOStreamInflate : public IOStream { protected: IOStream& mStream; Compression::Mode mMode; - SafeDataPointer mBuffer; + ScopedBuffer mBuffer; LocalStreamData * mLocalStream; }; diff --git a/include/eepp/system/pack.hpp b/include/eepp/system/pack.hpp index bb371436f..5bdb9469c 100755 --- a/include/eepp/system/pack.hpp +++ b/include/eepp/system/pack.hpp @@ -2,7 +2,7 @@ #define EE_SYSTEMCPACK_HPP #include -#include +#include #include namespace EE { namespace System { @@ -52,7 +52,7 @@ class EE_API Pack : protected Mutex { virtual bool extractFileToMemory( const std::string& path, std::vector& data ) = 0; /** Extract a file to memory from the pack file */ - virtual bool extractFileToMemory( const std::string& path, SafeDataPointer& data ) = 0; + virtual bool extractFileToMemory( const std::string& path, ScopedBuffer& data ) = 0; /** Check if a file exists in the pack file and return the number of the file, otherwise return -1. */ virtual Int32 exists( const std::string& path ) = 0; diff --git a/include/eepp/system/pak.hpp b/include/eepp/system/pak.hpp index 952bf6808..75668f444 100755 --- a/include/eepp/system/pak.hpp +++ b/include/eepp/system/pak.hpp @@ -53,7 +53,7 @@ class EE_API Pak : public Pack { bool extractFileToMemory( const std::string& path, std::vector& data ); /** Extract a file to memory from the pakFile */ - bool extractFileToMemory( const std::string& path, SafeDataPointer& data ); + bool extractFileToMemory( const std::string& path, ScopedBuffer& data ); /** Check if a file exists in the pakFile and return the number of the file, otherwise return -1. */ Int32 exists( const std::string& path ); diff --git a/include/eepp/system/safedatapointer.hpp b/include/eepp/system/safedatapointer.hpp deleted file mode 100644 index 0f572d1d2..000000000 --- a/include/eepp/system/safedatapointer.hpp +++ /dev/null @@ -1,68 +0,0 @@ -#ifndef EE_SYSTEM_SAFEDATAPOINTER -#define EE_SYSTEM_SAFEDATAPOINTER - -#include -#include -#include - -namespace EE { namespace System { - -/** @brief Keep a pointer and release it in the SafeDataPointer destructor */ -template -class TSafeDataPointer { - public: - TSafeDataPointer(); - - TSafeDataPointer( Uint32 size ); - - TSafeDataPointer( T * data, Uint32 size ); - - /** @brief The destructor deletes the buffer */ - ~TSafeDataPointer(); - - void clear(); - - /** Pointer to the buffer */ - T * data; - - /** Buffer size */ - Uint32 size; -}; - - -template -TSafeDataPointer::TSafeDataPointer() : - data( NULL ), - size( 0 ) -{ -} - -template -TSafeDataPointer::TSafeDataPointer( Uint32 size ) : - data( eeNewArray( T, size ) ), - size( size ) -{ -} - -template -TSafeDataPointer::TSafeDataPointer( T * data, Uint32 size ) : - data( data ), - size( size ) -{ -} - -template -TSafeDataPointer::~TSafeDataPointer() { - clear(); -} - -template -void TSafeDataPointer::clear() { - eeSAFE_DELETE_ARRAY( data ); -} - -typedef TSafeDataPointer SafeDataPointer; - -}} - -#endif diff --git a/include/eepp/system/scopedbuffer.hpp b/include/eepp/system/scopedbuffer.hpp new file mode 100644 index 000000000..a6d401924 --- /dev/null +++ b/include/eepp/system/scopedbuffer.hpp @@ -0,0 +1,153 @@ +#ifndef EE_SYSTEM_SCOPEDBUFFER +#define EE_SYSTEM_SCOPEDBUFFER + +#include +#include +#include +#include +#include + +namespace EE { namespace System { + +/** @brief Keep a pointer to a buffer and release it when the ScopedBuffer goes out of scope. + +The TScopedBuffer class template stores a pointer to a dynamically allocated array. +(Dynamically allocated arrays are allocated with the C++ new[] expression.) +The array pointed to is guaranteed to be deleted, either on destruction of the TScopedBuffer, +or via an explicit reset. + +The TScopedBuffer template is a simple solution for simple needs. +It supplies a basic "resource acquisition is initialization" facility, +without shared-ownership or transfer-of-ownership semantics. +Both its name and enforcement of semantics (by being NonCopyable) signal its +intent to retain ownership solely within the current scope. +*/ +template +class TScopedBuffer : NonCopyable { + public: + TScopedBuffer(); + + TScopedBuffer( std::size_t length ); + + TScopedBuffer( T * data, std::size_t length ); + + /** @brief The destructor deletes the buffer */ + ~TScopedBuffer(); + + void clear(); + + T& operator[]( std::size_t i ) const; + + bool operator()() const; + + T * get() const; + + void swap(TScopedBuffer& b); + + void reset(T * p = 0, const std::size_t& size = 0); + + void reset(const std::size_t& size = 0); + + bool isEmpty() const; + + const std::size_t& size() const; + + const std::size_t& length() const; + + private: + /** Pointer to the buffer */ + T * mData; + + /** Buffer size */ + std::size_t mSize; +}; + +template +TScopedBuffer::TScopedBuffer() : + mData( NULL ), + mSize( 0 ) +{ +} + +template +TScopedBuffer::TScopedBuffer( std::size_t length ) : + mData( eeNewArray( T, length ) ), + mSize( length ) +{ +} + +template +TScopedBuffer::TScopedBuffer( T * data, std::size_t length ) : + mData( data ), + mSize( length ) +{ +} + +template +TScopedBuffer::~TScopedBuffer() { + clear(); +} + +template +void TScopedBuffer::clear() { + eeSAFE_DELETE_ARRAY( mData ); + mSize = 0; +} + +template +T& TScopedBuffer::operator[]( std::size_t i ) const { + eeASSERT( i < mSize && NULL != mData ); + return mData[i]; +} + +template +bool TScopedBuffer::operator()() const { + return NULL != mData; +} + +template +T * TScopedBuffer::get() const { + return mData; +} + +template +const std::size_t& TScopedBuffer::size() const { + return mSize; +} + +template +const std::size_t& TScopedBuffer::length() const { + return mSize; +} + +template +bool TScopedBuffer::isEmpty() const { + return mData == NULL; +} + +template +void TScopedBuffer::swap(TScopedBuffer& b) { + std::swap(mData, b.mData); + std::swap(mSize, b.mSize); +} + +template +void TScopedBuffer::reset(T * p, const std::size_t& size) { + eeASSERT( p == 0 || p != mData ); + clear(); + mData = p; + mSize = size; +} + +template +void TScopedBuffer::reset(const std::size_t& size) { + clear(); + mData = eeNewArray( T, ( size ) ); + mSize = size; +} + +typedef TScopedBuffer ScopedBuffer; + +}} + +#endif diff --git a/include/eepp/system/zip.hpp b/include/eepp/system/zip.hpp index 9eadaf3cd..f64228ef8 100644 --- a/include/eepp/system/zip.hpp +++ b/include/eepp/system/zip.hpp @@ -51,7 +51,7 @@ class EE_API Zip : public Pack { bool extractFileToMemory( const std::string& path, std::vector& data ); /** Extract a file to memory from the pakFile */ - bool extractFileToMemory( const std::string& path, SafeDataPointer& data ); + bool extractFileToMemory( const std::string& path, ScopedBuffer& data ); /** Check if a file exists in the pack file and return the number of the file, otherwise return -1. */ Int32 exists( const std::string& path ); diff --git a/projects/linux/ee.files b/projects/linux/ee.files index e3841c305..ef9e03f61 100644 --- a/projects/linux/ee.files +++ b/projects/linux/ee.files @@ -267,7 +267,7 @@ ../../include/eepp/system/rc4.hpp ../../include/eepp/system/resourceloader.hpp ../../include/eepp/system/resourcemanager.hpp -../../include/eepp/system/safedatapointer.hpp +../../include/eepp/system/scopedbuffer.hpp ../../include/eepp/system/singleton.hpp ../../include/eepp/system/sys.hpp ../../include/eepp/system/thread.hpp diff --git a/src/eepp/audio/music.cpp b/src/eepp/audio/music.cpp index cf93d2944..cf72b7de9 100644 --- a/src/eepp/audio/music.cpp +++ b/src/eepp/audio/music.cpp @@ -81,7 +81,7 @@ bool Music::openFromStream(IOStream& stream) { bool Music::openFromPack(Pack * pack, const std::string & filePackPath) { if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, mData ) ) - return openFromMemory( reinterpret_cast ( mData.data ), mData.size ); + return openFromMemory( reinterpret_cast ( mData.get() ), mData.length() ); return false; } diff --git a/src/eepp/audio/soundbuffer.cpp b/src/eepp/audio/soundbuffer.cpp index 90dab69fa..6daf7a815 100644 --- a/src/eepp/audio/soundbuffer.cpp +++ b/src/eepp/audio/soundbuffer.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include #include #include #include @@ -105,10 +105,10 @@ bool SoundBuffer::loadFromSamples(const Int16* samples, Uint64 sampleCount, unsi bool SoundBuffer::loadFromPack(Pack * pack, std::string filePackPath) { bool Ret = false; - SafeDataPointer PData; + ScopedBuffer buffer; - if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, PData ) ) - Ret = loadFromMemory( reinterpret_cast ( PData.data ), PData.size ); + if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, buffer ) ) + Ret = loadFromMemory( reinterpret_cast ( buffer.get() ), buffer.length() ); return Ret; } diff --git a/src/eepp/audio/soundfilereadermp3.cpp b/src/eepp/audio/soundfilereadermp3.cpp index 203fce15b..9bc8e942b 100644 --- a/src/eepp/audio/soundfilereadermp3.cpp +++ b/src/eepp/audio/soundfilereadermp3.cpp @@ -6,7 +6,7 @@ #include #include #include -#include +#include static size_t drmp3_func_read(void* data, void* ptr, size_t size) { IOStream* stream = static_cast(data); @@ -73,15 +73,15 @@ Uint64 SoundFileReaderMp3::read(Int16* samples, Uint64 maxCount) { while (count < maxCount) { const int samplesToRead = static_cast(maxCount - count); int frames = samplesToRead / mChannelCount; - TSafeDataPointer rSamples( samplesToRead ); + TScopedBuffer rSamples( samplesToRead ); - long framesRead = drmp3_read_pcm_frames_f32( mMp3, frames, rSamples.data ); + long framesRead = drmp3_read_pcm_frames_f32( mMp3, frames, rSamples.get() ); if (framesRead > 0) { long samplesRead = framesRead * mChannelCount; for ( int i = 0; i < samplesRead; i++ ) - samples[i] = rSamples.data[i] * 32768.f; + samples[i] = rSamples[i] * 32768.f; count += samplesRead; samples += samplesRead; diff --git a/src/eepp/graphics/fonttruetype.cpp b/src/eepp/graphics/fonttruetype.cpp index 2c3f936fc..8792a8de2 100644 --- a/src/eepp/graphics/fonttruetype.cpp +++ b/src/eepp/graphics/fonttruetype.cpp @@ -242,7 +242,7 @@ bool FontTrueType::loadFromPack( Pack * pack, std::string filePackPath ) { mMemCopy.clear(); if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, mMemCopy ) ) { - Ret = loadFromMemory( mMemCopy.data, mMemCopy.size ); + Ret = loadFromMemory( mMemCopy.get(), mMemCopy.length() ); } return Ret; @@ -367,8 +367,9 @@ Texture* FontTrueType::getTexture(unsigned int characterSize) const { } FontTrueType& FontTrueType::operator =(const FontTrueType& right) { - FontTrueType temp(right); + FontTrueType temp(right.getName()); + temp.mMemCopy.swap(right.mMemCopy); std::swap(mLibrary, temp.mLibrary); std::swap(mFace, temp.mFace); std::swap(mStreamRec, temp.mStreamRec); diff --git a/src/eepp/graphics/image.cpp b/src/eepp/graphics/image.cpp index 142086d49..e87d04eb0 100644 --- a/src/eepp/graphics/image.cpp +++ b/src/eepp/graphics/image.cpp @@ -284,18 +284,18 @@ bool Image::getInfo( const std::string& path, int * width, int * height, int * c Pack * tPack = PackManager::instance()->exists( npath ); if ( NULL != tPack ) { - SafeDataPointer PData; + ScopedBuffer buffer; - tPack->extractFileToMemory( npath, PData ); + tPack->extractFileToMemory( npath, buffer ); - res = 0 != stbi_info_from_memory( PData.data, PData.size, width, height, channels ); + res = 0 != stbi_info_from_memory( buffer.get(), buffer.length(), width, height, channels ); - if ( !res && svg_test_from_memory( PData.data, PData.size ) ) { - SafeDataPointer data( PData.size + 1 ); - memcpy( data.data, PData.data, PData.size ); - data.data[PData.size] = '\0'; + if ( !res && svg_test_from_memory( buffer.get(), buffer.length() ) ) { + ScopedBuffer data( buffer.length() + 1 ); + memcpy( data.get(), buffer.get(), buffer.length() ); + data[buffer.length()] = '\0'; - NSVGimage * image = nsvgParse( (char*)data.data, "px", 96.0f ); + NSVGimage * image = nsvgParse( (char*)data.get(), "px", 96.0f ); if ( NULL != image ) { *width = image->width * imageFormatConfiguration.svgScale(); @@ -467,10 +467,10 @@ Image::Image( const Uint8 * imageData, const unsigned int & imageDataSize, const mLoadedFromStbi = true; } else if ( svg_test_from_memory( imageData, imageDataSize ) ) { - SafeDataPointer data( imageDataSize + 1 ); - memcpy( data.data, imageData, imageDataSize ); - data.data[imageDataSize] = '\0'; - svgLoad( nsvgParse( (char*)data.data, "px", 96.0f ) ); + ScopedBuffer data( imageDataSize + 1 ); + memcpy( data.get(), imageData, imageDataSize ); + data[imageDataSize] = '\0'; + svgLoad( nsvgParse( (char*)data.get(), "px", 96.0f ) ); } else { std::string reason = "."; @@ -526,14 +526,14 @@ Image::Image( IOStream & stream, const unsigned int& forceChannels, const Format mLoadedFromStbi = true; } else if ( svg_test_from_stream( stream ) ) { - SafeDataPointer data( stream.getSize() + 1 ); + ScopedBuffer data( stream.getSize() + 1 ); stream.seek( 0 ); - stream.read( (char*)data.data, data.size - 1 ); + stream.read( (char*)data.get(), data.length() - 1 ); - data.data[data.size - 1] = '\0'; + data[data.length() - 1] = '\0'; - svgLoad( nsvgParse( (char*)data.data, "px", 96.0f ) ); + svgLoad( nsvgParse( (char*)data.get(), "px", 96.0f ) ); } else { eePRINTL( "Failed to load image. Reason: %s", stbi_failure_reason() ); } @@ -580,12 +580,12 @@ void Image::svgLoad( NSVGimage * image ) { void Image::loadFromPack( Pack * Pack, const std::string& FilePackPath ) { if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( FilePackPath ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - Pack->extractFileToMemory( FilePackPath, PData ); + Pack->extractFileToMemory( FilePackPath, buffer ); int w, h, c; - Uint8 * data = stbi_load_from_memory( PData.data, PData.size, &w, &h, &c, mChannels ); + Uint8 * data = stbi_load_from_memory( buffer.get(), buffer.length(), &w, &h, &c, mChannels ); if ( NULL != data ) { mPixels = data; @@ -598,11 +598,11 @@ void Image::loadFromPack( Pack * Pack, const std::string& FilePackPath ) { mSize = mWidth * mHeight * mChannels; mLoadedFromStbi = true; - } else if ( svg_test_from_memory( PData.data, PData.size ) ) { - SafeDataPointer data( PData.size + 1 ); - memcpy( data.data, PData.data, PData.size ); - data.data[PData.size] = '\0'; - svgLoad( nsvgParse( (char*)data.data, "px", 96.0f ) ); + } else if ( svg_test_from_memory( buffer.get(), buffer.length() ) ) { + ScopedBuffer data( buffer.length() + 1 ); + memcpy( data.get(), buffer.get(), buffer.length() ); + data[buffer.length()] = '\0'; + svgLoad( nsvgParse( (char*)data.get(), "px", 96.0f ) ); } else { eePRINTL( "Failed to load image %s. Reason: %s", FilePackPath.c_str(), stbi_failure_reason() ); } diff --git a/src/eepp/graphics/shader.cpp b/src/eepp/graphics/shader.cpp index 43599342e..cb3b083f7 100644 --- a/src/eepp/graphics/shader.cpp +++ b/src/eepp/graphics/shader.cpp @@ -30,21 +30,21 @@ Shader::Shader( const Uint32& Type, const std::string& Filename ) { mFilename = FileSystem::fileNameFromPath( Filename ); if ( FileSystem::fileExists( Filename ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - FileSystem::fileGet( Filename, PData ); + FileSystem::fileGet( Filename, buffer ); - setSource( (const char*)PData.data, PData.size ); + setSource( (const char*)buffer.get(), buffer.length() ); } else { std::string tPath = Filename; Pack * tPack = NULL; if ( PackManager::instance()->isFallbackToPacksActive() && NULL != ( tPack = PackManager::instance()->exists( tPath ) ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - tPack->extractFileToMemory( tPath, PData ); + tPack->extractFileToMemory( tPath, buffer ); - setSource( reinterpret_cast ( PData.data ), PData.size ); + setSource( reinterpret_cast ( buffer.get() ), buffer.length() ); } else { eePRINTL( "Couldn't open shader object: %s", Filename.c_str() ); } @@ -62,16 +62,16 @@ Shader::Shader( const Uint32& Type, const char * Data, const Uint32& DataSize ) } Shader::Shader( const Uint32& Type, Pack * Pack, const std::string& Filename ) { - SafeDataPointer PData; + ScopedBuffer buffer; Init( Type ); mFilename = FileSystem::fileNameFromPath( Filename ); if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( Filename ) ) { - Pack->extractFileToMemory( Filename, PData ); + Pack->extractFileToMemory( Filename, buffer ); - setSource( reinterpret_cast ( PData.data ), PData.size ); + setSource( reinterpret_cast ( buffer.get() ), buffer.length() ); } compile(); diff --git a/src/eepp/graphics/textureatlasloader.cpp b/src/eepp/graphics/textureatlasloader.cpp index db6717475..ca53bad06 100644 --- a/src/eepp/graphics/textureatlasloader.cpp +++ b/src/eepp/graphics/textureatlasloader.cpp @@ -193,11 +193,11 @@ void TextureAtlasLoader::loadFromPack( Pack * Pack, const std::string& FilePackP if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( FilePackPath ) ) { mPack = Pack; - SafeDataPointer PData; + ScopedBuffer buffer; - Pack->extractFileToMemory( FilePackPath, PData ); + Pack->extractFileToMemory( FilePackPath, buffer ); - loadFromMemory( reinterpret_cast ( PData.data ), PData.size, FilePackPath ); + loadFromMemory( buffer.get(), buffer.length(), FilePackPath ); } } diff --git a/src/eepp/graphics/textureloader.cpp b/src/eepp/graphics/textureloader.cpp index d8caa35db..dc2e00012 100644 --- a/src/eepp/graphics/textureloader.cpp +++ b/src/eepp/graphics/textureloader.cpp @@ -278,11 +278,11 @@ void TextureLoader::loadFromFile() { } void TextureLoader::loadFromPack() { - SafeDataPointer PData; + ScopedBuffer buffer; - if ( NULL != mPack && mPack->isOpen() && mPack->extractFileToMemory( mFilepath, PData ) ) { - mImagePtr = PData.data; - mSize = PData.size; + if ( NULL != mPack && mPack->isOpen() && mPack->extractFileToMemory( mFilepath, buffer ) ) { + mImagePtr = buffer.get(); + mSize = buffer.length(); loadFromMemory(); } diff --git a/src/eepp/maps/tilemap.cpp b/src/eepp/maps/tilemap.cpp index 695a96917..9efd6ffff 100644 --- a/src/eepp/maps/tilemap.cpp +++ b/src/eepp/maps/tilemap.cpp @@ -1072,13 +1072,13 @@ bool TileMap::loadFromFile( const std::string& path ) { bool TileMap::loadFromPack( Pack * Pack, const std::string& FilePackPath ) { if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( FilePackPath ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - Pack->extractFileToMemory( FilePackPath, PData ); + Pack->extractFileToMemory( FilePackPath, buffer ); mPath = FilePackPath; - return loadFromMemory( reinterpret_cast ( PData.data ), PData.size ); + return loadFromMemory( reinterpret_cast ( buffer.get() ), buffer.length() ); } return false; diff --git a/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp b/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp index 2f32240a0..ef6791e7c 100644 --- a/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp +++ b/src/eepp/network/ssl/backend/mbedtls/mbedtlssocket.cpp @@ -14,7 +14,7 @@ bool MbedTLSSocket::init() { mbedtls_x509_crt_init(&sCACert); //! Load the certificates and config - SafeDataPointer data; + ScopedBuffer data; if ( FileSystem::fileExists( SSLSocket::CertificatesPath ) ) { FileSystem::fileGet( SSLSocket::CertificatesPath, data ); @@ -28,12 +28,12 @@ bool MbedTLSSocket::init() { } } - if ( data.size > 0 ) { - SafeDataPointer dataZeroEnded( data.size + 1 ); - memcpy( dataZeroEnded.data, data.data, data.size ); - dataZeroEnded.data[ data.size ] = '\0'; + if ( data.length() > 0 ) { + ScopedBuffer dataZeroEnded( data.length() + 1 ); + memcpy( dataZeroEnded.get(), data.get(), data.length() ); + dataZeroEnded[ data.length() ] = '\0'; - int err = mbedtls_x509_crt_parse( &sCACert, (const unsigned char*)dataZeroEnded.data, dataZeroEnded.size ); + int err = mbedtls_x509_crt_parse( &sCACert, (const unsigned char*)dataZeroEnded.get(), dataZeroEnded.length() ); if ( err != 0 ) { char errStr[ 1024 ]; diff --git a/src/eepp/network/ssl/backend/openssl/opensslsocket.cpp b/src/eepp/network/ssl/backend/openssl/opensslsocket.cpp index 43063f4e7..994f6b482 100644 --- a/src/eepp/network/ssl/backend/openssl/opensslsocket.cpp +++ b/src/eepp/network/ssl/backend/openssl/opensslsocket.cpp @@ -152,13 +152,13 @@ bool OpenSSLSocket::init() { //! Load the certificates and config if ( FileSystem::fileExists( SSLSocket::CertificatesPath ) ) { - SafeDataPointer data; + ScopedBuffer data; FileSystem::fileGet( SSLSocket::CertificatesPath, data ); - if ( data.size > 0 ) { + if ( data.length() > 0 ) { BIO* mem = BIO_new(BIO_s_mem()); - BIO_puts( mem, (const char*) data.data ); + BIO_puts( mem, (const char*) data.get() ); while( true ) { X509 * cert = PEM_read_bio_X509(mem, NULL, 0, NULL); diff --git a/src/eepp/system/compression.cpp b/src/eepp/system/compression.cpp index 440bc0e0e..38c056453 100644 --- a/src/eepp/system/compression.cpp +++ b/src/eepp/system/compression.cpp @@ -1,5 +1,5 @@ #include -#include +#include #include #include @@ -102,15 +102,15 @@ Compression::Status Compression::decompress(IOStream& dst, IOStream& src, Mode m case MODE_DEFLATE: case MODE_GZIP: { - SafeDataPointer buffer( DEFLATE_CHUNK_SIZE ); - SafeDataPointer bufferDst( DEFLATE_CHUNK_SIZE ); + ScopedBuffer buffer( DEFLATE_CHUNK_SIZE ); + ScopedBuffer bufferDst( DEFLATE_CHUNK_SIZE ); src.seek( 0 ); int windowBits = mode == Compression::MODE_DEFLATE ? MAX_WBITS : MAX_WBITS | 16; z_stream strm = {}; - strm.next_in = buffer.data; + strm.next_in = buffer.get(); int err = inflateInit2(&strm, windowBits); if (err != Z_OK) @@ -123,14 +123,14 @@ Compression::Status Compression::decompress(IOStream& dst, IOStream& src, Mode m Uint32 totalRead = 0; while ( totalRead < totalSize ) { - bytesRead = src.read( (char*)buffer.data, buffer.size ); + bytesRead = src.read( (char*)buffer.get(), buffer.length() ); strm.avail_in = bytesRead; - strm.next_in = buffer.data; + strm.next_in = buffer.get(); do { - strm.avail_out = bufferDst.size; - strm.next_out = bufferDst.data; + strm.avail_out = bufferDst.length(); + strm.next_out = bufferDst.get(); zlibStatus = inflate(&strm, Z_NO_FLUSH); switch (zlibStatus) { @@ -143,9 +143,9 @@ Compression::Status Compression::decompress(IOStream& dst, IOStream& src, Mode m return (Status)zlibStatus; } - have = bufferDst.size- strm.avail_out; + have = bufferDst.length()- strm.avail_out; - dst.write( (const char*)bufferDst.data, have ); + dst.write( (const char*)bufferDst.get(), have ); } while (strm.avail_out == 0); totalRead += bytesRead; diff --git a/src/eepp/system/directorypack.cpp b/src/eepp/system/directorypack.cpp index e171877a9..2b36fa16f 100644 --- a/src/eepp/system/directorypack.cpp +++ b/src/eepp/system/directorypack.cpp @@ -88,7 +88,7 @@ bool DirectoryPack::extractFileToMemory( const std::string& path, std::vector ( data.data ), data.size ); + fs.read( reinterpret_cast ( data.get() ), data.length() ); return true; } @@ -88,10 +85,8 @@ bool FileSystem::fileCopy( const std::string& src, const std::string& dst ) { Int64 allocate = ( size < chunksize ) ? size : chunksize; Int64 copysize = 0; - SafeDataPointer data; - data.size = (Uint32)allocate; - data.data = eeNewArray( Uint8, ( data.size ) ); - char * buff = (char*)data.data; + TScopedBuffer data( allocate ); + char * buff = data.get(); IOStreamFile in( src, "rb" ); IOStreamFile out( dst, "wb" ); @@ -105,7 +100,7 @@ bool FileSystem::fileCopy( const std::string& src, const std::string& dst ) { } in.read ( &buff[0], copysize ); - out.write ( (const char*)&buff[0], copysize ); + out.write ( &buff[0], copysize ); size_left -= copysize; } while ( size_left > 0 ); diff --git a/src/eepp/system/inifile.cpp b/src/eepp/system/inifile.cpp index 43720618e..66f833187 100755 --- a/src/eepp/system/inifile.cpp +++ b/src/eepp/system/inifile.cpp @@ -56,11 +56,11 @@ IniFile::IniFile( IOStream& stream, const bool& shouldReadFile ) : bool IniFile::loadFromPack( Pack * Pack, std::string iniPackPath ) { if ( NULL != Pack && Pack->isOpen() && -1 != Pack->exists( iniPackPath ) ) { - SafeDataPointer PData; + ScopedBuffer buffer; - Pack->extractFileToMemory( iniPackPath, PData ); + Pack->extractFileToMemory( iniPackPath, buffer ); - return loadFromMemory( PData.data, PData.size ); + return loadFromMemory( buffer.get(), buffer.length() ); } return false; diff --git a/src/eepp/system/iostreamdeflate.cpp b/src/eepp/system/iostreamdeflate.cpp index 9862ab702..f8c52afc3 100644 --- a/src/eepp/system/iostreamdeflate.cpp +++ b/src/eepp/system/iostreamdeflate.cpp @@ -38,24 +38,24 @@ IOStreamDeflate::~IOStreamDeflate() { if (rc != Z_OK && rc != Z_STREAM_END) return; - mStream.write((char*)mBuffer.data, mBuffer.size - zstr.avail_out); + mStream.write((char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); if (!mStream.isOpen()) return; - zstr.next_out = (unsigned char*) mBuffer.data; - zstr.avail_out = mBuffer.size; + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); while (rc != Z_STREAM_END) { rc = deflate(&zstr, Z_FINISH); if (rc != Z_OK && rc != Z_STREAM_END) return; - mStream.write((char*)mBuffer.data, mBuffer.size - zstr.avail_out); + mStream.write((char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); if (!mStream.isOpen()) return; - zstr.next_out = (unsigned char*) mBuffer.data; - zstr.avail_out = mBuffer.size; + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); } } } @@ -77,11 +77,11 @@ ios_size IOStreamDeflate::read(char * buffer, ios_size length) { ios_size n = 0; if ( mStream.isOpen()) { - n = mStream.read((char*)mBuffer.data, mBuffer.size); + n = mStream.read((char*)mBuffer.get(), mBuffer.length()); } if (n > 0) { - zstr.next_in = (unsigned char*) mBuffer.data; + zstr.next_in = (unsigned char*) mBuffer.get(); zstr.avail_in = n; } else { zstr.next_in = NULL; @@ -110,11 +110,11 @@ ios_size IOStreamDeflate::read(char * buffer, ios_size length) { ios_size n = 0; if (mStream.isOpen()) { - n = mStream.read((char*)mBuffer.data, mBuffer.size); + n = mStream.read((char*)mBuffer.get(), mBuffer.length()); } if (n > 0) { - zstr.next_in = (unsigned char*) mBuffer.data; + zstr.next_in = (unsigned char*) mBuffer.get(); zstr.avail_in = n; } else { zstr.next_in = NULL; @@ -135,8 +135,8 @@ ios_size IOStreamDeflate::write(const char * buffer, ios_size length) { zstr.next_in = (unsigned char*) buffer; zstr.avail_in = length; - zstr.next_out = mBuffer.data; - zstr.avail_out = mBuffer.size; + zstr.next_out = mBuffer.get(); + zstr.avail_out = mBuffer.length(); for (;;) { int rc = deflate(&zstr, Z_NO_FLUSH); @@ -145,23 +145,23 @@ ios_size IOStreamDeflate::write(const char * buffer, ios_size length) { return 0; if (zstr.avail_out == 0) { - ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size); + ios_size ret = mStream.write( (const char*)mBuffer.get(), mBuffer.length()); if (ret == 0) return 0; - zstr.next_out = (unsigned char*) mBuffer.data; - zstr.avail_out = mBuffer.size; + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); } if (zstr.avail_in == 0) { - ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size - zstr.avail_out); + ios_size ret = mStream.write( (const char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); if (ret == 0) return 0; - zstr.next_out = (unsigned char*) mBuffer.data; - zstr.avail_out = mBuffer.size; + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); break; } diff --git a/src/eepp/system/iostreaminflate.cpp b/src/eepp/system/iostreaminflate.cpp index 269a1a4dc..ee6d2f176 100644 --- a/src/eepp/system/iostreaminflate.cpp +++ b/src/eepp/system/iostreaminflate.cpp @@ -42,10 +42,10 @@ ios_size IOStreamInflate::read(char * buffer, ios_size length) { ios_size n = 0; if ( mStream.isOpen()) { - n = mStream.read((char*)mBuffer.data, mBuffer.size); + n = mStream.read((char*)mBuffer.get(), mBuffer.length()); } - zstr.next_in = (unsigned char*) mBuffer.data; + zstr.next_in = (unsigned char*) mBuffer.get(); zstr.avail_in = n; } @@ -78,11 +78,11 @@ ios_size IOStreamInflate::read(char * buffer, ios_size length) { ios_size n = 0; if (mStream.isOpen()) { - n = mStream.read((char*)mBuffer.data, mBuffer.size); + n = mStream.read((char*)mBuffer.get(), mBuffer.length()); } if (n > 0) { - zstr.next_in = (unsigned char*) mBuffer.data; + zstr.next_in = (unsigned char*) mBuffer.get(); zstr.avail_in = n; } else { return length - zstr.avail_out; @@ -99,14 +99,14 @@ ios_size IOStreamInflate::write(const char * buffer, ios_size length) { zstr.next_in = (unsigned char*) buffer; zstr.avail_in = length; - zstr.next_out = mBuffer.data; - zstr.avail_out = mBuffer.size; + zstr.next_out = mBuffer.get(); + zstr.avail_out = mBuffer.length(); for (;;) { int rc = inflate(&zstr, Z_NO_FLUSH); if (rc == Z_STREAM_END) { - length = mStream.write( (const char*)mBuffer.data, mBuffer.size - zstr.avail_out); + length = mStream.write( (const char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); mLocalStream->state = rc; @@ -117,23 +117,23 @@ ios_size IOStreamInflate::write(const char * buffer, ios_size length) { return 0; if (zstr.avail_out == 0) { - ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size); + ios_size ret = mStream.write( (const char*)mBuffer.get(), mBuffer.length()); if (ret == 0) return 0; - zstr.next_out = (unsigned char*) mBuffer.data; - zstr.avail_out = mBuffer.size; + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); } if (zstr.avail_in == 0) { - ios_size ret = mStream.write( (const char*)mBuffer.data, mBuffer.size - zstr.avail_out); + ios_size ret = mStream.write( (const char*)mBuffer.get(), mBuffer.length() - zstr.avail_out); if (ret == 0) return 0; - zstr.next_out = (unsigned char*) mBuffer.data; - zstr.avail_out = mBuffer.size; + zstr.next_out = (unsigned char*) mBuffer.get(); + zstr.avail_out = mBuffer.length(); break; } diff --git a/src/eepp/system/iostreamzip.cpp b/src/eepp/system/iostreamzip.cpp index f777e3364..5f61f2fca 100644 --- a/src/eepp/system/iostreamzip.cpp +++ b/src/eepp/system/iostreamzip.cpp @@ -58,8 +58,8 @@ ios_size IOStreamZip::seek( ios_size position ) { mFile = zip_fopen_index( mZip, zs.index, 0 ); if ( 0 != position ) { - SafeDataPointer ptr( position ); - read( (char*)ptr.data, position ); + ScopedBuffer ptr( position ); + read( (char*)ptr.get(), position ); } mPos = position; diff --git a/src/eepp/system/pak.cpp b/src/eepp/system/pak.cpp index 6b49350a5..2e1c713b2 100755 --- a/src/eepp/system/pak.cpp +++ b/src/eepp/system/pak.cpp @@ -133,10 +133,10 @@ bool Pak::extractFile( const std::string& path , const std::string& dest ) { Int32 Pos = exists( path ); if ( Pos != -1 ) { - SafeDataPointer data; + ScopedBuffer data; if ( extractFileToMemory( path, data ) ) { - FileSystem::fileWrite( path, data.data, data.size ); + FileSystem::fileWrite( path, data.get(), data.length() ); } Ret = true; @@ -173,7 +173,7 @@ bool Pak::extractFileToMemory( const std::string& path, std::vector& data return Ret; } -bool Pak::extractFileToMemory( const std::string& path, SafeDataPointer& data ) { +bool Pak::extractFileToMemory( const std::string& path, ScopedBuffer& data ) { if ( NULL == mPak.fs || !mPak.fs->isOpen() ) { return false; } @@ -185,11 +185,10 @@ bool Pak::extractFileToMemory( const std::string& path, SafeDataPointer& data ) Int32 Pos = exists( path ); if ( Pos != -1 ) { - data.size = mPakFiles[Pos].file_length; - data.data = eeNewArray( Uint8, ( data.size ) ); + data.reset( mPakFiles[Pos].file_length ); mPak.fs->seek( mPakFiles[Pos].file_position ); - mPak.fs->read( reinterpret_cast ( data.data ), mPakFiles[Pos].file_length ); + mPak.fs->read( reinterpret_cast ( data.get() ), data.length() ); Ret = true; } @@ -279,11 +278,11 @@ bool Pak::addFile( const std::string& path, const std::string& inpack ) { if ( path.size() > 56 ) return false; - SafeDataPointer file; + ScopedBuffer file; FileSystem::fileGet( path, file ); - return addFile( file.data, file.size, inpack ); + return addFile( file.get(), file.length(), inpack ); } bool Pak::addFiles( std::map paths ) { diff --git a/src/eepp/system/rc4.cpp b/src/eepp/system/rc4.cpp index 345dbb08c..200064fed 100644 --- a/src/eepp/system/rc4.cpp +++ b/src/eepp/system/rc4.cpp @@ -72,13 +72,13 @@ bool RC4::encryptFile( const std::string& SourceFile, const std::string& DestFil if ( !FileSystem::fileExists( SourceFile ) ) return false; - SafeDataPointer data; + ScopedBuffer data; FileSystem::fileGet( SourceFile, data ); - encryptByte( data.data, data.size ); + encryptByte( data.get(), data.length() ); - FileSystem::fileWrite( DestFile, data.data, data.size ); + FileSystem::fileWrite( DestFile, data.get(), data.length() ); return true; } diff --git a/src/eepp/system/translator.cpp b/src/eepp/system/translator.cpp index ceff51e46..06efba65d 100644 --- a/src/eepp/system/translator.cpp +++ b/src/eepp/system/translator.cpp @@ -114,11 +114,11 @@ void Translator::loadFromStream( IOStream& stream, std::string lang ) { return; ios_size bufferSize = stream.getSize(); - SafeDataPointer safeDataPointer( bufferSize ); - stream.read( reinterpret_cast( safeDataPointer.data ), safeDataPointer.size ); + TScopedBuffer scopedBuffer( bufferSize ); + stream.read( scopedBuffer.get(), scopedBuffer.length() ); pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_buffer( safeDataPointer.data, safeDataPointer.size ); + pugi::xml_parse_result result = doc.load_buffer( scopedBuffer.get(), scopedBuffer.length() ); if ( result ) { loadNodes( doc.first_child(), lang ); @@ -130,12 +130,12 @@ void Translator::loadFromStream( IOStream& stream, std::string lang ) { } void Translator::loadFromPack( Pack * pack, const std::string& FilePackPath, std::string lang ) { - SafeDataPointer PData; + ScopedBuffer buffer; - if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, PData ) ) { + if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, buffer ) ) { lang = lang.size() == 2 ? lang : FileSystem::fileRemoveExtension( FileSystem::fileNameFromPath( FilePackPath ) ); - loadFromMemory( PData.data, PData.size, lang ); + loadFromMemory( buffer.get(), buffer.length(), lang ); } } diff --git a/src/eepp/system/zip.cpp b/src/eepp/system/zip.cpp index b94a01f3b..f2bc5fada 100644 --- a/src/eepp/system/zip.cpp +++ b/src/eepp/system/zip.cpp @@ -80,11 +80,11 @@ bool Zip::close() { } bool Zip::addFile( const std::string& path, const std::string& inpack ) { - SafeDataPointer file; + ScopedBuffer file; FileSystem::fileGet( path, file ); - return addFile( file.data, file.size, inpack ); + return addFile( file.get(), file.length(), inpack ); } bool Zip::addFile( const Uint8 * data, const Uint32& dataSize, const std::string& inpack ) { @@ -148,12 +148,12 @@ bool Zip::extractFile( const std::string& path , const std::string& dest ) { bool Ret; - SafeDataPointer data; + ScopedBuffer data; Ret = extractFileToMemory( path, data ); if ( Ret ) - FileSystem::fileWrite( dest, data.data, data.size ); + FileSystem::fileWrite( dest, data.get(), data.length() ); unlock(); @@ -194,7 +194,7 @@ bool Zip::extractFileToMemory( const std::string& path, std::vector& data return Ret; } -bool Zip::extractFileToMemory( const std::string& path, SafeDataPointer& data ) { +bool Zip::extractFileToMemory( const std::string& path, ScopedBuffer& data ) { lock(); bool Ret = false; @@ -210,10 +210,9 @@ bool Zip::extractFileToMemory( const std::string& path, SafeDataPointer& data ) struct zip_file * zf = zip_fopen_index( mZip, zs.index, 0 ); if ( NULL != zf ) { - data.size = (Uint32)zs.size; - data.data = eeNewArray( Uint8, ( data.size ) ); + data.reset( zs.size ); - Result = (Int32)zip_fread( zf, (void*)data.data, data.size ); + Result = (Int32)zip_fread( zf, (void*)data.get(), data.length() ); zip_fclose(zf); diff --git a/src/eepp/ui/css/stylesheetparser.cpp b/src/eepp/ui/css/stylesheetparser.cpp index 529b6a448..2c33b8831 100644 --- a/src/eepp/ui/css/stylesheetparser.cpp +++ b/src/eepp/ui/css/stylesheetparser.cpp @@ -42,10 +42,10 @@ bool StyleSheetParser::loadFromPack( Pack * pack, std::string filePackPath ) { bool Ret = false; - SafeDataPointer PData; + ScopedBuffer buffer; - if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, PData ) ) { - Ret = loadFromMemory( PData.data, PData.size ); + if ( pack->isOpen() && pack->extractFileToMemory( filePackPath, buffer ) ) { + Ret = loadFromMemory( buffer.get(), buffer.length() ); } return Ret; diff --git a/src/eepp/ui/uimenu.cpp b/src/eepp/ui/uimenu.cpp index c6876a85c..e88a8c8d0 100644 --- a/src/eepp/ui/uimenu.cpp +++ b/src/eepp/ui/uimenu.cpp @@ -259,7 +259,7 @@ void UIMenu::insert( UINode * Control, const Uint32& Index ) { bool UIMenu::isSubMenu( Node * Ctrl ) { for ( Uint32 i = 0; i < mItems.size(); i++ ) { - if ( mItems[i]->isType( UI_TYPE_MENUSUBMENU ) ) { + if ( NULL != mItems[i] && mItems[i]->isType( UI_TYPE_MENUSUBMENU ) ) { UIMenuSubMenu * tMenu = reinterpret_cast ( mItems[i] ); if ( tMenu->getSubMenu() == Ctrl ) diff --git a/src/eepp/ui/uiscenenode.cpp b/src/eepp/ui/uiscenenode.cpp index 74655dbce..cc454ee3c 100644 --- a/src/eepp/ui/uiscenenode.cpp +++ b/src/eepp/ui/uiscenenode.cpp @@ -209,11 +209,11 @@ UIWidget * UISceneNode::loadLayoutFromStream( IOStream& stream, Node * parent ) return NULL; ios_size bufferSize = stream.getSize(); - SafeDataPointer safeDataPointer( eeNewArray( Uint8, bufferSize ), bufferSize ); - stream.read( reinterpret_cast( safeDataPointer.data ), safeDataPointer.size ); + TScopedBuffer scopedBuffer( bufferSize ); + stream.read( scopedBuffer.get(), scopedBuffer.length() ); pugi::xml_document doc; - pugi::xml_parse_result result = doc.load_buffer( safeDataPointer.data, safeDataPointer.size ); + pugi::xml_parse_result result = doc.load_buffer( scopedBuffer.get(), scopedBuffer.length() ); if ( result ) { return loadLayoutNodes( doc.first_child(), NULL != parent ? parent : this ); @@ -227,10 +227,10 @@ UIWidget * UISceneNode::loadLayoutFromStream( IOStream& stream, Node * parent ) } UIWidget * UISceneNode::loadLayoutFromPack( Pack * pack, const std::string& FilePackPath, Node * parent ) { - SafeDataPointer PData; + ScopedBuffer buffer; - if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, PData ) ) { - return loadLayoutFromMemory( PData.data, PData.size, parent ); + if ( pack->isOpen() && pack->extractFileToMemory( FilePackPath, buffer ) ) { + return loadLayoutFromMemory( buffer.get(), buffer.length(), parent ); } return NULL;