1 /*
2  * Copyright 1999-2019 Alibaba Cloud All rights reserved.
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 <algorithm>
18 #include <alibabacloud/core/AlibabaCloud.h>
19 #include <alibabacloud/core/CommonClient.h>
20 #include <alibabacloud/core/SimpleCredentialsProvider.h>
21 #include <alibabacloud/core/location/LocationClient.h>
22 #include <ctime>
23 #include <iomanip>
24 #include <sstream>
25 #ifdef USE_AOS_TIME_POSIX_API
26 #include <posix/timer.h>
27 #endif
28 #include <alibabacloud/core/Utils.h>
29 
30 namespace AlibabaCloud {
31 
32 namespace {
33 const std::string SERVICE_NAME = "Common";
34 }
35 
CommonClient(const Credentials & credentials,const ClientConfiguration & configuration)36 CommonClient::CommonClient(const Credentials &credentials,
37                            const ClientConfiguration &configuration)
38     : CoreClient(SERVICE_NAME, configuration),
39       credentialsProvider_(
40           std::make_shared<SimpleCredentialsProvider>(credentials)),
41       signer_(std::make_shared<HmacSha1Signer>()) {}
42 
CommonClient(const std::shared_ptr<CredentialsProvider> & credentialsProvider,const ClientConfiguration & configuration)43 CommonClient::CommonClient(
44     const std::shared_ptr<CredentialsProvider> &credentialsProvider,
45     const ClientConfiguration &configuration)
46     : CoreClient(SERVICE_NAME, configuration),
47       credentialsProvider_(credentialsProvider),
48       signer_(std::make_shared<HmacSha1Signer>()) {}
49 
CommonClient(const std::string & accessKeyId,const std::string & accessKeySecret,const ClientConfiguration & configuration)50 CommonClient::CommonClient(const std::string &accessKeyId,
51                            const std::string &accessKeySecret,
52                            const ClientConfiguration &configuration)
53     : CoreClient(SERVICE_NAME, configuration),
54       credentialsProvider_(std::make_shared<SimpleCredentialsProvider>(
55           accessKeyId, accessKeySecret)),
56       signer_(std::make_shared<HmacSha1Signer>()) {}
57 
~CommonClient()58 CommonClient::~CommonClient() {}
59 
60 CommonClient::JsonOutcome
makeRequest(const std::string & endpoint,const CommonRequest & msg,HttpRequest::Method method) const61 CommonClient::makeRequest(const std::string &endpoint, const CommonRequest &msg,
62                           HttpRequest::Method method) const {
63   auto outcome = AttemptRequest(endpoint, msg, method);
64   if (outcome.isSuccess())
65     return JsonOutcome(
66         std::string(outcome.result().body(), outcome.result().bodySize()));
67   else
68     return JsonOutcome(outcome.error());
69 }
70 
71 CommonClient::CommonResponseOutcome
commonResponse(const CommonRequest & request) const72 CommonClient::commonResponse(const CommonRequest &request) const {
73   auto outcome = makeRequest(request.domain(), request, request.httpMethod());
74   if (outcome.isSuccess())
75     return CommonResponseOutcome(CommonResponse(outcome.result()));
76   else
77     return CommonResponseOutcome(Error(outcome.error()));
78 }
79 
commonResponseAsync(const CommonRequest & request,const CommonResponseAsyncHandler & handler,const std::shared_ptr<const AsyncCallerContext> & context) const80 void CommonClient::commonResponseAsync(
81     const CommonRequest &request, const CommonResponseAsyncHandler &handler,
82     const std::shared_ptr<const AsyncCallerContext> &context) const {
83   auto fn = [this, request, handler, context]() {
84     handler(this, request, commonResponse(request), context);
85   };
86 
87   asyncExecute(new Runnable(fn));
88 }
89 
90 CommonClient::CommonResponseOutcomeCallable
commonResponseCallable(const CommonRequest & request) const91 CommonClient::commonResponseCallable(const CommonRequest &request) const {
92   auto task = std::make_shared<std::packaged_task<CommonResponseOutcome()>>(
93       [this, request]() { return this->commonResponse(request); });
94 
95   asyncExecute(new Runnable([task]() { (*task)(); }));
96   return task->get_future();
97 }
98 
buildHttpRequest(const std::string & endpoint,const ServiceRequest & msg,HttpRequest::Method method) const99 HttpRequest CommonClient::buildHttpRequest(const std::string &endpoint,
100                                            const ServiceRequest &msg,
101                                            HttpRequest::Method method) const {
102   return buildHttpRequest(endpoint, static_cast<const CommonRequest &>(msg), //lk change dynamic_cast to static_cast
103                           method);
104 }
105 
buildHttpRequest(const std::string & endpoint,const CommonRequest & msg,HttpRequest::Method method) const106 HttpRequest CommonClient::buildHttpRequest(const std::string &endpoint,
107                                            const CommonRequest &msg,
108                                            HttpRequest::Method method) const {
109   if (msg.requestPattern() == CommonRequest::RpcPattern)
110     return buildRpcHttpRequest(endpoint, msg, method);
111   else
112     return buildRoaHttpRequest(endpoint, msg, method);
113 }
114 
115 HttpRequest
buildRoaHttpRequest(const std::string & endpoint,const CommonRequest & msg,HttpRequest::Method method) const116 CommonClient::buildRoaHttpRequest(const std::string &endpoint,
117                                   const CommonRequest &msg,
118                                   HttpRequest::Method method) const {
119   const Credentials credentials = credentialsProvider_->getCredentials();
120 
121   Url url;
122   if (msg.scheme().empty()) {
123     url.setScheme("https");
124   } else {
125     url.setScheme(msg.scheme());
126   }
127   url.setHost(endpoint);
128   url.setPath(msg.resourcePath());
129 
130   auto params = msg.headerParameters();
131   std::map<std::string, std::string> queryParams;
132   for (const auto &p : params) {
133     if (!p.second.empty())
134       queryParams[p.first] = p.second;
135   }
136 
137   if (!queryParams.empty()) {
138     std::stringstream queryString;
139     for (const auto &p : queryParams) {
140       if (p.second.empty())
141         queryString << "&" << p.first;
142       else
143         queryString << "&" << p.first << "=" << p.second;
144     }
145     url.setQuery(queryString.str().substr(1));
146   }
147 
148   HttpRequest request(url);
149   request.setMethod(method);
150 
151   if (msg.connectTimeout() != kInvalidTimeout) {
152     request.setConnectTimeout(msg.connectTimeout());
153   } else {
154     request.setConnectTimeout(configuration().connectTimeout());
155   }
156 
157   if (msg.readTimeout() != kInvalidTimeout) {
158     request.setReadTimeout(msg.readTimeout());
159   } else {
160     request.setReadTimeout(configuration().readTimeout());
161   }
162 
163   if (msg.headerParameter("Accept").empty()) {
164     request.setHeader("Accept", "application/json");
165   } else {
166     request.setHeader("Accept", msg.headerParameter("Accept"));
167   }
168 
169   std::stringstream ss;
170   ss << msg.contentSize();
171   request.setHeader("Content-Length", ss.str());
172   if (msg.headerParameter("Content-Type").empty()) {
173     request.setHeader("Content-Type", "application/octet-stream");
174   } else {
175     request.setHeader("Content-Type", msg.headerParameter("Content-Type"));
176   }
177   request.setHeader("Content-MD5",
178                     ComputeContentMD5(msg.content(), msg.contentSize()));
179   request.setBody(msg.content(), msg.contentSize());
180 
181 #ifdef USE_AOS_TIME_POSIX_API
182     struct timespec currentTime;
183     time_t t;
184     clock_gettime(CLOCK_REALTIME, &currentTime);
185     t = currentTime.tv_nsec/1000000000 + currentTime.tv_sec;
186 #else
187     std::time_t t = std::time(nullptr);
188 #endif
189   std::stringstream date;
190 #if defined(__GNUG__) && __GNUC__ < 5
191   char tmbuff[26];
192   strftime(tmbuff, 26, "%a, %d %b %Y %T", std::gmtime(&t));
193   date << tmbuff << " GMT";
194 #else
195   date << std::put_time(std::gmtime(&t), "%a, %d %b %Y %T GMT");
196 #endif
197   request.setHeader("Date", date.str());
198   request.setHeader("Host", url.host());
199   request.setHeader("x-sdk-client",
200                     std::string("CPP/").append(ALIBABACLOUD_VERSION_STR));
201   request.setHeader("x-acs-region-id", configuration().regionId());
202   if (!credentials.sessionToken().empty())
203     request.setHeader("x-acs-security-token", credentials.sessionToken());
204   request.setHeader("x-acs-signature-method", signer_->name());
205   request.setHeader("x-acs-signature-nonce", GenerateUuid());
206   request.setHeader("x-acs-signature-version", signer_->version());
207   request.setHeader("x-acs-version", msg.version());
208 
209   std::stringstream plaintext;
210   plaintext << HttpMethodToString(method) << "\n"
211             << request.header("Accept") << "\n"
212             << request.header("Content-MD5") << "\n"
213             << request.header("Content-Type") << "\n"
214             << request.header("Date") << "\n"
215             << canonicalizedHeaders(request.headers());
216   if (!url.hasQuery())
217     plaintext << url.path();
218   else
219     plaintext << url.path() << "?" << url.query();
220 
221   std::stringstream sign;
222   sign << "acs " << credentials.accessKeyId() << ":"
223        << signer_->generate(plaintext.str(), credentials.accessKeySecret());
224   request.setHeader("Authorization", sign.str());
225   return request;
226 }
227 
228 HttpRequest
buildRpcHttpRequest(const std::string & endpoint,const CommonRequest & msg,HttpRequest::Method method) const229 CommonClient::buildRpcHttpRequest(const std::string &endpoint,
230                                   const CommonRequest &msg,
231                                   HttpRequest::Method method) const {
232   const Credentials credentials = credentialsProvider_->getCredentials();
233 
234   Url url;
235   if (msg.scheme().empty()) {
236     url.setScheme("https");
237   } else {
238     url.setScheme(msg.scheme());
239   }
240   url.setHost(endpoint);
241   url.setPath(msg.resourcePath());
242 
243   auto params = msg.queryParameters();
244   std::map<std::string, std::string> queryParams;
245   for (const auto &p : params) {
246     if (!p.second.empty())
247       queryParams[p.first] = p.second;
248   }
249 
250   queryParams["AccessKeyId"] = credentials.accessKeyId();
251   queryParams["Format"] = "JSON";
252   queryParams["RegionId"] = configuration().regionId();
253   queryParams["SecurityToken"] = credentials.sessionToken();
254   queryParams["SignatureMethod"] = signer_->name();
255   queryParams["SignatureNonce"] = GenerateUuid();
256   queryParams["SignatureVersion"] = signer_->version();
257 #ifdef USE_AOS_TIME_POSIX_API
258     struct timespec currentTime;
259     time_t t;
260     clock_gettime(CLOCK_REALTIME, &currentTime);
261     t = currentTime.tv_nsec/1000000000 + currentTime.tv_sec;
262 #else
263     std::time_t t = std::time(nullptr);
264 #endif
265   std::stringstream ss;
266 #if defined(__GNUG__) && __GNUC__ < 5
267   char tmbuff[26];
268   strftime(tmbuff, 26, "%FT%TZ", std::gmtime(&t));
269   ss << tmbuff;
270 #else
271   ss << std::put_time(std::gmtime(&t), "%FT%TZ");
272 #endif
273   queryParams["Timestamp"] = ss.str();
274   queryParams["Version"] = msg.version();
275 
276   std::string bodyParamString;
277   auto signParams = queryParams;
278   auto bodyParams = msg.bodyParameters();
279   for (const auto &p : bodyParams) {
280     bodyParamString += "&";
281     bodyParamString += (p.first + "=" + UrlEncode(p.second));
282     signParams[p.first] = p.second;
283   }
284 
285   std::stringstream plaintext;
286   plaintext << HttpMethodToString(method) << "&" << UrlEncode(url.path()) << "&"
287             << UrlEncode(canonicalizedQuery(signParams));
288   queryParams["Signature"] =
289       signer_->generate(plaintext.str(), credentials.accessKeySecret() + "&");
290 
291   std::stringstream queryString;
292   for (const auto &p : queryParams)
293     queryString << "&" << p.first << "=" << UrlEncode(p.second);
294   url.setQuery(queryString.str().substr(1));
295 
296   HttpRequest request(url);
297   if (msg.connectTimeout() != kInvalidTimeout) {
298     request.setConnectTimeout(msg.connectTimeout());
299   } else {
300     request.setConnectTimeout(configuration().connectTimeout());
301   }
302 
303   if (msg.readTimeout() != kInvalidTimeout) {
304     request.setReadTimeout(msg.readTimeout());
305   } else {
306     request.setReadTimeout(configuration().readTimeout());
307   }
308 
309   request.setMethod(method);
310   request.setHeader("Host", url.host());
311   request.setHeader("x-sdk-client",
312                     std::string("CPP/").append(ALIBABACLOUD_VERSION_STR));
313 
314   if (!bodyParamString.empty()) {
315     request.setBody(bodyParamString.c_str() + 1, bodyParamString.size() - 1);
316   }
317   return request;
318 }
319 
320 } // namespace AlibabaCloud
321