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