Skip to content

Commit 6581820

Browse files
committed
Initial implementation of AWSRpcV2CborClient
1 parent 1af13dc commit 6581820

File tree

3 files changed

+374
-2
lines changed

3 files changed

+374
-2
lines changed

src/aws-cpp-sdk-core/include/aws/core/client/AWSClient.h

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -369,9 +369,10 @@ namespace Aws
369369
} // namespace Client
370370
} // namespace Aws
371371

372-
#if !defined(AWS_JSON_CLIENT_H) && !defined(AWS_XML_CLIENT_H)
372+
#if !defined(AWS_JSON_CLIENT_H) && !defined(AWS_XML_CLIENT_H) && !defined(AWS_CBOR_CLIENT_H)
373373
/* Legacy backward compatibility macros to not break the build for ones including just AWSClient.h */
374374
#include <aws/core/client/AWSJsonClient.h>
375+
#include <aws/core/client/AWSRpcV2CborClient.h>
375376
#include <aws/core/client/AWSXmlClient.h>
376-
#endif // !defined(AWS_JSON_CLIENT_H) && !defined(AWS_XML_CLIENT_H)
377+
#endif // !defined(AWS_JSON_CLIENT_H) && !defined(AWS_XML_CLIENT_H) && !defined(AWS_CBOR_CLIENT_H)
377378
#endif // !defined(AWS_CLIENT_H)
Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#pragma once
7+
#if !defined(AWS_CBOR_CLIENT_H)
8+
#define AWS_CBOR_CLIENT_H
9+
10+
#include <aws/core/Core_EXPORTS.h>
11+
#include <aws/core/client/AWSClient.h>
12+
#include <aws/crt/cbor/Cbor.h>
13+
14+
namespace Aws
15+
{
16+
namespace Client
17+
{
18+
using RpcV2CborOutcome = Utils::Outcome<AmazonWebServiceResult<Aws::UniquePtr<Crt::Cbor::CborDecoder>>, AWSError<CoreErrors>>;
19+
20+
template <typename OutcomeT, typename ClientT, typename AWSEndpointT, typename RequestT, typename HandlerT>
21+
class BidirectionalEventStreamingTask;
22+
23+
/**
24+
* AWSClient that handles marshalling cbor response bodies. You would inherit from this class
25+
* to create a client that uses Cbor as its payload format.
26+
*/
27+
class AWS_CORE_API AWSRpcV2CborClient : public AWSClient
28+
{
29+
public:
30+
typedef AWSClient BASECLASS;
31+
32+
/**
33+
* Simply calls AWSClient constructor.
34+
*/
35+
AWSRpcV2CborClient(const Aws::Client::ClientConfiguration& configuration,
36+
const std::shared_ptr<Aws::Client::AWSAuthSigner>& signer,
37+
const std::shared_ptr<AWSErrorMarshaller>& errorMarshaller);
38+
39+
/**
40+
* Simply calls AWSClient constructor.
41+
*/
42+
AWSRpcV2CborClient(const Aws::Client::ClientConfiguration& configuration,
43+
const std::shared_ptr<Aws::Auth::AWSAuthSignerProvider>& signerProvider,
44+
const std::shared_ptr<AWSErrorMarshaller>& errorMarshaller);
45+
46+
virtual ~AWSRpcV2CborClient() = default;
47+
48+
protected:
49+
template <typename OutcomeT, typename ClientT, typename AWSEndpointT, typename RequestT, typename HandlerT>
50+
friend class BidirectionalEventStreamingTask; // allow BidirectionalEventStreamingTask to MakeRequests
51+
/**
52+
* Converts/Parses an http response into a meaningful AWSError object using the cbor message structure.
53+
*/
54+
virtual AWSError<CoreErrors> BuildAWSError(const std::shared_ptr<Aws::Http::HttpResponse>& response) const override;
55+
56+
/**
57+
* Returns a pointer to a Cbor decoder or an error from the request. Does some marshalling cbor and raw streams,
58+
* then just calls AttemptExhaustively.
59+
*
60+
* method defaults to POST
61+
*/
62+
RpcV2CborOutcome MakeRequest(const Aws::AmazonWebServiceRequest& request,
63+
const Aws::Endpoint::AWSEndpoint& endpoint,
64+
Http::HttpMethod method = Http::HttpMethod::HTTP_POST,
65+
const char* signerName = Aws::Auth::SIGV4_SIGNER,
66+
const char* signerRegionOverride = nullptr,
67+
const char* signerServiceNameOverride = nullptr) const;
68+
69+
RpcV2CborOutcome MakeRequest(const Aws::Endpoint::AWSEndpoint& endpoint,
70+
Http::HttpMethod method = Http::HttpMethod::HTTP_POST,
71+
const char* signerName = Aws::Auth::SIGV4_SIGNER,
72+
const char* signerRegionOverride = nullptr,
73+
const char* signerServiceNameOverride = nullptr) const;
74+
75+
/**
76+
* Returns a pointer to a Cbor decoder or an error from the request. Does some marshalling cbor and raw streams,
77+
* then just calls AttemptExhaustively.
78+
*
79+
* method defaults to POST
80+
*/
81+
RpcV2CborOutcome MakeRequest(const Aws::Http::URI& uri,
82+
const Aws::AmazonWebServiceRequest& request,
83+
Http::HttpMethod method = Http::HttpMethod::HTTP_POST,
84+
const char* signerName = Aws::Auth::SIGV4_SIGNER,
85+
const char* signerRegionOverride = nullptr,
86+
const char* signerServiceNameOverride = nullptr) const;
87+
88+
/**
89+
* Returns a pointer to a Cbor decoder or an error from the request. Does some marshalling json and raw streams,
90+
* then just calls AttemptExhaustively.
91+
*
92+
* requestName is used for metrics and defaults to empty string, to avoid empty names in metrics provide a valid
93+
* name.
94+
*
95+
* method defaults to POST
96+
*/
97+
RpcV2CborOutcome MakeRequest(const Aws::Http::URI& uri,
98+
Http::HttpMethod method = Http::HttpMethod::HTTP_POST,
99+
const char* signerName = Aws::Auth::SIGV4_SIGNER,
100+
const char* requestName = "",
101+
const char* signerRegionOverride = nullptr,
102+
const char* signerServiceNameOverride = nullptr) const;
103+
104+
RpcV2CborOutcome MakeEventStreamRequest(std::shared_ptr<Aws::Http::HttpRequest>& request) const;
105+
106+
private:
107+
Aws::UniquePtr<Aws::Crt::Cbor::CborDecoder> CreateCborDecoder(const HttpResponseOutcome& response) const;
108+
};
109+
} // namespace Client
110+
} // namespace Aws
111+
112+
#endif // !defined(AWS_CBOR_CLIENT_H)
Lines changed: 259 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,259 @@
1+
/**
2+
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3+
* SPDX-License-Identifier: Apache-2.0.
4+
*/
5+
6+
#include <aws/core/AmazonWebServiceRequest.h>
7+
#include <aws/core/auth/AWSAuthSignerProvider.h>
8+
#include <aws/core/client/AWSError.h>
9+
#include <aws/core/client/AWSErrorMarshaller.h>
10+
#include <aws/core/client/AWSRpcV2CborClient.h>
11+
#include <aws/core/client/ClientConfiguration.h>
12+
#include <aws/core/client/CoreErrors.h>
13+
#include <aws/core/client/RetryStrategy.h>
14+
#include <aws/core/http/HttpClient.h>
15+
#include <aws/core/http/HttpResponse.h>
16+
#include <aws/core/http/URI.h>
17+
#include <aws/core/monitoring/MonitoringManager.h>
18+
#include <aws/core/utils/Outcome.h>
19+
#include <aws/core/utils/UUID.h>
20+
#include <aws/core/utils/event/EventStream.h>
21+
#include <aws/core/utils/logging/LogMacros.h>
22+
#include <aws/core/utils/memory/stl/AWSStringStream.h>
23+
#include <aws/core/utils/xml/XmlSerializer.h>
24+
#include <smithy/tracing/TracingUtils.h>
25+
26+
#include <cassert>
27+
28+
#include "aws/crt/cbor/Cbor.h"
29+
30+
using namespace Aws;
31+
using namespace Aws::Client;
32+
using namespace Aws::Http;
33+
using namespace Aws::Utils;
34+
using namespace smithy::components::tracing;
35+
36+
static const char AWS_CBOR_CLIENT_LOG_TAG[] = "AWSRpcV2CborClient";
37+
38+
AWSRpcV2CborClient::AWSRpcV2CborClient(const Aws::Client::ClientConfiguration& configuration,
39+
const std::shared_ptr<Aws::Client::AWSAuthSigner>& signer,
40+
const std::shared_ptr<AWSErrorMarshaller>& errorMarshaller) :
41+
BASECLASS(configuration, signer, errorMarshaller)
42+
{
43+
}
44+
45+
AWSRpcV2CborClient::AWSRpcV2CborClient(const Aws::Client::ClientConfiguration& configuration,
46+
const std::shared_ptr<Aws::Auth::AWSAuthSignerProvider>& signerProvider,
47+
const std::shared_ptr<AWSErrorMarshaller>& errorMarshaller) :
48+
BASECLASS(configuration, signerProvider, errorMarshaller)
49+
{
50+
}
51+
52+
RpcV2CborOutcome AWSRpcV2CborClient::MakeRequest(const Aws::AmazonWebServiceRequest& request,
53+
const Aws::Endpoint::AWSEndpoint& endpoint,
54+
Http::HttpMethod method /* = Http::HttpMethod::HTTP_POST */,
55+
const char* signerName /* = Aws::Auth::NULL_SIGNER */,
56+
const char* signerRegionOverride /* = nullptr */,
57+
const char* signerServiceNameOverride /* = nullptr */) const
58+
{
59+
const Aws::Http::URI& uri = endpoint.GetURI();
60+
if (endpoint.GetAttributes()) {
61+
signerName = endpoint.GetAttributes()->authScheme.GetName().c_str();
62+
if (endpoint.GetAttributes()->authScheme.GetSigningRegion()) {
63+
signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegion()->c_str();
64+
}
65+
if (endpoint.GetAttributes()->authScheme.GetSigningRegionSet()) {
66+
signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegionSet()->c_str();
67+
}
68+
if (endpoint.GetAttributes()->authScheme.GetSigningName()) {
69+
signerServiceNameOverride = endpoint.GetAttributes()->authScheme.GetSigningName()->c_str();
70+
}
71+
}
72+
return MakeRequest(uri, request, method, signerName, signerRegionOverride, signerServiceNameOverride);
73+
}
74+
75+
RpcV2CborOutcome AWSRpcV2CborClient::MakeRequest(const Aws::Endpoint::AWSEndpoint& endpoint,
76+
Http::HttpMethod method /* = Http::HttpMethod::HTTP_POST */,
77+
const char* signerName /* = Aws::Auth::NULL_SIGNER */,
78+
const char* signerRegionOverride /* = nullptr */,
79+
const char* signerServiceNameOverride /* = nullptr */) const
80+
{
81+
const Aws::Http::URI& uri = endpoint.GetURI();
82+
if (endpoint.GetAttributes()) {
83+
signerName = endpoint.GetAttributes()->authScheme.GetName().c_str();
84+
if (endpoint.GetAttributes()->authScheme.GetSigningRegion()) {
85+
signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegion()->c_str();
86+
}
87+
if (endpoint.GetAttributes()->authScheme.GetSigningRegionSet()) {
88+
signerRegionOverride = endpoint.GetAttributes()->authScheme.GetSigningRegionSet()->c_str();
89+
}
90+
if (endpoint.GetAttributes()->authScheme.GetSigningName()) {
91+
signerServiceNameOverride = endpoint.GetAttributes()->authScheme.GetSigningName()->c_str();
92+
}
93+
}
94+
return MakeRequest(uri, method, signerName, signerRegionOverride, signerServiceNameOverride);
95+
}
96+
97+
RpcV2CborOutcome AWSRpcV2CborClient::MakeRequest(const Aws::Http::URI& uri,
98+
const Aws::AmazonWebServiceRequest& request,
99+
Http::HttpMethod method,
100+
const char* signerName,
101+
const char* signerRegionOverride,
102+
const char* signerServiceNameOverride) const
103+
{
104+
HttpResponseOutcome httpOutcome(BASECLASS::AttemptExhaustively(uri, request, method, signerName, signerRegionOverride, signerServiceNameOverride));
105+
if (!httpOutcome.IsSuccess())
106+
{
107+
return smithy::components::tracing::TracingUtils::MakeCallWithTiming<RpcV2CborOutcome>(
108+
[&]() -> RpcV2CborOutcome {
109+
return RpcV2CborOutcome(std::move(httpOutcome));
110+
},
111+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
112+
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
113+
{{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()}, {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
114+
}
115+
116+
if (httpOutcome.GetResult()->GetResponseBody().tellp() > 0){
117+
return smithy::components::tracing::TracingUtils::MakeCallWithTiming<RpcV2CborOutcome>(
118+
[&]() -> RpcV2CborOutcome {
119+
auto ptr = CreateCborDecoder(httpOutcome);
120+
return RpcV2CborOutcome(AmazonWebServiceResult<Aws::UniquePtr<Crt::Cbor::CborDecoder>>(std::move(ptr),
121+
httpOutcome.GetResult()->GetHeaders(),
122+
httpOutcome.GetResult()->GetResponseCode()));
123+
},
124+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
125+
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
126+
{{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()}, {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
127+
}
128+
return smithy::components::tracing::TracingUtils::MakeCallWithTiming<RpcV2CborOutcome>(
129+
[&]() -> RpcV2CborOutcome {
130+
return RpcV2CborOutcome(AmazonWebServiceResult<Aws::UniquePtr<Crt::Cbor::CborDecoder>>(nullptr, httpOutcome.GetResult()->GetHeaders()));
131+
},
132+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
133+
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
134+
{{TracingUtils::SMITHY_METHOD_DIMENSION, request.GetServiceRequestName()}, {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
135+
}
136+
137+
RpcV2CborOutcome AWSRpcV2CborClient::MakeRequest(const Aws::Http::URI& uri,
138+
Http::HttpMethod method,
139+
const char* signerName,
140+
const char* requestName,
141+
const char* signerRegionOverride,
142+
const char* signerServiceNameOverride) const
143+
{
144+
HttpResponseOutcome httpOutcome(BASECLASS::AttemptExhaustively(uri, method, signerName, requestName, signerRegionOverride, signerServiceNameOverride));
145+
if (!httpOutcome.IsSuccess())
146+
{
147+
return smithy::components::tracing::TracingUtils::MakeCallWithTiming<RpcV2CborOutcome>(
148+
[&]() -> RpcV2CborOutcome {
149+
return {std::move(httpOutcome)};
150+
},
151+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
152+
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
153+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName}, {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
154+
}
155+
156+
if (httpOutcome.GetResult()->GetResponseBody().tellp() > 0)
157+
{
158+
auto cborDecoder = CreateCborDecoder(httpOutcome);
159+
if (cborDecoder != nullptr) {
160+
return smithy::components::tracing::TracingUtils::MakeCallWithTiming<RpcV2CborOutcome>(
161+
[&]() -> RpcV2CborOutcome {
162+
return RpcV2CborOutcome(AWSError<CoreErrors>(CoreErrors::UNKNOWN, "Cbor Parser Error", "SAARTHI ERROR", false));
163+
},
164+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
165+
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
166+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName}, {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
167+
}
168+
169+
return smithy::components::tracing::TracingUtils::MakeCallWithTiming<RpcV2CborOutcome>(
170+
[&]() -> RpcV2CborOutcome {
171+
auto ptr = CreateCborDecoder(httpOutcome);
172+
return RpcV2CborOutcome(AmazonWebServiceResult<Aws::UniquePtr<Crt::Cbor::CborDecoder>>(std::move(ptr),
173+
httpOutcome.GetResult()->GetHeaders(),
174+
httpOutcome.GetResult()->GetResponseCode()));
175+
},
176+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
177+
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
178+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName}, {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
179+
}
180+
181+
return smithy::components::tracing::TracingUtils::MakeCallWithTiming<RpcV2CborOutcome>(
182+
[&]() -> RpcV2CborOutcome {
183+
return RpcV2CborOutcome(AmazonWebServiceResult<Aws::UniquePtr<Crt::Cbor::CborDecoder>>(nullptr, httpOutcome.GetResult()->GetHeaders()));
184+
},
185+
TracingUtils::SMITHY_CLIENT_DESERIALIZATION_METRIC,
186+
*m_telemetryProvider->getMeter(this->GetServiceClientName(), {}),
187+
{{TracingUtils::SMITHY_METHOD_DIMENSION, requestName}, {TracingUtils::SMITHY_SERVICE_DIMENSION, this->GetServiceClientName()}});
188+
}
189+
190+
RpcV2CborOutcome AWSRpcV2CborClient::MakeEventStreamRequest(std::shared_ptr<Aws::Http::HttpRequest>& request) const {
191+
// request is assumed to be signed
192+
std::shared_ptr<HttpResponse> httpResponse = MakeHttpRequest(request);
193+
194+
if (DoesResponseGenerateError(httpResponse)) {
195+
AWS_LOGSTREAM_DEBUG(AWS_CBOR_CLIENT_LOG_TAG, "Request returned error. Attempting to generate appropriate error codes from response");
196+
auto error = BuildAWSError(httpResponse);
197+
return RpcV2CborOutcome(std::move(error));
198+
}
199+
200+
AWS_LOGSTREAM_DEBUG(AWS_CBOR_CLIENT_LOG_TAG, "Request returned successful response.");
201+
202+
HttpResponseOutcome httpOutcome(std::move(httpResponse));
203+
204+
if (httpOutcome.GetResult()->GetResponseBody().tellp() > 0) {
205+
Aws::StringStream ss;
206+
ss << httpOutcome.GetResult()->GetResponseBody().rdbuf();
207+
Aws::Crt::ByteCursor cborValue = Aws::Crt::ByteCursorFromCString(ss.str().c_str());
208+
if (cborValue.ptr != nullptr) {
209+
return RpcV2CborOutcome(AWSError<CoreErrors>(CoreErrors::UNKNOWN, "Cbor Parser Error", "SAARTHI ERROR", false));
210+
}
211+
212+
auto ptr = CreateCborDecoder(httpOutcome);
213+
return RpcV2CborOutcome(AmazonWebServiceResult<Aws::UniquePtr<Crt::Cbor::CborDecoder>>(std::move(ptr), httpOutcome.GetResult()->GetHeaders(),
214+
httpOutcome.GetResult()->GetResponseCode()));
215+
}
216+
217+
return RpcV2CborOutcome(AmazonWebServiceResult<Aws::UniquePtr<Crt::Cbor::CborDecoder>>(nullptr, httpOutcome.GetResult()->GetHeaders()));
218+
}
219+
220+
AWSError<CoreErrors> AWSRpcV2CborClient::BuildAWSError(
221+
const std::shared_ptr<Aws::Http::HttpResponse>& httpResponse) const
222+
{
223+
AWSError<CoreErrors> error;
224+
if (httpResponse->HasClientError())
225+
{
226+
bool retryable = httpResponse->GetClientErrorType() == CoreErrors::NETWORK_CONNECTION ? true : false;
227+
error = AWSError<CoreErrors>(httpResponse->GetClientErrorType(), "", httpResponse->GetClientErrorMessage(), retryable);
228+
}
229+
else if (!httpResponse->GetResponseBody() || httpResponse->GetResponseBody().tellp() < 1)
230+
{
231+
auto responseCode = httpResponse->GetResponseCode();
232+
auto errorCode = AWSClient::GuessBodylessErrorType(responseCode);
233+
234+
Aws::StringStream ss;
235+
ss << "No response body.";
236+
error = AWSError<CoreErrors>(errorCode, "", ss.str(),
237+
IsRetryableHttpResponseCode(responseCode));
238+
}
239+
else
240+
{
241+
assert(httpResponse->GetResponseCode() != HttpResponseCode::OK);
242+
error = GetErrorMarshaller()->Marshall(*httpResponse);
243+
}
244+
245+
error.SetResponseHeaders(httpResponse->GetHeaders());
246+
error.SetResponseCode(httpResponse->GetResponseCode());
247+
error.SetRemoteHostIpAddress(httpResponse->GetOriginatingRequest().GetResolvedRemoteHost());
248+
AWS_LOGSTREAM_ERROR(AWS_CBOR_CLIENT_LOG_TAG, error);
249+
return error;
250+
}
251+
252+
Aws::UniquePtr<Aws::Crt::Cbor::CborDecoder> AWSRpcV2CborClient::CreateCborDecoder(
253+
const HttpResponseOutcome& httpOutcome) const
254+
{
255+
Aws::StringStream ss;
256+
ss << httpOutcome.GetResult()->GetResponseBody().rdbuf();
257+
Aws::Crt::ByteCursor cborDecoder= Aws::Crt::ByteCursorFromCString(ss.str().c_str());
258+
return Aws::MakeUnique<Crt::Cbor::CborDecoder>("TODO:", cborDecoder);
259+
}

0 commit comments

Comments
 (0)