1 /* Copyright 2016 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 #include <algorithm>
17 
18 #include "tensorflow/core/platform/cloud/curl_http_request.h"
19 
20 #include "tensorflow/core/lib/core/errors.h"
21 #include "tensorflow/core/lib/gtl/map_util.h"
22 #include "tensorflow/core/lib/strings/scanner.h"
23 #include "tensorflow/core/lib/strings/str_util.h"
24 #include "tensorflow/core/platform/macros.h"
25 #include "tensorflow/core/platform/types.h"
26 #include "tensorflow/core/public/version.h"
27 
28 #define CHECK_CURL_OK(expr) CHECK_EQ(expr, CURLE_OK)
29 
30 namespace tensorflow {
31 
32 namespace {
33 
34 // Set to 1 to enable verbose debug output from curl.
35 constexpr uint64 kVerboseOutput = 0;
36 
37 // Proxy to the real libcurl implementation.
38 class LibCurlProxy : public LibCurl {
39  public:
Load()40   static LibCurlProxy* Load() {
41     static LibCurlProxy* libcurl = []() -> LibCurlProxy* {
42       curl_global_init(CURL_GLOBAL_ALL);
43       return new LibCurlProxy;
44     }();
45     return libcurl;
46   }
47 
curl_easy_init()48   CURL* curl_easy_init() override { return ::curl_easy_init(); }
49 
curl_easy_setopt(CURL * curl,CURLoption option,uint64 param)50   CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
51                             uint64 param) override {
52     return ::curl_easy_setopt(curl, option, param);
53   }
54 
curl_easy_setopt(CURL * curl,CURLoption option,const char * param)55   CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
56                             const char* param) override {
57     return ::curl_easy_setopt(curl, option, param);
58   }
59 
curl_easy_setopt(CURL * curl,CURLoption option,void * param)60   CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
61                             void* param) override {
62     return ::curl_easy_setopt(curl, option, param);
63   }
64 
curl_easy_setopt(CURL * curl,CURLoption option,size_t (* param)(void *,size_t,size_t,FILE *))65   CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
66                             size_t (*param)(void*, size_t, size_t,
67                                             FILE*)) override {
68     return ::curl_easy_setopt(curl, option, param);
69   }
70 
curl_easy_setopt(CURL * curl,CURLoption option,size_t (* param)(const void *,size_t,size_t,void *))71   CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
72                             size_t (*param)(const void*, size_t, size_t,
73                                             void*)) override {
74     return ::curl_easy_setopt(curl, option, param);
75   }
76 
curl_easy_setopt(CURL * curl,CURLoption option,int (* param)(void * clientp,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow))77   CURLcode curl_easy_setopt(CURL* curl, CURLoption option,
78                             int (*param)(void* clientp, curl_off_t dltotal,
79                                          curl_off_t dlnow, curl_off_t ultotal,
80                                          curl_off_t ulnow)) override {
81     return ::curl_easy_setopt(curl, option, param);
82   }
83 
curl_easy_perform(CURL * curl)84   CURLcode curl_easy_perform(CURL* curl) override {
85     return ::curl_easy_perform(curl);
86   }
87 
curl_easy_getinfo(CURL * curl,CURLINFO info,uint64 * value)88   CURLcode curl_easy_getinfo(CURL* curl, CURLINFO info,
89                              uint64* value) override {
90     return ::curl_easy_getinfo(curl, info, value);
91   }
92 
curl_easy_getinfo(CURL * curl,CURLINFO info,double * value)93   CURLcode curl_easy_getinfo(CURL* curl, CURLINFO info,
94                              double* value) override {
95     return ::curl_easy_getinfo(curl, info, value);
96   }
97 
curl_easy_cleanup(CURL * curl)98   void curl_easy_cleanup(CURL* curl) override {
99     return ::curl_easy_cleanup(curl);
100   }
101 
curl_easy_escape(CURL * curl,const char * str,int length)102   char* curl_easy_escape(CURL* curl, const char* str, int length) override {
103     return ::curl_easy_escape(curl, str, length);
104   }
105 
curl_slist_append(curl_slist * list,const char * str)106   curl_slist* curl_slist_append(curl_slist* list, const char* str) override {
107     return ::curl_slist_append(list, str);
108   }
109 
curl_slist_free_all(curl_slist * list)110   void curl_slist_free_all(curl_slist* list) override {
111     return ::curl_slist_free_all(list);
112   }
113 
curl_free(void * p)114   void curl_free(void* p) override { ::curl_free(p); }
115 };
116 }  // namespace
117 
CurlHttpRequest()118 CurlHttpRequest::CurlHttpRequest() : CurlHttpRequest(LibCurlProxy::Load()) {}
119 
CurlHttpRequest(LibCurl * libcurl,Env * env)120 CurlHttpRequest::CurlHttpRequest(LibCurl* libcurl, Env* env)
121     : libcurl_(libcurl), env_(env) {
122   default_response_buffer_.reserve(CURL_MAX_WRITE_SIZE);
123 
124   curl_ = libcurl_->curl_easy_init();
125   CHECK(curl_ != nullptr) << "Couldn't initialize a curl session.";
126 
127   // NOTE: CURL_CA_BUNDLE=/etc/ssl/certs/ca-certificates.crt is configured by
128   //       default in //third_party:curl.BUILD and can be customized via an
129   //       environment variable.
130 
131   CHECK_CURL_OK(
132       libcurl_->curl_easy_setopt(curl_, CURLOPT_VERBOSE, kVerboseOutput));
133   CHECK_CURL_OK(libcurl_->curl_easy_setopt(
134       curl_, CURLOPT_USERAGENT,
135       strings::StrCat("TensorFlow/", TF_VERSION_STRING).c_str()));
136   // Do not use signals for timeouts - does not work in multi-threaded programs.
137   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1L));
138 
139   // TODO(b/74351157): Enable HTTP/2.
140 
141   // Set up the progress meter.
142   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_NOPROGRESS, 0ULL));
143   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_XFERINFODATA, this));
144   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_XFERINFOFUNCTION,
145                                            &CurlHttpRequest::ProgressCallback));
146 
147   // If response buffer is not set, libcurl will print results to stdout,
148   // so we always set it.
149   SetResultBuffer(&default_response_buffer_);
150 }
151 
~CurlHttpRequest()152 CurlHttpRequest::~CurlHttpRequest() {
153   if (curl_headers_) {
154     libcurl_->curl_slist_free_all(curl_headers_);
155   }
156   if (resolve_list_) {
157     libcurl_->curl_slist_free_all(resolve_list_);
158   }
159   if (put_body_) {
160     fclose(put_body_);
161   }
162   if (curl_) {
163     libcurl_->curl_easy_cleanup(curl_);
164   }
165 }
166 
EscapeString(const string & str)167 string CurlHttpRequest::EscapeString(const string& str) {
168   char* out_char_str = libcurl_->curl_easy_escape(curl_, str.c_str(), 0);
169   string out_str(out_char_str);
170   libcurl_->curl_free(out_char_str);
171   return out_str;
172 }
173 
SetUri(const string & uri)174 void CurlHttpRequest::SetUri(const string& uri) {
175   CheckNotSent();
176   is_uri_set_ = true;
177   uri_ = uri;
178   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_URL, uri.c_str()));
179 }
180 
SetRange(uint64 start,uint64 end)181 void CurlHttpRequest::SetRange(uint64 start, uint64 end) {
182   CheckNotSent();
183   CHECK_CURL_OK(libcurl_->curl_easy_setopt(
184       curl_, CURLOPT_RANGE, strings::StrCat(start, "-", end).c_str()));
185 }
186 
AddHeader(const string & name,const string & value)187 void CurlHttpRequest::AddHeader(const string& name, const string& value) {
188   CheckNotSent();
189   curl_headers_ = libcurl_->curl_slist_append(
190       curl_headers_, strings::StrCat(name, ": ", value).c_str());
191 }
192 
AddResolveOverride(const string & hostname,int64 port,const string & ip_addr)193 void CurlHttpRequest::AddResolveOverride(const string& hostname, int64 port,
194                                          const string& ip_addr) {
195   CheckNotSent();
196   // Resolve values are hostname:port:IP.add.ress
197   resolve_list_ = libcurl_->curl_slist_append(
198       resolve_list_,
199       strings::StrCat(hostname, ":", port, ":", ip_addr).c_str());
200 }
201 
AddAuthBearerHeader(const string & auth_token)202 void CurlHttpRequest::AddAuthBearerHeader(const string& auth_token) {
203   CheckNotSent();
204   if (!auth_token.empty()) {
205     AddHeader("Authorization", strings::StrCat("Bearer ", auth_token));
206   }
207 }
208 
SetRequestStats(RequestStats * stats)209 void CurlHttpRequest::SetRequestStats(RequestStats* stats) {
210   CheckNotSent();
211   CHECK(stats_ == nullptr) << "SetRequestStats already called";
212   stats_ = stats;
213 }
214 
SetDeleteRequest()215 void CurlHttpRequest::SetDeleteRequest() {
216   CheckNotSent();
217   CheckMethodNotSet();
218   is_method_set_ = true;
219   method_ = RequestMethod::kDelete;
220   CHECK_CURL_OK(
221       libcurl_->curl_easy_setopt(curl_, CURLOPT_CUSTOMREQUEST, "DELETE"));
222 }
223 
SetPutFromFile(const string & body_filepath,size_t offset)224 Status CurlHttpRequest::SetPutFromFile(const string& body_filepath,
225                                        size_t offset) {
226   CheckNotSent();
227   CheckMethodNotSet();
228   is_method_set_ = true;
229   method_ = RequestMethod::kPut;
230   if (put_body_) {
231     fclose(put_body_);
232   }
233   put_body_ = fopen(body_filepath.c_str(), "r");
234   if (!put_body_) {
235     return errors::InvalidArgument("Couldn't open the specified file: " +
236                                    body_filepath);
237   }
238   fseek(put_body_, 0, SEEK_END);
239   const auto size = ftell(put_body_) - offset;
240   fseek(put_body_, offset, SEEK_SET);
241 
242   curl_headers_ = libcurl_->curl_slist_append(
243       curl_headers_, strings::StrCat("Content-Length: ", size).c_str());
244   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_PUT, 1));
245   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_READDATA,
246                                            reinterpret_cast<void*>(put_body_)));
247   // Using the default CURLOPT_READFUNCTION, which is doing an fread() on the
248   // FILE * userdata set with CURLOPT_READDATA.
249   return Status::OK();
250 }
251 
SetPutEmptyBody()252 void CurlHttpRequest::SetPutEmptyBody() {
253   CheckNotSent();
254   CheckMethodNotSet();
255   is_method_set_ = true;
256   method_ = RequestMethod::kPut;
257   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_PUT, 1));
258   AddHeader("Content-Length", "0");
259   AddHeader("Transfer-Encoding", "identity");
260   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_READDATA,
261                                            reinterpret_cast<void*>(this)));
262   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_READFUNCTION,
263                                            &CurlHttpRequest::ReadCallback));
264 }
265 
SetPostFromBuffer(const char * buffer,size_t size)266 void CurlHttpRequest::SetPostFromBuffer(const char* buffer, size_t size) {
267   CheckNotSent();
268   CheckMethodNotSet();
269   is_method_set_ = true;
270   method_ = RequestMethod::kPost;
271   curl_headers_ = libcurl_->curl_slist_append(
272       curl_headers_, strings::StrCat("Content-Length: ", size).c_str());
273   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_POST, 1));
274   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_READDATA,
275                                            reinterpret_cast<void*>(this)));
276   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_READFUNCTION,
277                                            &CurlHttpRequest::ReadCallback));
278   post_body_buffer_ = StringPiece(buffer, size);
279 }
280 
SetPostEmptyBody()281 void CurlHttpRequest::SetPostEmptyBody() {
282   CheckNotSent();
283   CheckMethodNotSet();
284   is_method_set_ = true;
285   method_ = RequestMethod::kPost;
286   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_POST, 1));
287   AddHeader("Content-Length", "0");
288   AddHeader("Transfer-Encoding", "identity");
289   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_READDATA,
290                                            reinterpret_cast<void*>(this)));
291   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_READFUNCTION,
292                                            &CurlHttpRequest::ReadCallback));
293 }
294 
SetResultBuffer(std::vector<char> * out_buffer)295 void CurlHttpRequest::SetResultBuffer(std::vector<char>* out_buffer) {
296   CheckNotSent();
297   CHECK(out_buffer != nullptr);
298 
299   out_buffer->clear();
300   response_buffer_ = out_buffer;
301 
302   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_WRITEDATA,
303                                            reinterpret_cast<void*>(this)));
304   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION,
305                                            &CurlHttpRequest::WriteCallback));
306 }
307 
SetResultBufferDirect(char * buffer,size_t size)308 void CurlHttpRequest::SetResultBufferDirect(char* buffer, size_t size) {
309   CHECK(buffer != nullptr);
310   CheckNotSent();
311 
312   direct_response_ = DirectResponseState{buffer, size, 0, 0};
313   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_WRITEDATA,
314                                            reinterpret_cast<void*>(this)));
315   CHECK_CURL_OK(libcurl_->curl_easy_setopt(
316       curl_, CURLOPT_WRITEFUNCTION, &CurlHttpRequest::WriteCallbackDirect));
317 }
318 
IsDirectResponse() const319 bool CurlHttpRequest::IsDirectResponse() const {
320   return direct_response_.buffer_ != nullptr;
321 }
322 
WriteCallbackDirect(const void * ptr,size_t size,size_t nmemb,void * userdata)323 size_t CurlHttpRequest::WriteCallbackDirect(const void* ptr, size_t size,
324                                             size_t nmemb, void* userdata) {
325   CHECK(ptr != nullptr);
326   auto that = reinterpret_cast<CurlHttpRequest*>(userdata);
327   DirectResponseState* state = &that->direct_response_;
328   CHECK(state->buffer_ != nullptr);
329   CHECK(state->bytes_transferred_ <= state->buffer_size_);
330 
331   size_t curl_bytes_received = size * nmemb;
332   size_t user_buffer_bytes_available =
333       state->buffer_size_ - state->bytes_transferred_;
334   size_t bytes_to_copy =
335       std::min<size_t>(curl_bytes_received, user_buffer_bytes_available);
336   memcpy(&state->buffer_[state->bytes_transferred_], ptr, bytes_to_copy);
337   state->bytes_transferred_ += bytes_to_copy;
338   state->bytes_received_ += curl_bytes_received;
339   // If we didn't have room to store the full response, returning less than
340   // curl_bytes_received here will abort the transfer and curl_easy_perform()
341   // will return CURLE_WRITE_ERROR. We will detect and handle this error there,
342   // and can use state->bytes_received_ as stored above for logging purposes.
343   return bytes_to_copy;
344 }
345 
GetResultBufferDirectBytesTransferred()346 size_t CurlHttpRequest::GetResultBufferDirectBytesTransferred() {
347   CHECK(direct_response_.buffer_ != nullptr);
348   return direct_response_.bytes_transferred_;
349 }
350 
SetTimeouts(uint32 connection,uint32 inactivity,uint32 total)351 void CurlHttpRequest::SetTimeouts(uint32 connection, uint32 inactivity,
352                                   uint32 total) {
353   CheckNotSent();
354   connect_timeout_secs_ = connection;
355   inactivity_timeout_secs_ = inactivity;
356   request_timeout_secs_ = total;
357 }
358 
WriteCallback(const void * ptr,size_t size,size_t nmemb,void * this_object)359 size_t CurlHttpRequest::WriteCallback(const void* ptr, size_t size,
360                                       size_t nmemb, void* this_object) {
361   CHECK(ptr);
362   auto that = reinterpret_cast<CurlHttpRequest*>(this_object);
363   CHECK(that->response_buffer_);
364   const size_t bytes_to_copy = size * nmemb;
365   that->response_buffer_->insert(
366       that->response_buffer_->end(), reinterpret_cast<const char*>(ptr),
367       reinterpret_cast<const char*>(ptr) + bytes_to_copy);
368 
369   return bytes_to_copy;
370 }
371 
ReadCallback(void * ptr,size_t size,size_t nmemb,FILE * this_object)372 size_t CurlHttpRequest::ReadCallback(void* ptr, size_t size, size_t nmemb,
373                                      FILE* this_object) {
374   CHECK(ptr);
375   auto that = reinterpret_cast<CurlHttpRequest*>(this_object);
376   CHECK(that->post_body_read_ <= that->post_body_buffer_.size());
377   const size_t bytes_to_copy = std::min(
378       size * nmemb, that->post_body_buffer_.size() - that->post_body_read_);
379   memcpy(ptr, that->post_body_buffer_.data() + that->post_body_read_,
380          bytes_to_copy);
381   that->post_body_read_ += bytes_to_copy;
382   return bytes_to_copy;
383 }
384 
HeaderCallback(const void * ptr,size_t size,size_t nmemb,void * this_object)385 size_t CurlHttpRequest::HeaderCallback(const void* ptr, size_t size,
386                                        size_t nmemb, void* this_object) {
387   CHECK(ptr);
388   auto that = reinterpret_cast<CurlHttpRequest*>(this_object);
389   StringPiece header(reinterpret_cast<const char*>(ptr), size * nmemb);
390   StringPiece name, value;
391   // The supplied header has the form "<name>: <value>", parse it.
392   if (strings::Scanner(header)
393           .ScanEscapedUntil(':')
394           .StopCapture()
395           .OneLiteral(": ")
396           .GetResult(&value, &name)) {
397     string str_value(value);
398     str_util::StripTrailingWhitespace(&str_value);
399     that->response_headers_[string(name)] = str_value;
400   }
401   return size * nmemb;
402 }
403 
Send()404 Status CurlHttpRequest::Send() {
405   CheckNotSent();
406   CHECK(is_uri_set_) << "URI has not been set.";
407 
408   is_sent_ = true;
409 
410   if (curl_headers_) {
411     CHECK_CURL_OK(
412         libcurl_->curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, curl_headers_));
413   }
414   if (resolve_list_) {
415     CHECK_CURL_OK(
416         libcurl_->curl_easy_setopt(curl_, CURLOPT_RESOLVE, resolve_list_));
417   }
418   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_HEADERDATA,
419                                            reinterpret_cast<void*>(this)));
420   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION,
421                                            &CurlHttpRequest::HeaderCallback));
422 
423   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_TIMEOUT,
424                                            request_timeout_secs_));
425   CHECK_CURL_OK(libcurl_->curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT,
426                                            connect_timeout_secs_));
427 
428   char error_buffer[CURL_ERROR_SIZE] = {0};
429   CHECK_CURL_OK(
430       libcurl_->curl_easy_setopt(curl_, CURLOPT_ERRORBUFFER, error_buffer));
431 
432   if (stats_ != nullptr) {
433     stats_->RecordRequest(this, uri_, method_);
434   }
435 
436   const CURLcode curl_result = libcurl_->curl_easy_perform(curl_);
437   TF_RETURN_IF_ERROR(CURLcodeToStatus(curl_result, error_buffer));
438 
439   double written_size = 0;
440   CHECK_CURL_OK(libcurl_->curl_easy_getinfo(curl_, CURLINFO_SIZE_DOWNLOAD,
441                                             &written_size));
442 
443   CHECK_CURL_OK(libcurl_->curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE,
444                                             &response_code_));
445 
446   auto get_error_message = [this]() -> string {
447     string error_message = strings::StrCat(
448         "Error executing an HTTP request: HTTP response code ", response_code_);
449     StringPiece body = GetResponse();
450     if (!body.empty()) {
451       return strings::StrCat(
452           error_message, " with body '",
453           body.substr(0, std::min(body.size(), response_to_error_limit_)), "'");
454     }
455     return error_message;
456   };
457 
458   Status result;
459   switch (response_code_) {
460     // The group of response codes indicating that the request achieved
461     // the expected goal.
462     case 200:  // OK
463     case 201:  // Created
464     case 204:  // No Content
465     case 206:  // Partial Content
466       result = Status::OK();
467       break;
468 
469     case 416:  // Requested Range Not Satisfiable
470       // The requested range had no overlap with the available range.
471       // This doesn't indicate an error, but we should produce an empty response
472       // body. (Not all servers do; GCS returns a short error message body.)
473       response_buffer_->clear();
474       if (IsDirectResponse()) {
475         direct_response_.bytes_transferred_ = 0;
476       }
477       result = Status::OK();
478       break;
479 
480     // INVALID_ARGUMENT indicates a problem with how the request is constructed.
481     case 400:  // Bad Request
482     case 411:  // Length Required
483       result = errors::InvalidArgument(get_error_message());
484       break;
485 
486     // PERMISSION_DENIED indicates an authentication or an authorization issue.
487     case 401:  // Unauthorized
488     case 403:  // Forbidden
489       result = errors::PermissionDenied(get_error_message());
490       break;
491 
492     // NOT_FOUND indicates that the requested resource does not exist.
493     case 404:  // Not found
494     case 410:  // Gone
495       result = errors::NotFound(get_error_message());
496       break;
497 
498     // FAILED_PRECONDITION indicates that the request failed because some
499     // of the underlying assumptions were not satisfied. The request
500     // shouldn't be retried unless the external context has changed.
501     case 302:  // Found
502     case 303:  // See Other
503     case 304:  // Not Modified
504     case 307:  // Temporary Redirect
505     case 412:  // Precondition Failed
506     case 413:  // Payload Too Large
507       result = errors::FailedPrecondition(get_error_message());
508       break;
509 
510     // UNAVAILABLE indicates a problem that can go away if the request
511     // is just retried without any modification. 308 return codes are intended
512     // for write requests that can be retried. See the documentation and the
513     // official library:
514     // https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload
515     // https://github.com/google/apitools/blob/master/apitools/base/py/transfer.py
516     case 308:  // Resume Incomplete
517     case 409:  // Conflict
518     case 429:  // Too Many Requests
519     case 500:  // Internal Server Error
520     case 502:  // Bad Gateway
521     case 503:  // Service Unavailable
522     default:   // All other HTTP response codes also should be retried.
523       result = errors::Unavailable(get_error_message());
524       break;
525   }
526   if (!result.ok()) {
527     response_buffer_->clear();
528   }
529 
530   if (stats_ != nullptr) {
531     stats_->RecordResponse(this, uri_, method_, result);
532   }
533 
534   return result;
535 }
536 
CheckMethodNotSet() const537 void CurlHttpRequest::CheckMethodNotSet() const {
538   CHECK(!is_method_set_) << "HTTP method has been already set.";
539 }
540 
CheckNotSent() const541 void CurlHttpRequest::CheckNotSent() const {
542   CHECK(!is_sent_) << "The request has already been sent.";
543 }
544 
GetResponse() const545 StringPiece CurlHttpRequest::GetResponse() const {
546   StringPiece response;
547   if (IsDirectResponse()) {
548     response = StringPiece(direct_response_.buffer_,
549                            direct_response_.bytes_transferred_);
550   } else {
551     response = StringPiece(response_buffer_->data(), response_buffer_->size());
552   }
553   return response;
554 }
555 
GetResponseHeader(const string & name) const556 string CurlHttpRequest::GetResponseHeader(const string& name) const {
557   const auto& header = response_headers_.find(name);
558   return header != response_headers_.end() ? header->second : "";
559 }
560 
GetResponseCode() const561 uint64 CurlHttpRequest::GetResponseCode() const { return response_code_; }
562 
563 // Cancels the transmission if no progress has been made for too long.
ProgressCallback(void * this_object,curl_off_t dltotal,curl_off_t dlnow,curl_off_t ultotal,curl_off_t ulnow)564 int CurlHttpRequest::ProgressCallback(void* this_object, curl_off_t dltotal,
565                                       curl_off_t dlnow, curl_off_t ultotal,
566                                       curl_off_t ulnow) {
567   auto that = reinterpret_cast<CurlHttpRequest*>(this_object);
568   const auto now = that->env_->NowSeconds();
569   const auto current_progress = dlnow + ulnow;
570   if (that->last_progress_timestamp_ == 0 ||
571       current_progress > that->last_progress_bytes_) {
572     // This is the first time the callback is called or some progress
573     // was made since the last tick.
574     that->last_progress_timestamp_ = now;
575     that->last_progress_bytes_ = current_progress;
576     return 0;
577   }
578 
579   if (now - that->last_progress_timestamp_ > that->inactivity_timeout_secs_) {
580     double lookup_time = -1;
581     const auto lookup_time_status = that->libcurl_->curl_easy_getinfo(
582         that->curl_, CURLINFO_NAMELOOKUP_TIME, &lookup_time);
583 
584     double connect_time = -1;
585     const auto connect_time_status = that->libcurl_->curl_easy_getinfo(
586         that->curl_, CURLINFO_CONNECT_TIME, &connect_time);
587 
588     double pretransfer_time = -1;
589     const auto pretransfer_time_status = that->libcurl_->curl_easy_getinfo(
590         that->curl_, CURLINFO_PRETRANSFER_TIME, &pretransfer_time);
591 
592     double starttransfer_time = -1;
593     const auto starttransfer_time_status = that->libcurl_->curl_easy_getinfo(
594         that->curl_, CURLINFO_PRETRANSFER_TIME, &starttransfer_time);
595 
596     LOG(ERROR) << "The transmission  of request " << this_object
597                << " (URI: " << that->uri_ << ") has been stuck at "
598                << current_progress << " of " << dltotal + ultotal
599                << " bytes for " << now - that->last_progress_timestamp_
600                << " seconds and will be aborted. CURL timing information: "
601                << "lookup time: " << lookup_time << " ("
602                << curl_easy_strerror(lookup_time_status)
603                << "), connect time: " << connect_time << " ("
604                << curl_easy_strerror(connect_time_status)
605                << "), pre-transfer time: " << pretransfer_time << " ("
606                << curl_easy_strerror(pretransfer_time_status)
607                << "), start-transfer time: " << starttransfer_time << " ("
608                << curl_easy_strerror(starttransfer_time_status) << ")";
609     return 1;  // Will abort the request.
610   }
611 
612   // No progress was made since the last call, but we should wait a bit longer.
613   return 0;
614 }
615 
CURLcodeToStatus(CURLcode code,const char * error_buffer)616 Status CurlHttpRequest::CURLcodeToStatus(CURLcode code,
617                                          const char* error_buffer) {
618   if (code == CURLE_OK) {
619     return Status::OK();
620   }
621   string error_message = strings::StrCat(
622       "Error executing an HTTP request: libcurl code ", code, " meaning '",
623       curl_easy_strerror(code), "', error details: ");
624   // Special-case response-too-large errors as FAILED_PRECONDITION.
625   if (code == CURLE_WRITE_ERROR && IsDirectResponse() &&
626       direct_response_.bytes_received_ > direct_response_.buffer_size_) {
627     string overflow_message = strings::StrCat(
628         "Received ", direct_response_.bytes_received_, " response bytes ",
629         "for a ", direct_response_.buffer_size_, "-byte buffer");
630     uint64 response_code = 0;
631     const CURLcode get_response_result = libcurl_->curl_easy_getinfo(
632         curl_, CURLINFO_RESPONSE_CODE, &response_code);
633     // Special-case 416 Range Not Satisfied responses; they sometimes have
634     // a response body (e.g. GCS sends one with an error message) but we
635     // pretend as though they don't, so actually ignore this error.
636     if (get_response_result == CURLE_OK && response_code == 416) {
637       return Status::OK();
638     }
639     return errors::FailedPrecondition(
640         strings::StrCat(error_message, overflow_message));
641   }
642   // Return Unavailable to retry by default. There may be other permanent
643   // failures that should be distinguished.
644   return errors::Unavailable(
645       strings::StrCat(error_message, *error_buffer ? error_buffer : "(none)"));
646 }
647 
648 }  // namespace tensorflow
649