From c8628a020541398030fc41824e8eebd2f72f1659 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mart=C3=ADn=20Lucas=20Golini?= Date: Tue, 23 Apr 2019 00:14:13 -0300 Subject: [PATCH] Added support for http requests to follow redirections. --HG-- branch : dev --- .hgignore | 2 + include/eepp/network/http.hpp | 14 +++++- include/eepp/system/thread.hpp | 8 ++-- src/eepp/network/http.cpp | 50 ++++++++++++++++++++-- src/eepp/system/thread.cpp | 2 +- src/examples/http_request/http_request.cpp | 34 +++++++-------- 6 files changed, 82 insertions(+), 28 deletions(-) diff --git a/.hgignore b/.hgignore index 56dbef0ce..d0e186abb 100644 --- a/.hgignore +++ b/.hgignore @@ -51,3 +51,5 @@ bin/libeepp.dylib .idea eepp.kdev4 bin/eepp-MapEditor* +eepp.tags +eepp.geany diff --git a/include/eepp/network/http.hpp b/include/eepp/network/http.hpp index 7f631c2c7..0bebdb406 100644 --- a/include/eepp/network/http.hpp +++ b/include/eepp/network/http.hpp @@ -45,8 +45,10 @@ class EE_API Http : NonCopyable { ** @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 */ - Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = true, bool validateHostname = true ); + ** @param validateHostname Enables hostname validation for https request + ** @param followRedirect Allow follor redirects to the request. + */ + Request(const std::string& uri = "/", Method method = Get, const std::string& body = "", bool validateCertificate = true, bool validateHostname = true, bool followRedirect = true); /** @brief Set the value of a field ** The field is created if it doesn't exist. The name of @@ -99,6 +101,12 @@ class EE_API Http : NonCopyable { /** 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 ); private: friend class Http; @@ -126,6 +134,8 @@ class EE_API Http : NonCopyable { 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 }; /** @brief Define a HTTP response */ diff --git a/include/eepp/system/thread.hpp b/include/eepp/system/thread.hpp index 68a0da530..ed0d55196 100755 --- a/include/eepp/system/thread.hpp +++ b/include/eepp/system/thread.hpp @@ -127,7 +127,7 @@ namespace Private { struct ThreadFunc { virtual ~ThreadFunc() {} - virtual void Run() = 0; + virtual void run() = 0; }; // Specialization using a functor (including free functions) with no argument @@ -135,7 +135,7 @@ template struct ThreadFunctor : ThreadFunc { ThreadFunctor(T functor) : mFunctor(functor) {} - virtual void Run() {mFunctor();} + virtual void run() {mFunctor();} T mFunctor; }; @@ -144,7 +144,7 @@ template struct ThreadFunctorWithArg : ThreadFunc { ThreadFunctorWithArg(F function, A arg) : mFunction(function), mArg(arg) {} - virtual void Run() {mFunction(mArg);} + virtual void run() {mFunction(mArg);} F mFunction; A mArg; }; @@ -154,7 +154,7 @@ template struct ThreadMemberFunc : ThreadFunc { ThreadMemberFunc(void(C::*function)(), C* object) : mFunction(function), mObject(object) {} - virtual void Run() {(mObject->*mFunction)();} + virtual void run() {(mObject->*mFunction)();} void(C::*mFunction)(); C* mObject; }; diff --git a/src/eepp/network/http.cpp b/src/eepp/network/http.cpp index 6334bb916..9df192c2e 100644 --- a/src/eepp/network/http.cpp +++ b/src/eepp/network/http.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include #include @@ -12,9 +13,11 @@ using namespace EE::Network::SSL; namespace EE { namespace Network { -Http::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname ) : +Http::Request::Request(const std::string& uri, Method method, const std::string& body, bool validateCertificate, bool validateHostname , bool followRedirect) : mValidateCertificate( validateCertificate ), - mValidateHostname( validateHostname ) + mValidateHostname( validateHostname ), + mFollowRedirect( followRedirect ), + mRedirectionCount( 0 ) { setMethod(method); setUri(uri); @@ -67,6 +70,14 @@ void Http::Request::setValidateHostname(bool enable) { mValidateHostname = enable; } +const bool &Http::Request::getFollowRedirect() const { + return mFollowRedirect; +} + +void Http::Request::setFollowRedirect(bool follow) { + mFollowRedirect = follow; +} + std::string Http::Request::prepare() const { std::ostringstream out; @@ -261,6 +272,8 @@ Http::~Http() { } void Http::setHost(const std::string& host, unsigned short port, bool useSSL) { + bool sameHost( host == mHostName && port == mPort && useSSL == mIsSSL ); + // Check the protocol if (String::toLower(host.substr(0, 7)) == "http://") { // HTTP protocol @@ -292,6 +305,15 @@ void Http::setHost(const std::string& host, unsigned short port, bool useSSL) { mHostName.erase(mHostName.size() - 1); 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 + // the old connection to prepare a new one. + if ( !sameHost && NULL != mConnection ) { + TcpSocket * tcp = mConnection; + eeSAFE_DELETE( tcp ); + mConnection = NULL; + } } Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { @@ -336,6 +358,26 @@ Http::Response Http::sendRequest(const Http::Request& request, Time timeout) { 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() ) { + + 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); + + setHost( uri.getHost(), uri.getPort(), uri.getScheme() == "https" ? true : false ); + Http::Request newRequest(request); + newRequest.setUri( uri.getPathEtc() ); + + return sendRequest( request, timeout ); + } + } + return received; } @@ -576,8 +618,8 @@ void Http::sendAsyncRequest( AsyncResponseCallback cb, const Http::Request& requ mThreads.push_back( thread ); } -void Http::downloadAsyncRequest(Http::AsyncResponseCallback cb, const Http::Request & request, IOStream & writeTo, Time timeout) { - AsyncRequest * thread = eeNew( AsyncRequest, ( this, cb, request, timeout ) ); +void Http::downloadAsyncRequest(Http::AsyncResponseCallback cb, const Http::Request& request, IOStream& writeTo, Time timeout) { + AsyncRequest * thread = eeNew( AsyncRequest, ( this, cb, request, writeTo, timeout ) ); thread->launch(); diff --git a/src/eepp/system/thread.cpp b/src/eepp/system/thread.cpp index 5816fd79e..af84821f4 100755 --- a/src/eepp/system/thread.cpp +++ b/src/eepp/system/thread.cpp @@ -47,7 +47,7 @@ Uint32 Thread::getId() { } void Thread::run() { - mEntryPoint->Run(); + mEntryPoint->run(); } }} diff --git a/src/examples/http_request/http_request.cpp b/src/examples/http_request/http_request.cpp index f76649df0..3ba9ec953 100644 --- a/src/examples/http_request/http_request.cpp +++ b/src/examples/http_request/http_request.cpp @@ -40,29 +40,29 @@ EE_MAIN_FUNC int main (int argc, char * argv []) { http.setHost( uri.getHost(), uri.getPort() ); // Set the path and query parts for the request - request.setUri( uri.getPathAndQuery() ); - } + request.setUri( uri.getPathEtc() ); - // Send the request - Http::Response response = http.sendRequest(request); + // Send the request + Http::Response response = http.sendRequest(request); - // Check the status code and display the result - Http::Response::Status status = response.getStatus(); + // Check the status code and display the result + Http::Response::Status status = response.getStatus(); - if ( status == Http::Response::Ok ) { - Http::Response::FieldTable headers = response.getHeaders(); + if ( status == Http::Response::Ok ) { + Http::Response::FieldTable headers = response.getHeaders(); - std::cout << "Headers: " << std::endl; + std::cout << "Headers: " << std::endl; - for ( auto head = headers.begin(); head != headers.end(); ++head ) { - std::cout << "\t" << head->first << ": " << head->second << std::endl; + for ( auto head = headers.begin(); head != headers.end(); ++head ) { + std::cout << "\t" << head->first << ": " << head->second << std::endl; + } + + std::cout << std::endl << "Body: " << std::endl; + + std::cout << response.getBody() << std::endl; + } else { + std::cout << "Error " << status << std::endl; } - - std::cout << std::endl << "Body: " << std::endl; - - std::cout << response.getBody() << std::endl; - } else { - std::cout << "Error " << status << std::endl; } }