/* -*-c++-*- */ /* osgEarth - Geospatial SDK for OpenSceneGraph * Copyright 2020 Pelican Mapping * http://osgearth.org * * osgEarth is free software; you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see */ #ifndef OSGEARTH_HTTP_CLIENT_H #define OSGEARTH_HTTP_CLIENT_H 1 #include #include #include #include #include #include #include #include #include #include namespace osgEarth { class ProgressCallback; } namespace osgEarth { namespace Util { using namespace osgEarth; /** * An HTTP request for use with the HTTPClient class. */ class OSGEARTH_EXPORT HTTPRequest { public: /** Constructs a new HTTP request that will acces the specified base URL. */ HTTPRequest( const std::string& url ); /** copy constructor. */ HTTPRequest( const HTTPRequest& rhs ); /** dtor */ virtual ~HTTPRequest() { } /** Adds an HTTP parameter to the request query string. */ void addParameter( const std::string& name, const std::string& value ); void addParameter( const std::string& name, int value ); void addParameter( const std::string& name, double value ); using Parameters = std::unordered_map; /** Ready-only access to the parameter list (as built with addParameter) */ const Parameters& getParameters() const; //! Add a header name/value pair to an HTTP request void addHeader( const std::string& name, const std::string& value ); //! Collection of headers in this request const Headers& getHeaders() const; //! Collection of headers in this request Headers& getHeaders(); /** * Sets the last modified date of any locally cached data for this request. This will * automatically add a If-Modified-Since header to the request */ void setLastModified( const DateTime &lastModified ); /** Gets a copy of the complete URL (base URL + query string) for this request */ std::string getURL() const; private: Parameters _parameters; Headers _headers; std::string _url; }; /** * An HTTP response object for use with the HTTPClient class - supports * multi-part mime responses. */ class OSGEARTH_EXPORT HTTPResponse { public: enum Code { NONE = 0, OK = 200, NOT_MODIFIED = 304, BAD_REQUEST = 400, FORBIDDEN = 403, NOT_FOUND = 404, CONFLICT = 409, INTERNAL_SERVER_ERROR = 500 }; enum CodeCategory { CATEGORY_UNKNOWN = 0, CATEGORY_INFORMATIONAL = 100, CATEGORY_SUCCESS = 200, CATEGORY_REDIRECTION = 300, CATEGORY_CLIENT_ERROR = 400, CATEGORY_SERVER_ERROR = 500 }; public: /** Constructs a response with the specified HTTP response code */ HTTPResponse( long code =0L ); /** Copy constructor */ HTTPResponse( const HTTPResponse& rhs ); /** dtor */ virtual ~HTTPResponse() { } /** Gets the HTTP response code (Code) in this response */ unsigned getCode() const; /** Gets the HTTP response code category for this response */ unsigned getCodeCategory() const; /** True is the HTTP response code is OK (200) */ bool isOK() const; /** True if the request associated with this response was cancelled before it completed */ void setCanceled(bool value) { _canceled = value; } bool isCanceled() const { return _canceled; } /** Gets the number of parts in a (possibly multipart mime) response */ unsigned int getNumParts() const; /** Gets the input stream for the nth part in the response */ std::istream& getPartStream( unsigned int n ) const; /** Gets the nth response part as a string */ std::string getPartAsString( unsigned int n ) const; /** Gets the length of the nth response part */ unsigned int getPartSize( unsigned int n ) const; /** Gets the HTTP header associated with the nth multipart/mime response part */ const std::string& getPartHeader( unsigned int n, const std::string& name ) const; /** Gets the master mime-type returned by the request */ void setMimeType(const std::string& value) { _mimeType = value; } const std::string& getMimeType() const; /** How long did it take to fetch this response (in seconds) */ void setDuration(double value) { _duration_s = value; } double getDuration() const { return _duration_s; } void setMessage(const std::string& value) { _message = value; } const std::string& getMessage() const { return _message; } void setLastModified(TimeStamp value) { _lastModified = value; } TimeStamp getLastModified() const { return _lastModified; } bool getFromCache() const { return _fromCache; } void setFromCache(bool fromCache) { _fromCache = fromCache; } struct Part : public osg::Referenced { Part() : _size(0) { } Headers _headers; unsigned int _size; std::stringstream _stream; }; typedef std::vector< osg::ref_ptr > Parts; Parts& getParts() { return _parts; } private: Parts _parts; long _response_code; std::string _mimeType; bool _canceled; double _duration_s; TimeStamp _lastModified; std::string _message; bool _fromCache; Config getHeadersAsConfig() const; void setHeadersFromConfig(const Config& conf); friend class HTTPClient; }; /** * Object that lets you modify and incoming URL before it's passed to the server */ struct OSGEARTH_EXPORT URLRewriter : public osg::Referenced { virtual std::string rewrite( const std::string& url ) = 0; }; /** * A configuration handler to apply settings. It can be used for setting client certificates */ struct OSGEARTH_EXPORT ConfigHandler : public osg::Referenced { virtual void onInitialize(void* handle) = 0; virtual void onGet(void* handle) = 0; }; /** * Utility class for making HTTP requests. */ class OSGEARTH_EXPORT HTTPClient { public: //! Interface for pluggable HTTP implementations class Implementation : public osg::Referenced { public: virtual void initialize() = 0; virtual HTTPResponse doGet( const HTTPRequest& request, const osgDB::Options* options, ProgressCallback* progress ) const = 0; virtual void setUserAgent(const std::string&) { } virtual void setTimeout(long) { } virtual void setConnectTimeout(long) { } //! Implementation-specific handle if applicable virtual void* getHandle() const { return NULL; } protected: virtual ~Implementation() {} }; //! Factory object to create implementation instances. class ImplementationFactory { public: virtual Implementation* create() const = 0; virtual ~ImplementationFactory() {}; }; //! Install an implementation factory. Do this before anything else static void setImplementationFactory(ImplementationFactory* factory); /** * Returns true is the result code represents a recoverable situation, * i.e. one in which retrying might work. */ static bool isRecoverable(ReadResult::Code code) { return code == ReadResult::RESULT_OK || code == ReadResult::RESULT_SERVER_ERROR || code == ReadResult::RESULT_TIMEOUT || code == ReadResult::RESULT_CANCELED; } /** Gets the user-agent string that all HTTP requests will use. */ static const std::string& getUserAgent(); /** Sets a user-agent string to use in all HTTP requests. */ static void setUserAgent(const std::string& userAgent); /** Sets up proxy info to use in all HTTP requests. */ static void setProxySettings( const optional &proxySettings ); /** Gets up proxy info to use in all HTTP requests. */ static const optional & getProxySettings(); /** Gets the timeout in seconds to use for HTTP requests.*/ static long getTimeout(); /** Sets the timeout in seconds to use for HTTP requests. Setting to 0 (default) is infinite timeout */ static void setTimeout( long timeout ); /** Sets the suggested delay (in seconds) before a retry should be attempted in the case of a canceled request */ static void setRetryDelay(float value_seconds); static float getRetryDelay(); /** Gets the timeout in seconds to use for HTTP connect requests.*/ static long getConnectTimeout(); /** Sets the timeout in seconds to use for HTTP connect requests. Setting to 0 (default) is infinite timeout */ static void setConnectTimeout( long timeout ); /** * Gets the URLRewriter that is used to modify urls before sending them to the server */ static URLRewriter* getURLRewriter(); /** * Sets the URLRewriter that is used to modify urls before sending them to the server */ static void setURLRewriter( URLRewriter* rewriter ); static ConfigHandler* getConfigHandler(); /** * Sets the CurlConfigHandler to configurate the CURL library. It can be used for apply client certificates */ static void setConfigHandler(ConfigHandler* handler); /** * One time thread safe initialization. In osgEarth, you don't need * to call this directly; osgEarth::Registry will call it at * startup. */ static void globalInit(); public: /** * Reads an image. */ static ReadResult readImage( const HTTPRequest& request, const osgDB::Options* dbOptions =0L, ProgressCallback* progress =0L ); /** * Reads an osg::Node. */ static ReadResult readNode( const HTTPRequest& request, const osgDB::Options* dbOptions =0L, ProgressCallback* progress =0L ); /** * Reads an object. */ static ReadResult readObject( const HTTPRequest& request, const osgDB::Options* dbOptions =0L, ProgressCallback* progress =0L ); /** * Reads a string. */ static ReadResult readString( const HTTPRequest& request, const osgDB::Options* dbOptions =0L, ProgressCallback* progress =0L ); /** * Downloads a file directly to disk. */ static bool download( const std::string& uri, const std::string& localPath ); public: /** * Performs an HTTP "GET". */ static HTTPResponse get( const HTTPRequest& request, const osgDB::Options* dbOptions =0L, ProgressCallback* progress =0L ); static HTTPResponse get( const std::string& url, const osgDB::Options* options =0L, ProgressCallback* progress =0L ); public: HTTPClient(); virtual ~HTTPClient(); private: void readOptions( const osgDB::ReaderWriter::Options* options, std::string &proxy_host, std::string &proxy_port ) const; HTTPResponse doGet( const HTTPRequest& request, const osgDB::Options* options =0L, ProgressCallback* callback =0L ) const; ReadResult doReadObject( const HTTPRequest& request, const osgDB::Options* dbOptions, ProgressCallback* progress ); ReadResult doReadImage( const HTTPRequest& request, const osgDB::Options* dbOptions, ProgressCallback* progress ); ReadResult doReadNode( const HTTPRequest& request, const osgDB::Options* dbOptions, ProgressCallback* progress ); ReadResult doReadString( const HTTPRequest& request, const osgDB::Options* dbOptions, ProgressCallback* progress ); /** * Convenience method for downloading a URL directly to a file */ bool doDownload(const std::string& url, const std::string& filename); private: void* _curl_handle; std::string _previousPassword; long _previousHttpAuthentication; bool _initialized; long _simResponseCode; osg::ref_ptr _impl; void initialize() const; void initializeImpl(); static ImplementationFactory* _implFactory; static HTTPClient& getClient(); }; class OSGEARTH_EXPORT CURLHTTPImplementationFactory : public HTTPClient::ImplementationFactory { public: HTTPClient::Implementation* create() const; }; class OSGEARTH_EXPORT WinInetHTTPImplementationFactory : public HTTPClient::ImplementationFactory { public: HTTPClient::Implementation* create() const; }; } } #endif // OSGEARTH_HTTP_CLIENT_H