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