Aws::String AWSAuthV4Signer::ComputePayloadHash(Aws::Http::HttpRequest& request) const { if (!request.GetContentBody()) { AWS_LOGSTREAM_DEBUG(v4LogTag, "Using cached empty string sha256 " << EMPTY_STRING_SHA256 << " because payload is empty."); return EMPTY_STRING_SHA256; } //compute hash on payload if it exists. auto hashResult = m_hash->Calculate(*request.GetContentBody()); if(request.GetContentBody()) { request.GetContentBody()->clear(); request.GetContentBody()->seekg(0); } if (!hashResult.IsSuccess()) { AWS_LOGSTREAM_ERROR(v4LogTag, "Unable to hash (sha256) request body"); return ""; } auto sha256Digest = hashResult.GetResult(); Aws::String payloadHash(HashingUtils::HexEncode(sha256Digest)); AWS_LOGSTREAM_DEBUG(v4LogTag, "Calculated sha256 " << payloadHash << " for payload."); return payloadHash; }
bool HttpClient::ContinueRequest(const Aws::Http::HttpRequest& request) const { if (request.GetContinueRequestHandler()) { return request.GetContinueRequestHandler()(&request); } return true; }
Aws::String AWSAuthV4Signer::ComputePayloadHash(Aws::Http::HttpRequest& request) const { //compute hash on payload if it exists. auto hashResult = request.GetContentBody() ? m_hash->Calculate(*request.GetContentBody()) : m_hash->Calculate(""); if (!hashResult.IsSuccess()) { AWS_LOG_ERROR(v4LogTag, "Unable to hash (sha256) request body"); return ""; } auto sha256Digest = hashResult.GetResult(); Aws::String payloadHash(HashingUtils::HexEncode(sha256Digest)); AWS_LOGSTREAM_DEBUG(v4LogTag, "Calculated sha256 " << payloadHash << " for payload."); return std::move(payloadHash); }
bool AWSAuthV4Signer::PresignRequest(Aws::Http::HttpRequest& request, const char* region, const char* serviceName, long long expirationTimeInSeconds) const { AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); //don't sign anonymous requests if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) { return true; } Aws::StringStream intConversionStream; intConversionStream << expirationTimeInSeconds; request.AddQueryStringParameter(Http::X_AMZ_EXPIRES_HEADER, intConversionStream.str()); if (!credentials.GetSessionToken().empty()) { request.AddQueryStringParameter(Http::AWS_SECURITY_TOKEN, credentials.GetSessionToken()); } //calculate date header to use in internal signature (this also goes into date header). DateTime now = GetSigningTimestamp(); Aws::String dateQueryValue = now.ToGmtString(LONG_DATE_FORMAT_STR); request.AddQueryStringParameter(Http::AWS_DATE_HEADER, dateQueryValue); Aws::StringStream ss; ss << Http::HOST_HEADER << ":" << request.GetHeaderValue(Http::HOST_HEADER) << NEWLINE; Aws::String canonicalHeadersString(ss.str()); ss.str(""); AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Header String: " << canonicalHeadersString); //calculate signed headers parameter Aws::String signedHeadersValue(Http::HOST_HEADER); request.AddQueryStringParameter(X_AMZ_SIGNED_HEADERS, signedHeadersValue); AWS_LOGSTREAM_DEBUG(v4LogTag, "Signed Headers value: " << signedHeadersValue); Aws::String simpleDate = now.ToGmtString(SIMPLE_DATE_FORMAT_STR); ss << credentials.GetAWSAccessKeyId() << "/" << simpleDate << "/" << region << "/" << serviceName << "/" << AWS4_REQUEST; request.AddQueryStringParameter(X_AMZ_ALGORITHM, AWS_HMAC_SHA256); request.AddQueryStringParameter(X_AMZ_CREDENTIAL, ss.str()); ss.str(""); //generate generalized canonicalized request string. Aws::String canonicalRequestString = CanonicalizeRequestSigningString(request, m_urlEscapePath); //append v4 stuff to the canonical request string. canonicalRequestString.append(canonicalHeadersString); canonicalRequestString.append(NEWLINE); canonicalRequestString.append(signedHeadersValue); canonicalRequestString.append(NEWLINE); canonicalRequestString.append(UNSIGNED_PAYLOAD); AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Request String: " << canonicalRequestString); //now compute sha256 on that request string auto hashResult = m_hash->Calculate(canonicalRequestString); if (!hashResult.IsSuccess()) { AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string \"" << canonicalRequestString << "\""); return false; } auto sha256Digest = hashResult.GetResult(); auto cannonicalRequestHash = HashingUtils::HexEncode(sha256Digest); auto stringToSign = GenerateStringToSign(dateQueryValue, simpleDate, cannonicalRequestHash); auto finalSigningHash = GenerateSignature(credentials, stringToSign, simpleDate); if (finalSigningHash.empty()) { return false; } //add that the signature to the query string request.AddQueryStringParameter(X_AMZ_SIGNATURE, finalSigningHash); return true; }
bool AWSAuthV4Signer::SignRequest(Aws::Http::HttpRequest& request, bool signBody) const { AWSCredentials credentials = m_credentialsProvider->GetAWSCredentials(); //don't sign anonymous requests if (credentials.GetAWSAccessKeyId().empty() || credentials.GetAWSSecretKey().empty()) { return true; } if (!credentials.GetSessionToken().empty()) { request.SetAwsSessionToken(credentials.GetSessionToken()); } Aws::String payloadHash(UNSIGNED_PAYLOAD); if(signBody || request.GetUri().GetScheme() != Http::Scheme::HTTPS) { payloadHash.assign(ComputePayloadHash(request)); if (payloadHash.empty()) { return false; } } else { AWS_LOGSTREAM_DEBUG(v4LogTag, "Note: Http payloads are not being signed. signPayloads=" << m_signPayloads << " http scheme=" << Http::SchemeMapper::ToString(request.GetUri().GetScheme())); } if(m_includeSha256HashHeader) { request.SetHeaderValue("x-amz-content-sha256", payloadHash); } //calculate date header to use in internal signature (this also goes into date header). DateTime now = GetSigningTimestamp(); Aws::String dateHeaderValue = now.ToGmtString(LONG_DATE_FORMAT_STR); request.SetHeaderValue(AWS_DATE_HEADER, dateHeaderValue); Aws::StringStream headersStream; Aws::StringStream signedHeadersStream; for (const auto& header : CanonicalizeHeaders(request.GetHeaders())) { if(ShouldSignHeader(header.first)) { headersStream << header.first.c_str() << ":" << header.second.c_str() << NEWLINE; signedHeadersStream << header.first.c_str() << ";"; } } Aws::String canonicalHeadersString = headersStream.str(); AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Header String: " << canonicalHeadersString); //calculate signed headers parameter Aws::String signedHeadersValue = signedHeadersStream.str(); //remove that last semi-colon signedHeadersValue.erase(signedHeadersValue.length() - 1); AWS_LOGSTREAM_DEBUG(v4LogTag, "Signed Headers value:" << signedHeadersValue); //generate generalized canonicalized request string. Aws::String canonicalRequestString = CanonicalizeRequestSigningString(request, m_urlEscapePath); //append v4 stuff to the canonical request string. canonicalRequestString.append(canonicalHeadersString); canonicalRequestString.append(NEWLINE); canonicalRequestString.append(signedHeadersValue); canonicalRequestString.append(NEWLINE); canonicalRequestString.append(payloadHash); AWS_LOGSTREAM_DEBUG(v4LogTag, "Canonical Request String: " << canonicalRequestString); //now compute sha256 on that request string auto hashResult = m_hash->Calculate(canonicalRequestString); if (!hashResult.IsSuccess()) { AWS_LOGSTREAM_ERROR(v4LogTag, "Failed to hash (sha256) request string \"" << canonicalRequestString << "\""); return false; } auto sha256Digest = hashResult.GetResult(); Aws::String cannonicalRequestHash = HashingUtils::HexEncode(sha256Digest); Aws::String simpleDate = now.ToGmtString(SIMPLE_DATE_FORMAT_STR); Aws::String stringToSign = GenerateStringToSign(dateHeaderValue, simpleDate, cannonicalRequestHash); auto finalSignature = GenerateSignature(credentials, stringToSign, simpleDate); Aws::StringStream ss; ss << AWS_HMAC_SHA256 << " " << CREDENTIAL << EQ << credentials.GetAWSAccessKeyId() << "/" << simpleDate << "/" << m_region << "/" << m_serviceName << "/" << AWS4_REQUEST << ", " << SIGNED_HEADERS << EQ << signedHeadersValue << ", " << SIGNATURE << EQ << finalSignature; auto awsAuthString = ss.str(); AWS_LOGSTREAM_DEBUG(v4LogTag, "Signing request with: " << awsAuthString); request.SetAwsAuthorization(awsAuthString); return true; }
std::shared_ptr<Aws::Http::HttpResponse> NetlibHttpClient::MakeRequest( Aws::Http::HttpRequest& request, Aws::Utils::RateLimits::RateLimiterInterface* readLimiter, Aws::Utils::RateLimits::RateLimiterInterface* writeLimiter) const { // AWS allows rate limiters to be passed around, but we are doing rate // limiting on the logger plugin side and so don't implement this. if (readLimiter != nullptr || writeLimiter != nullptr) { LOG(WARNING) << "Read/write limiters are unsupported"; } Aws::Http::URI uri = request.GetUri(); uri.SetPath(Aws::Http::URI::URLEncodePath(uri.GetPath())); Aws::String url = uri.GetURIString(); bn::http::client client = TLSTransport().getClient(); bn::http::client::request req(url); for (const auto& requestHeader : request.GetHeaders()) { req << bn::header(requestHeader.first, requestHeader.second); } std::string body; if (request.GetContentBody()) { std::stringstream ss; ss << request.GetContentBody()->rdbuf(); body = ss.str(); } auto response = std::make_shared<Standard::StandardHttpResponse>(request); try { bn::http::client::response resp; switch (request.GetMethod()) { case Aws::Http::HttpMethod::HTTP_GET: resp = client.get(req); if (resp.status() == 301 || resp.status() == 302) { VLOG(1) << "Attempting custom redirect as cpp-netlib does not support " "redirects"; for (const auto& header : resp.headers()) { if (header.first == "Location") { req.uri(header.second); resp = client.get(req); } } } break; case Aws::Http::HttpMethod::HTTP_POST: resp = client.post(req, body, request.GetContentType()); break; case Aws::Http::HttpMethod::HTTP_PUT: resp = client.put(req, body, request.GetContentType()); break; case Aws::Http::HttpMethod::HTTP_HEAD: resp = client.head(req); break; case Aws::Http::HttpMethod::HTTP_PATCH: LOG(ERROR) << "cpp-netlib does not support HTTP PATCH"; return nullptr; break; case Aws::Http::HttpMethod::HTTP_DELETE: resp = client.delete_(req); break; default: LOG(ERROR) << "Unrecognized HTTP Method used: " << static_cast<int>(request.GetMethod()); return nullptr; break; } response->SetResponseCode( static_cast<Aws::Http::HttpResponseCode>(resp.status())); for (const auto& header : resp.headers()) { if (header.first == "content-type") { response->SetContentType(header.second); } response->AddHeader(header.first, header.second); } response->GetResponseBody() << resp.body(); } catch (const std::exception& /*e*/) { LOG(ERROR) << "Exception making HTTP request to URL: " << url; return nullptr; } return response; }
std::shared_ptr<HttpResponse> WinSyncHttpClient::BuildSuccessResponse(const Aws::Http::HttpRequest& request, void* hHttpRequest, Aws::Utils::RateLimits::RateLimiterInterface* readLimiter) const { auto response = Aws::MakeShared<StandardHttpResponse>(GetLogTag(), request); Aws::StringStream ss; uint64_t read = 0; DoQueryHeaders(hHttpRequest, response, ss, read); if(readLimiter != nullptr && read > 0) { readLimiter->ApplyAndPayForCost(read); } Aws::Vector<Aws::String> rawHeaders = StringUtils::SplitOnLine(ss.str()); for (auto& header : rawHeaders) { Aws::Vector<Aws::String> keyValuePair = StringUtils::Split(header, ':'); if (keyValuePair.size() > 1) { Aws::String headerName = keyValuePair[0]; headerName = StringUtils::Trim(headerName.c_str()); Aws::String headerValue(keyValuePair[1]); for (unsigned i = 2; i < keyValuePair.size(); ++i) { headerValue += ":"; headerValue += keyValuePair[i]; } response->AddHeader(headerName, StringUtils::Trim(headerValue.c_str())); } } if (request.GetMethod() != HttpMethod::HTTP_HEAD) { char body[1024]; uint64_t bodySize = sizeof(body); read = 0; bool success = true; while (DoReadData(hHttpRequest, body, bodySize, read) && read > 0 && success) { response->GetResponseBody().write(body, read); if (read > 0) { if (readLimiter != nullptr) { readLimiter->ApplyAndPayForCost(read); } auto& receivedHandler = request.GetDataReceivedEventHandler(); if (receivedHandler) { receivedHandler(&request, response.get(), (long long)read); } } success = success && IsRequestProcessingEnabled(); } if(!success) { return nullptr; } } //go ahead and flush the response body. response->GetResponseBody().flush(); return response; }