/*
* Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
* A copy of the License is located at
*
* http://aws.amazon.com/apache2.0
*
* or in the "license" file accompanying this file. This file is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
* express or implied. See the License for the specific language governing
* permissions and limitations under the License.
*/
using Amazon.Runtime;
using Amazon.Runtime.Internal;
using Amazon.Runtime.Internal.Util;
using Amazon.Util;
using Aws.Crt.Http;
using System;
using System.Collections.Generic;
using System.IO;
namespace AWSSDK.Extensions.CrtIntegration
{
///
/// Utility functions for converting between the SDK and CRT HTTP Request objects
///
public class CrtHttpRequestConverter
{
// CRT calculates and sets these headers when signing, the SDK must not pass them in
// See s_forbidden_headers in aws_signing.c
private static readonly HashSet CrtForbiddenHeaders = new HashSet(StringComparer.OrdinalIgnoreCase)
{
HeaderKeys.XAmzContentSha256Header,
HeaderKeys.XAmzDateHeader,
HeaderKeys.AuthorizationHeader,
HeaderKeys.XAmzRegionSetHeader,
HeaderKeys.XAmzSecurityTokenHeader
};
// CRT calculates and sets these query params when signing, the SDK must not pass them in
// See s_forbidden_params in aws_signing.c
private static readonly HashSet CrtForbiddenQueryParams = new HashSet(StringComparer.OrdinalIgnoreCase)
{
HeaderKeys.XAmzSignature,
HeaderKeys.XAmzDateHeader,
HeaderKeys.XAmzCredential,
HeaderKeys.XAmzAlgorithm,
HeaderKeys.XAmzSignedHeadersHeader,
HeaderKeys.XAmzSecurityTokenHeader,
HeaderKeys.XAmzExpires
};
///
/// Converts the SDK's IRequest to the CRT's HttpRequest Object
///
/// SDK request
/// CRT request
public static Aws.Crt.Http.HttpRequest ConvertToCrtRequest(IRequest request)
{
// Remove any query params that CRT will set
if (request.ParameterCollection != null && request.ParameterCollection.Count > 0)
{
foreach (var queryParam in request.ParameterCollection.GetSortedParametersList())
{
if (CrtForbiddenQueryParams.Contains(queryParam.Key))
{
request.ParameterCollection.Remove(queryParam.Key);
}
}
}
var crtRequest = new Aws.Crt.Http.HttpRequest
{
// Using OriginalString here because ComposeUrl -> ResolveResourcePath ->
// JoinResourcePathSegments -> UrlEncode will escape some sequeneces (e.g. Ä -> %C3%84)
// but initializing that as a Uri will convert it back to Ä
Uri = AmazonServiceClient.ComposeUrl(request, false).OriginalString,
Method = request.HttpMethod
};
if (request.ContentStream != null)
{
if (request.ContentStream.CanSeek)
{
crtRequest.BodyStream = request.ContentStream;
}
else if (request.ContentStream is WrapperStream wrappedStream)
{
crtRequest.BodyStream = wrappedStream.GetSeekableBaseStream();
}
else
{
throw new AWSCommonRuntimeException("Unable to pass an HTTP request with a non-seekable content stream to CRT.");
}
}
else if (request.Content != null)
{
crtRequest.BodyStream = new MemoryStream(request.Content);
}
var headerList = new List(request.Headers.Count);
foreach (var header in request.Headers)
{
// Skip CRT-calculated headers
if (!CrtForbiddenHeaders.Contains(header.Key))
{
headerList.Add(new HttpHeader(header.Key, header.Value));
}
}
crtRequest.Headers = headerList.ToArray();
return crtRequest;
}
///
/// Copies the headers from a CRT requst back to an existing SDK request
///
/// SDK request
/// CRT request
public static void CopyHeadersFromCrtRequest(IRequest request, Aws.Crt.Http.HttpRequest crtRequest)
{
// Replace all of the SDK request's headers with the CRT headers (i.e. may now include signing-related headers)
request.Headers.Clear();
foreach (var header in crtRequest.Headers)
{
request.Headers.Add(header.Name, header.Value);
}
}
///
/// Extracts the list of signed headers from the 'Authorization' header set by CRT
///
/// Signed CRT HTTPRequest
/// semicolon-delimited list of signed headers
public static string ExtractSignedHeaders(Aws.Crt.Http.HttpRequest crtRequest)
{
const string startOfSignedHeadersPiece = "SignedHeaders=";
foreach (var header in crtRequest.Headers)
{
if (header.Name == HeaderKeys.AuthorizationHeader)
{
foreach (var piece in header.Value.Split(' '))
{
if (piece.StartsWith(startOfSignedHeadersPiece))
{
var signedHeaders = piece.Substring(startOfSignedHeadersPiece.Length);
signedHeaders = signedHeaders.TrimEnd(','); // Remove trailing , separating SignedHeaders from Signature
return signedHeaders;
}
}
}
}
return "";
}
}
}