in tensorflow_io/core/filesystems/http/http_filesystem.cc [215:391]
void Send(TF_Status* status) {
CURLcode s = CURLE_OK;
if (curl_headers_) {
if ((s = curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, curl_headers_)) !=
CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to set CURLOPT_HTTPHEADER: ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
}
if (resolve_list_) {
if ((s = curl_easy_setopt(curl_, CURLOPT_RESOLVE, resolve_list_)) !=
CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to set CURLOPT_RESOLVE: ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
}
if ((s = curl_easy_setopt(curl_, CURLOPT_HEADERDATA,
reinterpret_cast<void*>(this))) != CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to set CURLOPT_HEADERDATA: ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
if ((s = curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION,
&CurlHttpRequest::HeaderCallback)) != CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to set CURLOPT_HEADERFUNCTION: ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
if ((s = curl_easy_setopt(curl_, CURLOPT_TIMEOUT, request_timeout_secs_)) !=
CURLE_OK) {
std::string error_message = absl::StrCat(
"Unable to set CURLOPT_TIMEOUT (", request_timeout_secs_, "): ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
if ((s = curl_easy_setopt(curl_, CURLOPT_CONNECTTIMEOUT,
connect_timeout_secs_)) != CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to set CURLOPT_CONNECTTIMEOUT (",
connect_timeout_secs_, "): ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
char error_buffer[CURL_ERROR_SIZE] = {0};
if ((s = curl_easy_setopt(curl_, CURLOPT_ERRORBUFFER, error_buffer)) !=
CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to set CURLOPT_ERRORBUFFER: ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
if ((s = curl_easy_perform(curl_)) != CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to perform (", s, "): ", error_buffer);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
double written_size = 0;
if ((s = curl_easy_getinfo(curl_, CURLINFO_SIZE_DOWNLOAD, &written_size)) !=
CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to set CURLINFO_SIZE_DOWNLOAD: ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
if ((s = curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE,
&response_code_)) != CURLE_OK) {
std::string error_message =
absl::StrCat("Unable to set CURLINFO_RESPONSE_CODE: ", s);
TF_SetStatus(status, TF_INTERNAL, error_message.c_str());
return;
}
auto get_error_message = [this]() -> std::string {
std::string error_message =
absl::StrCat("Error executing an HTTP request: HTTP response code ",
response_code_);
absl::string_view body = GetResponse();
if (!body.empty()) {
return absl::StrCat(
error_message, " with body '",
body.substr(0, std::min(body.size(), response_to_error_limit_)),
"'");
}
return error_message;
};
switch (response_code_) {
// The group of response codes indicating that the request achieved
// the expected goal.
case 200: // OK
case 201: // Created
case 204: // No Content
case 206: // Partial Content
TF_SetStatus(status, TF_OK, "");
break;
case 416: // Requested Range Not Satisfiable
// The requested range had no overlap with the available range.
// This doesn't indicate an error, but we should produce an empty
// response body. (Not all servers do; GCS returns a short error message
// body.)
response_buffer_.clear();
if (IsDirectResponse()) {
direct_response_.bytes_transferred_ = 0;
}
TF_SetStatus(status, TF_OK, "");
break;
// INVALID_ARGUMENT indicates a problem with how the request is
// constructed.
case 400: // Bad Request
case 406: // Not Acceptable
case 411: // Length Required
case 414: // URI Too Long
TF_SetStatus(status, TF_INVALID_ARGUMENT, get_error_message().c_str());
break;
// PERMISSION_DENIED indicates an authentication or an authorization
// issue.
case 401: // Unauthorized
case 403: // Forbidden
case 407: // Proxy Authorization Required
TF_SetStatus(status, TF_PERMISSION_DENIED, get_error_message().c_str());
break;
// NOT_FOUND indicates that the requested resource does not exist.
case 404: // Not found
case 410: // Gone
TF_SetStatus(status, TF_NOT_FOUND, get_error_message().c_str());
break;
// FAILED_PRECONDITION indicates that the request failed because some
// of the underlying assumptions were not satisfied. The request
// shouldn't be retried unless the external context has changed.
case 302: // Found
case 303: // See Other
case 304: // Not Modified
case 307: // Temporary Redirect
case 412: // Precondition Failed
case 413: // Payload Too Large
TF_SetStatus(status, TF_FAILED_PRECONDITION,
get_error_message().c_str());
break;
// UNAVAILABLE indicates a problem that can go away if the request
// is just retried without any modification. 308 return codes are intended
// for write requests that can be retried. See the documentation and the
// official library:
// https://cloud.google.com/storage/docs/json_api/v1/how-tos/resumable-upload
// https://github.com/google/apitools/blob/master/apitools/base/py/transfer.py
case 308: // Resume Incomplete
case 409: // Conflict
case 429: // Too Many Requests
case 500: // Internal Server Error
case 502: // Bad Gateway
case 503: // Service Unavailable
default: // All other HTTP response codes also should be retried.
TF_SetStatus(status, TF_UNAVAILABLE, get_error_message().c_str());
break;
}
if (TF_GetCode(status) != TF_OK) {
response_buffer_.clear();
}
}