curl_http_request.h revision b1d89c147d079ab97356b6a677cbf5ee726313d6
1/* Copyright 2017 The TensorFlow Authors. All Rights Reserved.
2
3Licensed under the Apache License, Version 2.0 (the "License");
4you may not use this file except in compliance with the License.
5You may obtain a copy of the License at
6
7    http://www.apache.org/licenses/LICENSE-2.0
8
9Unless required by applicable law or agreed to in writing, software
10distributed under the License is distributed on an "AS IS" BASIS,
11WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12See the License for the specific language governing permissions and
13limitations 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
32namespace tensorflow {
33
34class 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();
46class 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  Status Init() override;
61
62  /// Sets the request URI.
63  Status SetUri(const string& uri) override;
64
65  /// \brief Sets the Range header.
66  ///
67  /// Used for random seeks, for example "0-999" returns the first 1000 bytes
68  /// (note that the right border is included).
69  Status SetRange(uint64 start, uint64 end) override;
70
71  /// Sets a request header.
72  Status AddHeader(const string& name, const string& value) override;
73
74  Status AddResolveOverride(const string& hostname, int64 port,
75                            const string& ip_addr) override;
76
77  /// Sets the 'Authorization' header to the value of 'Bearer ' + auth_token.
78  Status AddAuthBearerHeader(const string& auth_token) override;
79
80  /// Makes the request a DELETE request.
81  Status SetDeleteRequest() override;
82
83  /// \brief Makes the request a PUT request.
84  ///
85  /// The request body will be taken from the specified file starting from
86  /// the given offset.
87  Status SetPutFromFile(const string& body_filepath, size_t offset) override;
88
89  /// Makes the request a PUT request with an empty body.
90  Status SetPutEmptyBody() override;
91
92  /// \brief Makes the request a POST request.
93  ///
94  /// The request body will be taken from the specified buffer.
95  Status SetPostFromBuffer(const char* buffer, size_t size) override;
96
97  /// Makes the request a POST request with an empty body.
98  Status SetPostEmptyBody() override;
99
100  /// \brief Specifies the buffer for receiving the response body.
101  ///
102  /// Size of out_buffer after an access will be exactly the number of bytes
103  /// read. Existing content of the vector will be cleared.
104  Status SetResultBuffer(std::vector<char>* out_buffer) override;
105
106  /// \brief Returns the response headers of a completed request.
107  ///
108  /// If the header is not found, returns an empty string.
109  string GetResponseHeader(const string& name) const override;
110
111  /// Returns the response code of a completed request.
112  uint64 GetResponseCode() const override;
113
114  /// \brief Sends the formed request.
115  ///
116  /// If the result buffer was defined, the response will be written there.
117  /// The object is not designed to be re-used after Send() is executed.
118  Status Send() override;
119
120  // Url encodes str and returns a new string.
121  string EscapeString(const string& str) override;
122
123 private:
124  /// A write callback in the form which can be accepted by libcurl.
125  static size_t WriteCallback(const void* ptr, size_t size, size_t nmemb,
126                              void* userdata);
127  /// A read callback in the form which can be accepted by libcurl.
128  static size_t ReadCallback(void* ptr, size_t size, size_t nmemb,
129                             FILE* userdata);
130  /// A header callback in the form which can be accepted by libcurl.
131  static size_t HeaderCallback(const void* ptr, size_t size, size_t nmemb,
132                               void* this_object);
133  /// A progress meter callback in the form which can be accepted by libcurl.
134  static int ProgressCallback(void* this_object, curl_off_t dltotal,
135                              curl_off_t dlnow, curl_off_t ultotal,
136                              curl_off_t ulnow);
137  Status CheckInitialized() const;
138  Status CheckMethodNotSet() const;
139  Status CheckNotSent() const;
140
141  LibCurl* libcurl_;
142  Env* env_;
143
144  FILE* put_body_ = nullptr;
145
146  StringPiece post_body_buffer_;
147  size_t post_body_read_ = 0;
148
149  std::vector<char>* response_buffer_ = nullptr;
150  CURL* curl_ = nullptr;
151  curl_slist* curl_headers_ = nullptr;
152  curl_slist* resolve_list_ = nullptr;
153
154  std::vector<char> default_response_buffer_;
155
156  std::unordered_map<string, string> response_headers_;
157  uint64 response_code_ = 0;
158
159  // The timestamp of the last activity related to the request execution, in
160  // seconds since epoch.
161  uint64 last_progress_timestamp_ = 0;
162  // The last progress in terms of bytes transmitted.
163  curl_off_t last_progress_bytes_ = 0;
164
165  // Members to enforce the usage flow.
166  bool is_initialized_ = false;
167  bool is_uri_set_ = false;
168  bool is_method_set_ = false;
169  bool is_sent_ = false;
170
171  TF_DISALLOW_COPY_AND_ASSIGN(CurlHttpRequest);
172};
173
174/// \brief A proxy to the libcurl C interface as a dependency injection measure.
175///
176/// This class is meant as a very thin wrapper for the libcurl C library.
177class LibCurl {
178 public:
179  virtual ~LibCurl() {}
180
181  virtual CURL* curl_easy_init() = 0;
182  virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
183                                    uint64 param) = 0;
184  virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
185                                    const char* param) = 0;
186  virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
187                                    void* param) = 0;
188  virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
189                                    size_t (*param)(void*, size_t, size_t,
190                                                    FILE*)) = 0;
191  virtual CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
192                                    size_t (*param)(const void*, size_t, size_t,
193                                                    void*)) = 0;
194  virtual CURLcode curl_easy_setopt(
195      CURL* curl, CURLoption option,
196      int (*param)(void* clientp, curl_off_t dltotal, curl_off_t dlnow,
197                   curl_off_t ultotal, curl_off_t ulnow)) = 0;
198  virtual CURLcode curl_easy_perform(CURL* curl) = 0;
199  virtual CURLcode curl_easy_getinfo(CURL* curl, CURLINFO info,
200                                     uint64* value) = 0;
201  virtual CURLcode curl_easy_getinfo(CURL* curl, CURLINFO info,
202                                     double* value) = 0;
203  virtual void curl_easy_cleanup(CURL* curl) = 0;
204  virtual curl_slist* curl_slist_append(curl_slist* list, const char* str) = 0;
205  virtual void curl_slist_free_all(curl_slist* list) = 0;
206  virtual char* curl_easy_escape(CURL* curl, const char* str, int length) = 0;
207  virtual void curl_free(void* p) = 0;
208};
209
210}  // namespace tensorflow
211
212#endif  // TENSORFLOW_CORE_PLATFORM_CLOUD_CURL_HTTP_REQUEST_H_
213