/** * @file llurlrequest.h * @author Phoenix * @date 2005-04-21 * @brief Declaration of url based requests on pipes. * * Copyright (c) 2005-$CurrentYear$, Linden Research, Inc. * $License$ */ #ifndef LL_LLURLREQUEST_H #define LL_LLURLREQUEST_H /** * This file holds the declaration of useful classes for dealing with * url based client requests. */ #include #include "lliopipe.h" #include "llchainio.h" class LLURLRequestDetail; class LLURLRequestComplete; /** * @class LLURLRequest * @brief Class to handle url based requests. * @see LLIOPipe * * Currently, this class is implemented on top of curl. From the * vantage of a programmer using this class, you do not care so much, * but it's useful to know since in order to accomplish 'non-blocking' * behavior, we have to use a more expensive curl interface which can * still block if the server enters a half-accepted state. It would be * worth the time and effort to eventually port this to a raw client * socket. */ class LLURLRequest : public LLIOPipe { public: /** * @brief This enumeration is for specifying the type of request. */ enum ERequestAction { INVALID, HTTP_GET, HTTP_PUT, HTTP_POST, HTTP_DELETE, REQUEST_ACTION_COUNT }; /** * @brief Constructor. * * @param action One of the ERequestAction enumerations. */ LLURLRequest(ERequestAction action); /** * @brief Constructor. * * @param action One of the ERequestAction enumerations. * @param url The url of the request. It should already be encoded. */ LLURLRequest(ERequestAction action, const std::string& url); /** * @brief Destructor. */ virtual ~LLURLRequest(); /* @name Instance methods */ //@{ /** * @brief Set the url for the request * * This method assumes the url is encoded appropriately for the * request. * The url must be set somehow before the first call to process(), * or the url will not be set correctly. * */ void setURL(const std::string& url); /** * @brief Add a header to the http post. * * The header must be correctly formatted for HTTP requests. This * provides a raw interface if you know what kind of request you * will be making during construction of this instance. All * required headers will be automatically constructed, so this is * usually useful for encoding parameters. */ void addHeader(const char* header); /** * @brief Check remote server certificate signed by a known root CA. * * Set whether request will check that remote server * certificates are signed by a known root CA when using HTTPS. * Use the supplied root certificate bundle if supplied, else use * the standard bundle as found by libcurl and openssl. */ void checkRootCertificate(bool check, const char* caBundle = NULL); /** * @brief Request a particular response encoding if available. * * This call is a shortcut for requesting a particular encoding * from the server, eg, 'gzip'. */ void requestEncoding(const char* encoding); /** * @brief Return at most size bytes of body. * * If the body had more bytes than this limit, they will not be * returned and the connection closed. In this case, STATUS_STOP * will be passed to responseStatus(); */ void setBodyLimit(U32 size); /** * @brief Set a completion callback for this URLRequest. * * The callback is added to this URLRequet's pump when either the * entire buffer is known or an error like timeout or connection * refused has happened. In the case of a complete transfer, this * object builds a response chain such that the callback and the * next process consumer get to read the output. * * This setup is a little fragile since the url request consumer * might not just read the data - it may do a channel change, * which invalidates the input to the callback, but it works well * in practice. */ void setCallback(LLURLRequestComplete* callback); //@} /** * @ brief Set certificate authority file used to verify HTTPS certs. */ static void setCertificateAuthorityFile(const std::string& file_name); /** * @ brief Set certificate authority path used to verify HTTPS certs. */ static void setCertificateAuthorityPath(const std::string& path); /* @name LLIOPipe virtual implementations */ public: /** * @brief Give this pipe a chance to handle a generated error */ virtual EStatus handleError(EStatus status, LLPumpIO* pump); protected: /** * @brief Process the data in buffer */ virtual EStatus process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump); //@} protected: enum EState { STATE_INITIALIZED, STATE_WAITING_FOR_RESPONSE, STATE_PROCESSING_RESPONSE, STATE_HAVE_RESPONSE, }; EState mState; ERequestAction mAction; LLURLRequestDetail* mDetail; LLIOPipe::ptr_t mCompletionCallback; private: /** * @brief Initialize the object. Called during construction. */ void initialize(); /** * @brief Handle action specific url request configuration. * * @return Returns true if this is configured. */ bool configure(); /** * @brief Download callback method. */ static size_t downCallback( void* data, size_t size, size_t nmemb, void* user); /** * @brief Upload callback method. */ static size_t upCallback( void* data, size_t size, size_t nmemb, void* user); /** * @brief Declaration of unimplemented method to prevent copy * construction. */ LLURLRequest(const LLURLRequest&); }; /** * @class LLContextURLExtractor * @brief This class unpacks the url out of a agent usher service so * it can be packed into a LLURLRequest object. * @see LLIOPipe * * This class assumes that the context is a map that contains an entry * named CONTEXT_DEST_URI_SD_LABEL. */ class LLContextURLExtractor : public LLIOPipe { public: LLContextURLExtractor(LLURLRequest* req) : mRequest(req) {} ~LLContextURLExtractor() {} protected: /* @name LLIOPipe virtual implementations */ //@{ /** * @brief Process the data in buffer */ virtual EStatus process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump); //@} protected: LLURLRequest* mRequest; }; /** * @class LLURLRequestComplete * @brief Class which can optionally be used with an LLURLRequest to * get notification when the url request is complete. */ class LLURLRequestComplete : public LLIOPipe { public: virtual void header(const std::string& header, const std::string& value); ///< Called once for each header received, prior to httpStatus virtual void httpStatus(U32 status, const std::string& reason); ///< Always called on request completion, prior to complete virtual void complete( const LLChannelDescriptors& channels, const buffer_ptr_t& buffer); /** * @brief This method is called when we got a valid response. * * It is up to class implementers to do something useful here. */ virtual void response( const LLChannelDescriptors& channels, const buffer_ptr_t& buffer); /** * @brief This method is called if there was no response. * * It is up to class implementers to do something useful here. */ virtual void noResponse(); /** * @brief This method will be called by the LLURLRequest object. * * If this is set to STATUS_OK or STATUS_STOP, then the transfer * is asssumed to have worked. This will lead to calling response() * on the next call to process(). Otherwise, this object will call * noResponse() on the next call to process. * @param status The status of the URLRequest. */ void responseStatus(EStatus status); // constructor & destructor. LLURLRequestComplete(); virtual ~LLURLRequestComplete(); protected: /* @name LLIOPipe virtual implementations */ //@{ /** * @brief Process the data in buffer */ virtual EStatus process_impl( const LLChannelDescriptors& channels, buffer_ptr_t& buffer, bool& eos, LLSD& context, LLPumpIO* pump); //@} // value to note if we actually got the response. This value // depends on correct useage from the LLURLRequest instance. EStatus mRequestStatus; }; /** * @class LLURLRequestClientFactory * @brief Template class to build url request based client chains * * This class eases construction of a basic sd rpc client. Here is an * example of it's use: * * class LLUsefulService : public LLService { ... }
* LLService::registerCreator(
* "useful",
* LLService::creator_t(new LLURLRequestClientFactory))
*
* * This class should work, but I never got around to using/testing it. * */ #if 0 template class LLURLRequestClientFactory : public LLChainIOFactory { public: LLURLRequestClientFactory(LLURLRequest::ERequestAction action) {} LLURLRequestClientFactory( LLURLRequest::ERequestAction action, const std::string& fixed_url) : mAction(action), mURL(fixed_url) { } virtual bool build(LLPumpIO::chain_t& chain, LLSD context) const { lldebugs << "LLURLRequestClientFactory::build" << llendl; LLIOPipe::ptr_t service(new Client); chain.push_back(service); LLURLRequest* http(new LLURLRequest(mAction)); LLIOPipe::ptr_t http_pipe(http); // *FIX: how do we know the content type? //http->addHeader("Content-Type: text/llsd"); if(mURL.empty()) { chain.push_back(LLIOPipe::ptr_t(new LLContextURLExtractor(http))); } else { http->setURL(mURL); } chain.push_back(http_pipe); chain.push_back(service); return true; } protected: LLURLRequest::ERequestAction mAction; std::string mURL; }; #endif /** * External constants */ extern const std::string CONTEXT_DEST_URI_SD_LABEL; #endif // LL_LLURLREQUEST_H