Home | History | Annotate | Download | only in cloud
      1 /* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
      2 
      3 Licensed under the Apache License, Version 2.0 (the "License");
      4 you may not use this file except in compliance with the License.
      5 You may obtain a copy of the License at
      6 
      7     http://www.apache.org/licenses/LICENSE-2.0
      8 
      9 Unless required by applicable law or agreed to in writing, software
     10 distributed under the License is distributed on an "AS IS" BASIS,
     11 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     12 See the License for the specific language governing permissions and
     13 limitations under the License.
     14 ==============================================================================*/
     15 
     16 #ifndef TENSORFLOW_CORE_PLATFORM_CLOUD_CURL_HTTP_REQUEST_H_
     17 #define TENSORFLOW_CORE_PLATFORM_CLOUD_CURL_HTTP_REQUEST_H_
     18 
     19 #include <string>
     20 #include <unordered_map>
     21 #include <vector>
     22 #include <curl/curl.h>
     23 #include "tensorflow/core/lib/core/errors.h"
     24 #include "tensorflow/core/lib/core/status.h"
     25 #include "tensorflow/core/lib/core/stringpiece.h"
     26 #include "tensorflow/core/platform/cloud/http_request.h"
     27 #include "tensorflow/core/platform/env.h"
     28 #include "tensorflow/core/platform/macros.h"
     29 #include "tensorflow/core/platform/protobuf.h"
     30 #include "tensorflow/core/platform/types.h"
     31 
     32 namespace tensorflow {
     33 
     34 class LibCurl;  // libcurl interface as a class, for dependency injection.
     35 
     36 /// \brief A basic HTTP client based on the libcurl library.
     37 ///
     38 /// The usage pattern for the class reflects the one of the libcurl library:
     39 /// create a request object, set request parameters and call Send().
     40 ///
     41 /// For example:
     42 ///   std::unique_ptr<HttpRequest> request(http_request_factory->Create());
     43 ///   request->SetUri("http://www.google.com");
     44 ///   request->SetResultsBuffer(out_buffer);
     45 ///   request->Send();
     46 class CurlHttpRequest : public HttpRequest {
     47  public:
     48   class Factory : public HttpRequest::Factory {
     49    public:
     50     virtual ~Factory() {}
     51     virtual HttpRequest* Create() { return new CurlHttpRequest(); }
     52   };
     53 
     54   CurlHttpRequest();
     55   explicit CurlHttpRequest(LibCurl* libcurl)
     56       : CurlHttpRequest(libcurl, Env::Default()) {}
     57   CurlHttpRequest(LibCurl* libcurl, Env* env);
     58   ~CurlHttpRequest() override;
     59 
     60   /// Sets the request URI.
     61   void SetUri(const string& uri) override;
     62 
     63   /// \brief Sets the Range header.
     64   ///
     65   /// Used for random seeks, for example "0-999" returns the first 1000 bytes
     66   /// (note that the right border is included).
     67   void SetRange(uint64 start, uint64 end) override;
     68 
     69   /// Sets a request header.
     70   void AddHeader(const string& name, const string& value) override;
     71 
     72   void AddResolveOverride(const string& hostname, int64 port,
     73                           const string& ip_addr) override;
     74 
     75   /// Sets the 'Authorization' header to the value of 'Bearer ' + auth_token.
     76   void AddAuthBearerHeader(const string& auth_token) override;
     77 
     78   /// Makes the request a DELETE request.
     79   void SetDeleteRequest() override;
     80 
     81   /// \brief Makes the request a PUT request.
     82   ///
     83   /// The request body will be taken from the specified file starting from
     84   /// the given offset.
     85   Status SetPutFromFile(const string& body_filepath, size_t offset) override;
     86 
     87   /// Makes the request a PUT request with an empty body.
     88   void SetPutEmptyBody() override;
     89 
     90   /// \brief Makes the request a POST request.
     91   ///
     92   /// The request body will be taken from the specified buffer.
     93   void SetPostFromBuffer(const char* buffer, size_t size) override;
     94 
     95   /// Makes the request a POST request with an empty body.
     96   void SetPostEmptyBody() override;
     97 
     98   /// \brief Specifies the buffer for receiving the response body.
     99   ///
    100   /// Size of out_buffer after an access will be exactly the number of bytes
    101   /// read. Existing content of the vector will be cleared.
    102   void SetResultBuffer(std::vector<char>* out_buffer) override;
    103 
    104   /// \brief Specifies the buffer for receiving the response body, when the
    105   /// caller knows the maximum size of the response body.
    106   ///
    107   /// This method allows the caller to receive the response body without an
    108   /// additional intermediate buffer allocation and copy.  This method should
    109   /// be called before calling Send(). After Send() has succeeded, the caller
    110   /// should use the GetResultBufferDirectBytesTransferred() method in order
    111   /// to learn how many bytes were transferred.
    112   ///
    113   /// Using this method is mutually exclusive with using SetResultBuffer().
    114   void SetResultBufferDirect(char* buffer, size_t size) override;
    115 
    116   /// \brief Distinguish response type (direct vs. implicit).
    117   bool IsDirectResponse() const;
    118 
    119   /// \brief Returns the number of bytes (of the response body) that were
    120   /// transferred, when using the SetResultBufferDirect() method. The returned
    121   /// value will always be less than or equal to the 'size' parameter that
    122   /// was passed to SetResultBufferDirect(). If the actual HTTP response body
    123   /// was greater than 'size' bytes, then this transfer method will only copy
    124   /// the first 'size' bytes, and the rest will be ignored.
    125   size_t GetResultBufferDirectBytesTransferred() override;
    126 
    127   /// \brief Returns the response headers of a completed request.
    128   ///
    129   /// If the header is not found, returns an empty string.
    130   string GetResponseHeader(const string& name) const override;
    131 
    132   /// Returns the response code of a completed request.
    133   uint64 GetResponseCode() const override;
    134 
    135   /// \brief Sends the formed request.
    136   ///
    137   /// If the result buffer was defined, the response will be written there.
    138   /// The object is not designed to be re-used after Send() is executed.
    139   Status Send() override;
    140 
    141   // Url encodes str and returns a new string.
    142   string EscapeString(const string& str) override;
    143 
    144   void SetTimeouts(uint32 connection, uint32 inactivity, uint32 total) override;
    145 
    146  private:
    147   /// A write callback in the form which can be accepted by libcurl.
    148   static size_t WriteCallback(const void* ptr, size_t size, size_t nmemb,
    149                               void* userdata);
    150 
    151   /// Processes response body content received when using SetResultBufferDirect.
    152   static size_t WriteCallbackDirect(const void* ptr, size_t size, size_t nmemb,
    153                                     void* userdata);
    154   /// A read callback in the form which can be accepted by libcurl.
    155   static size_t ReadCallback(void* ptr, size_t size, size_t nmemb,
    156                              FILE* userdata);
    157   /// A header callback in the form which can be accepted by libcurl.
    158   static size_t HeaderCallback(const void* ptr, size_t size, size_t nmemb,
    159                                void* this_object);
    160   /// A progress meter callback in the form which can be accepted by libcurl.
    161   static int ProgressCallback(void* this_object, curl_off_t dltotal,
    162                               curl_off_t dlnow, curl_off_t ultotal,
    163                               curl_off_t ulnow);
    164   void CheckMethodNotSet() const;
    165   void CheckNotSent() const;
    166   StringPiece GetResponse() const;
    167 
    168   LibCurl* libcurl_;
    169   Env* env_;
    170 
    171   FILE* put_body_ = nullptr;
    172 
    173   StringPiece post_body_buffer_;
    174   size_t post_body_read_ = 0;
    175 
    176   std::vector<char>* response_buffer_ = nullptr;
    177 
    178   struct DirectResponseState {
    179     char* buffer_;
    180     size_t buffer_size_;
    181     size_t bytes_transferred_;
    182   };
    183   DirectResponseState direct_response_ = {};
    184 
    185   CURL* curl_ = nullptr;
    186   curl_slist* curl_headers_ = nullptr;
    187   curl_slist* resolve_list_ = nullptr;
    188 
    189   std::vector<char> default_response_buffer_;
    190 
    191   std::unordered_map<string, string> response_headers_;
    192   uint64 response_code_ = 0;
    193 
    194   // The timestamp of the last activity related to the request execution, in
    195   // seconds since epoch.
    196   uint64 last_progress_timestamp_ = 0;
    197   // The last progress in terms of bytes transmitted.
    198   curl_off_t last_progress_bytes_ = 0;
    199 
    200   // The maximum period of request inactivity.
    201   uint32 inactivity_timeout_secs_ = 60;  // 1 minute
    202 
    203   // Timeout for the connection phase.
    204   uint32 connect_timeout_secs_ = 120;  // 2 minutes
    205 
    206   // Tiemout for the whole request. Set only to prevent hanging indefinitely.
    207   uint32 request_timeout_secs_ = 3600;  // 1 hour
    208 
    209   // Members to enforce the usage flow.
    210   bool is_uri_set_ = false;
    211   bool is_method_set_ = false;
    212   bool is_sent_ = false;
    213 
    214   // Store the URI to help disambiguate requests when errors occur.
    215   string uri_;
    216 
    217   // Limit the size of a http response that is copied into an error message.
    218   const size_t response_to_error_limit_ = 500;
    219 
    220   TF_DISALLOW_COPY_AND_ASSIGN(CurlHttpRequest);
    221 };
    222 
    223 /// \brief A proxy to the libcurl C interface as a dependency injection measure.
    224 ///
    225 /// This class is meant as a very thin wrapper for the libcurl C library.
    226 class LibCurl {
    227  public:
    228   virtual ~LibCurl() {}
    229 
    230   virtual CURL* curl_easy_init() = 0;
    231   virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
    232                                     uint64 param) = 0;
    233   virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
    234                                     const char* param) = 0;
    235   virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
    236                                     void* param) = 0;
    237   virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
    238                                     size_t (*param)(void*, size_t, size_t,
    239                                                     FILE*)) = 0;
    240   virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
    241                                     size_t (*param)(const void*, size_t, size_t,
    242                                                     void*)) = 0;
    243   virtual CURLcode curl_easy_setopt(
    244       CURL* curl, CURLoption option,
    245       int (*param)(void* clientp, curl_off_t dltotal, curl_off_t dlnow,
    246                    curl_off_t ultotal, curl_off_t ulnow)) = 0;
    247   virtual CURLcode curl_easy_perform(CURL* curl) = 0;
    248   virtual CURLcode curl_easy_getinfo(CURL* curl, CURLINFO info,
    249                                      uint64* value) = 0;
    250   virtual CURLcode curl_easy_getinfo(CURL* curl, CURLINFO info,
    251                                      double* value) = 0;
    252   virtual void curl_easy_cleanup(CURL* curl) = 0;
    253   virtual curl_slist* curl_slist_append(curl_slist* list, const char* str) = 0;
    254   virtual void curl_slist_free_all(curl_slist* list) = 0;
    255   virtual char* curl_easy_escape(CURL* curl, const char* str, int length) = 0;
    256   virtual void curl_free(void* p) = 0;
    257 
    258   virtual const char* curl_easy_strerror(CURLcode errornum) = 0;
    259 };
    260 
    261 }  // namespace tensorflow
    262 
    263 #endif  // TENSORFLOW_CORE_PLATFORM_CLOUD_CURL_HTTP_REQUEST_H_
    264