Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support query compatible trait only for json protocol #3204

Merged
merged 16 commits into from
Nov 26, 2024
4 changes: 2 additions & 2 deletions .github/workflows/clang-format.yml
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ jobs:
run: |
clang-format --version
if [ -s diff_output.patch ]; then
python3 clang-format-diff.py -p1 -style=file:.clang-format < diff_output.patch > formatted_differences.patch 2> error.log || true
python3 clang-format-diff.py -iregex '.*\.(cpp|cc|c\+\+|cxx|c|h|hh|hpp)' -p1 -style=file:.clang-format < diff_output.patch > formatted_differences.patch 2> error.log || true
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

without this just the java file fails clang format in the pipeline, locally , no matter which version

if [ -s error.log ]; then
echo "Errors from clang-format-diff.py:"
cat error.log
Expand All @@ -77,4 +77,4 @@ jobs:
cat formatted_differences.patch
rm formatted_differences.patch
exit 1
fi
fi
3 changes: 3 additions & 0 deletions src/aws-cpp-sdk-core/include/aws/core/client/AWSClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ namespace Aws
std::shared_ptr<Aws::Http::HttpResponse> MakeHttpRequest(std::shared_ptr<Aws::Http::HttpRequest>& request) const;
Aws::String m_region;

void SetFeatureHeaders(Aws::Http::HeaderValueCollection&& headers);

/**
* Adds "X-Amzn-Trace-Id" header with the value of _X_AMZN_TRACE_ID if both
* environment variables AWS_LAMBDA_FUNCTION_NAME and _X_AMZN_TRACE_ID are set.
Expand Down Expand Up @@ -356,6 +358,7 @@ namespace Aws
Aws::String m_serviceName = "AWSBaseClient";
Aws::Client::RequestCompressionConfig m_requestCompressionConfig;
Aws::Vector<std::shared_ptr<smithy::interceptor::Interceptor>> m_interceptors;
Aws::Http::HeaderValueCollection m_featureHeaders;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess smithy client needs to be updated as well.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I will extend this to smithy client

};

AWS_CORE_API Aws::String GetAuthorizationHeader(const Aws::Http::HttpRequest& httpRequest);
Expand Down
43 changes: 24 additions & 19 deletions src/aws-cpp-sdk-core/include/aws/core/client/AWSErrorMarshaller.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,32 +80,37 @@ namespace Aws
{
using AWSErrorMarshaller::Marshall;
public:
/**
* Converts an exceptionName and message into an Error object, if it can be parsed. Otherwise, it returns
* and AWSError with CoreErrors::UNKNOWN as the error type.
*/
AWSError<CoreErrors> Marshall(const Aws::Http::HttpResponse& response) const override;
JsonErrorMarshaller(bool queryCompatibilityMode);
JsonErrorMarshaller() = default;
/**
* Converts an exceptionName and message into an Error object, if it
* can be parsed. Otherwise, it returns and AWSError with
* CoreErrors::UNKNOWN as the error type.
*/
AWSError<CoreErrors> Marshall(const Aws::Http::HttpResponse& response) const override;

AWSError<CoreErrors> BuildAWSError(const std::shared_ptr<Http::HttpResponse>& httpResponse) const override;
AWSError<CoreErrors> BuildAWSError(const std::shared_ptr<Http::HttpResponse>& httpResponse) const override;

protected:
const Aws::Utils::Json::JsonValue& GetJsonPayloadFromError(const AWSError<CoreErrors>&) const;
const Aws::Utils::Json::JsonValue& GetJsonPayloadFromError(const AWSError<CoreErrors>&) const;
bool isQueryCompatibleMode() const;
const bool m_queryCompatibilityMode{false};
};

