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