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;
}
}