class AWS_CORE_API XmlErrorMarshaller : public AWSErrorMarshaller
{
using AWSErrorMarshaller::Marshall;
public:
/**
* Converts an exceptionName and message into an Error object, if it can be parsed. Otherwise, it returns
* and AWSError with CoreErrors::UNKNOWN as the error type.
*/
AWSError<CoreErrors> Marshall(const Aws::Http::HttpResponse& response) const override;
class AWS_CORE_API XmlErrorMarshaller : public AWSErrorMarshaller {
using AWSErrorMarshaller::Marshall;

AWSError<CoreErrors> BuildAWSError(const std::shared_ptr<Http::HttpResponse>& httpResponse) const override;
public:
/**
* Converts an exceptionName and message into an Error object, if it can be parsed. Otherwise, it returns
* and AWSError with CoreErrors::UNKNOWN as the error type.
*/
AWSError<CoreErrors> Marshall(const Aws::Http::HttpResponse& response) const override;

protected:
const Aws::Utils::Xml::XmlDocument& GetXmlPayloadFromError(const AWSError<CoreErrors>&) const;
AWSError<CoreErrors> BuildAWSError(const std::shared_ptr<Http::HttpResponse>& httpResponse) const override;

protected:
const Aws::Utils::Xml::XmlDocument& GetXmlPayloadFromError(const AWSError<CoreErrors>&) const;
};

} // namespace Client
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,17 @@ namespace client
virtual SelectAuthSchemeOptionOutcome SelectAuthSchemeOption(const AwsSmithyClientAsyncRequestContext& ctx) const = 0;
virtual SigningOutcome SignRequest(std::shared_ptr<HttpRequest> httpRequest, const AuthSchemeOption& targetAuthSchemeOption) const = 0;
virtual bool AdjustClockSkew(HttpResponseOutcome& outcome, const AuthSchemeOption& authSchemeOption) const = 0;
void SetFeatureHeaders(Aws::Http::HeaderValueCollection&& headers);
sbera87 marked this conversation as resolved.
Show resolved Hide resolved

protected:
protected:
Aws::UniquePtr<Aws::Client::ClientConfiguration> m_clientConfig;
sbera87 marked this conversation as resolved.
Show resolved Hide resolved
Aws::String m_serviceName;
Aws::String m_userAgent;

