1 // Copyright 2018 Google LLC
2 //
3 // Licensed under the Apache License, Version 2.0 (the "License");
4 // you may not use this file except in compliance with the License.
5 // You may obtain a copy of the License at
6 //
7 // http://www.apache.org/licenses/LICENSE-2.0
8 //
9 // Unless required by applicable law or agreed to in writing, software
10 // distributed under the License is distributed on an "AS IS" BASIS,
11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 // See the License for the specific language governing permissions and
13 // limitations under the License.
14
15 #include "google/cloud/storage/internal/curl_request_builder.h"
16 #include "google/cloud/storage/version.h"
17 #include "google/cloud/internal/build_info.h"
18
19 namespace google {
20 namespace cloud {
21 namespace storage {
22 inline namespace STORAGE_CLIENT_NS {
23 namespace internal {
24
25 #ifndef GOOGLE_CLOUD_CPP_STORAGE_INITIAL_BUFFER_SIZE
26 #define GOOGLE_CLOUD_CPP_STORAGE_INITIAL_BUFFER_SIZE (128 * 1024)
27 #endif // GOOGLE_CLOUD_CPP_STORAGE_INITIAL_BUFFER_SIZE
28
CurlRequestBuilder(std::string base_url,std::shared_ptr<CurlHandleFactory> factory)29 CurlRequestBuilder::CurlRequestBuilder(
30 std::string base_url, std::shared_ptr<CurlHandleFactory> factory)
31 : factory_(std::move(factory)),
32 handle_(factory_->CreateHandle()),
33 headers_(nullptr, &curl_slist_free_all),
34 url_(std::move(base_url)),
35 query_parameter_separator_("?"),
36 logging_enabled_(false),
37 download_stall_timeout_(0) {}
38
BuildRequest()39 CurlRequest CurlRequestBuilder::BuildRequest() {
40 ValidateBuilderState(__func__);
41 CurlRequest request;
42 request.url_ = std::move(url_);
43 request.headers_ = std::move(headers_);
44 request.user_agent_ = user_agent_prefix_ + UserAgentSuffix();
45 request.handle_ = std::move(handle_);
46 request.factory_ = std::move(factory_);
47 request.logging_enabled_ = logging_enabled_;
48 request.socket_options_ = socket_options_;
49 return request;
50 }
51
BuildDownloadRequest(std::string payload)52 CurlDownloadRequest CurlRequestBuilder::BuildDownloadRequest(
53 std::string payload) {
54 ValidateBuilderState(__func__);
55 CurlDownloadRequest request;
56 request.url_ = std::move(url_);
57 request.headers_ = std::move(headers_);
58 request.user_agent_ = user_agent_prefix_ + UserAgentSuffix();
59 request.payload_ = std::move(payload);
60 request.handle_ = std::move(handle_);
61 request.multi_ = factory_->CreateMultiHandle();
62 request.factory_ = factory_;
63 request.logging_enabled_ = logging_enabled_;
64 request.socket_options_ = socket_options_;
65 request.download_stall_timeout_ = download_stall_timeout_;
66 request.SetOptions();
67 return request;
68 }
69
ApplyClientOptions(ClientOptions const & options)70 CurlRequestBuilder& CurlRequestBuilder::ApplyClientOptions(
71 ClientOptions const& options) {
72 ValidateBuilderState(__func__);
73 logging_enabled_ = options.enable_http_tracing();
74 socket_options_.recv_buffer_size_ = options.maximum_socket_recv_size();
75 socket_options_.send_buffer_size_ = options.maximum_socket_send_size();
76 user_agent_prefix_ = options.user_agent_prefix() + user_agent_prefix_;
77 download_stall_timeout_ = options.download_stall_timeout();
78 return *this;
79 }
80
AddHeader(std::string const & header)81 CurlRequestBuilder& CurlRequestBuilder::AddHeader(std::string const& header) {
82 ValidateBuilderState(__func__);
83 auto new_header = curl_slist_append(headers_.get(), header.c_str());
84 (void)headers_.release();
85 headers_.reset(new_header);
86 return *this;
87 }
88
AddQueryParameter(std::string const & key,std::string const & value)89 CurlRequestBuilder& CurlRequestBuilder::AddQueryParameter(
90 std::string const& key, std::string const& value) {
91 ValidateBuilderState(__func__);
92 std::string parameter = query_parameter_separator_;
93 parameter += handle_.MakeEscapedString(key).get();
94 parameter += "=";
95 parameter += handle_.MakeEscapedString(value).get();
96 query_parameter_separator_ = "&";
97 url_.append(parameter);
98 return *this;
99 }
100
SetMethod(std::string const & method)101 CurlRequestBuilder& CurlRequestBuilder::SetMethod(std::string const& method) {
102 ValidateBuilderState(__func__);
103 handle_.SetOption(CURLOPT_CUSTOMREQUEST, method.c_str());
104 return *this;
105 }
106
SetCurlShare(CURLSH * share)107 CurlRequestBuilder& CurlRequestBuilder::SetCurlShare(CURLSH* share) {
108 handle_.SetOption(CURLOPT_SHARE, share);
109 return *this;
110 }
111
UserAgentSuffix() const112 std::string CurlRequestBuilder::UserAgentSuffix() const {
113 ValidateBuilderState(__func__);
114 // Pre-compute and cache the user agent string:
115 static std::string const kUserAgentSuffix = [] {
116 std::string agent = "gcloud-cpp/" + storage::version_string() + " ";
117 agent += curl_version();
118 agent += " " + google::cloud::internal::compiler();
119 return agent;
120 }();
121 return kUserAgentSuffix;
122 }
123
ValidateBuilderState(char const * where) const124 void CurlRequestBuilder::ValidateBuilderState(char const* where) const {
125 if (handle_.handle_.get() == nullptr) {
126 std::string msg = "Attempt to use invalidated CurlRequest in ";
127 msg += where;
128 google::cloud::internal::ThrowRuntimeError(msg);
129 }
130 }
131 } // namespace internal
132 } // namespace STORAGE_CLIENT_NS
133 } // namespace storage
134 } // namespace cloud
135 } // namespace google
136