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] 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;