std::shared_ptr<Aws::Http::HttpClient> m_httpClient;
std::shared_ptr<Aws::Client::AWSErrorMarshaller> m_errorMarshaller;
Aws::Vector<std::shared_ptr<smithy::interceptor::Interceptor>> m_interceptors{};
Aws::Http::HeaderValueCollection m_featureHeaders;
};
} // namespace client
} // namespace smithy
3 changes: 3 additions & 0 deletions src/aws-cpp-sdk-core/source/client/AWSClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -881,6 +881,7 @@ void AWSClient::BuildHttpRequest(const Aws::AmazonWebServiceRequest& request, co
//do headers first since the request likely will set content-length as its own header.
AddHeadersToRequest(httpRequest, request.GetHeaders());
AddHeadersToRequest(httpRequest, request.GetAdditionalCustomHeaders());
AddHeadersToRequest(httpRequest, m_featureHeaders);

if (request.IsEventStreamRequest())
{
Expand Down Expand Up @@ -1049,3 +1050,5 @@ void AWSClient::AppendRecursionDetectionHeader(std::shared_ptr<Aws::Http::HttpRe

ioRequest->SetHeaderValue(Aws::Http::X_AMZN_TRACE_ID_HEADER, xAmznTraceIdVal);
}

void AWSClient::SetFeatureHeaders(Aws::Http::HeaderValueCollection&& headers) { m_featureHeaders = std::move(headers); }
177 changes: 96 additions & 81 deletions src/aws-cpp-sdk-core/source/client/AWSErrorMarshaller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -40,95 +40,110 @@ static CoreErrors GuessBodylessErrorType(const Aws::Http::HttpResponseCode respo
}
}

AWSError<CoreErrors> JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const
{
Aws::StringStream memoryStream;
std::copy(std::istreambuf_iterator<char>(httpResponse.GetResponseBody()), std::istreambuf_iterator<char>(), std::ostreambuf_iterator<char>(memoryStream));
Aws::String rawPayloadStr = memoryStream.str();

JsonValue exceptionPayload(rawPayloadStr);
JsonView payloadView(exceptionPayload);
AWSError<CoreErrors> error;
if (exceptionPayload.WasParseSuccessful())
{
AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << payloadView.WriteReadable());

Aws::String message(payloadView.ValueExists(MESSAGE_CAMEL_CASE) ? payloadView.GetString(MESSAGE_CAMEL_CASE) :
payloadView.ValueExists(MESSAGE_LOWER_CASE) ? payloadView.GetString(MESSAGE_LOWER_CASE) : "");
JsonErrorMarshaller::JsonErrorMarshaller(bool queryCompatibilityMode)
: AWSErrorMarshaller(), m_queryCompatibilityMode{queryCompatibilityMode} {}

bool JsonErrorMarshaller::isQueryCompatibleMode() const { return m_queryCompatibilityMode; }

AWSError<CoreErrors> JsonErrorMarshaller::Marshall(const Aws::Http::HttpResponse& httpResponse) const {
Aws::StringStream memoryStream;
std::copy(std::istreambuf_iterator<char>(httpResponse.GetResponseBody()), std::istreambuf_iterator<char>(),
std::ostreambuf_iterator<char>(memoryStream));
Aws::String rawPayloadStr = memoryStream.str();

JsonValue exceptionPayload(rawPayloadStr);
JsonView payloadView(exceptionPayload);
AWSError<CoreErrors> error;
if (exceptionPayload.WasParseSuccessful()) {
AWS_LOGSTREAM_TRACE(AWS_ERROR_MARSHALLER_LOG_TAG, "Error response is " << payloadView.WriteReadable());

Aws::String message(payloadView.ValueExists(MESSAGE_CAMEL_CASE) ? payloadView.GetString(MESSAGE_CAMEL_CASE)
: payloadView.ValueExists(MESSAGE_LOWER_CASE) ? payloadView.GetString(MESSAGE_LOWER_CASE)
: "");

if (httpResponse.HasHeader(ERROR_TYPE_HEADER)) {
error = Marshall(httpResponse.GetHeader(ERROR_TYPE_HEADER), message);
} else if (payloadView.ValueExists(TYPE)) {
error = Marshall(payloadView.GetString(TYPE), message);
} else {
error = FindErrorByHttpResponseCode(httpResponse.GetResponseCode());
error.SetMessage(message);
}

if (httpResponse.HasHeader(ERROR_TYPE_HEADER))
{
error = Marshall(httpResponse.GetHeader(ERROR_TYPE_HEADER), message);
}
else if (payloadView.ValueExists(TYPE))
{
error = Marshall(payloadView.GetString(TYPE), message);
if (isQueryCompatibleMode() && !error.GetExceptionName().empty()) {
/*
AWS Query-Compatible mode: This is a special setting that allows
certain AWS services to communicate using a specific "query"
format, which can send customized error codes. Users are divided
into different groups based on how they communicate with the
service: Group #1: Users using the AWS Query format, receiving
custom error codes. Group #2: Users using the regular AWS JSON
format without the trait, receiving standard error codes. Group #3:
Users using the AWS JSON format with the trait, receiving custom
error codes.

The header "x-amzn-query-error" shouldn't be present if it's not
awsQueryCompatible, so added checks for it.
*/

if (httpResponse.HasHeader(QUERY_ERROR_HEADER)) {
auto errorCodeString = httpResponse.GetHeader(QUERY_ERROR_HEADER);
auto locationOfSemicolon = errorCodeString.find_first_of(';');
Aws::String errorCode;

if (locationOfSemicolon != Aws::String::npos) {
errorCode = errorCodeString.substr(0, locationOfSemicolon);
} else {
errorCode = errorCodeString;
}
else
{
error = FindErrorByHttpResponseCode(httpResponse.GetResponseCode());
error.SetMessage(message);
}

if (httpResponse.HasHeader(QUERY_ERROR_HEADER))
{
auto errorCodeString = httpResponse.GetHeader(QUERY_ERROR_HEADER);
auto locationOfSemicolon = errorCodeString.find_first_of(';');
Aws::String errorCode;

if (locationOfSemicolon != Aws::String::npos)
{
errorCode = errorCodeString.substr(0, locationOfSemicolon);
}
else
{
errorCode = errorCodeString;
}

error.SetExceptionName(errorCode);
error.SetExceptionName(errorCode);
}
// check for exception name from payload field 'type'
else if (payloadView.ValueExists(TYPE)) {
// handle missing header and parse code from message
const auto& typeStr = payloadView.GetString(TYPE);
auto locationOfPound = typeStr.find_first_of('#');
if (locationOfPound != Aws::String::npos) {
error.SetExceptionName(typeStr.substr(locationOfPound + 1));
}
}
}
else
{
bool isRetryable = IsRetryableHttpResponseCode(httpResponse.GetResponseCode());
AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG, "Failed to parse error payload: " << httpResponse.GetResponseCode() << ": " << rawPayloadStr);
error = AWSError<CoreErrors>(CoreErrors::UNKNOWN, "", "Failed to parse error payload: " + rawPayloadStr, isRetryable);
}

error.SetRequestId(httpResponse.HasHeader(REQUEST_ID_HEADER) ? httpResponse.GetHeader(REQUEST_ID_HEADER) : "");
error.SetJsonPayload(std::move(exceptionPayload));
return error;
}

AWSError<CoreErrors> JsonErrorMarshaller::BuildAWSError(const std::shared_ptr<Http::HttpResponse>& httpResponse) const
{
AWSError<CoreErrors> error;
if (httpResponse->HasClientError())
{
bool retryable = httpResponse->GetClientErrorType() == CoreErrors::NETWORK_CONNECTION ? true : false;
error = AWSError<CoreErrors>(httpResponse->GetClientErrorType(), "", httpResponse->GetClientErrorMessage(), retryable);
}
else if (!httpResponse->GetResponseBody() || httpResponse->GetResponseBody().tellp() < 1)
{
auto responseCode = httpResponse->GetResponseCode();
auto errorCode = GuessBodylessErrorType(responseCode);
} else {
bool isRetryable = IsRetryableHttpResponseCode(httpResponse.GetResponseCode());
AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG,
"Failed to parse error payload: " << httpResponse.GetResponseCode() << ": " << rawPayloadStr);
error = AWSError<CoreErrors>(CoreErrors::UNKNOWN, "", "Failed to parse error payload: " + rawPayloadStr, isRetryable);
}

Aws::StringStream ss;
ss << "No response body.";
error = AWSError<CoreErrors>(errorCode, "", ss.str(),
IsRetryableHttpResponseCode(responseCode));
}
else
{
assert(httpResponse->GetResponseCode() != HttpResponseCode::OK);
error = Marshall(*httpResponse);
}
error.SetRequestId(httpResponse.HasHeader(REQUEST_ID_HEADER) ? httpResponse.GetHeader(REQUEST_ID_HEADER) : "");
error.SetJsonPayload(std::move(exceptionPayload));
return error;
}

error.SetResponseHeaders(httpResponse->GetHeaders());
error.SetResponseCode(httpResponse->GetResponseCode());
error.SetRemoteHostIpAddress(httpResponse->GetOriginatingRequest().GetResolvedRemoteHost());
AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG, error);
return error;
AWSError<CoreErrors> JsonErrorMarshaller::BuildAWSError(const std::shared_ptr<Http::HttpResponse>& httpResponse) const {
AWSError<CoreErrors> error;
if (httpResponse->HasClientError()) {
bool retryable = httpResponse->GetClientErrorType() == CoreErrors::NETWORK_CONNECTION ? true : false;
error = AWSError<CoreErrors>(httpResponse->GetClientErrorType(), "", httpResponse->GetClientErrorMessage(), retryable);
} else if (!httpResponse->GetResponseBody() || httpResponse->GetResponseBody().tellp() < 1) {
auto responseCode = httpResponse->GetResponseCode();
auto errorCode = GuessBodylessErrorType(responseCode);

Aws::StringStream ss;
ss << "No response body.";
error = AWSError<CoreErrors>(errorCode, "", ss.str(), IsRetryableHttpResponseCode(responseCode));
} else {
assert(httpResponse->GetResponseCode() != HttpResponseCode::OK);
error = Marshall(*httpResponse);
}

error.SetResponseHeaders(httpResponse->GetHeaders());
error.SetResponseCode(httpResponse->GetResponseCode());
error.SetRemoteHostIpAddress(httpResponse->GetOriginatingRequest().GetResolvedRemoteHost());
AWS_LOGSTREAM_ERROR(AWS_ERROR_MARSHALLER_LOG_TAG, error);
return error;
}

const JsonValue& JsonErrorMarshaller::GetJsonPayloadFromError(const AWSError<CoreErrors>& error) const
Expand Down
54 changes: 23 additions & 31 deletions src/aws-cpp-sdk-core/source/internal/AWSHttpResourceClient.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -602,41 +602,33 @@ namespace Aws
SSOCredentialsClient::SSOCredentialsClient(const Aws::Client::ClientConfiguration& clientConfiguration, Aws::Http::Scheme scheme, const Aws::String& region)
: AWSHttpResourceClient(clientConfiguration, SSO_RESOURCE_CLIENT_LOG_TAG)
{
SetErrorMarshaller(Aws::MakeUnique<Aws::Client::JsonErrorMarshaller>(SSO_RESOURCE_CLIENT_LOG_TAG));
(Aws::MakeUnique<Aws::Client::JsonErrorMarshaller>(SSO_RESOURCE_CLIENT_LOG_TAG));

m_endpoint = buildEndpoint(scheme, region, "portal.sso.", "federation/credentials");
sbera87 marked this conversation as resolved.
Show resolved Hide resolved
m_oidcEndpoint = buildEndpoint(scheme, region, "oidc.", "token");
m_endpoint = buildEndpoint(scheme, region, "portal.sso.", "federation/credentials");
m_oidcEndpoint = buildEndpoint(scheme, region, "oidc.", "token");

AWS_LOGSTREAM_INFO(SSO_RESOURCE_CLIENT_LOG_TAG, "Creating SSO ResourceClient with endpoint: " << m_endpoint);
AWS_LOGSTREAM_INFO(SSO_RESOURCE_CLIENT_LOG_TAG, "Creating SSO ResourceClient with endpoint: " << m_endpoint);
}

Aws::String SSOCredentialsClient::buildEndpoint(
Aws::Http::Scheme scheme,
const Aws::String& region,
const Aws::String& domain,
const Aws::String& endpoint)
{
Aws::StringStream ss;
if (scheme == Aws::Http::Scheme::HTTP)
{
ss << "http://";
}
else
{
ss << "https://";
}

static const int CN_NORTH_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTH_1);
static const int CN_NORTHWEST_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTHWEST_1);
auto hash = Aws::Utils::HashingUtils::HashString(region.c_str());

