xref: /aosp_15_r20/external/federated-compute/fcp/client/http/curl/curl_http_client.cc (revision 14675a029014e728ec732f129a32e299b2da0601)
1 /*
2  * Copyright 2022 Google LLC
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 
17 #include "fcp/client/http/curl/curl_http_client.h"
18 
19 #include <memory>
20 #include <string>
21 #include <utility>
22 #include <vector>
23 
24 #include "absl/synchronization/mutex.h"
25 #include "fcp/base/monitoring.h"
26 #include "fcp/client/http/curl/curl_http_request_handle.h"
27 #include "fcp/client/http/http_client.h"
28 #include "fcp/client/http/http_client_util.h"
29 
30 namespace fcp::client::http::curl {
31 namespace {
32 // Cleans completed requests and calls the required callbacks.
ReadCompleteMessages(CurlMultiHandle * multi_handle)33 void ReadCompleteMessages(CurlMultiHandle* multi_handle) {
34   CURLMsg* msg;
35   int messages_in_queue = 0;
36   while ((msg = multi_handle->InfoRead(&messages_in_queue))) {
37     if (msg->msg == CURLMSG_DONE) {
38       FCP_LOG(INFO) << CurlEasyHandle::StrError(msg->data.result);
39       void* user_data;
40       curl_easy_getinfo(msg->easy_handle, CURLINFO_PRIVATE, &user_data);
41       FCP_CHECK(user_data != nullptr);
42 
43       auto handle = static_cast<CurlHttpRequestHandle*>(user_data);
44       handle->MarkAsCompleted();
45       handle->RemoveFromMulti(multi_handle);
46     }
47   }
48 }
49 
50 // Processes multiple requests while blocked.
PerformMultiHandlesBlocked(CurlMultiHandle * multi_handle)51 absl::Status PerformMultiHandlesBlocked(CurlMultiHandle* multi_handle) {
52   int num_running_handles = -1;
53   while (num_running_handles) {
54     CURLMcode code = multi_handle->Perform(&num_running_handles);
55     if (code != CURLM_OK) {
56       FCP_LOG(ERROR) << "MultiPerform failed with code: " << code;
57       return absl::InternalError(
58           absl::StrCat("MultiPerform failed with code: ", code));
59     }
60 
61     ReadCompleteMessages(multi_handle);
62 
63     if (num_running_handles > 0) {
64       code = multi_handle->Poll(/*extra_fds*/ nullptr,
65                                 /*extra_nfds*/ 0, /*timeout_ms*/ 1000,
66                                 /*numfds*/ nullptr);
67     }
68   }
69 
70   return absl::OkStatus();
71 }
72 }  // namespace
73 
CurlHttpClient(CurlApi * curl_api,std::string test_cert_path)74 CurlHttpClient::CurlHttpClient(CurlApi* curl_api, std::string test_cert_path)
75     : curl_api_(curl_api), test_cert_path_(std::move(test_cert_path)) {
76   FCP_CHECK(curl_api_ != nullptr);
77 }
78 
EnqueueRequest(std::unique_ptr<HttpRequest> request)79 std::unique_ptr<HttpRequestHandle> CurlHttpClient::EnqueueRequest(
80     std::unique_ptr<HttpRequest> request) {
81   FCP_LOG(INFO) << "Creating a " << ConvertMethodToString(request->method())
82                 << " request to " << request->uri() << " with body "
83                 << request->HasBody() << " with headers "
84                 << request->extra_headers().size();
85   for (const auto& [key, value] : request->extra_headers()) {
86     FCP_LOG(INFO) << key << ": " << value;
87   }
88 
89   return std::make_unique<CurlHttpRequestHandle>(
90       std::move(request), curl_api_->CreateEasyHandle(), test_cert_path_);
91 }
92 
PerformRequests(std::vector<std::pair<HttpRequestHandle *,HttpRequestCallback * >> requests)93 absl::Status CurlHttpClient::PerformRequests(
94     std::vector<std::pair<HttpRequestHandle*, HttpRequestCallback*>> requests) {
95   FCP_LOG(INFO) << "PerformRequests";
96   std::unique_ptr<CurlMultiHandle> multi_handle =
97       curl_api_->CreateMultiHandle();
98   FCP_CHECK(multi_handle != nullptr);
99 
100   for (const auto& [request_handle, callback] : requests) {
101     FCP_CHECK(request_handle != nullptr);
102     FCP_CHECK(callback != nullptr);
103 
104     auto http_request_handle =
105         static_cast<CurlHttpRequestHandle*>(request_handle);
106     FCP_RETURN_IF_ERROR(
107         http_request_handle->AddToMulti(multi_handle.get(), callback));
108   }
109 
110   return PerformMultiHandlesBlocked(multi_handle.get());
111 }
112 
113 }  // namespace fcp::client::http::curl
114