From f656170159bb9cf8db7931847f8a61120a328fc7 Mon Sep 17 00:00:00 2001 From: Mykhailo Matviiv Date: Tue, 9 Jan 2024 16:58:33 +0100 Subject: [PATCH 1/2] Refactor request URI handling to support multiple services --- src/Benchmarks/Benchmarks/SignerBenchmark.cs | 4 +- .../Configs/RegionEndpoint.cs | 40 +++++++++++------- .../DynamoDbContext,BatchWriteItem.cs | 4 +- .../DynamoDbContext.BatchGetItem.cs | 4 +- .../DynamoDbContext.DeleteItem.cs | 10 ++--- .../DynamoDbContext.GetItem.cs | 8 ++-- .../DynamoDbContext.PutItem.cs | 4 +- .../DynamoDbContext/DynamoDbContext.Query.cs | 8 ++-- .../DynamoDbContext/DynamoDbContext.Scan.cs | 6 +-- .../DynamoDbContext.TransactGetItems.cs | 4 +- .../DynamoDbContext.TransactWriteItems.cs | 4 +- .../DynamoDbContext.UpdateItem.cs | 4 +- .../DynamoDbContext/DynamoDbContext.cs | 5 ++- .../DynamoDbLowLevelContext.cs | 22 +++++----- .../DynamoDbManagementContext.cs | 5 ++- .../Internal/Constants/ServiceNames.cs | 1 + .../NoAllocStringBuilderExtensions.cs | 2 +- src/EfficientDynamoDb/Internal/HttpApi.cs | 41 ++++++++++--------- .../Internal/Signing/AwsRequestSigner.cs | 5 ++- .../Builders/AuthorizationHeaderBuilder.cs | 4 +- .../Signing/Models/SigningMetadata.cs | 5 ++- 21 files changed, 105 insertions(+), 85 deletions(-) diff --git a/src/Benchmarks/Benchmarks/SignerBenchmark.cs b/src/Benchmarks/Benchmarks/SignerBenchmark.cs index da9cc554..0ab576b9 100644 --- a/src/Benchmarks/Benchmarks/SignerBenchmark.cs +++ b/src/Benchmarks/Benchmarks/SignerBenchmark.cs @@ -36,7 +36,7 @@ public async Task SetupAsync() }; var httpContent = new GetItemHttpContent(request, request.TableName, request.Key.PartitionKeyName!, request.Key.SortKeyName); - _httpRequest = new HttpRequestMessage(HttpMethod.Post, RegionEndpoint.USEast1.RequestUri) + _httpRequest = new HttpRequestMessage(HttpMethod.Post, RegionEndpoint.USEast1.BuildRequestUri(ServiceNames.DynamoDb)) { Content = httpContent }; @@ -52,7 +52,7 @@ public HttpRequestMessage SigningNative() CleanupHeaders(_httpRequest); var meta = new SigningMetadata(RegionEndpoint.USEast1, new AwsCredentials("accessKey", "secretKey"), DateTime.UtcNow, - _httpClient.DefaultRequestHeaders, null); + _httpClient.DefaultRequestHeaders, null, ServiceNames.DynamoDb); AwsRequestSigner.Sign(_httpRequest, _contentStream, meta); return _httpRequest; diff --git a/src/EfficientDynamoDb/Configs/RegionEndpoint.cs b/src/EfficientDynamoDb/Configs/RegionEndpoint.cs index 8a8700cc..9968072f 100644 --- a/src/EfficientDynamoDb/Configs/RegionEndpoint.cs +++ b/src/EfficientDynamoDb/Configs/RegionEndpoint.cs @@ -7,36 +7,46 @@ public class RegionEndpoint private const string ChinaEndpointFormat = "https://{0}.{1}.amazonaws.com.cn"; // 0 - Service Name, 1 - Region private const string RegularEndpointFormat = "https://{0}.{1}.amazonaws.com"; // 0 - Service Name, 1 - Region - internal const string ServiceName = "dynamodb"; + // internal const string ServiceName = "dynamodb"; + + private readonly string? _requestUriOverride; - internal string RequestUri { get; private set; } - public string Region { get; } public static RegionEndpoint Create(string region) { - var format = - region.StartsWith("cn-", StringComparison.OrdinalIgnoreCase) - ? ChinaEndpointFormat - : RegularEndpointFormat; - - return new RegionEndpoint(region, format); + return new RegionEndpoint(region); } public static RegionEndpoint Create(string region, string requestUri) { - return new RegionEndpoint(region, RegularEndpointFormat) { RequestUri = requestUri }; + return new RegionEndpoint(region, requestUri); } public static RegionEndpoint Create(RegionEndpoint region, string requestUri) { - return new RegionEndpoint(region.Region, RegularEndpointFormat) { RequestUri = requestUri }; + return new RegionEndpoint(region.Region, requestUri); } - private RegionEndpoint(string region, string uriFormat = RegularEndpointFormat) + private RegionEndpoint(string region) { Region = region; - RequestUri = string.Format(uriFormat, ServiceName, region); + } + + private RegionEndpoint(string region, string requestUriOverride) : this(region) + { + _requestUriOverride = requestUriOverride; + } + + internal string BuildRequestUri(string serviceName) + { + if (!string.IsNullOrEmpty(_requestUriOverride)) + return _requestUriOverride; + + var format = Region.StartsWith("cn-", StringComparison.OrdinalIgnoreCase) + ? ChinaEndpointFormat + : RegularEndpointFormat; + return string.Format(format, serviceName, Region); } /// @@ -122,12 +132,12 @@ private RegionEndpoint(string region, string uriFormat = RegularEndpointFormat) /// /// The China (Beijing) endpoint. /// - public static RegionEndpoint CNNorth1 => new RegionEndpoint("cn-north-1", ChinaEndpointFormat); + public static RegionEndpoint CNNorth1 => new RegionEndpoint("cn-north-1"); /// /// The China (Ningxia) endpoint. /// - public static RegionEndpoint CNNorthWest1 => new RegionEndpoint("cn-northwest-1", ChinaEndpointFormat); + public static RegionEndpoint CNNorthWest1 => new RegionEndpoint("cn-northwest-1"); /// /// The Europe (Frankfurt) endpoint. diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext,BatchWriteItem.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext,BatchWriteItem.cs index 1546af2a..6be1951e 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext,BatchWriteItem.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext,BatchWriteItem.cs @@ -19,7 +19,7 @@ internal async Task BatchWriteItemAsync(BuilderNode node, CancellationToken canc { using var httpContent = new BatchWriteItemHighLevelHttpContent(this, node, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var documentResult = await DynamoDbLowLevelContext.ReadDocumentAsync(response, BatchWriteItemParsingOptions.Instance, cancellationToken).ConfigureAwait(false); var attempt = 0; @@ -35,7 +35,7 @@ internal async Task BatchWriteItemAsync(BuilderNode node, CancellationToken canc await Task.Delay(delay, cancellationToken).ConfigureAwait(false); using var unprocessedHttpContent = new BatchWriteItemHttpContent(new BatchWriteItemRequest{RequestItems = unprocessedItems}, null); - using var unprocessedResponse = await Api.SendAsync(Config, unprocessedHttpContent, cancellationToken).ConfigureAwait(false); + using var unprocessedResponse = await Api.SendAsync(unprocessedHttpContent, cancellationToken).ConfigureAwait(false); documentResult = await DynamoDbLowLevelContext.ReadDocumentAsync(unprocessedResponse, BatchWriteItemParsingOptions.Instance, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.BatchGetItem.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.BatchGetItem.cs index eb9f27f9..c4bf7e3c 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.BatchGetItem.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.BatchGetItem.cs @@ -20,7 +20,7 @@ internal async Task> BatchGetItemAsync(BuilderNode node, { using var httpContent = new BatchGetItemHighLevelHttpContent(this, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadAsync>(response, cancellationToken).ConfigureAwait(false); List? items = null; if (result.Responses?.Count > 0) @@ -43,7 +43,7 @@ internal async Task> BatchGetItemAsync(BuilderNode node, await Task.Delay(delay, cancellationToken).ConfigureAwait(false); using var unprocessedHttpContent = new BatchGetItemHttpContent(new BatchGetItemRequest {RequestItems = result.UnprocessedKeys}, null); - using var unprocessedResponse = await Api.SendAsync(Config, unprocessedHttpContent, cancellationToken).ConfigureAwait(false); + using var unprocessedResponse = await Api.SendAsync(unprocessedHttpContent, cancellationToken).ConfigureAwait(false); result = await ReadAsync>(unprocessedResponse, cancellationToken).ConfigureAwait(false); if (result.Responses?.Count > 0) diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.DeleteItem.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.DeleteItem.cs index 83803872..baa434e2 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.DeleteItem.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.DeleteItem.cs @@ -30,7 +30,7 @@ public async Task DeleteItemAsync(object partitionKey, CancellationToke { using var httpContent = new DeleteItemByPkObjectHttpContent(this, partitionKey); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); await ReadAsync(response, cancellationToken).ConfigureAwait(false); } @@ -50,7 +50,7 @@ public async Task DeleteItemAsync(object partitionKey, object sortKey, { using var httpContent = new DeleteItemByPkAndSkObjectHttpContent(this, partitionKey, sortKey); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); await ReadAsync(response, cancellationToken).ConfigureAwait(false); } @@ -60,7 +60,7 @@ internal async Task> DeleteItemResponseAsync>(response, cancellationToken).ConfigureAwait(false); } @@ -70,7 +70,7 @@ internal async Task> DeleteItemResponseAsync>(response, cancellationToken).ConfigureAwait(false); return result.Attributes; @@ -80,7 +80,7 @@ internal async Task DeleteItemAsync(DdbClassInfo classInfo, BuilderNode node, Ca { using var httpContent = new DeleteItemHighLevelHttpContent(this, classInfo, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); await ReadAsync(response, cancellationToken).ConfigureAwait(false); } diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.GetItem.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.GetItem.cs index 92fc7f76..35a7e86b 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.GetItem.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.GetItem.cs @@ -32,7 +32,7 @@ public partial class DynamoDbContext { using var httpContent = new GetItemByPkObjectHttpContent(this, partitionKey); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadAsync>(response, cancellationToken).ConfigureAwait(false); return result.Item; @@ -55,7 +55,7 @@ public partial class DynamoDbContext { using var httpContent = new GetItemByPkAndSkObjectHttpContent(this, partitionKey, sortKey); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadAsync>(response, cancellationToken).ConfigureAwait(false); return result.Item; @@ -98,7 +98,7 @@ public partial class DynamoDbContext { using var httpContent = new GetItemHighLevelHttpContent(this, classInfo, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadAsync>(response, cancellationToken).ConfigureAwait(false); return result.Item; @@ -108,7 +108,7 @@ internal async Task> GetItemResponseAsync>(response, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.PutItem.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.PutItem.cs index af4a5c6d..76a4e428 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.PutItem.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.PutItem.cs @@ -31,7 +31,7 @@ internal async Task> PutItemResponseAsync>(response, cancellationToken).ConfigureAwait(false); } @@ -41,7 +41,7 @@ internal async Task> PutItemResponseAsync>(response, cancellationToken).ConfigureAwait(false); return result.Item; diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.Query.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.Query.cs index cd2e5f75..68734a1d 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.Query.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.Query.cs @@ -29,7 +29,7 @@ internal async Task> QueryListAsync(string? tabl var contentNode = isFirst ? node : new PaginationTokenNode(result?.PaginationToken, node); using var httpContent = new QueryHighLevelHttpContent(this, tableName, contentNode); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); result = await ReadAsync>(response, cancellationToken).ConfigureAwait(false); if (items == null) @@ -47,7 +47,7 @@ internal async Task> QueryPageAsync(string? tableN { using var httpContent = new QueryHighLevelHttpContent(this, tableName, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadAsync>(response, cancellationToken).ConfigureAwait(false); return new PagedResult(result.Items, result.PaginationToken); @@ -64,7 +64,7 @@ internal async IAsyncEnumerable> QueryAsyncEnumerable>(response, cancellationToken).ConfigureAwait(false); yield return result.Items; @@ -78,7 +78,7 @@ internal async Task> QueryAsync(string? ta { using var httpContent = new QueryHighLevelHttpContent(this, tableName, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); return await ReadAsync>(response, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.Scan.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.Scan.cs index 4363ae5e..45b5e35a 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.Scan.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.Scan.cs @@ -22,7 +22,7 @@ internal async Task> ScanPageAsync(string? tableNa { using var httpContent = new ScanHighLevelHttpContent(this, tableName, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadAsync>(response, cancellationToken).ConfigureAwait(false); return new PagedResult(result.Items, result.PaginationToken); @@ -38,7 +38,7 @@ internal async IAsyncEnumerable> ScanAsyncEnumerable>(response, cancellationToken).ConfigureAwait(false); yield return result.Items; @@ -54,7 +54,7 @@ internal async Task> ScanAsync(string? tabl { using var httpContent = new ScanHighLevelHttpContent(this, tableName, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); return await ReadAsync>(response, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.TransactGetItems.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.TransactGetItems.cs index 6601fa11..75eaa49f 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.TransactGetItems.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.TransactGetItems.cs @@ -19,7 +19,7 @@ public partial class DynamoDbContext { using var httpContent = new TransactGetItemsHighLevelHttpContent(this, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadAsync>(response, cancellationToken).ConfigureAwait(false); var entities = new List(result.Responses.Count); foreach (var item in result.Responses) @@ -32,7 +32,7 @@ internal async Task> TransactGetItemsRes { using var httpContent = new TransactGetItemsHighLevelHttpContent(this, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); return await ReadAsync>(response, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.TransactWriteItems.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.TransactWriteItems.cs index 56d005cb..8ae063f6 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.TransactWriteItems.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.TransactWriteItems.cs @@ -19,7 +19,7 @@ internal async Task TransactWriteItemsAsync(BuilderNode node, CancellationToken { using var httpContent = new TransactWriteItemsHighLevelHttpContent(this, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); await ReadAsync(response, cancellationToken).ConfigureAwait(false); } @@ -27,7 +27,7 @@ internal async Task TransactWriteItemsResponse { using var httpContent = new TransactWriteItemsHighLevelHttpContent(this, node); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); return await ReadAsync(response, cancellationToken).ConfigureAwait(false); } } diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.UpdateItem.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.UpdateItem.cs index 70db182e..d36101b2 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.UpdateItem.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.UpdateItem.cs @@ -21,7 +21,7 @@ internal async Task> UpdateItemResponseAsync>(response, cancellationToken).ConfigureAwait(false); } @@ -31,7 +31,7 @@ internal async Task> UpdateItemResponseAsync>(response, cancellationToken).ConfigureAwait(false); return result.Item; diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.cs index 2b448172..5531d626 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbContext.cs @@ -5,6 +5,7 @@ using EfficientDynamoDb.DocumentModel; using EfficientDynamoDb.Exceptions; using EfficientDynamoDb.Internal; +using EfficientDynamoDb.Internal.Constants; using EfficientDynamoDb.Internal.Converters.Json; using EfficientDynamoDb.Internal.Extensions; using EfficientDynamoDb.Internal.Reader; @@ -28,7 +29,7 @@ public partial class DynamoDbContext : IDynamoDbContext public DynamoDbContext(DynamoDbContextConfig config) { - _lowContext = new DynamoDbLowLevelContext(config, new HttpApi(config.HttpClientFactory)); + _lowContext = new DynamoDbLowLevelContext(config, new HttpApi(config, ServiceNames.DynamoDb)); } public T ToObject(Document document) where T : class => document.ToObject(Config.Metadata); @@ -37,7 +38,7 @@ public DynamoDbContext(DynamoDbContextConfig config) async Task IDynamoDbContext.ExecuteAsync(HttpContent httpContent, CancellationToken cancellationToken) where TResponse : class { - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); return await ReadAsync(response, cancellationToken).ConfigureAwait(false); } diff --git a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbLowLevelContext.cs b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbLowLevelContext.cs index 9888613f..062dee4b 100644 --- a/src/EfficientDynamoDb/DynamoDbContext/DynamoDbLowLevelContext.cs +++ b/src/EfficientDynamoDb/DynamoDbContext/DynamoDbLowLevelContext.cs @@ -56,7 +56,7 @@ public async Task BatchGetItemAsync(BatchGetItemRequest re { using var httpContent = new BatchGetItemHttpContent(request, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, BatchGetItemParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return BatchGetItemResponseParser.Parse(result!); @@ -66,7 +66,7 @@ public async Task BatchWriteItemAsync(BatchWriteItemRequ { using var httpContent = new BatchWriteItemHttpContent(request, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, BatchWriteItemParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return BatchWriteItemResponseParser.Parse(result!); @@ -76,7 +76,7 @@ public async Task QueryAsync(QueryRequest request, CancellationTo { using var httpContent = new QueryHttpContent(request, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, QueryParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return QueryResponseParser.Parse(result!); @@ -86,7 +86,7 @@ public async Task ScanAsync(ScanRequest request, CancellationToken { using var httpContent = new ScanHttpContent(request, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, QueryParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return ScanResponseParser.Parse(result!); @@ -96,7 +96,7 @@ public async Task TransactGetItemsAsync(TransactGetIte { using var httpContent = new TransactGetItemsHttpContent(request, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, TransactGetItemsParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return TransactGetItemsResponseParser.Parse(result!); @@ -106,7 +106,7 @@ public async Task PutItemAsync(PutItemRequest request, Cancella { using var httpContent = new PutItemHttpContent(request, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, PutItemParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return PutItemResponseParser.Parse(result); @@ -116,7 +116,7 @@ public async Task UpdateItemAsync(UpdateItemRequest request, { using var httpContent = await BuildHttpContentAsync(request).ConfigureAwait(false); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, UpdateItemParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return UpdateItemResponseParser.Parse(result); @@ -130,7 +130,7 @@ public async Task DeleteItemAsync(DeleteItemRequest request, using var httpContent = new DeleteItemHttpContent(request, pkName, skName, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, PutItemParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return DeleteItemResponseParser.Parse(result); @@ -140,7 +140,7 @@ public async Task TransactWriteItemsAsync(TransactWr { using var httpContent = new TransactWriteItemsHttpContent(request, Config.TableNamePrefix); - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, TransactWriteItemsParsingOptions.Instance, cancellationToken).ConfigureAwait(false); return TransactWriteItemsResponseParser.Parse(result); @@ -152,7 +152,7 @@ public async Task TransactWriteItemsAsync(TransactWr private async ValueTask GetItemInternalAsync(HttpContent httpContent, CancellationToken cancellationToken = default) { - using var response = await Api.SendAsync(Config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await Api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); var result = await ReadDocumentAsync(response, GetItemParsingOptions.Instance, cancellationToken).ConfigureAwait(false); // TODO: Consider removing root dictionary @@ -189,7 +189,7 @@ private async ValueTask BuildHttpContentAsync(UpdateItemRequest req async Task<(string Pk, string? Sk)> CreateKeyNamesTaskAsync(string table) { - var response = await Api.SendAsync(Config, new DescribeTableRequestHttpContent(Config.TableNamePrefix, tableName)) + var response = await Api.SendAsync(new DescribeTableRequestHttpContent(Config.TableNamePrefix, tableName)) .ConfigureAwait(false); var keySchema = response.Table.KeySchema; diff --git a/src/EfficientDynamoDb/DynamoDbManagementContext.cs b/src/EfficientDynamoDb/DynamoDbManagementContext.cs index 6afe46a8..5e699175 100644 --- a/src/EfficientDynamoDb/DynamoDbManagementContext.cs +++ b/src/EfficientDynamoDb/DynamoDbManagementContext.cs @@ -1,6 +1,7 @@ using System.Threading; using System.Threading.Tasks; using EfficientDynamoDb.Internal; +using EfficientDynamoDb.Internal.Constants; using EfficientDynamoDb.Internal.Operations.DescribeTable; using EfficientDynamoDb.Operations.DescribeTable; @@ -13,7 +14,7 @@ public class DynamoDbManagementContext public DynamoDbManagementContext(DynamoDbContextConfig config) { - _api = new HttpApi(config.HttpClientFactory); + _api = new HttpApi(config, ServiceNames.DynamoDb); _config = config; } @@ -21,7 +22,7 @@ public async Task DescribeTableAsync(string tableName, Ca { var httpContent = new DescribeTableRequestHttpContent(_config.TableNamePrefix, tableName); - var response = await _api.SendAsync(_config, httpContent, cancellationToken).ConfigureAwait(false); + var response = await _api.SendAsync(httpContent, cancellationToken).ConfigureAwait(false); return response; } diff --git a/src/EfficientDynamoDb/Internal/Constants/ServiceNames.cs b/src/EfficientDynamoDb/Internal/Constants/ServiceNames.cs index 67476430..2341cdba 100644 --- a/src/EfficientDynamoDb/Internal/Constants/ServiceNames.cs +++ b/src/EfficientDynamoDb/Internal/Constants/ServiceNames.cs @@ -3,6 +3,7 @@ namespace EfficientDynamoDb.Internal.Constants internal static class ServiceNames { public const string DynamoDb = "dynamodb"; + public const string DynamoDbStreams = "streams.dynamodb"; public const string S3 = "s3"; } } \ No newline at end of file diff --git a/src/EfficientDynamoDb/Internal/Extensions/NoAllocStringBuilderExtensions.cs b/src/EfficientDynamoDb/Internal/Extensions/NoAllocStringBuilderExtensions.cs index f2d226d4..f4063d52 100644 --- a/src/EfficientDynamoDb/Internal/Extensions/NoAllocStringBuilderExtensions.cs +++ b/src/EfficientDynamoDb/Internal/Extensions/NoAllocStringBuilderExtensions.cs @@ -15,7 +15,7 @@ public static void AppendCredentialScope(this ref NoAllocStringBuilder builder, builder.Append('/'); builder.Append(metadata.RegionEndpoint.Region); builder.Append('/'); - builder.Append(RegionEndpoint.ServiceName); + builder.Append(metadata.ServiceName); builder.Append('/'); builder.Append(SigningConstants.AwsSignTerminator); } diff --git a/src/EfficientDynamoDb/Internal/HttpApi.cs b/src/EfficientDynamoDb/Internal/HttpApi.cs index 58211a54..2a62de32 100644 --- a/src/EfficientDynamoDb/Internal/HttpApi.cs +++ b/src/EfficientDynamoDb/Internal/HttpApi.cs @@ -3,7 +3,6 @@ using System.Text.Json; using System.Threading; using System.Threading.Tasks; -using EfficientDynamoDb.Configs.Http; using EfficientDynamoDb.Exceptions; using EfficientDynamoDb.Internal.JsonConverters; using EfficientDynamoDb.Internal.Signing; @@ -13,14 +12,18 @@ namespace EfficientDynamoDb.Internal { internal class HttpApi { - private readonly IHttpClientFactory _httpClientFactory; + private readonly DynamoDbContextConfig _config; + private readonly string _serviceName; + private readonly string _requestUri; - public HttpApi(IHttpClientFactory httpClientFactory) + public HttpApi(DynamoDbContextConfig config, string serviceName) { - _httpClientFactory = httpClientFactory; + _config = config; + _serviceName = serviceName; + _requestUri = config.RegionEndpoint.BuildRequestUri(serviceName); } - public async ValueTask SendAsync(DynamoDbContextConfig config, HttpContent httpContent, CancellationToken cancellationToken = default) + public async ValueTask SendAsync(HttpContent httpContent, CancellationToken cancellationToken = default) { try { @@ -35,25 +38,25 @@ public async ValueTask SendAsync(DynamoDbContextConfig conf TimeSpan delay; try { - using var request = new HttpRequestMessage(HttpMethod.Post, config.RegionEndpoint.RequestUri) + using var request = new HttpRequestMessage(HttpMethod.Post, _requestUri) { Content = httpContent }; try { - var httpClient = _httpClientFactory.CreateHttpClient(); + var httpClient = _config.HttpClientFactory.CreateHttpClient(); var stream = await httpContent.ReadAsStreamAsync().ConfigureAwait(false); - var credentials = await config.CredentialsProvider.GetCredentialsAsync(cancellationToken).ConfigureAwait(false); + var credentials = await _config.CredentialsProvider.GetCredentialsAsync(cancellationToken).ConfigureAwait(false); - var metadata = new SigningMetadata(config.RegionEndpoint, credentials, DateTime.UtcNow, httpClient.DefaultRequestHeaders, - httpClient.BaseAddress); + var metadata = new SigningMetadata(_config.RegionEndpoint, credentials, DateTime.UtcNow, httpClient.DefaultRequestHeaders, + httpClient.BaseAddress, _serviceName); AwsRequestSigner.Sign(request, (RecyclableMemoryStream) stream, in metadata); var response = await httpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken).ConfigureAwait(false); if (!response.IsSuccessStatusCode) - await ErrorHandler.ProcessErrorAsync(config.Metadata, response, cancellationToken).ConfigureAwait(false); + await ErrorHandler.ProcessErrorAsync(_config.Metadata, response, cancellationToken).ConfigureAwait(false); return response; } @@ -64,32 +67,32 @@ public async ValueTask SendAsync(DynamoDbContextConfig conf } catch (InternalServerErrorException) { - if (!config.RetryStrategies.InternalServerErrorStrategy.TryGetRetryDelay(internalServerErrorRetries++, out delay)) + if (!_config.RetryStrategies.InternalServerErrorStrategy.TryGetRetryDelay(internalServerErrorRetries++, out delay)) throw; } catch (LimitExceededException) { - if (!config.RetryStrategies.LimitExceededStrategy.TryGetRetryDelay(limitExceededRetries++, out delay)) + if (!_config.RetryStrategies.LimitExceededStrategy.TryGetRetryDelay(limitExceededRetries++, out delay)) throw; } catch (ProvisionedThroughputExceededException) { - if (!config.RetryStrategies.ProvisionedThroughputExceededStrategy.TryGetRetryDelay(provisionedThroughputExceededRetries++, out delay)) + if (!_config.RetryStrategies.ProvisionedThroughputExceededStrategy.TryGetRetryDelay(provisionedThroughputExceededRetries++, out delay)) throw; } catch (RequestLimitExceededException) { - if (!config.RetryStrategies.RequestLimitExceededStrategy.TryGetRetryDelay(requestLimitExceededRetries++, out delay)) + if (!_config.RetryStrategies.RequestLimitExceededStrategy.TryGetRetryDelay(requestLimitExceededRetries++, out delay)) throw; } catch (ServiceUnavailableException) { - if (!config.RetryStrategies.ServiceUnavailableStrategy.TryGetRetryDelay(serviceUnavailableRetries++, out delay)) + if (!_config.RetryStrategies.ServiceUnavailableStrategy.TryGetRetryDelay(serviceUnavailableRetries++, out delay)) throw; } catch (ThrottlingException) { - if (!config.RetryStrategies.ThrottlingStrategy.TryGetRetryDelay(throttlingRetries++, out delay)) + if (!_config.RetryStrategies.ThrottlingStrategy.TryGetRetryDelay(throttlingRetries++, out delay)) throw; } @@ -102,9 +105,9 @@ public async ValueTask SendAsync(DynamoDbContextConfig conf } } - public async ValueTask SendAsync(DynamoDbContextConfig config, HttpContent httpContent, CancellationToken cancellationToken = default) + public async ValueTask SendAsync(HttpContent httpContent, CancellationToken cancellationToken = default) { - using var response = await SendAsync(config, httpContent, cancellationToken).ConfigureAwait(false); + using var response = await SendAsync(httpContent, cancellationToken).ConfigureAwait(false); await using var responseStream = await response.Content.ReadAsStreamAsync().ConfigureAwait(false); return (await JsonSerializer.DeserializeAsync(responseStream, diff --git a/src/EfficientDynamoDb/Internal/Signing/AwsRequestSigner.cs b/src/EfficientDynamoDb/Internal/Signing/AwsRequestSigner.cs index 498b9178..aae4047d 100644 --- a/src/EfficientDynamoDb/Internal/Signing/AwsRequestSigner.cs +++ b/src/EfficientDynamoDb/Internal/Signing/AwsRequestSigner.cs @@ -16,7 +16,7 @@ internal static class AwsRequestSigner public static void Sign(HttpRequestMessage request, RecyclableMemoryStream content, in SigningMetadata metadata) { - ValidateInput(request, RegionEndpoint.ServiceName); + ValidateInput(request, metadata.ServiceName); UpdateRequestUri(metadata.BaseAddress, request); request.Headers.Add(HeaderKeys.XAmzDateHeader, metadata.TimestampIso8601BasicDateTimeString); @@ -36,7 +36,8 @@ public static void Sign(HttpRequestMessage request, RecyclableMemoryStream conte { CanonicalRequestBuilder.Build(request, in contentHash, in metadata, ref builder, ref signedHeadersBuilder); StringToSignBuilder.Build(ref builder, in metadata); - var authorizationHeader = AuthorizationHeaderBuilder.Build(ref builder, ref signedHeadersBuilder, in metadata); + var authorizationHeader = AuthorizationHeaderBuilder. + Build(ref builder, ref signedHeadersBuilder, in metadata); request.Headers.TryAddWithoutValidation(HeaderKeys.AuthorizationHeader, authorizationHeader); } diff --git a/src/EfficientDynamoDb/Internal/Signing/Builders/AuthorizationHeaderBuilder.cs b/src/EfficientDynamoDb/Internal/Signing/Builders/AuthorizationHeaderBuilder.cs index 869e443a..9d989823 100644 --- a/src/EfficientDynamoDb/Internal/Signing/Builders/AuthorizationHeaderBuilder.cs +++ b/src/EfficientDynamoDb/Internal/Signing/Builders/AuthorizationHeaderBuilder.cs @@ -54,8 +54,8 @@ public static string Build(ref NoAllocStringBuilder builder, ref NoAllocStringBu ComputeKeyedSha256Hash(algorithm, ref sourceDataBuffer, ref destinationDataBuffer, ref keysBuffer, metadata.RegionEndpoint.Region); algorithm.Key = keysBuffer; - - ComputeKeyedSha256Hash(algorithm, ref sourceDataBuffer, ref destinationDataBuffer, ref keysBuffer, RegionEndpoint.ServiceName); + + ComputeKeyedSha256Hash(algorithm, ref sourceDataBuffer, ref destinationDataBuffer, ref keysBuffer, metadata.ServiceName); algorithm.Key = keysBuffer; ComputeKeyedSha256Hash(algorithm, ref sourceDataBuffer, ref destinationDataBuffer, ref keysBuffer, SigningConstants.AwsSignTerminator); diff --git a/src/EfficientDynamoDb/Internal/Signing/Models/SigningMetadata.cs b/src/EfficientDynamoDb/Internal/Signing/Models/SigningMetadata.cs index 143a27af..b6e6cec0 100644 --- a/src/EfficientDynamoDb/Internal/Signing/Models/SigningMetadata.cs +++ b/src/EfficientDynamoDb/Internal/Signing/Models/SigningMetadata.cs @@ -23,9 +23,11 @@ internal readonly struct SigningMetadata public bool HasDefaultRequestHeaders { get; } public Uri? BaseAddress { get; } + + public string ServiceName { get; } public SigningMetadata(RegionEndpoint regionEndpoint, AwsCredentials credentials, in DateTime timestamp, - HttpRequestHeaders defaultRequestHeaders, Uri? baseAddress) + HttpRequestHeaders defaultRequestHeaders, Uri? baseAddress, string serviceName) { RegionEndpoint = regionEndpoint; Credentials = credentials; @@ -33,6 +35,7 @@ public SigningMetadata(RegionEndpoint regionEndpoint, AwsCredentials credentials DefaultRequestHeaders = defaultRequestHeaders; HasDefaultRequestHeaders = defaultRequestHeaders.Any(); BaseAddress = baseAddress; + ServiceName = serviceName; } } } \ No newline at end of file From cf115369c0be0d2fe9eb7b73f6bf3f590fc561f1 Mon Sep 17 00:00:00 2001 From: Mykhailo Matviiv Date: Sun, 21 Jan 2024 19:10:03 +0100 Subject: [PATCH 2/2] Fix service name signing --- .../Internal/Extensions/NoAllocStringBuilderExtensions.cs | 3 ++- .../Internal/Signing/Builders/AuthorizationHeaderBuilder.cs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/EfficientDynamoDb/Internal/Extensions/NoAllocStringBuilderExtensions.cs b/src/EfficientDynamoDb/Internal/Extensions/NoAllocStringBuilderExtensions.cs index f4063d52..bdae2b60 100644 --- a/src/EfficientDynamoDb/Internal/Extensions/NoAllocStringBuilderExtensions.cs +++ b/src/EfficientDynamoDb/Internal/Extensions/NoAllocStringBuilderExtensions.cs @@ -1,5 +1,6 @@ using System.Runtime.CompilerServices; using EfficientDynamoDb.Configs; +using EfficientDynamoDb.Internal.Constants; using EfficientDynamoDb.Internal.Core; using EfficientDynamoDb.Internal.Signing; using EfficientDynamoDb.Internal.Signing.Constants; @@ -15,7 +16,7 @@ public static void AppendCredentialScope(this ref NoAllocStringBuilder builder, builder.Append('/'); builder.Append(metadata.RegionEndpoint.Region); builder.Append('/'); - builder.Append(metadata.ServiceName); + builder.Append(ServiceNames.DynamoDb); builder.Append('/'); builder.Append(SigningConstants.AwsSignTerminator); } diff --git a/src/EfficientDynamoDb/Internal/Signing/Builders/AuthorizationHeaderBuilder.cs b/src/EfficientDynamoDb/Internal/Signing/Builders/AuthorizationHeaderBuilder.cs index 9d989823..22f43a29 100644 --- a/src/EfficientDynamoDb/Internal/Signing/Builders/AuthorizationHeaderBuilder.cs +++ b/src/EfficientDynamoDb/Internal/Signing/Builders/AuthorizationHeaderBuilder.cs @@ -55,7 +55,7 @@ public static string Build(ref NoAllocStringBuilder builder, ref NoAllocStringBu ComputeKeyedSha256Hash(algorithm, ref sourceDataBuffer, ref destinationDataBuffer, ref keysBuffer, metadata.RegionEndpoint.Region); algorithm.Key = keysBuffer; - ComputeKeyedSha256Hash(algorithm, ref sourceDataBuffer, ref destinationDataBuffer, ref keysBuffer, metadata.ServiceName); + ComputeKeyedSha256Hash(algorithm, ref sourceDataBuffer, ref destinationDataBuffer, ref keysBuffer, ServiceNames.DynamoDb); algorithm.Key = keysBuffer; ComputeKeyedSha256Hash(algorithm, ref sourceDataBuffer, ref destinationDataBuffer, ref keysBuffer, SigningConstants.AwsSignTerminator);