AWS_LOGSTREAM_DEBUG(SSO_RESOURCE_CLIENT_LOG_TAG, "Preparing SSO client for region: " << region);
ss << domain << region << ".amazonaws.com/" << endpoint;
if (hash == CN_NORTH_1_HASH || hash == CN_NORTHWEST_1_HASH)
{
ss << ".cn";
}
return ss.str();
Aws::String SSOCredentialsClient::buildEndpoint(Aws::Http::Scheme scheme, const Aws::String& region, const Aws::String& domain,
const Aws::String& endpoint) {
Aws::StringStream ss;
if (scheme == Aws::Http::Scheme::HTTP) {
ss << "http://";
} else {
ss << "https://";
}

static const int CN_NORTH_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTH_1);
static const int CN_NORTHWEST_1_HASH = Aws::Utils::HashingUtils::HashString(Aws::Region::CN_NORTHWEST_1);
auto hash = Aws::Utils::HashingUtils::HashString(region.c_str());

AWS_LOGSTREAM_DEBUG(SSO_RESOURCE_CLIENT_LOG_TAG, "Preparing SSO client for region: " << region);
ss << domain << region << ".amazonaws.com/" << endpoint;
if (hash == CN_NORTH_1_HASH || hash == CN_NORTHWEST_1_HASH) {
ss << ".cn";
}
return ss.str();
}

SSOCredentialsClient::SSOGetRoleCredentialsResult SSOCredentialsClient::GetSSOCredentials(const SSOGetRoleCredentialsRequest &request)
Expand Down
Loading
Loading