1 /*
2  * Copyright 2009-2017 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 <alibabacloud/oss/client/RetryStrategy.h>
18 #include <alibabacloud/oss/utils/Executor.h>
19 #include <external/tinyxml2/tinyxml2.h>
20 #include "Client.h"
21 #include "../http/CurlHttpClient.h"
22 #include "utils/Utils.h"
23 #include "../auth/Signer.h"
24 #include <sstream>
25 #include <ctime>
26 #ifdef USE_AOS_TIME_POSIX_API
27 #include <posix/timer.h>
28 #endif
29 
30 using namespace AlibabaCloud::OSS;
31 using namespace tinyxml2;
32 
Client(const std::string & servicename,const ClientConfiguration & configuration)33 Client::Client(const std::string & servicename, const ClientConfiguration &configuration) :
34     requestDateOffset_(0),
35     serviceName_(servicename),
36     configuration_(configuration),
37     httpClient_(configuration.httpClient? configuration.httpClient:std::make_shared<CurlHttpClient>(configuration))
38 {
39 }
40 
~Client()41 Client::~Client()
42 {
43 }
44 
configuration() const45 const ClientConfiguration& Client::configuration()const
46 {
47     return configuration_;
48 }
49 
serviceName() const50 std::string Client::serviceName()const
51 {
52     return serviceName_;
53 }
54 
AttemptRequest(const std::string & endpoint,const ServiceRequest & request,Http::Method method) const55 Client::ClientOutcome Client::AttemptRequest(const std::string & endpoint, const ServiceRequest & request, Http::Method method) const
56 {
57     for (int retry =0; ;retry++) {
58         auto outcome = AttemptOnceRequest(endpoint, request, method);
59         if (outcome.isSuccess()) {
60             return outcome;
61         }
62         else if (!httpClient_->isEnable()) {
63             return outcome;
64         }
65         else {
66             if (configuration_.enableDateSkewAdjustment &&
67                 outcome.error().Status() == 403 &&
68                 outcome.error().Message().find("RequestTimeTooSkewed")) {
69                 auto serverTimeStr = analyzeServerTime(outcome.error().Message());
70                 auto serverTime = UtcToUnixTime(serverTimeStr);
71                 if (serverTime != -1) {
72 #ifdef USE_AOS_TIME_POSIX_API
73                     struct timespec currentTime;
74                     time_t localTime;
75                     clock_gettime(CLOCK_REALTIME, &currentTime);
76                     localTime = currentTime.tv_nsec/1000000000 + currentTime.tv_sec;
77 #else
78                     std::time_t localTime = std::time(nullptr);
79 #endif
80                     setRequestDateOffset(serverTime - localTime);
81                 }
82             }
83             RetryStrategy *retryStrategy = configuration().retryStrategy.get();
84             if (retryStrategy == nullptr || !retryStrategy->shouldRetry(outcome.error(), retry)) {
85                 return outcome;
86             }
87             long sleepTmeMs = retryStrategy->calcDelayTimeMs(outcome.error(), retry);
88             httpClient_->waitForRetry(sleepTmeMs);
89         }
90     }
91 }
92 
AttemptOnceRequest(const std::string & endpoint,const ServiceRequest & request,Http::Method method) const93 Client::ClientOutcome Client::AttemptOnceRequest(const std::string & endpoint, const ServiceRequest & request, Http::Method method) const
94 {
95     if (!httpClient_->isEnable()) {
96         return ClientOutcome(Error("ClientError:100002", "Disable all requests by upper."));
97     }
98 
99     auto r = buildHttpRequest(endpoint, request, method);
100     auto response = httpClient_->makeRequest(r);
101 
102     if(hasResponseError(response)) {
103         return ClientOutcome(buildError(response));
104     } else {
105         return ClientOutcome(response);
106     }
107 }
108 
analyzeServerTime(const std::string & message) const109 std::string Client::analyzeServerTime(const std::string &message) const
110 {
111     XMLDocument doc;
112     if (doc.Parse(message.c_str(), message.size()) == XML_SUCCESS) {
113         XMLElement* root = doc.RootElement();
114         if (root && !std::strncmp("Error", root->Name(), 5)) {
115             XMLElement *node;
116             node = root->FirstChildElement("ServerTime");
117             return (node ? node->GetText() : "");
118         }
119     }
120     return "";
121 }
122 
buildError(const std::shared_ptr<HttpResponse> & response) const123 Error Client::buildError(const std::shared_ptr<HttpResponse> &response) const
124 {
125     Error error;
126     if (response == nullptr) {
127         error.setCode("NullptrError");
128         error.setMessage("HttpResponse is nullptr, should not be here.");
129         return error;
130     }
131 
132     long responseCode = response->statusCode();
133     error.setStatus(responseCode);
134     std::stringstream ss;
135     if ((responseCode == 203) ||
136         (responseCode > 299 && responseCode < 600)) {
137         ss << "ServerError:" << responseCode;
138         error.setCode(ss.str());
139         if (response->Body() != nullptr) {
140             std::istreambuf_iterator<char> isb(*response->Body().get()), end;
141             error.setMessage(std::string(isb, end));
142         }
143     } else {
144         ss << "ClientError:" << responseCode;
145         error.setCode(ss.str());
146         error.setMessage(response->statusMsg());
147     }
148     error.setHeaders(response->Headers());
149     return error;
150 }
151 
hasResponseError(const std::shared_ptr<HttpResponse> & response) const152 bool Client::hasResponseError(const std::shared_ptr<HttpResponse>&response)const
153 {
154     if (!response) {
155         return true;
156     }
157     return (response->statusCode()/100 != 2);
158 }
159 
disableRequest()160 void Client::disableRequest()
161 {
162     httpClient_->disable();
163 }
164 
enableRequest()165 void Client::enableRequest()
166 {
167     httpClient_->enable();
168 }
169 
isEnableRequest() const170 bool Client::isEnableRequest() const
171 {
172     return httpClient_->isEnable();
173 }
174 
setRequestDateOffset(uint64_t offset) const175 void Client::setRequestDateOffset(uint64_t offset) const
176 {
177     requestDateOffset_ = offset;
178 }
179 
getRequestDateOffset() const180 uint64_t Client::getRequestDateOffset() const
181 {
182     return requestDateOffset_;
183 }