bool BackgroundTransportCurl::PerformBackground()

in app/rest/transport_curl.cc [441:583]


bool BackgroundTransportCurl::PerformBackground(Request* request) {
  RequestOptions& options = request->options();
  CheckOk(curl_easy_setopt(curl_, CURLOPT_ERRORBUFFER, err_buf_),
          "set error buffer");

  // Ensure that we are only accepting HTTP(S) traffic.
  // TODO(73540740): Remove support for HTTP.
  CheckOk(curl_easy_setopt(curl_, CURLOPT_PROTOCOLS,
                           CURLPROTO_HTTP | CURLPROTO_HTTPS),
          "set valid protocols");

  // Verify SSL.
  CheckOk(curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 1L), "verify peer");
  CheckOk(curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 2L), "verify host");

#ifdef FIREBASE_SSL_CAPATH
  CheckOk(curl_easy_setopt(curl_, CURLOPT_CAPATH, FIREBASE_SSL_CAPATH),
          "CA Path");
#endif

  // Set callback functions.
  CheckOk(curl_easy_setopt(curl_, CURLOPT_HEADERFUNCTION, CurlHeaderCallback),
          "set http header callback");
  CheckOk(curl_easy_setopt(curl_, CURLOPT_HEADERDATA, response_),
          "set http header callback data");
  CheckOk(curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, CurlWriteCallback),
          "set http body write callback");
  CheckOk(curl_easy_setopt(curl_, CURLOPT_WRITEDATA, response_),
          "set http body write callback data");
  CheckOk(curl_easy_setopt(curl_, CURLOPT_READFUNCTION, CurlReadCallback),
          "set http body read callback");
  CheckOk(curl_easy_setopt(curl_, CURLOPT_READDATA, request),
          "set http body read callback data");
  CheckOk(curl_easy_setopt(curl_, CURLOPT_TIMEOUT_MS, options.timeout_ms),
          "set http timeout milliseconds");

  // curl library is using http2 as default, so need to specify this.
  CheckOk(curl_easy_setopt(curl_, CURLOPT_HTTP_VERSION, CURL_HTTP_VERSION_1_1),
          "set http version to http1");

  // SDK error in initialization stage is not recoverable.
  FIREBASE_ASSERT(err_code_ == CURLE_OK);

  err_code_ = CURLE_OK;

  // Set VERBOSE for debug; does not affect the HTTP connection.
  if (options.verbose) {
    curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1L);
  }

  if (request_header_) {
    curl_slist_free_all(request_header_);
    request_header_ = nullptr;
  }

  for (const auto& pair : options.header) {
    std::string header;
    header.reserve(pair.first.size() + pair.second.size() + 1);
    header.append(pair.first);
    header.append(1, util::kHttpHeaderSeparator);
    header.append(pair.second);
    request_header_ = curl_slist_append(request_header_, header.c_str());
  }
  std::string method = util::ToUpper(options.method);
  if (method == util::kPost && request_->options().stream_post_fields) {
    size_t transfer_size = request_->GetPostFieldsSize();
    // If the upload size is unknown use chunked encoding.
    if (transfer_size == ~static_cast<size_t>(0)) {
      // To use POST to a HTTP 1.1 sever we need to use chunked encoding.
      // This is not supported by HTTP 1.0 servers which need to know the size
      // of the request up front.
      request_header_ =
          curl_slist_append(request_header_, "Transfer-Encoding: chunked");
    } else {
      // Set the expected POST size, to be compatible with HTTP 1.0 servers.
      curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE,
                       static_cast<long>(transfer_size));  // NOLINT
      if (controller_) {
        controller_->set_transfer_size(static_cast<int64_t>(transfer_size));
      }
    }
  }

  if (request_header_ != nullptr) {
    CheckOk(curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, request_header_),
            "set http header");
  }

  // Set PRIVATE pointer, so that we can get the response when the request
  // completes.
  CheckOk(curl_easy_setopt(curl_, CURLOPT_PRIVATE, this),
          "set private pointer");
  // Set URL
  CheckOk(curl_easy_setopt(curl_, CURLOPT_URL, options.url.c_str()),
          "set http url");
  // Set the method.
  if (method == util::kGet) {
    CheckOk(curl_easy_setopt(curl_, CURLOPT_HTTPGET, 1L), "set http method");
  } else if (method == util::kPost) {
    CheckOk(curl_easy_setopt(curl_, CURLOPT_POST, 1L), "set http method");
  } else {
    // For all other method. Note: If your code is depending on a documented
    // method e.g. PUT, try add another else-if branch above instead of relying
    // on this generic mechanism.
    CheckOk(curl_easy_setopt(curl_, CURLOPT_CUSTOMREQUEST, method.c_str()),
            "set http method");
  }

  // If streaming is disabled, copy post fields into a string to upload in a
  // single chunk.
  std::string post_fields_temp;
  if (!options.stream_post_fields &&
      request_->ReadBodyIntoString(&post_fields_temp)) {
    // If the request's post_fields are passed to ReadBodyIntoString, it takes
    // an early out because it assumes the work is already done.
    // However we still need it to do the compression, so, we have to write to a
    // temporary and then assign it to the request's post_fields.
    request_->options().post_fields = post_fields_temp;

    // Specify the size to make sure the whole post_fields is consumed.
    CheckOk(curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE,
                             options.post_fields.size()),
            "set http post fields");
    CheckOk(curl_easy_setopt(curl_, CURLOPT_POSTFIELDS,
                             options.post_fields.c_str()),
            "set http post fields");
    if (controller_) {
      controller_->set_transfer_size(
          static_cast<int64_t>(options.post_fields.size()));
    }
  }

  if (err_code_ == CURLE_OK) {
    // Add the easy handle to the multi handle to prepare operation.
    return curl_multi_add_handle(curl_multi_, curl_) == CURLM_OK;
  } else {
    // If any error happens during the setup, we do not perform http request and
    // return immediately instead.
    response_->set_sdk_error_code(err_code_);
    set_canceled(true);
    return false;
  }
}