From 76096ad876d5f4ce733d3a2762fd5163828d3160 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Fri, 23 Aug 2024 13:21:54 -0700 Subject: [PATCH 1/7] Auto - Update S3 Ruleset & Partition (#450) --- .../aws_s3_endpoint_resolver_partition.c | 315 +++++++++--------- 1 file changed, 159 insertions(+), 156 deletions(-) diff --git a/source/s3_endpoint_resolver/aws_s3_endpoint_resolver_partition.c b/source/s3_endpoint_resolver/aws_s3_endpoint_resolver_partition.c index bc0f02467..abb6880fb 100644 --- a/source/s3_endpoint_resolver/aws_s3_endpoint_resolver_partition.c +++ b/source/s3_endpoint_resolver/aws_s3_endpoint_resolver_partition.c @@ -54,167 +54,170 @@ static const char s_generated_array[] = { 'i', 'f', 'i', 'c', ' ', '(', 'J', 'a', 'k', 'a', 'r', 't', 'a', ')', '"', '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '4', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', - 'i', 'c', ' ', '(', 'M', 'e', 'l', 'b', 'o', 'u', 'r', 'n', 'e', ')', '"', '}', ',', '"', 'a', 'w', - 's', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', - 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'g', - 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'c', 'a', '-', 'c', - 'e', 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', - 'i', 'o', 'n', '"', ':', '"', 'C', 'a', 'n', 'a', 'd', 'a', ' ', '(', 'C', 'e', 'n', 't', 'r', 'a', - 'l', ')', '"', '}', ',', '"', 'c', 'a', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', - 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'a', 'n', 'a', 'd', 'a', ' ', - 'W', 'e', 's', 't', ' ', '(', 'C', 'a', 'l', 'g', 'a', 'r', 'y', ')', '"', '}', ',', '"', 'e', 'u', + 'i', 'c', ' ', '(', 'M', 'e', 'l', 'b', 'o', 'u', 'r', 'n', 'e', ')', '"', '}', ',', '"', 'a', 'p', + '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '5', '"', ':', '{', '"', 'd', 'e', 's', 'c', + 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', + 'i', 'c', ' ', '(', 'M', 'a', 'l', 'a', 'y', 's', 'i', 'a', ')', '"', '}', ',', '"', 'a', 'w', 's', + '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', + 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'g', 'l', + 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'c', 'a', '-', 'c', 'e', + 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', + 'o', 'n', '"', ':', '"', 'C', 'a', 'n', 'a', 'd', 'a', ' ', '(', 'C', 'e', 'n', 't', 'r', 'a', 'l', + ')', '"', '}', ',', '"', 'c', 'a', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', + 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'a', 'n', 'a', 'd', 'a', ' ', 'W', + 'e', 's', 't', ' ', '(', 'C', 'a', 'l', 'g', 'a', 'r', 'y', ')', '"', '}', ',', '"', 'e', 'u', '-', + 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', + 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'F', 'r', 'a', 'n', 'k', + 'f', 'u', 'r', 't', ')', '"', '}', ',', '"', 'e', 'u', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', + '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', + 'u', 'r', 'o', 'p', 'e', ' ', '(', 'Z', 'u', 'r', 'i', 'c', 'h', ')', '"', '}', ',', '"', 'e', 'u', + '-', 'n', 'o', 'r', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', + 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'S', 't', 'o', 'c', 'k', 'h', + 'o', 'l', 'm', ')', '"', '}', ',', '"', 'e', 'u', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', + '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', + 'p', 'e', ' ', '(', 'M', 'i', 'l', 'a', 'n', ')', '"', '}', ',', '"', 'e', 'u', '-', 's', 'o', 'u', + 't', 'h', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', + ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'S', 'p', 'a', 'i', 'n', ')', '"', '}', ',', '"', + 'e', 'u', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', + 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'I', 'r', 'e', 'l', 'a', + 'n', 'd', ')', '"', '}', ',', '"', 'e', 'u', '-', 'w', 'e', 's', 't', '-', '2', '"', ':', '{', '"', + 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', + ' ', '(', 'L', 'o', 'n', 'd', 'o', 'n', ')', '"', '}', ',', '"', 'e', 'u', '-', 'w', 'e', 's', 't', + '-', '3', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', + 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'P', 'a', 'r', 'i', 's', ')', '"', '}', ',', '"', 'i', 'l', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', - 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'F', 'r', 'a', 'n', - 'k', 'f', 'u', 'r', 't', ')', '"', '}', ',', '"', 'e', 'u', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', - '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', - 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'Z', 'u', 'r', 'i', 'c', 'h', ')', '"', '}', ',', '"', 'e', - 'u', '-', 'n', 'o', 'r', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', - 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'S', 't', 'o', 'c', 'k', - 'h', 'o', 'l', 'm', ')', '"', '}', ',', '"', 'e', 'u', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', - ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', - 'o', 'p', 'e', ' ', '(', 'M', 'i', 'l', 'a', 'n', ')', '"', '}', ',', '"', 'e', 'u', '-', 's', 'o', - 'u', 't', 'h', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', - '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'S', 'p', 'a', 'i', 'n', ')', '"', '}', ',', - '"', 'e', 'u', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', - 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'I', 'r', 'e', 'l', - 'a', 'n', 'd', ')', '"', '}', ',', '"', 'e', 'u', '-', 'w', 'e', 's', 't', '-', '2', '"', ':', '{', - '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', - 'e', ' ', '(', 'L', 'o', 'n', 'd', 'o', 'n', ')', '"', '}', ',', '"', 'e', 'u', '-', 'w', 'e', 's', - 't', '-', '3', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', - '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'P', 'a', 'r', 'i', 's', ')', '"', '}', ',', '"', 'i', - 'l', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', - 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'I', 's', 'r', 'a', 'e', 'l', ' ', '(', 'T', 'e', 'l', - ' ', 'A', 'v', 'i', 'v', ')', '"', '}', ',', '"', 'm', 'e', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', + 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'I', 's', 'r', 'a', 'e', 'l', ' ', '(', 'T', 'e', 'l', ' ', + 'A', 'v', 'i', 'v', ')', '"', '}', ',', '"', 'm', 'e', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', + '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'M', + 'i', 'd', 'd', 'l', 'e', ' ', 'E', 'a', 's', 't', ' ', '(', 'U', 'A', 'E', ')', '"', '}', ',', '"', + 'm', 'e', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', + 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'M', 'i', 'd', 'd', 'l', 'e', ' ', 'E', 'a', 's', 't', ' ', + '(', 'B', 'a', 'h', 'r', 'a', 'i', 'n', ')', '"', '}', ',', '"', 's', 'a', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', - 'M', 'i', 'd', 'd', 'l', 'e', ' ', 'E', 'a', 's', 't', ' ', '(', 'U', 'A', 'E', ')', '"', '}', ',', - '"', 'm', 'e', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', - 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'M', 'i', 'd', 'd', 'l', 'e', ' ', 'E', 'a', 's', 't', - ' ', '(', 'B', 'a', 'h', 'r', 'a', 'i', 'n', ')', '"', '}', ',', '"', 's', 'a', '-', 'e', 'a', 's', - 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', - '"', 'S', 'o', 'u', 't', 'h', ' ', 'A', 'm', 'e', 'r', 'i', 'c', 'a', ' ', '(', 'S', 'a', 'o', ' ', - 'P', 'a', 'u', 'l', 'o', ')', '"', '}', ',', '"', 'u', 's', '-', 'e', 'a', 's', 't', '-', '1', '"', - ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', - 'E', 'a', 's', 't', ' ', '(', 'N', '.', ' ', 'V', 'i', 'r', 'g', 'i', 'n', 'i', 'a', ')', '"', '}', - ',', '"', 'u', 's', '-', 'e', 'a', 's', 't', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', - 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'E', 'a', 's', 't', ' ', '(', 'O', 'h', - 'i', 'o', ')', '"', '}', ',', '"', 'u', 's', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', - 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'W', 'e', 's', - 't', ' ', '(', 'N', '.', ' ', 'C', 'a', 'l', 'i', 'f', 'o', 'r', 'n', 'i', 'a', ')', '"', '}', ',', - '"', 'u', 's', '-', 'w', 'e', 's', 't', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', - 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'W', 'e', 's', 't', ' ', '(', 'O', 'r', 'e', - 'g', 'o', 'n', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', - 'c', 'n', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', - 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'm', 'a', 'z', 'o', 'n', 'a', 'w', 's', '.', 'c', 'o', - 'm', '.', 'c', 'n', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', - 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'p', 'i', '.', 'a', 'm', 'a', 'z', 'o', 'n', 'w', 'e', - 'b', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', '.', 'c', 'o', 'm', '.', 'c', 'n', '"', ',', '"', 'i', - 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', - ':', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', 'w', 'e', 's', 't', '-', '1', '"', ',', '"', 'n', - 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'c', 'n', '"', ',', '"', 's', 'u', 'p', 'p', 'o', - 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', ',', '"', - 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', - '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'c', 'n', '\\', '\\', - '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', - 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'c', 'n', '-', 'g', 'l', 'o', 'b', 'a', 'l', - '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', - 'S', ' ', 'C', 'h', 'i', 'n', 'a', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', - 'n', '"', '}', ',', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', - 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'h', 'i', 'n', 'a', ' ', '(', - 'B', 'e', 'i', 'j', 'i', 'n', 'g', ')', '"', '}', ',', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', - 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', - 'n', '"', ':', '"', 'C', 'h', 'i', 'n', 'a', ' ', '(', 'N', 'i', 'n', 'g', 'x', 'i', 'a', ')', '"', - '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'u', 's', '-', 'g', 'o', - 'v', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', + 'S', 'o', 'u', 't', 'h', ' ', 'A', 'm', 'e', 'r', 'i', 'c', 'a', ' ', '(', 'S', 'a', 'o', ' ', 'P', + 'a', 'u', 'l', 'o', ')', '"', '}', ',', '"', 'u', 's', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', + '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'E', + 'a', 's', 't', ' ', '(', 'N', '.', ' ', 'V', 'i', 'r', 'g', 'i', 'n', 'i', 'a', ')', '"', '}', ',', + '"', 'u', 's', '-', 'e', 'a', 's', 't', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', + 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'E', 'a', 's', 't', ' ', '(', 'O', 'h', 'i', + 'o', ')', '"', '}', ',', '"', 'u', 's', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'W', 'e', 's', 't', + ' ', '(', 'N', '.', ' ', 'C', 'a', 'l', 'i', 'f', 'o', 'r', 'n', 'i', 'a', ')', '"', '}', ',', '"', + 'u', 's', '-', 'w', 'e', 's', 't', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', + 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'W', 'e', 's', 't', ' ', '(', 'O', 'r', 'e', 'g', + 'o', 'n', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'c', + 'n', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'm', 'a', 'z', 'o', 'n', 'a', 'w', 's', '.', 'c', 'o', 'm', - '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', - 'x', '"', ':', '"', 'a', 'p', 'i', '.', 'a', 'w', 's', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', - 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', - 'g', 'o', 'v', '-', 'w', 'e', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', - 'a', 'w', 's', '-', 'u', 's', '-', 'g', 'o', 'v', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', - 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', ',', '"', 's', 'u', - 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', - 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'g', - 'o', 'v', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', - 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'u', 's', '-', 'g', 'o', - 'v', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', - 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', 'o', 'u', 'd', ' ', '(', - 'U', 'S', ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', - '"', 'u', 's', '-', 'g', 'o', 'v', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', - 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', - 'l', 'o', 'u', 'd', ' ', '(', 'U', 'S', '-', 'E', 'a', 's', 't', ')', '"', '}', ',', '"', 'u', 's', - '-', 'g', 'o', 'v', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', - 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', 'o', 'u', - 'd', ' ', '(', 'U', 'S', '-', 'W', 'e', 's', 't', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', - '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', - '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', '2', 's', '.', - 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', - 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', '2', 's', '.', 'i', 'c', '.', 'g', 'o', 'v', - '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', - 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'e', 'a', 's', 't', '-', '1', '"', - ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '"', ',', '"', 's', - 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', - 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', - 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', - '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', - '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', - 's', '-', 'i', 's', 'o', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', - 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'I', 'S', 'O', ' ', '(', 'U', + '.', 'c', 'n', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', + 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'p', 'i', '.', 'a', 'm', 'a', 'z', 'o', 'n', 'w', 'e', 'b', + 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', '.', 'c', 'o', 'm', '.', 'c', 'n', '"', ',', '"', 'i', 'm', + 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', + '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', 'w', 'e', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', + 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'c', 'n', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', + 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', ',', '"', 's', + 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', + 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'c', 'n', '\\', '\\', '-', + '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', + 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'c', 'n', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', + ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', + ' ', 'C', 'h', 'i', 'n', 'a', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', + '"', '}', ',', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', + 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'h', 'i', 'n', 'a', ' ', '(', 'B', + 'e', 'i', 'j', 'i', 'n', 'g', ')', '"', '}', ',', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', 'w', + 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', + '"', ':', '"', 'C', 'h', 'i', 'n', 'a', ' ', '(', 'N', 'i', 'n', 'g', 'x', 'i', 'a', ')', '"', '}', + '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'u', 's', '-', 'g', 'o', 'v', + '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', + 'f', 'i', 'x', '"', ':', '"', 'a', 'm', 'a', 'z', 'o', 'n', 'a', 'w', 's', '.', 'c', 'o', 'm', '"', + ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', + '"', ':', '"', 'a', 'p', 'i', '.', 'a', 'w', 's', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', + 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'g', + 'o', 'v', '-', 'w', 'e', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', + 'w', 's', '-', 'u', 's', '-', 'g', 'o', 'v', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', + 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', ',', '"', 's', 'u', 'p', + 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', + 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'g', 'o', + 'v', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', + 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'u', 's', '-', 'g', 'o', 'v', + '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', + 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', 'o', 'u', 'd', ' ', '(', 'U', 'S', ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', - 'u', 's', '-', 'i', 's', 'o', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', - 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'I', 'S', 'O', ' ', 'E', 'a', - 's', 't', '"', '}', ',', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'w', 'e', 's', 't', '-', '1', '"', - ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', - 'I', 'S', 'O', ' ', 'W', 'E', 'S', 'T', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', - 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'b', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', - ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 's', 'c', '2', 's', '.', - 's', 'g', 'o', 'v', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', - 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 's', 'c', '2', 's', '.', 's', 'g', 'o', - 'v', '.', 'g', 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', - 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', 'b', '-', 'e', - 'a', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', - 's', 'o', '-', 'b', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', - 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', - 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', - 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', 'b', '\\', '\\', - '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', - 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'b', '-', 'g', 'l', 'o', - 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', - '"', 'A', 'W', 'S', ' ', 'I', 'S', 'O', 'B', ' ', '(', 'U', 'S', ')', ' ', 'g', 'l', 'o', 'b', 'a', - 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'u', 's', '-', 'i', 's', 'o', 'b', '-', - 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', - 'n', '"', ':', '"', 'U', 'S', ' ', 'I', 'S', 'O', 'B', ' ', 'E', 'a', 's', 't', ' ', '(', 'O', 'h', - 'i', 'o', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'i', - 's', 'o', '-', 'e', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', - 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 'l', 'o', 'u', 'd', '.', 'a', 'd', 'c', '-', - 'e', '.', 'u', 'k', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', - 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 'l', 'o', 'u', 'd', '.', 'a', 'd', 'c', '-', 'e', '.', - 'u', 'k', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', - 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'e', 'u', '-', 'i', 's', 'o', 'e', '-', 'w', 'e', 's', 't', - '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', - 'e', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', - 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', - 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', - 'e', 'x', '"', ':', '"', '^', 'e', 'u', '\\', '\\', '-', 'i', 's', 'o', 'e', '\\', '\\', '-', '\\', '\\', - 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', - '"', ':', '{', '"', 'e', 'u', '-', 'i', 's', 'o', 'e', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', - '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'U', ' ', 'I', - 'S', 'O', 'E', ' ', 'W', 'e', 's', 't', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', - 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'f', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', - ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 's', 'p', '.', 'h', - 'c', 'i', '.', 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', - 'k', 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 's', 'p', '.', 'h', 'c', 'i', - '.', 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', - 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', 'f', - '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', - 's', '-', 'i', 's', 'o', '-', 'f', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', - 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', - 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', - 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', - 'f', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', - 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '}', '}', ']', ',', '"', 'v', 'e', 'r', 's', 'i', 'o', - 'n', '"', ':', '"', '1', '.', '1', '"', '}'}; + 'u', 's', '-', 'g', 'o', 'v', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', + 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', + 'o', 'u', 'd', ' ', '(', 'U', 'S', '-', 'E', 'a', 's', 't', ')', '"', '}', ',', '"', 'u', 's', '-', + 'g', 'o', 'v', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', + 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', 'o', 'u', 'd', + ' ', '(', 'U', 'S', '-', 'W', 'e', 's', 't', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', + ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', + ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', '2', 's', '.', 'i', + 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', + 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', '2', 's', '.', 'i', 'c', '.', 'g', 'o', 'v', '"', + ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', + 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'e', 'a', 's', 't', '-', '1', '"', ',', + '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '"', ',', '"', 's', 'u', + 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', + 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', + 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', + 'u', 's', '\\', '\\', '-', 'i', 's', 'o', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', + 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', + '-', 'i', 's', 'o', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', + 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'I', 'S', 'O', ' ', '(', 'U', 'S', + ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'u', + 's', '-', 'i', 's', 'o', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', + 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'I', 'S', 'O', ' ', 'E', 'a', 's', + 't', '"', '}', ',', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', + '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'I', + 'S', 'O', ' ', 'W', 'E', 'S', 'T', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', + 'w', 's', '-', 'i', 's', 'o', '-', 'b', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', + '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 's', 'c', '2', 's', '.', 's', + 'g', 'o', 'v', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', + 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 's', 'c', '2', 's', '.', 's', 'g', 'o', 'v', + '.', 'g', 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', + 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', 'b', '-', 'e', 'a', + 's', 't', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', + 'o', '-', 'b', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', + 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', + 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', + 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', 'b', '\\', '\\', '-', + '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', + 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'b', '-', 'g', 'l', 'o', 'b', + 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', + 'A', 'W', 'S', ' ', 'I', 'S', 'O', 'B', ' ', '(', 'U', 'S', ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', + ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'u', 's', '-', 'i', 's', 'o', 'b', '-', 'e', + 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', + '"', ':', '"', 'U', 'S', ' ', 'I', 'S', 'O', 'B', ' ', 'E', 'a', 's', 't', ' ', '(', 'O', 'h', 'i', + 'o', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', + 'o', '-', 'e', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', + 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 'l', 'o', 'u', 'd', '.', 'a', 'd', 'c', '-', 'e', + '.', 'u', 'k', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', + 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 'l', 'o', 'u', 'd', '.', 'a', 'd', 'c', '-', 'e', '.', 'u', + 'k', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', + 'g', 'i', 'o', 'n', '"', ':', '"', 'e', 'u', '-', 'i', 's', 'o', 'e', '-', 'w', 'e', 's', 't', '-', + '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'e', + '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', + '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', + 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', + 'x', '"', ':', '"', '^', 'e', 'u', '\\', '\\', '-', 'i', 's', 'o', 'e', '\\', '\\', '-', '\\', '\\', 'w', + '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', + ':', '{', '"', 'e', 'u', '-', 'i', 's', 'o', 'e', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', + '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'U', ' ', 'I', 'S', + 'O', 'E', ' ', 'W', 'e', 's', 't', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', + 'w', 's', '-', 'i', 's', 'o', '-', 'f', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', + '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 's', 'p', '.', 'h', 'c', + 'i', '.', 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', + 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 's', 'p', '.', 'h', 'c', 'i', '.', + 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', + 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', 'f', '-', + 's', 'o', 'u', 't', 'h', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', + '-', 'i', 's', 'o', '-', 'f', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', + 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', + 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', + 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', 'f', + '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', + 'g', 'i', 'o', 'n', 's', '"', ':', '{', '}', '}', ']', ',', '"', 'v', 'e', 'r', 's', 'i', 'o', 'n', + '"', ':', '"', '1', '.', '1', '"', '}'}; const struct aws_byte_cursor aws_s3_endpoint_resolver_partitions = { - .len = 4029, + .len = 4088, .ptr = (uint8_t *) s_generated_array }; From 502cd6249c6523583c19b122c65e02cf74301b3e Mon Sep 17 00:00:00 2001 From: Dengke Tang Date: Wed, 4 Sep 2024 09:10:01 -0700 Subject: [PATCH 2/7] S3 receive filepath (#449) --- include/aws/s3/private/s3_meta_request_impl.h | 5 + include/aws/s3/s3.h | 2 + include/aws/s3/s3_client.h | 50 ++++ source/s3.c | 2 + source/s3_meta_request.c | 122 +++++++- tests/CMakeLists.txt | 7 + tests/s3_data_plane_tests.c | 263 +++++++++++++++++- tests/s3_tester.c | 80 ++++-- tests/s3_tester.h | 7 + 9 files changed, 503 insertions(+), 35 deletions(-) diff --git a/include/aws/s3/private/s3_meta_request_impl.h b/include/aws/s3/private/s3_meta_request_impl.h index 560e71c67..bde40ebc4 100644 --- a/include/aws/s3/private/s3_meta_request_impl.h +++ b/include/aws/s3/private/s3_meta_request_impl.h @@ -279,6 +279,11 @@ struct aws_s3_meta_request { /* running checksum of all the parts of a default get, or ranged get meta request*/ struct aws_s3_checksum *meta_request_level_running_response_sum; + + /* The receiving file handler */ + FILE *recv_file; + struct aws_string *recv_filepath; + bool recv_file_delete_on_failure; }; /* Info for each part, that we need to remember until we send CompleteMultipartUpload */ diff --git a/include/aws/s3/s3.h b/include/aws/s3/s3.h index 4dc0fe905..c5416f10b 100644 --- a/include/aws/s3/s3.h +++ b/include/aws/s3/s3.h @@ -46,6 +46,8 @@ enum aws_s3_errors { AWS_ERROR_S3EXPRESS_CREATE_SESSION_FAILED, AWS_ERROR_S3_INTERNAL_PART_SIZE_MISMATCH_RETRYING_WITH_RANGE, AWS_ERROR_S3_REQUEST_HAS_COMPLETED, + AWS_ERROR_S3_RECV_FILE_ALREADY_EXISTS, + AWS_ERROR_S3_RECV_FILE_NOT_FOUND, AWS_ERROR_S3_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_S3_PACKAGE_ID) }; diff --git a/include/aws/s3/s3_client.h b/include/aws/s3/s3_client.h index 84782f6aa..365f18372 100644 --- a/include/aws/s3/s3_client.h +++ b/include/aws/s3/s3_client.h @@ -249,6 +249,28 @@ enum aws_s3_checksum_location { AWS_SCL_TRAILER, }; +enum aws_s3_recv_file_option { + /** + * Create a new file if it doesn't exist, otherwise replace the existing file. + */ + AWS_S3_RECV_FILE_CREATE_OR_REPLACE = 0, + /** + * Always create a new file. If the file already exists, AWS_ERROR_S3_RECV_FILE_ALREADY_EXISTS will be raised. + */ + AWS_S3_RECV_FILE_CREATE_NEW, + /** + * Create a new file if it doesn't exist, otherwise append to the existing file. + */ + AWS_S3_RECV_FILE_CREATE_OR_APPEND, + + /** + * Write to an existing file at the specified position, defined by the `recv_file_position`. + * If the file does not exist, AWS_ERROR_S3_RECV_FILE_NOT_FOUND will be raised. + * If `recv_file_position` is not configured, start overwriting data at the beginning of the + * file (byte 0). + */ + AWS_S3_RECV_FILE_WRITE_TO_POSITION, +}; /** * Info about a single part, for you to review before the upload completes. */ @@ -632,6 +654,34 @@ struct aws_s3_meta_request_options { * Do not set the message's body-stream if the body is being passed by other means (see note above) */ struct aws_http_message *message; + /** + * Optional. + * If set, the received data will be written into this file. + * the `body_callback` will NOT be invoked. + * This gives a better performance when receiving data to write to a file. + * See `aws_s3_recv_file_option` for the configuration on the receive file. + */ + struct aws_byte_cursor recv_filepath; + + /** + * Optional. + * Default to AWS_S3_RECV_FILE_CREATE_OR_REPLACE. + * This only works with recv_filepath set. + * See `aws_s3_recv_file_option`. + */ + enum aws_s3_recv_file_option recv_file_option; + /** + * Optional. + * The specified position to start writing at for the recv file when `recv_file_option` is set to + * AWS_S3_RECV_FILE_WRITE_TO_POSITION, ignored otherwise. + */ + uint64_t recv_file_position; + /** + * Set it to be true to delete the receive file on failure, otherwise, the file will be left as-is. + * This only works with recv_filepath set. + */ + bool recv_file_delete_on_failure; + /** * Optional. * If set, this file is sent as the request body. diff --git a/source/s3.c b/source/s3.c index 894bbe591..266582d0c 100644 --- a/source/s3.c +++ b/source/s3.c @@ -49,6 +49,8 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3EXPRESS_CREATE_SESSION_FAILED, "CreateSession call failed when signing with S3 Express."), AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_INTERNAL_PART_SIZE_MISMATCH_RETRYING_WITH_RANGE, "part_size mismatch, possibly due to wrong object_size_hint. Retrying with Range instead of partNumber."), AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_REQUEST_HAS_COMPLETED, "Request has already completed, action cannot be performed."), + AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_RECV_FILE_ALREADY_EXISTS, "File already exists, cannot create as new."), + AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_RECV_FILE_NOT_FOUND, "The receive file doesn't exist, cannot create as configuration required."), }; /* clang-format on */ diff --git a/source/s3_meta_request.c b/source/s3_meta_request.c index d3be24a1e..ccafe63ca 100644 --- a/source/s3_meta_request.c +++ b/source/s3_meta_request.c @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -25,6 +26,7 @@ #include #include #include +#include #include static const size_t s_dynamic_body_initial_buf_size = KB_TO_BYTES(1); @@ -232,6 +234,57 @@ int aws_s3_meta_request_init_base( /* Keep original message around, for headers, method, and synchronous body-stream (if any) */ meta_request->initial_request_message = aws_http_message_acquire(options->message); + if (options->recv_filepath.len > 0) { + + meta_request->recv_filepath = aws_string_new_from_cursor(allocator, &options->recv_filepath); + switch (options->recv_file_option) { + case AWS_S3_RECV_FILE_CREATE_OR_REPLACE: + meta_request->recv_file = aws_fopen(aws_string_c_str(meta_request->recv_filepath), "wb"); + break; + + case AWS_S3_RECV_FILE_CREATE_NEW: + if (aws_path_exists(meta_request->recv_filepath)) { + AWS_LOGF_ERROR( + AWS_LS_S3_META_REQUEST, + "id=%p Cannot receive file via CREATE_NEW: file already exists", + (void *)meta_request); + aws_raise_error(AWS_ERROR_S3_RECV_FILE_ALREADY_EXISTS); + break; + } else { + meta_request->recv_file = aws_fopen(aws_string_c_str(meta_request->recv_filepath), "wb"); + break; + } + case AWS_S3_RECV_FILE_CREATE_OR_APPEND: + meta_request->recv_file = aws_fopen(aws_string_c_str(meta_request->recv_filepath), "ab"); + break; + case AWS_S3_RECV_FILE_WRITE_TO_POSITION: + if (!aws_path_exists(meta_request->recv_filepath)) { + AWS_LOGF_ERROR( + AWS_LS_S3_META_REQUEST, + "id=%p Cannot receive file via WRITE_TO_POSITION: file not found.", + (void *)meta_request); + aws_raise_error(AWS_ERROR_S3_RECV_FILE_NOT_FOUND); + break; + } else { + meta_request->recv_file = aws_fopen(aws_string_c_str(meta_request->recv_filepath), "r+"); + if (meta_request->recv_file && + aws_fseek(meta_request->recv_file, options->recv_file_position, SEEK_SET) != AWS_OP_SUCCESS) { + /* error out. */ + goto error; + } + break; + } + + default: + AWS_ASSERT(false); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + break; + } + if (!meta_request->recv_file) { + goto error; + } + } + /* If the request's body is being passed in some other way, set that up. * (we checked earlier that the request body is not being passed multiple ways) */ if (options->send_filepath.len > 0) { @@ -440,6 +493,15 @@ static void s_s3_meta_request_destroy(void *user_data) { /* endpoint should have already been released and set NULL by the meta request finish call. * But call release() again, just in case we're tearing down a half-initialized meta request */ aws_s3_endpoint_release(meta_request->endpoint); + if (meta_request->recv_file) { + fclose(meta_request->recv_file); + meta_request->recv_file = NULL; + if (meta_request->recv_file_delete_on_failure) { + /* If the meta request succeed, the file should be closed from finish call. So it must be failing. */ + aws_file_delete(meta_request->recv_filepath); + } + } + aws_string_destroy(meta_request->recv_filepath); /* Client may be NULL if meta request failed mid-creation (or this some weird testing mock with no client) */ if (meta_request->client != NULL) { @@ -1779,19 +1841,47 @@ static void s_s3_meta_request_event_delivery_task(struct aws_task *task, void *a if (error_code == AWS_ERROR_SUCCESS && response_body.len > 0) { if (meta_request->meta_request_level_running_response_sum) { - aws_checksum_update(meta_request->meta_request_level_running_response_sum, &response_body); + if (aws_checksum_update( + meta_request->meta_request_level_running_response_sum, &response_body)) { + error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_S3_META_REQUEST, + "id=%p Failed to update checksum. last error:%s", + (void *)meta_request, + aws_error_name(error_code)); + } } - if (meta_request->body_callback != NULL && - meta_request->body_callback( - meta_request, &response_body, request->part_range_start, meta_request->user_data)) { - - error_code = aws_last_error_or_unknown(); - AWS_LOGF_ERROR( - AWS_LS_S3_META_REQUEST, - "id=%p Response body callback raised error %d (%s).", - (void *)meta_request, - error_code, - aws_error_str(error_code)); + if (error_code == AWS_ERROR_SUCCESS) { + if (meta_request->recv_file) { + /* Write the data directly to the file. No need to seek, since the event will always be + * delivered with the right order. */ + if (fwrite((void *)response_body.ptr, response_body.len, 1, meta_request->recv_file) < 1) { + int errno_value = ferror(meta_request->recv_file) ? errno : 0; /* Always cache errno */ + aws_translate_and_raise_io_error_or(errno_value, AWS_ERROR_FILE_WRITE_FAILURE); + error_code = aws_last_error(); + AWS_LOGF_ERROR( + AWS_LS_S3_META_REQUEST, + "id=%p Failed writing to file. errno:%d. aws-error:%s", + (void *)meta_request, + errno_value, + aws_error_name(error_code)); + } + if (meta_request->client->enable_read_backpressure) { + aws_s3_meta_request_increment_read_window(meta_request, response_body.len); + } + } else if ( + meta_request->body_callback != NULL && + meta_request->body_callback( + meta_request, &response_body, request->part_range_start, meta_request->user_data)) { + + error_code = aws_last_error_or_unknown(); + AWS_LOGF_ERROR( + AWS_LS_S3_META_REQUEST, + "id=%p Response body callback raised error %d (%s).", + (void *)meta_request, + error_code, + aws_error_str(error_code)); + } } } aws_atomic_fetch_sub(&client->stats.num_requests_streaming_response, 1); @@ -1979,6 +2069,14 @@ void aws_s3_meta_request_finish_default(struct aws_s3_meta_request *meta_request pending_async_write_waker(pending_async_write_waker_user_data); } + if (meta_request->recv_file) { + fclose(meta_request->recv_file); + meta_request->recv_file = NULL; + if (finish_result.error_code && meta_request->recv_file_delete_on_failure) { + aws_file_delete(meta_request->recv_filepath); + } + } + while (!aws_linked_list_empty(&release_request_list)) { struct aws_linked_list_node *request_node = aws_linked_list_pop_front(&release_request_list); struct aws_s3_request *release_request = AWS_CONTAINER_OF(request_node, struct aws_s3_request, node); diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 2d08afb32..423c1bf54 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -67,6 +67,10 @@ add_net_test_case(test_s3_get_object_tls_disabled) add_net_test_case(test_s3_get_object_tls_enabled) add_net_test_case(test_s3_get_object_tls_default) add_net_test_case(test_s3_get_object_less_than_part_size) +add_net_test_case(test_s3_get_object_file_path) +add_net_test_case(test_s3_get_object_file_path_create_new) +add_net_test_case(test_s3_get_object_file_path_append) +add_net_test_case(test_s3_get_object_file_path_to_position) add_net_test_case(test_s3_get_object_empty_object) add_net_test_case(test_s3_get_object_multiple) add_net_test_case(test_s3_get_object_multiple_serial) @@ -77,6 +81,9 @@ add_net_test_case(test_s3_default_get_object_looks_like_async_error_xml) add_net_test_case(test_s3_get_object_backpressure_small_increments) add_net_test_case(test_s3_get_object_backpressure_big_increments) add_net_test_case(test_s3_get_object_backpressure_initial_size_zero) +add_net_test_case(test_s3_get_object_backpressure_small_increments_recv_filepath) +add_net_test_case(test_s3_get_object_backpressure_big_increments_recv_filepath) +add_net_test_case(test_s3_get_object_backpressure_initial_size_zero_recv_filepath) add_net_test_case(test_s3_get_object_part) add_net_test_case(test_s3_no_signing) add_net_test_case(test_s3_signing_override) diff --git a/tests/s3_data_plane_tests.c b/tests/s3_data_plane_tests.c index 35acd3f78..e36590a23 100644 --- a/tests/s3_data_plane_tests.c +++ b/tests/s3_data_plane_tests.c @@ -1405,6 +1405,192 @@ static int s_test_s3_get_object_multiple_serial(struct aws_allocator *allocator, return 0; } +AWS_TEST_CASE(test_s3_get_object_file_path, s_test_s3_get_object_file_path) +static int s_test_s3_get_object_file_path(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_s3_tester tester; + AWS_ZERO_STRUCT(tester); + ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); + + struct aws_s3_tester_client_options client_options = { + .part_size = MB_TO_BYTES(5), + }; + + struct aws_s3_client *client = NULL; + ASSERT_SUCCESS(aws_s3_tester_client_new(&tester, &client_options, &client)); + + struct aws_byte_cursor object_path = aws_byte_cursor_from_c_str("/pre-existing-1MB"); + struct aws_s3_tester_meta_request_options get_options = { + .allocator = allocator, + .meta_request_type = AWS_S3_META_REQUEST_TYPE_GET_OBJECT, + .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS, + .client = client, + .get_options = + { + .object_path = object_path, + .file_on_disk = true, + }, + }; + + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, NULL)); + + client = aws_s3_client_release(client); + aws_s3_tester_clean_up(&tester); + return 0; +} + +AWS_TEST_CASE(test_s3_get_object_file_path_create_new, s_test_s3_get_object_file_path_create_new) +static int s_test_s3_get_object_file_path_create_new(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_s3_tester tester; + AWS_ZERO_STRUCT(tester); + ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); + + struct aws_s3_tester_client_options client_options = { + .part_size = MB_TO_BYTES(5), + }; + + struct aws_s3_client *client = NULL; + ASSERT_SUCCESS(aws_s3_tester_client_new(&tester, &client_options, &client)); + + struct aws_byte_cursor object_path = aws_byte_cursor_from_c_str("/pre-existing-1MB"); + struct aws_s3_tester_meta_request_options get_options = { + .allocator = allocator, + .meta_request_type = AWS_S3_META_REQUEST_TYPE_GET_OBJECT, + .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE, + .client = client, + .get_options = + { + .object_path = object_path, + .file_on_disk = true, + .recv_file_option = AWS_S3_RECV_FILE_CREATE_NEW, + .pre_exist_file_length = 10, + .recv_file_delete_on_failure = true, + }, + }; + + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, NULL)); + ASSERT_UINT_EQUALS(AWS_ERROR_S3_RECV_FILE_ALREADY_EXISTS, aws_last_error()); + + /* The request failed without invoking the finish callback. Reset the finish and shutdown count */ + aws_s3_tester_lock_synced_data(&tester); + ASSERT_UINT_EQUALS(0, tester.synced_data.meta_request_shutdown_count); + ASSERT_UINT_EQUALS(0, tester.synced_data.meta_request_finish_count); + tester.synced_data.desired_meta_request_shutdown_count = 0; + tester.synced_data.desired_meta_request_finish_count = 0; + aws_s3_tester_unlock_synced_data(&tester); + + get_options.validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS; + get_options.get_options.pre_exist_file_length = 0; + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, NULL)); + + client = aws_s3_client_release(client); + aws_s3_tester_clean_up(&tester); + return 0; +} + +AWS_TEST_CASE(test_s3_get_object_file_path_append, s_test_s3_get_object_file_path_append) +static int s_test_s3_get_object_file_path_append(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_s3_tester tester; + AWS_ZERO_STRUCT(tester); + ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); + + struct aws_s3_tester_client_options client_options = { + .part_size = MB_TO_BYTES(5), + }; + + struct aws_s3_meta_request_test_results meta_request_test_results; + aws_s3_meta_request_test_results_init(&meta_request_test_results, allocator); + struct aws_s3_client *client = NULL; + ASSERT_SUCCESS(aws_s3_tester_client_new(&tester, &client_options, &client)); + + struct aws_byte_cursor object_path = aws_byte_cursor_from_c_str("/pre-existing-1MB"); + uint64_t pre_exist_file_length = 10; + struct aws_s3_tester_meta_request_options get_options = { + .allocator = allocator, + .meta_request_type = AWS_S3_META_REQUEST_TYPE_GET_OBJECT, + .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS, + .client = client, + .get_options = + { + .object_path = object_path, + .file_on_disk = true, + .recv_file_option = AWS_S3_RECV_FILE_CREATE_OR_APPEND, + .pre_exist_file_length = pre_exist_file_length, + }, + }; + + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, &meta_request_test_results)); + ASSERT_UINT_EQUALS(pre_exist_file_length + MB_TO_BYTES(1), meta_request_test_results.received_file_size); + ASSERT_UINT_EQUALS(MB_TO_BYTES(1), meta_request_test_results.progress.total_bytes_transferred); + aws_s3_meta_request_test_results_clean_up(&meta_request_test_results); + client = aws_s3_client_release(client); + aws_s3_tester_clean_up(&tester); + return 0; +} + +AWS_TEST_CASE(test_s3_get_object_file_path_to_position, s_test_s3_get_object_file_path_to_position) +static int s_test_s3_get_object_file_path_to_position(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_s3_tester tester; + AWS_ZERO_STRUCT(tester); + ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); + + struct aws_s3_tester_client_options client_options = { + .part_size = MB_TO_BYTES(5), + }; + + struct aws_s3_meta_request_test_results meta_request_test_results; + aws_s3_meta_request_test_results_init(&meta_request_test_results, allocator); + struct aws_s3_client *client = NULL; + ASSERT_SUCCESS(aws_s3_tester_client_new(&tester, &client_options, &client)); + + struct aws_byte_cursor object_path = aws_byte_cursor_from_c_str("/pre-existing-1MB"); + uint64_t pre_exist_file_length = 10; + struct aws_s3_tester_meta_request_options get_options = { + .allocator = allocator, + .meta_request_type = AWS_S3_META_REQUEST_TYPE_GET_OBJECT, + .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE, + .client = client, + .get_options = + { + .object_path = object_path, + .file_on_disk = true, + .recv_file_option = AWS_S3_RECV_FILE_WRITE_TO_POSITION, + .pre_exist_file_length = 0, + }, + }; + + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, NULL)); + ASSERT_UINT_EQUALS(AWS_ERROR_S3_RECV_FILE_NOT_FOUND, aws_last_error()); + + /* The request failed without invoking the finish callback. Reset the finish and shutdown count */ + aws_s3_tester_lock_synced_data(&tester); + ASSERT_UINT_EQUALS(0, tester.synced_data.meta_request_shutdown_count); + ASSERT_UINT_EQUALS(0, tester.synced_data.meta_request_finish_count); + tester.synced_data.desired_meta_request_shutdown_count = 0; + tester.synced_data.desired_meta_request_finish_count = 0; + aws_s3_tester_unlock_synced_data(&tester); + + get_options.validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_SUCCESS; + get_options.get_options.pre_exist_file_length = pre_exist_file_length; + get_options.get_options.recv_file_position = 20; + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, &meta_request_test_results)); + ASSERT_UINT_EQUALS( + get_options.get_options.recv_file_position + MB_TO_BYTES(1), meta_request_test_results.received_file_size); + ASSERT_UINT_EQUALS(MB_TO_BYTES(1), meta_request_test_results.progress.total_bytes_transferred); + + aws_s3_meta_request_test_results_clean_up(&meta_request_test_results); + client = aws_s3_client_release(client); + aws_s3_tester_clean_up(&tester); + return 0; +} + AWS_TEST_CASE(test_s3_get_object_empty_object, s_test_s3_get_object_empty_default) static int s_test_s3_get_object_empty_default(struct aws_allocator *allocator, void *ctx) { (void)ctx; @@ -1558,7 +1744,8 @@ static int s_test_s3_get_object_backpressure_helper( struct aws_allocator *allocator, size_t part_size, size_t window_initial_size, - uint64_t window_increment_size) { + uint64_t window_increment_size, + bool file_on_disk) { struct aws_s3_tester tester; ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); @@ -1586,6 +1773,11 @@ static int s_test_s3_get_object_backpressure_helper( .type = AWS_S3_META_REQUEST_TYPE_GET_OBJECT, .message = message, }; + struct aws_string *filepath_str = NULL; + if (file_on_disk) { + filepath_str = aws_s3_tester_create_file(allocator, g_pre_existing_object_1MB, NULL); + options.recv_filepath = aws_byte_cursor_from_string(filepath_str); + } struct aws_s3_meta_request_test_results meta_request_test_results; aws_s3_meta_request_test_results_init(&meta_request_test_results, allocator); @@ -1627,6 +1819,10 @@ static int s_test_s3_get_object_backpressure_helper( client = aws_s3_client_release(client); aws_s3_tester_clean_up(&tester); + if (filepath_str) { + aws_file_delete(filepath_str); + aws_string_destroy(filepath_str); + } return 0; } @@ -1640,7 +1836,8 @@ static int s_test_s3_get_object_backpressure_small_increments(struct aws_allocat size_t part_size = file_size / 4; size_t window_initial_size = 1024; uint64_t window_increment_size = part_size / 2; - return s_test_s3_get_object_backpressure_helper(allocator, part_size, window_initial_size, window_increment_size); + return s_test_s3_get_object_backpressure_helper( + allocator, part_size, window_initial_size, window_increment_size, false); } AWS_TEST_CASE(test_s3_get_object_backpressure_big_increments, s_test_s3_get_object_backpressure_big_increments) @@ -1652,7 +1849,8 @@ static int s_test_s3_get_object_backpressure_big_increments(struct aws_allocator size_t part_size = file_size / 8; size_t window_initial_size = 1024; uint64_t window_increment_size = part_size * 3; - return s_test_s3_get_object_backpressure_helper(allocator, part_size, window_initial_size, window_increment_size); + return s_test_s3_get_object_backpressure_helper( + allocator, part_size, window_initial_size, window_increment_size, false); } AWS_TEST_CASE(test_s3_get_object_backpressure_initial_size_zero, s_test_s3_get_object_backpressure_initial_size_zero) @@ -1663,7 +1861,56 @@ static int s_test_s3_get_object_backpressure_initial_size_zero(struct aws_alloca size_t part_size = file_size / 4; size_t window_initial_size = 0; uint64_t window_increment_size = part_size / 2; - return s_test_s3_get_object_backpressure_helper(allocator, part_size, window_initial_size, window_increment_size); + return s_test_s3_get_object_backpressure_helper( + allocator, part_size, window_initial_size, window_increment_size, false); +} + +AWS_TEST_CASE( + test_s3_get_object_backpressure_small_increments_recv_filepath, + s_test_s3_get_object_backpressure_small_increments_recv_filepath) +static int s_test_s3_get_object_backpressure_small_increments_recv_filepath( + struct aws_allocator *allocator, + void *ctx) { + /* Test increments smaller than part-size. + * Only 1 part at a time should be in flight */ + (void)ctx; + size_t file_size = 1 * 1024 * 1024; /* Test downloads 1MB file */ + size_t part_size = file_size / 4; + size_t window_initial_size = 1024; + uint64_t window_increment_size = part_size / 2; + return s_test_s3_get_object_backpressure_helper( + allocator, part_size, window_initial_size, window_increment_size, true); +} + +AWS_TEST_CASE( + test_s3_get_object_backpressure_big_increments_recv_filepath, + s_test_s3_get_object_backpressure_big_increments_recv_filepath) +static int s_test_s3_get_object_backpressure_big_increments_recv_filepath(struct aws_allocator *allocator, void *ctx) { + /* Test increments larger than part-size. + * Multiple parts should be in flight at a time */ + (void)ctx; + size_t file_size = 1 * 1024 * 1024; /* Test downloads 1MB file */ + size_t part_size = file_size / 8; + size_t window_initial_size = 1024; + uint64_t window_increment_size = part_size * 3; + return s_test_s3_get_object_backpressure_helper( + allocator, part_size, window_initial_size, window_increment_size, true); +} + +AWS_TEST_CASE( + test_s3_get_object_backpressure_initial_size_zero_recv_filepath, + s_test_s3_get_object_backpressure_initial_size_zero_recv_filepath) +static int s_test_s3_get_object_backpressure_initial_size_zero_recv_filepath( + struct aws_allocator *allocator, + void *ctx) { + /* Test with initial window size of zero */ + (void)ctx; + size_t file_size = 1 * 1024 * 1024; /* Test downloads 1MB file */ + size_t part_size = file_size / 4; + size_t window_initial_size = 0; + uint64_t window_increment_size = part_size / 2; + return s_test_s3_get_object_backpressure_helper( + allocator, part_size, window_initial_size, window_increment_size, true); } AWS_TEST_CASE(test_s3_get_object_part, s_test_s3_get_object_part) @@ -4228,10 +4475,10 @@ static int s_test_s3_round_trip_with_filepath_helper( }, }; - ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &put_options, NULL)); + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &put_options, &test_results)); + aws_s3_meta_request_test_results_clean_up(&test_results); /*** GET FILE ***/ - aws_s3_meta_request_test_results_clean_up(&test_results); aws_s3_meta_request_test_results_init(&test_results, allocator); struct aws_s3_tester_meta_request_options get_options = { @@ -4242,11 +4489,13 @@ static int s_test_s3_round_trip_with_filepath_helper( .get_options = { .object_path = object_path, + .file_on_disk = true, }, }; ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &get_options, &test_results)); - ASSERT_UINT_EQUALS(MB_TO_BYTES(put_options.put_options.object_size_mb), test_results.received_body_size); + ASSERT_UINT_EQUALS( + MB_TO_BYTES(put_options.put_options.object_size_mb), test_results.progress.total_bytes_transferred); aws_s3_meta_request_test_results_clean_up(&test_results); aws_byte_buf_clean_up(&path_buf); diff --git a/tests/s3_tester.c b/tests/s3_tester.c index 78575e154..d5ddf4830 100644 --- a/tests/s3_tester.c +++ b/tests/s3_tester.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include #include @@ -126,7 +127,6 @@ static int s_s3_test_meta_request_body_callback( struct aws_s3_meta_request_test_results *meta_request_test_results = user_data; meta_request_test_results->received_body_size += body->len; aws_atomic_fetch_add(&meta_request_test_results->received_body_size_delta, body->len); - AWS_LOGF_DEBUG( AWS_LS_S3_GENERAL, "Received range %" PRIu64 "-%" PRIu64 ". Expected range start: %" PRIu64, @@ -545,7 +545,6 @@ void aws_s3_meta_request_test_results_clean_up(struct aws_s3_meta_request_test_r if (test_meta_request == NULL) { return; } - aws_http_headers_release(test_meta_request->error_response_headers); aws_byte_buf_clean_up(&test_meta_request->error_response_body); aws_string_destroy(test_meta_request->error_response_operation_name); @@ -1525,6 +1524,24 @@ int aws_s3_tester_send_meta_request_with_options( aws_http_message_add_header(message, range_header); } + if (options->get_options.file_on_disk) { + if (options->get_options.pre_exist_file_length > 0) { + char *buffer = aws_mem_calloc(allocator, (size_t)options->get_options.pre_exist_file_length + 1, 1); + memset(buffer, 'a', (size_t)options->get_options.pre_exist_file_length); + buffer[(size_t)options->get_options.pre_exist_file_length] = '\0'; + struct aws_byte_cursor cursor = aws_byte_cursor_from_c_str(buffer); + struct aws_input_stream *buffer_input_stream = aws_input_stream_new_from_cursor(allocator, &cursor); + filepath_str = + aws_s3_tester_create_file(allocator, options->get_options.object_path, buffer_input_stream); + aws_input_stream_release(buffer_input_stream); + aws_mem_release(allocator, buffer); + } else { + filepath_str = aws_s3_tester_create_file(allocator, options->get_options.object_path, NULL); + } + meta_request_options.recv_filepath = aws_byte_cursor_from_string(filepath_str); + meta_request_options.recv_file_option = options->get_options.recv_file_option; + meta_request_options.recv_file_position = options->get_options.recv_file_position; + } meta_request_options.message = message; } else if ( @@ -1755,6 +1772,17 @@ int aws_s3_tester_send_meta_request_with_options( ASSERT_UINT_EQUALS(0, aws_atomic_load_int(&client->stats.num_requests_stream_queued_waiting)); ASSERT_UINT_EQUALS(0, aws_atomic_load_int(&client->stats.num_requests_streaming_response)); ASSERT_SUCCESS(s_tester_check_client_thread_data(client)); + if (options->get_options.file_on_disk) { + /* Validate the size match. */ + ASSERT_NOT_NULL(filepath_str); + FILE *file = aws_fopen(aws_string_c_str(filepath_str), "rb"); + ASSERT_NOT_NULL(file); + ASSERT_SUCCESS(aws_file_get_length(file, &out_results->received_file_size)); + if (options->get_options.recv_file_option == AWS_S3_RECV_FILE_CREATE_OR_REPLACE) { + ASSERT_UINT_EQUALS(out_results->progress.total_bytes_transferred, out_results->received_file_size); + } + fclose(file); + } break; case AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE: ASSERT_FALSE(out_results->finished_error_code == AWS_ERROR_SUCCESS); @@ -1773,6 +1801,14 @@ int aws_s3_tester_send_meta_request_with_options( if (!options->dont_wait_for_shutdown) { aws_s3_tester_wait_for_meta_request_shutdown(tester); } + if (filepath_str && out_results->finished_error_code != AWS_ERROR_SUCCESS) { + if (options->get_options.recv_file_delete_on_failure) { + /* Check the file already gone on failure */ + ASSERT_FALSE(aws_path_exists(filepath_str)); + } else { + ASSERT_TRUE(aws_path_exists(filepath_str)); + } + } } aws_s3_meta_request_test_results_clean_up(&meta_request_test_results); @@ -1938,9 +1974,9 @@ int aws_s3_tester_validate_get_object_results( AWS_LS_S3_GENERAL, "Content length in header is %" PRIu64 " and received body size is %" PRIu64, content_length, - meta_request_test_results->received_body_size); + meta_request_test_results->progress.total_bytes_transferred); - ASSERT_TRUE(content_length == meta_request_test_results->received_body_size); + ASSERT_TRUE(content_length == meta_request_test_results->progress.total_bytes_transferred); ASSERT_UINT_EQUALS(content_length, meta_request_test_results->progress.total_bytes_transferred); ASSERT_UINT_EQUALS(content_length, meta_request_test_results->progress.content_length); @@ -2140,30 +2176,42 @@ struct aws_string *aws_s3_tester_create_file( struct aws_byte_buf filepath_buf; aws_byte_buf_init(&filepath_buf, allocator, 128); + struct aws_byte_cursor filepath_prefix = aws_byte_cursor_from_c_str("tmp"); aws_byte_buf_append_dynamic(&filepath_buf, &filepath_prefix); aws_byte_buf_append_dynamic(&filepath_buf, &test_object_path); + struct aws_uuid uuid; + aws_uuid_init(&uuid); + uint8_t uuid_array[AWS_UUID_STR_LEN] = {0}; + struct aws_byte_buf uuid_buf = aws_byte_buf_from_array(uuid_array, sizeof(uuid_array)); + uuid_buf.len = 0; + aws_uuid_to_str(&uuid, &uuid_buf); + struct aws_byte_cursor uuid_cursor = aws_byte_cursor_from_buf(&uuid_buf); + aws_byte_buf_append_dynamic(&filepath_buf, &uuid_cursor); + for (size_t i = 0; i < filepath_buf.len; ++i) { if (!isalnum(filepath_buf.buffer[i])) { filepath_buf.buffer[i] = '_'; /* sanitize filename */ } } + struct aws_string *filepath_str = aws_string_new_from_buf(allocator, &filepath_buf); aws_byte_buf_clean_up(&filepath_buf); - FILE *file = aws_fopen(aws_string_c_str(filepath_str), "wb"); - AWS_FATAL_ASSERT(file != NULL); - - int64_t stream_length = 0; - AWS_FATAL_ASSERT(aws_input_stream_get_length(input_stream, &stream_length) == AWS_OP_SUCCESS); + if (input_stream) { + FILE *file = aws_fopen(aws_string_c_str(filepath_str), "wb"); + AWS_FATAL_ASSERT(file != NULL); + int64_t stream_length = 0; + AWS_FATAL_ASSERT(aws_input_stream_get_length(input_stream, &stream_length) == AWS_OP_SUCCESS); - struct aws_byte_buf data_buf; - AWS_FATAL_ASSERT(aws_byte_buf_init(&data_buf, allocator, (size_t)stream_length) == AWS_OP_SUCCESS); - AWS_FATAL_ASSERT(aws_input_stream_read(input_stream, &data_buf) == AWS_OP_SUCCESS); - AWS_FATAL_ASSERT((size_t)stream_length == data_buf.len); - AWS_FATAL_ASSERT(data_buf.len == fwrite(data_buf.buffer, 1, data_buf.len, file)); - fclose(file); - aws_byte_buf_clean_up(&data_buf); + struct aws_byte_buf data_buf; + AWS_FATAL_ASSERT(aws_byte_buf_init(&data_buf, allocator, (size_t)stream_length) == AWS_OP_SUCCESS); + AWS_FATAL_ASSERT(aws_input_stream_read(input_stream, &data_buf) == AWS_OP_SUCCESS); + AWS_FATAL_ASSERT((size_t)stream_length == data_buf.len); + AWS_FATAL_ASSERT(data_buf.len == fwrite(data_buf.buffer, 1, data_buf.len, file)); + fclose(file); + aws_byte_buf_clean_up(&data_buf); + } return filepath_str; } diff --git a/tests/s3_tester.h b/tests/s3_tester.h index 4b2f9689b..3c2b1f343 100644 --- a/tests/s3_tester.h +++ b/tests/s3_tester.h @@ -188,6 +188,12 @@ struct aws_s3_tester_meta_request_options { struct aws_byte_cursor object_range; /* Get the part from S3, starts from 1. 0 means not set. */ int part_number; + bool file_on_disk; + enum aws_s3_recv_file_option recv_file_option; + uint64_t recv_file_position; + bool recv_file_delete_on_failure; + /* If larger than 0, create a pre-exist file with the length */ + uint64_t pre_exist_file_length; } get_options; /* Put Object Meta request specific options. */ @@ -244,6 +250,7 @@ struct aws_s3_meta_request_test_results { struct aws_http_headers *response_headers; uint64_t expected_range_start; uint64_t received_body_size; + int64_t received_file_size; /* an atomic for tests that want to check from the main thread whether data is still arriving */ struct aws_atomic_var received_body_size_delta; int finished_response_status; From 64c35be93fa1d6cb89123968c3a2a70487d0f2aa Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Thu, 5 Sep 2024 10:17:01 -0700 Subject: [PATCH 3/7] Auto - Update S3 Ruleset & Partition (#451) Co-authored-by: Dengke --- .github/workflows/ci.yml | 2 +- .../aws_s3_endpoint_resolver_partition.c | 372 +++++++++--------- 2 files changed, 187 insertions(+), 187 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4ea4be901..3be2c6fdc 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -6,7 +6,7 @@ on: - 'main' env: - BUILDER_VERSION: v0.9.62 + BUILDER_VERSION: v0.9.64 BUILDER_SOURCE: releases BUILDER_HOST: https://d19elf31gohf1l.cloudfront.net PACKAGE_NAME: aws-c-s3 diff --git a/source/s3_endpoint_resolver/aws_s3_endpoint_resolver_partition.c b/source/s3_endpoint_resolver/aws_s3_endpoint_resolver_partition.c index abb6880fb..fa8f4569a 100644 --- a/source/s3_endpoint_resolver/aws_s3_endpoint_resolver_partition.c +++ b/source/s3_endpoint_resolver/aws_s3_endpoint_resolver_partition.c @@ -22,202 +22,202 @@ static const char s_generated_array[] = { 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', '(', 'u', 's', '|', 'e', 'u', '|', 'a', 'p', '|', 's', 'a', '|', - 'c', 'a', '|', 'm', 'e', '|', 'a', 'f', '|', 'i', 'l', ')', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', - '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', - '"', 'a', 'f', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', - 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'f', 'r', 'i', 'c', 'a', ' ', '(', 'C', 'a', 'p', - 'e', ' ', 'T', 'o', 'w', 'n', ')', '"', '}', ',', '"', 'a', 'p', '-', 'e', 'a', 's', 't', '-', '1', + 'c', 'a', '|', 'm', 'e', '|', 'a', 'f', '|', 'i', 'l', '|', 'm', 'x', ')', '\\', '\\', '-', '\\', '\\', + 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', + '"', ':', '{', '"', 'a', 'f', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', + 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'f', 'r', 'i', 'c', 'a', ' ', '(', + 'C', 'a', 'p', 'e', ' ', 'T', 'o', 'w', 'n', ')', '"', '}', ',', '"', 'a', 'p', '-', 'e', 'a', 's', + 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', + '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'H', 'o', 'n', 'g', ' ', + 'K', 'o', 'n', 'g', ')', '"', '}', ',', '"', 'a', 'p', '-', 'n', 'o', 'r', 't', 'h', 'e', 'a', 's', + 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', + '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'T', 'o', 'k', 'y', 'o', + ')', '"', '}', ',', '"', 'a', 'p', '-', 'n', 'o', 'r', 't', 'h', 'e', 'a', 's', 't', '-', '2', '"', + ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', + 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'S', 'e', 'o', 'u', 'l', ')', '"', '}', ',', + '"', 'a', 'p', '-', 'n', 'o', 'r', 't', 'h', 'e', 'a', 's', 't', '-', '3', '"', ':', '{', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', + 'c', 'i', 'f', 'i', 'c', ' ', '(', 'O', 's', 'a', 'k', 'a', ')', '"', '}', ',', '"', 'a', 'p', '-', + 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', + 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'M', + 'u', 'm', 'b', 'a', 'i', ')', '"', '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', - 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'H', 'o', 'n', 'g', ' ', 'K', 'o', 'n', - 'g', ')', '"', '}', ',', '"', 'a', 'p', '-', 'n', 'o', 'r', 't', 'h', 'e', 'a', 's', 't', '-', '1', + 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'H', 'y', 'd', 'e', 'r', 'a', 'b', 'a', + 'd', ')', '"', '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', - 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'T', 'o', 'k', 'y', 'o', ')', '"', '}', - ',', '"', 'a', 'p', '-', 'n', 'o', 'r', 't', 'h', 'e', 'a', 's', 't', '-', '2', '"', ':', '{', '"', - 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', - 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'S', 'e', 'o', 'u', 'l', ')', '"', '}', ',', '"', 'a', 'p', - '-', 'n', 'o', 'r', 't', 'h', 'e', 'a', 's', 't', '-', '3', '"', ':', '{', '"', 'd', 'e', 's', 'c', - 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', - 'i', 'c', ' ', '(', 'O', 's', 'a', 'k', 'a', ')', '"', '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', - 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', - ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'M', 'u', 'm', 'b', - 'a', 'i', ')', '"', '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', '-', '2', '"', ':', '{', - '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', - 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'H', 'y', 'd', 'e', 'r', 'a', 'b', 'a', 'd', ')', '"', - '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', - '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', - 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'S', 'i', 'n', 'g', 'a', 'p', 'o', 'r', 'e', ')', '"', - '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '2', '"', ':', '{', + 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'S', 'i', 'n', 'g', 'a', 'p', 'o', 'r', + 'e', ')', '"', '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '2', + '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', + 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'S', 'y', 'd', 'n', 'e', 'y', ')', '"', + '}', ',', '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '3', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', - 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'S', 'y', 'd', 'n', 'e', 'y', ')', '"', '}', ',', '"', - 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '3', '"', ':', '{', '"', 'd', 'e', - 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', - 'i', 'f', 'i', 'c', ' ', '(', 'J', 'a', 'k', 'a', 'r', 't', 'a', ')', '"', '}', ',', '"', 'a', 'p', - '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '4', '"', ':', '{', '"', 'd', 'e', 's', 'c', - 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', - 'i', 'c', ' ', '(', 'M', 'e', 'l', 'b', 'o', 'u', 'r', 'n', 'e', ')', '"', '}', ',', '"', 'a', 'p', - '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '5', '"', ':', '{', '"', 'd', 'e', 's', 'c', - 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', 'c', 'i', 'f', - 'i', 'c', ' ', '(', 'M', 'a', 'l', 'a', 'y', 's', 'i', 'a', ')', '"', '}', ',', '"', 'a', 'w', 's', - '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', - 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', ' ', 'g', 'l', - 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'c', 'a', '-', 'c', 'e', - 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', - 'o', 'n', '"', ':', '"', 'C', 'a', 'n', 'a', 'd', 'a', ' ', '(', 'C', 'e', 'n', 't', 'r', 'a', 'l', - ')', '"', '}', ',', '"', 'c', 'a', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', - 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'a', 'n', 'a', 'd', 'a', ' ', 'W', - 'e', 's', 't', ' ', '(', 'C', 'a', 'l', 'g', 'a', 'r', 'y', ')', '"', '}', ',', '"', 'e', 'u', '-', - 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', - 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'F', 'r', 'a', 'n', 'k', - 'f', 'u', 'r', 't', ')', '"', '}', ',', '"', 'e', 'u', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', - '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', - 'u', 'r', 'o', 'p', 'e', ' ', '(', 'Z', 'u', 'r', 'i', 'c', 'h', ')', '"', '}', ',', '"', 'e', 'u', - '-', 'n', 'o', 'r', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', - 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'S', 't', 'o', 'c', 'k', 'h', - 'o', 'l', 'm', ')', '"', '}', ',', '"', 'e', 'u', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', - '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', - 'p', 'e', ' ', '(', 'M', 'i', 'l', 'a', 'n', ')', '"', '}', ',', '"', 'e', 'u', '-', 's', 'o', 'u', - 't', 'h', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', - ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'S', 'p', 'a', 'i', 'n', ')', '"', '}', ',', '"', - 'e', 'u', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', - 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'I', 'r', 'e', 'l', 'a', - 'n', 'd', ')', '"', '}', ',', '"', 'e', 'u', '-', 'w', 'e', 's', 't', '-', '2', '"', ':', '{', '"', - 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', - ' ', '(', 'L', 'o', 'n', 'd', 'o', 'n', ')', '"', '}', ',', '"', 'e', 'u', '-', 'w', 'e', 's', 't', - '-', '3', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', - 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'P', 'a', 'r', 'i', 's', ')', '"', '}', ',', '"', 'i', 'l', + 'P', 'a', 'c', 'i', 'f', 'i', 'c', ' ', '(', 'J', 'a', 'k', 'a', 'r', 't', 'a', ')', '"', '}', ',', + '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '4', '"', ':', '{', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', + 'c', 'i', 'f', 'i', 'c', ' ', '(', 'M', 'e', 'l', 'b', 'o', 'u', 'r', 'n', 'e', ')', '"', '}', ',', + '"', 'a', 'p', '-', 's', 'o', 'u', 't', 'h', 'e', 'a', 's', 't', '-', '5', '"', ':', '{', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 's', 'i', 'a', ' ', 'P', 'a', + 'c', 'i', 'f', 'i', 'c', ' ', '(', 'M', 'a', 'l', 'a', 'y', 's', 'i', 'a', ')', '"', '}', ',', '"', + 'a', 'w', 's', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', + 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'S', 't', 'a', 'n', 'd', 'a', 'r', 'd', + ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'c', 'a', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', - 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'I', 's', 'r', 'a', 'e', 'l', ' ', '(', 'T', 'e', 'l', ' ', - 'A', 'v', 'i', 'v', ')', '"', '}', ',', '"', 'm', 'e', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', - '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'M', - 'i', 'd', 'd', 'l', 'e', ' ', 'E', 'a', 's', 't', ' ', '(', 'U', 'A', 'E', ')', '"', '}', ',', '"', - 'm', 'e', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', - 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'M', 'i', 'd', 'd', 'l', 'e', ' ', 'E', 'a', 's', 't', ' ', - '(', 'B', 'a', 'h', 'r', 'a', 'i', 'n', ')', '"', '}', ',', '"', 's', 'a', '-', 'e', 'a', 's', 't', - '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', - 'S', 'o', 'u', 't', 'h', ' ', 'A', 'm', 'e', 'r', 'i', 'c', 'a', ' ', '(', 'S', 'a', 'o', ' ', 'P', - 'a', 'u', 'l', 'o', ')', '"', '}', ',', '"', 'u', 's', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', - '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'E', - 'a', 's', 't', ' ', '(', 'N', '.', ' ', 'V', 'i', 'r', 'g', 'i', 'n', 'i', 'a', ')', '"', '}', ',', - '"', 'u', 's', '-', 'e', 'a', 's', 't', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', - 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'E', 'a', 's', 't', ' ', '(', 'O', 'h', 'i', - 'o', ')', '"', '}', ',', '"', 'u', 's', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', - 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'W', 'e', 's', 't', - ' ', '(', 'N', '.', ' ', 'C', 'a', 'l', 'i', 'f', 'o', 'r', 'n', 'i', 'a', ')', '"', '}', ',', '"', - 'u', 's', '-', 'w', 'e', 's', 't', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', - 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'W', 'e', 's', 't', ' ', '(', 'O', 'r', 'e', 'g', - 'o', 'n', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'c', - 'n', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', - 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'm', 'a', 'z', 'o', 'n', 'a', 'w', 's', '.', 'c', 'o', 'm', - '.', 'c', 'n', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', - 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'p', 'i', '.', 'a', 'm', 'a', 'z', 'o', 'n', 'w', 'e', 'b', - 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', '.', 'c', 'o', 'm', '.', 'c', 'n', '"', ',', '"', 'i', 'm', - 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', - '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', 'w', 'e', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', - 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'c', 'n', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', - 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', ',', '"', 's', - 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', - 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'c', 'n', '\\', '\\', '-', - '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', - 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'c', 'n', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', - ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', - ' ', 'C', 'h', 'i', 'n', 'a', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', - '"', '}', ',', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', - 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'h', 'i', 'n', 'a', ' ', '(', 'B', - 'e', 'i', 'j', 'i', 'n', 'g', ')', '"', '}', ',', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', 'w', - 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', - '"', ':', '"', 'C', 'h', 'i', 'n', 'a', ' ', '(', 'N', 'i', 'n', 'g', 'x', 'i', 'a', ')', '"', '}', - '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'u', 's', '-', 'g', 'o', 'v', - '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', - 'f', 'i', 'x', '"', ':', '"', 'a', 'm', 'a', 'z', 'o', 'n', 'a', 'w', 's', '.', 'c', 'o', 'm', '"', - ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', - '"', ':', '"', 'a', 'p', 'i', '.', 'a', 'w', 's', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', - 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'g', - 'o', 'v', '-', 'w', 'e', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', - 'w', 's', '-', 'u', 's', '-', 'g', 'o', 'v', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', - 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', ',', '"', 's', 'u', 'p', - 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', - 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'g', 'o', - 'v', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', - 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'u', 's', '-', 'g', 'o', 'v', - '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', - 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', 'o', 'u', 'd', ' ', '(', 'U', - 'S', ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', - 'u', 's', '-', 'g', 'o', 'v', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', - 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', - 'o', 'u', 'd', ' ', '(', 'U', 'S', '-', 'E', 'a', 's', 't', ')', '"', '}', ',', '"', 'u', 's', '-', - 'g', 'o', 'v', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', + 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'a', 'n', 'a', 'd', 'a', ' ', '(', 'C', 'e', 'n', 't', + 'r', 'a', 'l', ')', '"', '}', ',', '"', 'c', 'a', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', + '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'a', 'n', 'a', 'd', + 'a', ' ', 'W', 'e', 's', 't', ' ', '(', 'C', 'a', 'l', 'g', 'a', 'r', 'y', ')', '"', '}', ',', '"', + 'e', 'u', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', + 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'F', 'r', + 'a', 'n', 'k', 'f', 'u', 'r', 't', ')', '"', '}', ',', '"', 'e', 'u', '-', 'c', 'e', 'n', 't', 'r', + 'a', 'l', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', + ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'Z', 'u', 'r', 'i', 'c', 'h', ')', '"', '}', ',', + '"', 'e', 'u', '-', 'n', 'o', 'r', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', + 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'S', 't', 'o', + 'c', 'k', 'h', 'o', 'l', 'm', ')', '"', '}', ',', '"', 'e', 'u', '-', 's', 'o', 'u', 't', 'h', '-', + '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', + 'u', 'r', 'o', 'p', 'e', ' ', '(', 'M', 'i', 'l', 'a', 'n', ')', '"', '}', ',', '"', 'e', 'u', '-', + 's', 'o', 'u', 't', 'h', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', + 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'S', 'p', 'a', 'i', 'n', ')', '"', + '}', ',', '"', 'e', 'u', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', + 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'I', 'r', + 'e', 'l', 'a', 'n', 'd', ')', '"', '}', ',', '"', 'e', 'u', '-', 'w', 'e', 's', 't', '-', '2', '"', + ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'u', 'r', + 'o', 'p', 'e', ' ', '(', 'L', 'o', 'n', 'd', 'o', 'n', ')', '"', '}', ',', '"', 'e', 'u', '-', 'w', + 'e', 's', 't', '-', '3', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', + '"', ':', '"', 'E', 'u', 'r', 'o', 'p', 'e', ' ', '(', 'P', 'a', 'r', 'i', 's', ')', '"', '}', ',', + '"', 'i', 'l', '-', 'c', 'e', 'n', 't', 'r', 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', + 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'I', 's', 'r', 'a', 'e', 'l', ' ', '(', 'T', + 'e', 'l', ' ', 'A', 'v', 'i', 'v', ')', '"', '}', ',', '"', 'm', 'e', '-', 'c', 'e', 'n', 't', 'r', + 'a', 'l', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', + ':', '"', 'M', 'i', 'd', 'd', 'l', 'e', ' ', 'E', 'a', 's', 't', ' ', '(', 'U', 'A', 'E', ')', '"', + '}', ',', '"', 'm', 'e', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', + 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'M', 'i', 'd', 'd', 'l', 'e', ' ', 'E', 'a', + 's', 't', ' ', '(', 'B', 'a', 'h', 'r', 'a', 'i', 'n', ')', '"', '}', ',', '"', 's', 'a', '-', 'e', + 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', + '"', ':', '"', 'S', 'o', 'u', 't', 'h', ' ', 'A', 'm', 'e', 'r', 'i', 'c', 'a', ' ', '(', 'S', 'a', + 'o', ' ', 'P', 'a', 'u', 'l', 'o', ')', '"', '}', ',', '"', 'u', 's', '-', 'e', 'a', 's', 't', '-', + '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', + 'S', ' ', 'E', 'a', 's', 't', ' ', '(', 'N', '.', ' ', 'V', 'i', 'r', 'g', 'i', 'n', 'i', 'a', ')', + '"', '}', ',', '"', 'u', 's', '-', 'e', 'a', 's', 't', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', + 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'E', 'a', 's', 't', ' ', '(', + 'O', 'h', 'i', 'o', ')', '"', '}', ',', '"', 'u', 's', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', + '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'W', + 'e', 's', 't', ' ', '(', 'N', '.', ' ', 'C', 'a', 'l', 'i', 'f', 'o', 'r', 'n', 'i', 'a', ')', '"', + '}', ',', '"', 'u', 's', '-', 'w', 'e', 's', 't', '-', '2', '"', ':', '{', '"', 'd', 'e', 's', 'c', + 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'W', 'e', 's', 't', ' ', '(', 'O', + 'r', 'e', 'g', 'o', 'n', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', + 's', '-', 'c', 'n', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', + 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'm', 'a', 'z', 'o', 'n', 'a', 'w', 's', '.', + 'c', 'o', 'm', '.', 'c', 'n', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', + 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'p', 'i', '.', 'a', 'm', 'a', 'z', 'o', 'n', + 'w', 'e', 'b', 's', 'e', 'r', 'v', 'i', 'c', 'e', 's', '.', 'c', 'o', 'm', '.', 'c', 'n', '"', ',', + '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', + 'n', '"', ':', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', 'w', 'e', 's', 't', '-', '1', '"', ',', + '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'c', 'n', '"', ',', '"', 's', 'u', 'p', + 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', + ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', + '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'c', 'n', + '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', + 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'c', 'n', '-', 'g', 'l', 'o', 'b', + 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', + 'A', 'W', 'S', ' ', 'C', 'h', 'i', 'n', 'a', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', + 'i', 'o', 'n', '"', '}', ',', '"', 'c', 'n', '-', 'n', 'o', 'r', 't', 'h', '-', '1', '"', ':', '{', + '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'C', 'h', 'i', 'n', 'a', + ' ', '(', 'B', 'e', 'i', 'j', 'i', 'n', 'g', ')', '"', '}', ',', '"', 'c', 'n', '-', 'n', 'o', 'r', + 't', 'h', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', + 'i', 'o', 'n', '"', ':', '"', 'C', 'h', 'i', 'n', 'a', ' ', '(', 'N', 'i', 'n', 'g', 'x', 'i', 'a', + ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'u', 's', '-', + 'g', 'o', 'v', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', + 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'a', 'm', 'a', 'z', 'o', 'n', 'a', 'w', 's', '.', 'c', + 'o', 'm', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', 'f', + 'f', 'i', 'x', '"', ':', '"', 'a', 'p', 'i', '.', 'a', 'w', 's', '"', ',', '"', 'i', 'm', 'p', 'l', + 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', + 's', '-', 'g', 'o', 'v', '-', 'w', 'e', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', + ':', '"', 'a', 'w', 's', '-', 'u', 's', '-', 'g', 'o', 'v', '"', ',', '"', 's', 'u', 'p', 'p', 'o', + 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 't', 'r', 'u', 'e', ',', '"', + 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', + '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', + '-', 'g', 'o', 'v', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', + ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'u', 's', '-', + 'g', 'o', 'v', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', 'o', 'u', 'd', - ' ', '(', 'U', 'S', '-', 'W', 'e', 's', 't', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', - ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', - ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', '2', 's', '.', 'i', - 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', - 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', '2', 's', '.', 'i', 'c', '.', 'g', 'o', 'v', '"', - ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', - 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'e', 'a', 's', 't', '-', '1', '"', ',', - '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '"', ',', '"', 's', 'u', - 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', - 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', - 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', - 'u', 's', '\\', '\\', '-', 'i', 's', 'o', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', - 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', - '-', 'i', 's', 'o', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', - 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'I', 'S', 'O', ' ', '(', 'U', 'S', - ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'u', - 's', '-', 'i', 's', 'o', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', - 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'I', 'S', 'O', ' ', 'E', 'a', 's', - 't', '"', '}', ',', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', - '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'I', - 'S', 'O', ' ', 'W', 'E', 'S', 'T', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', - 'w', 's', '-', 'i', 's', 'o', '-', 'b', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', - '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 's', 'c', '2', 's', '.', 's', - 'g', 'o', 'v', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', - 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 's', 'c', '2', 's', '.', 's', 'g', 'o', 'v', - '.', 'g', 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', - 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', 'b', '-', 'e', 'a', + ' ', '(', 'U', 'S', ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', + '}', ',', '"', 'u', 's', '-', 'g', 'o', 'v', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', + 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', + 'v', 'C', 'l', 'o', 'u', 'd', ' ', '(', 'U', 'S', '-', 'E', 'a', 's', 't', ')', '"', '}', ',', '"', + 'u', 's', '-', 'g', 'o', 'v', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', + 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'G', 'o', 'v', 'C', 'l', + 'o', 'u', 'd', ' ', '(', 'U', 'S', '-', 'W', 'e', 's', 't', ')', '"', '}', '}', '}', ',', '{', '"', + 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '"', ',', '"', 'o', 'u', 't', 'p', 'u', + 't', 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', '2', + 's', '.', 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', + 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', '2', 's', '.', 'i', 'c', '.', 'g', + 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', + 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'e', 'a', 's', 't', '-', + '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '"', ',', + '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', + 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', + ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', + ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', + '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', + 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'g', 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', + 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'A', 'W', 'S', ' ', 'I', 'S', 'O', ' ', + '(', 'U', 'S', ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', + ',', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', + 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'I', 'S', 'O', ' ', + 'E', 'a', 's', 't', '"', '}', ',', '"', 'u', 's', '-', 'i', 's', 'o', '-', 'w', 'e', 's', 't', '-', + '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'U', + 'S', ' ', 'I', 'S', 'O', ' ', 'W', 'E', 'S', 'T', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', + ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'b', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', + 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 's', 'c', '2', + 's', '.', 's', 'g', 'o', 'v', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', + 'c', 'k', 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 's', 'c', '2', 's', '.', 's', + 'g', 'o', 'v', '.', 'g', 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', + 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', 'b', + '-', 'e', 'a', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', + '-', 'i', 's', 'o', '-', 'b', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', + 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', + 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', + 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', 'b', + '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', + 'g', 'i', 'o', 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'b', '-', 'g', + 'l', 'o', 'b', 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', + '"', ':', '"', 'A', 'W', 'S', ' ', 'I', 'S', 'O', 'B', ' ', '(', 'U', 'S', ')', ' ', 'g', 'l', 'o', + 'b', 'a', 'l', ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'u', 's', '-', 'i', 's', 'o', + 'b', '-', 'e', 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', + 'i', 'o', 'n', '"', ':', '"', 'U', 'S', ' ', 'I', 'S', 'O', 'B', ' ', 'E', 'a', 's', 't', ' ', '(', + 'O', 'h', 'i', 'o', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', + '-', 'i', 's', 'o', '-', 'e', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', + 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 'l', 'o', 'u', 'd', '.', 'a', 'd', + 'c', '-', 'e', '.', 'u', 'k', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', + 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 'l', 'o', 'u', 'd', '.', 'a', 'd', 'c', '-', + 'e', '.', 'u', 'k', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', + 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'e', 'u', '-', 'i', 's', 'o', 'e', '-', 'w', 'e', 's', 't', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', - 'o', '-', 'b', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', + 'o', '-', 'e', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', - 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', 'b', '\\', '\\', '-', + 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'e', 'u', '\\', '\\', '-', 'i', 's', 'o', 'e', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', - 'n', 's', '"', ':', '{', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'b', '-', 'g', 'l', 'o', 'b', - 'a', 'l', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', - 'A', 'W', 'S', ' ', 'I', 'S', 'O', 'B', ' ', '(', 'U', 'S', ')', ' ', 'g', 'l', 'o', 'b', 'a', 'l', - ' ', 'r', 'e', 'g', 'i', 'o', 'n', '"', '}', ',', '"', 'u', 's', '-', 'i', 's', 'o', 'b', '-', 'e', - 'a', 's', 't', '-', '1', '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', - '"', ':', '"', 'U', 'S', ' ', 'I', 'S', 'O', 'B', ' ', 'E', 'a', 's', 't', ' ', '(', 'O', 'h', 'i', - 'o', ')', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', - 'o', '-', 'e', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', '{', '"', 'd', 'n', 's', - 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 'l', 'o', 'u', 'd', '.', 'a', 'd', 'c', '-', 'e', - '.', 'u', 'k', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', - 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 'l', 'o', 'u', 'd', '.', 'a', 'd', 'c', '-', 'e', '.', 'u', - 'k', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', - 'g', 'i', 'o', 'n', '"', ':', '"', 'e', 'u', '-', 'i', 's', 'o', 'e', '-', 'w', 'e', 's', 't', '-', - '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'e', - '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', - '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', - 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', - 'x', '"', ':', '"', '^', 'e', 'u', '\\', '\\', '-', 'i', 's', 'o', 'e', '\\', '\\', '-', '\\', '\\', 'w', - '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', - ':', '{', '"', 'e', 'u', '-', 'i', 's', 'o', 'e', '-', 'w', 'e', 's', 't', '-', '1', '"', ':', '{', - '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'U', ' ', 'I', 'S', - 'O', 'E', ' ', 'W', 'e', 's', 't', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', ':', '"', 'a', - 'w', 's', '-', 'i', 's', 'o', '-', 'f', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', 's', '"', ':', - '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 's', 'p', '.', 'h', 'c', - 'i', '.', 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', - 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 's', 'p', '.', 'h', 'c', 'i', '.', - 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', 'G', 'l', 'o', - 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', 'o', 'f', '-', - 's', 'o', 'u', 't', 'h', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', 'a', 'w', 's', - '-', 'i', 's', 'o', '-', 'f', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', 'D', 'u', 'a', - 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', 'p', 'p', 'o', - 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', 'e', 'g', 'i', - 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', 's', 'o', 'f', - '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', '"', 'r', 'e', - 'g', 'i', 'o', 'n', 's', '"', ':', '{', '}', '}', ']', ',', '"', 'v', 'e', 'r', 's', 'i', 'o', 'n', - '"', ':', '"', '1', '.', '1', '"', '}'}; + 'n', 's', '"', ':', '{', '"', 'e', 'u', '-', 'i', 's', 'o', 'e', '-', 'w', 'e', 's', 't', '-', '1', + '"', ':', '{', '"', 'd', 'e', 's', 'c', 'r', 'i', 'p', 't', 'i', 'o', 'n', '"', ':', '"', 'E', 'U', + ' ', 'I', 'S', 'O', 'E', ' ', 'W', 'e', 's', 't', '"', '}', '}', '}', ',', '{', '"', 'i', 'd', '"', + ':', '"', 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'f', '"', ',', '"', 'o', 'u', 't', 'p', 'u', 't', + 's', '"', ':', '{', '"', 'd', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 's', 'p', + '.', 'h', 'c', 'i', '.', 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'd', 'u', 'a', 'l', 'S', 't', + 'a', 'c', 'k', 'D', 'n', 's', 'S', 'u', 'f', 'f', 'i', 'x', '"', ':', '"', 'c', 's', 'p', '.', 'h', + 'c', 'i', '.', 'i', 'c', '.', 'g', 'o', 'v', '"', ',', '"', 'i', 'm', 'p', 'l', 'i', 'c', 'i', 't', + 'G', 'l', 'o', 'b', 'a', 'l', 'R', 'e', 'g', 'i', 'o', 'n', '"', ':', '"', 'u', 's', '-', 'i', 's', + 'o', 'f', '-', 's', 'o', 'u', 't', 'h', '-', '1', '"', ',', '"', 'n', 'a', 'm', 'e', '"', ':', '"', + 'a', 'w', 's', '-', 'i', 's', 'o', '-', 'f', '"', ',', '"', 's', 'u', 'p', 'p', 'o', 'r', 't', 's', + 'D', 'u', 'a', 'l', 'S', 't', 'a', 'c', 'k', '"', ':', 'f', 'a', 'l', 's', 'e', ',', '"', 's', 'u', + 'p', 'p', 'o', 'r', 't', 's', 'F', 'I', 'P', 'S', '"', ':', 't', 'r', 'u', 'e', '}', ',', '"', 'r', + 'e', 'g', 'i', 'o', 'n', 'R', 'e', 'g', 'e', 'x', '"', ':', '"', '^', 'u', 's', '\\', '\\', '-', 'i', + 's', 'o', 'f', '\\', '\\', '-', '\\', '\\', 'w', '+', '\\', '\\', '-', '\\', '\\', 'd', '+', '$', '"', ',', + '"', 'r', 'e', 'g', 'i', 'o', 'n', 's', '"', ':', '{', '}', '}', ']', ',', '"', 'v', 'e', 'r', 's', + 'i', 'o', 'n', '"', ':', '"', '1', '.', '1', '"', '}'}; const struct aws_byte_cursor aws_s3_endpoint_resolver_partitions = { - .len = 4088, + .len = 4091, .ptr = (uint8_t *) s_generated_array }; From aede1d8c24f9f580d5a96c089878e9b258b88d04 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 18 Sep 2024 14:46:59 -0700 Subject: [PATCH 4/7] Fix MemoryLimit primary_allocated stat (#453) --- source/s3_buffer_pool.c | 1 + tests/s3_buffer_pool_tests.c | 1 + 2 files changed, 2 insertions(+) diff --git a/source/s3_buffer_pool.c b/source/s3_buffer_pool.c index a4396c6a3..9d63fe65e 100644 --- a/source/s3_buffer_pool.c +++ b/source/s3_buffer_pool.c @@ -232,6 +232,7 @@ void s_buffer_pool_trim_synced(struct aws_s3_buffer_pool *buffer_pool) { aws_array_list_get_at_ptr(&buffer_pool->blocks, (void **)&block, i); if (block->alloc_bit_mask == 0) { + buffer_pool->primary_allocated -= block->block_size; aws_mem_release(buffer_pool->base_allocator, block->block_ptr); aws_array_list_erase(&buffer_pool->blocks, i); /* do not increment since we just released element */ diff --git a/tests/s3_buffer_pool_tests.c b/tests/s3_buffer_pool_tests.c index bdf05378d..5f184745e 100644 --- a/tests/s3_buffer_pool_tests.c +++ b/tests/s3_buffer_pool_tests.c @@ -158,6 +158,7 @@ static int s_test_s3_buffer_pool_trim(struct aws_allocator *allocator, void *ctx struct aws_s3_buffer_pool_usage_stats stats_after = aws_s3_buffer_pool_get_usage(buffer_pool); ASSERT_TRUE(stats_before.primary_num_blocks > stats_after.primary_num_blocks); + ASSERT_TRUE(stats_before.primary_allocated > stats_after.primary_allocated); for (size_t i = 20; i < 40; ++i) { aws_s3_buffer_pool_release_ticket(buffer_pool, tickets[i]); From ab70f742962131f1d024deb794d86f210bfa4252 Mon Sep 17 00:00:00 2001 From: Dengke Tang Date: Mon, 14 Oct 2024 10:25:12 -0700 Subject: [PATCH 5/7] supports copy object for same directory bucket (#455) Co-authored-by: Waqar Ahmed Khan --- include/aws/s3/private/s3_meta_request_impl.h | 8 + include/aws/s3/s3_client.h | 1 + source/s3_copy_object.c | 26 +- source/s3_meta_request.c | 20 +- tests/CMakeLists.txt | 2 + tests/s3_data_plane_tests.c | 282 +++--------------- tests/s3_s3express_client_test.c | 44 +++ tests/s3_tester.c | 258 ++++++++++++++++ tests/s3_tester.h | 27 ++ tests/test_helper/README.md | 4 + tests/test_helper/test_helper.py | 9 +- 11 files changed, 435 insertions(+), 246 deletions(-) diff --git a/include/aws/s3/private/s3_meta_request_impl.h b/include/aws/s3/private/s3_meta_request_impl.h index bde40ebc4..024680c8f 100644 --- a/include/aws/s3/private/s3_meta_request_impl.h +++ b/include/aws/s3/private/s3_meta_request_impl.h @@ -350,6 +350,14 @@ void aws_s3_meta_request_init_signing_date_time_default( struct aws_s3_meta_request *meta_request, struct aws_date_time *date_time); +AWS_S3_API +void aws_s3_meta_request_sign_request_default_impl( + struct aws_s3_meta_request *meta_request, + struct aws_s3_request *request, + aws_signing_complete_fn *on_signing_complete, + void *user_data, + bool disable_s3_express_signing); + AWS_S3_API void aws_s3_meta_request_sign_request_default( struct aws_s3_meta_request *meta_request, diff --git a/include/aws/s3/s3_client.h b/include/aws/s3/s3_client.h index 365f18372..3609b343a 100644 --- a/include/aws/s3/s3_client.h +++ b/include/aws/s3/s3_client.h @@ -82,6 +82,7 @@ enum aws_s3_meta_request_type { * - only {bucket}/{key} format is supported for source and passing arn as * source will not work * - source bucket is assumed to be in the same region as dest + * - source bucket and dest bucket must both be either directory buckets or regular buckets. */ AWS_S3_META_REQUEST_TYPE_COPY_OBJECT, diff --git a/source/s3_copy_object.c b/source/s3_copy_object.c index 86dec71a4..432a6902a 100644 --- a/source/s3_copy_object.c +++ b/source/s3_copy_object.c @@ -37,12 +37,18 @@ static void s_s3_copy_object_request_finished( struct aws_s3_request *request, int error_code); +static void s_s3_copy_object_sign_request( + struct aws_s3_meta_request *meta_request, + struct aws_s3_request *request, + aws_signing_complete_fn *on_signing_complete, + void *user_data); + static struct aws_s3_meta_request_vtable s_s3_copy_object_vtable = { .update = s_s3_copy_object_update, .send_request_finish = aws_s3_meta_request_send_request_finish_default, .prepare_request = s_s3_copy_object_prepare_request, .init_signing_date_time = aws_s3_meta_request_init_signing_date_time_default, - .sign_request = aws_s3_meta_request_sign_request_default, + .sign_request = s_s3_copy_object_sign_request, .finished_request = s_s3_copy_object_request_finished, .destroy = s_s3_meta_request_copy_object_destroy, .finish = aws_s3_meta_request_finish_default, @@ -796,3 +802,21 @@ static void s_s3_copy_object_request_finished( aws_s3_meta_request_unlock_synced_data(meta_request); } + +static void s_s3_copy_object_sign_request( + struct aws_s3_meta_request *meta_request, + struct aws_s3_request *request, + aws_signing_complete_fn *on_signing_complete, + void *user_data) { + + /** + * https://docs.aws.amazon.com/AmazonS3/latest/API/API_UploadPartCopy.html + * https://docs.aws.amazon.com/AmazonS3/latest/API/API_CopyObject.html + * For CopyObject and UploadPartCopy, the request has to be signed with IAM credentials for directory buckets. + * Disable S3 express signing for those types. + */ + bool disable_s3_express_signing = request->request_tag == AWS_S3_COPY_OBJECT_REQUEST_TAG_BYPASS || + request->request_tag == AWS_S3_COPY_OBJECT_REQUEST_TAG_MULTIPART_COPY; + aws_s3_meta_request_sign_request_default_impl( + meta_request, request, on_signing_complete, user_data, disable_s3_express_signing); +} diff --git a/source/s3_meta_request.c b/source/s3_meta_request.c index ccafe63ca..a78034c4c 100644 --- a/source/s3_meta_request.c +++ b/source/s3_meta_request.c @@ -900,12 +900,12 @@ static int s_meta_request_resolve_signing_config( return AWS_OP_SUCCESS; } -/* Handles signing a message for the caller. */ -void aws_s3_meta_request_sign_request_default( +void aws_s3_meta_request_sign_request_default_impl( struct aws_s3_meta_request *meta_request, struct aws_s3_request *request, aws_signing_complete_fn *on_signing_complete, - void *user_data) { + void *user_data, + bool disable_s3_express_signing) { AWS_PRECONDITION(meta_request); AWS_PRECONDITION(request); AWS_PRECONDITION(on_signing_complete); @@ -947,7 +947,7 @@ void aws_s3_meta_request_sign_request_default( return; } - if (signing_config.algorithm == AWS_SIGNING_ALGORITHM_V4_S3EXPRESS) { + if (signing_config.algorithm == AWS_SIGNING_ALGORITHM_V4_S3EXPRESS && !disable_s3_express_signing) { /* Fetch credentials from S3 Express provider. */ struct aws_get_s3express_credentials_user_data *context = aws_mem_calloc(meta_request->allocator, 1, sizeof(struct aws_get_s3express_credentials_user_data)); @@ -998,6 +998,9 @@ void aws_s3_meta_request_sign_request_default( } } else { /* Regular signing. */ + if (disable_s3_express_signing) { + signing_config.algorithm = AWS_SIGNING_ALGORITHM_V4; + } s_s3_meta_request_init_signing_date_time(meta_request, &signing_config.date); if (aws_sign_request_aws( meta_request->allocator, @@ -1015,6 +1018,15 @@ void aws_s3_meta_request_sign_request_default( } } +/* Handles signing a message for the caller. */ +void aws_s3_meta_request_sign_request_default( + struct aws_s3_meta_request *meta_request, + struct aws_s3_request *request, + aws_signing_complete_fn *on_signing_complete, + void *user_data) { + aws_s3_meta_request_sign_request_default_impl(meta_request, request, on_signing_complete, user_data, false); +} + /* Handle the signing result */ static void s_s3_meta_request_request_on_signed( struct aws_signing_result *signing_result, diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 423c1bf54..81462114e 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -348,6 +348,8 @@ add_net_test_case(s3express_client_put_object_long_running_session_refresh) add_net_test_case(s3express_client_get_object) add_net_test_case(s3express_client_get_object_multiple) add_net_test_case(s3express_client_get_object_create_session_error) +add_net_test_case(s3express_client_copy_object) +add_net_test_case(s3express_client_copy_object_multipart) add_net_test_case(meta_request_auto_ranged_get_new_error_handling) add_net_test_case(meta_request_auto_ranged_put_new_error_handling) diff --git a/tests/s3_data_plane_tests.c b/tests/s3_data_plane_tests.c index e36590a23..1e7beb96d 100644 --- a/tests/s3_data_plane_tests.c +++ b/tests/s3_data_plane_tests.c @@ -6261,167 +6261,15 @@ static int s_test_s3_invalid_empty_file_with_range(struct aws_allocator *allocat return 0; } -static const struct aws_byte_cursor g_x_amz_copy_source_name = - AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source"); - -struct aws_http_message *copy_object_request_new( - struct aws_allocator *allocator, - struct aws_byte_cursor x_amz_source, - struct aws_byte_cursor endpoint, - struct aws_byte_cursor destination_key) { - - AWS_PRECONDITION(allocator); - - struct aws_http_message *message = aws_http_message_new_request(allocator); - - if (message == NULL) { - return NULL; - } - - /* the URI path is / followed by the key */ - char destination_path[1024]; - snprintf(destination_path, sizeof(destination_path), "/%.*s", (int)destination_key.len, destination_key.ptr); - struct aws_byte_cursor unencoded_destination_path = aws_byte_cursor_from_c_str(destination_path); - struct aws_byte_buf copy_destination_path_encoded; - aws_byte_buf_init(©_destination_path_encoded, allocator, 1024); - aws_byte_buf_append_encoding_uri_path(©_destination_path_encoded, &unencoded_destination_path); - if (aws_http_message_set_request_path(message, aws_byte_cursor_from_buf(©_destination_path_encoded))) { - goto error_clean_up_message; - } - - struct aws_http_header host_header = {.name = g_host_header_name, .value = endpoint}; - if (aws_http_message_add_header(message, host_header)) { - goto error_clean_up_message; - } - - struct aws_byte_buf copy_source_value_encoded; - aws_byte_buf_init(©_source_value_encoded, allocator, 1024); - aws_byte_buf_append_encoding_uri_path(©_source_value_encoded, &x_amz_source); - - struct aws_http_header copy_source_header = { - .name = g_x_amz_copy_source_name, - .value = aws_byte_cursor_from_buf(©_source_value_encoded), - }; - - if (aws_http_message_add_header(message, copy_source_header)) { - goto error_clean_up_message; - } - - if (aws_http_message_set_request_method(message, aws_http_method_put)) { - goto error_clean_up_message; - } - - aws_byte_buf_clean_up(©_source_value_encoded); - aws_byte_buf_clean_up(©_destination_path_encoded); - return message; - -error_clean_up_message: - - aws_byte_buf_clean_up(©_source_value_encoded); - aws_byte_buf_clean_up(©_destination_path_encoded); - if (message != NULL) { - aws_http_message_release(message); - message = NULL; - } - - return NULL; -} - -struct copy_object_test_data { - struct aws_mutex mutex; - struct aws_condition_variable c_var; - bool execution_completed; - bool headers_callback_was_invoked; - int meta_request_error_code; - int response_status_code; - uint64_t progress_callback_content_length; - uint64_t progress_callback_total_bytes_transferred; -}; - -static void s_copy_object_meta_request_finish( - struct aws_s3_meta_request *meta_request, - const struct aws_s3_meta_request_result *meta_request_result, - void *user_data) { - - (void)meta_request; - - struct copy_object_test_data *test_data = user_data; - - /* if error response body is available, dump it to test result to help investigation of failed tests */ - if (meta_request_result->error_response_body != NULL && meta_request_result->error_response_body->len > 0) { - AWS_LOGF_ERROR( - AWS_LS_S3_GENERAL, - "Response error body: %.*s", - (int)meta_request_result->error_response_body->len, - meta_request_result->error_response_body->buffer); - } - - aws_mutex_lock(&test_data->mutex); - test_data->meta_request_error_code = meta_request_result->error_code; - test_data->response_status_code = meta_request_result->response_status; - test_data->execution_completed = true; - aws_mutex_unlock(&test_data->mutex); - aws_condition_variable_notify_one(&test_data->c_var); -} - -static int s_copy_object_meta_request_headers_callback( - struct aws_s3_meta_request *meta_request, - const struct aws_http_headers *headers, - int response_status, - void *user_data) { - - (void)meta_request; - (void)headers; - (void)response_status; - - struct copy_object_test_data *test_data = user_data; - - aws_mutex_lock(&test_data->mutex); - test_data->headers_callback_was_invoked = true; - aws_mutex_unlock(&test_data->mutex); - - return AWS_OP_SUCCESS; -} - -static void s_copy_object_meta_request_progress_callback( - struct aws_s3_meta_request *meta_request, - const struct aws_s3_meta_request_progress *progress, - void *user_data) { - - (void)meta_request; - struct copy_object_test_data *test_data = user_data; - - aws_mutex_lock(&test_data->mutex); - test_data->progress_callback_content_length = progress->content_length; - test_data->progress_callback_total_bytes_transferred += progress->bytes_transferred; - aws_mutex_unlock(&test_data->mutex); -} - -static bool s_copy_test_completion_predicate(void *arg) { - struct copy_object_test_data *test_data = arg; - return test_data->execution_completed; -} - -static int s_test_s3_copy_object_from_x_amz_copy_source( +static int s_test_s3_copy_object_helper( struct aws_allocator *allocator, - struct aws_byte_cursor x_amz_copy_source, + struct aws_byte_cursor source_key, struct aws_byte_cursor destination_key, int expected_error_code, int expected_response_status, uint64_t expected_size) { - struct aws_s3_tester tester; - AWS_ZERO_STRUCT(tester); - ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); - - struct aws_s3_client_config client_config; - AWS_ZERO_STRUCT(client_config); - - ASSERT_SUCCESS(aws_s3_tester_bind_client( - &tester, &client_config, AWS_S3_TESTER_BIND_CLIENT_REGION | AWS_S3_TESTER_BIND_CLIENT_SIGNING)); - - struct aws_s3_client *client = aws_s3_client_new(allocator, &client_config); - + struct aws_byte_cursor source_bucket = g_test_bucket_name; struct aws_byte_cursor destination_bucket = g_test_bucket_name; char endpoint[1024]; @@ -6433,83 +6281,16 @@ static int s_test_s3_copy_object_from_x_amz_copy_source( destination_bucket.ptr, g_test_s3_region.ptr); - /* creates a CopyObject request */ - struct aws_http_message *message = - copy_object_request_new(allocator, x_amz_copy_source, aws_byte_cursor_from_c_str(endpoint), destination_key); - - struct copy_object_test_data test_data; - AWS_ZERO_STRUCT(test_data); - - test_data.c_var = (struct aws_condition_variable)AWS_CONDITION_VARIABLE_INIT; - aws_mutex_init(&test_data.mutex); - - struct aws_s3_meta_request_options meta_request_options = { - .user_data = &test_data, - .body_callback = NULL, - .signing_config = client_config.signing_config, - .finish_callback = s_copy_object_meta_request_finish, - .headers_callback = s_copy_object_meta_request_headers_callback, - .progress_callback = s_copy_object_meta_request_progress_callback, - .message = message, - .shutdown_callback = NULL, - .type = AWS_S3_META_REQUEST_TYPE_COPY_OBJECT, - }; - - struct aws_s3_meta_request *meta_request = aws_s3_client_make_meta_request(client, &meta_request_options); - ASSERT_NOT_NULL(meta_request); - - /* wait completion of the meta request */ - aws_mutex_lock(&test_data.mutex); - aws_condition_variable_wait_pred(&test_data.c_var, &test_data.mutex, s_copy_test_completion_predicate, &test_data); - aws_mutex_unlock(&test_data.mutex); - - /* assert error_code and response_status_code */ - ASSERT_INT_EQUALS(expected_error_code, test_data.meta_request_error_code); - ASSERT_INT_EQUALS(expected_response_status, test_data.response_status_code); - - /* assert that progress_callback matches the expected size*/ - if (test_data.meta_request_error_code == AWS_ERROR_SUCCESS) { - ASSERT_UINT_EQUALS(expected_size, test_data.progress_callback_total_bytes_transferred); - ASSERT_UINT_EQUALS(expected_size, test_data.progress_callback_content_length); - } - - /* assert headers callback was invoked */ - ASSERT_TRUE(test_data.headers_callback_was_invoked); - - aws_s3_meta_request_release(meta_request); - aws_mutex_clean_up(&test_data.mutex); - aws_http_message_destroy(message); - client = aws_s3_client_release(client); - - aws_s3_tester_clean_up(&tester); - - return 0; -} - -static int s_test_s3_copy_object_helper( - struct aws_allocator *allocator, - struct aws_byte_cursor source_key, - struct aws_byte_cursor destination_key, - int expected_error_code, - int expected_response_status, - uint64_t expected_size) { - - struct aws_byte_cursor source_bucket = g_test_bucket_name; - - char copy_source_value[1024]; - snprintf( - copy_source_value, - sizeof(copy_source_value), - "%.*s/%.*s", - (int)source_bucket.len, - source_bucket.ptr, - (int)source_key.len, - source_key.ptr); - - struct aws_byte_cursor x_amz_copy_source = aws_byte_cursor_from_c_str(copy_source_value); - - return s_test_s3_copy_object_from_x_amz_copy_source( - allocator, x_amz_copy_source, destination_key, expected_error_code, expected_response_status, expected_size); + return aws_test_s3_copy_object_helper( + allocator, + source_bucket, + source_key, + aws_byte_cursor_from_c_str(endpoint), + destination_key, + expected_error_code, + expected_response_status, + expected_size, + false); } AWS_TEST_CASE(test_s3_copy_small_object, s_test_s3_copy_small_object) @@ -6594,9 +6375,26 @@ static int s_test_s3_copy_source_prefixed_by_slash(struct aws_allocator *allocat source_key.ptr); struct aws_byte_cursor x_amz_copy_source = aws_byte_cursor_from_c_str(copy_source_value); + struct aws_byte_cursor destination_bucket = g_test_bucket_name; - return s_test_s3_copy_object_from_x_amz_copy_source( - allocator, x_amz_copy_source, destination_key, AWS_ERROR_SUCCESS, AWS_HTTP_STATUS_CODE_200_OK, MB_TO_BYTES(1)); + char endpoint[1024]; + snprintf( + endpoint, + sizeof(endpoint), + "%.*s.s3.%s.amazonaws.com", + (int)destination_bucket.len, + destination_bucket.ptr, + g_test_s3_region.ptr); + + return aws_test_s3_copy_object_from_x_amz_copy_source( + allocator, + x_amz_copy_source, + aws_byte_cursor_from_c_str(endpoint), + destination_key, + AWS_ERROR_SUCCESS, + AWS_HTTP_STATUS_CODE_200_OK, + MB_TO_BYTES(1), + false); } /** @@ -6624,14 +6422,26 @@ static int s_test_s3_copy_source_prefixed_by_slash_multipart(struct aws_allocato source_key.ptr); struct aws_byte_cursor x_amz_copy_source = aws_byte_cursor_from_c_str(copy_source_value); + struct aws_byte_cursor destination_bucket = g_test_bucket_name; + + char endpoint[1024]; + snprintf( + endpoint, + sizeof(endpoint), + "%.*s.s3.%s.amazonaws.com", + (int)destination_bucket.len, + destination_bucket.ptr, + g_test_s3_region.ptr); - return s_test_s3_copy_object_from_x_amz_copy_source( + return aws_test_s3_copy_object_from_x_amz_copy_source( allocator, x_amz_copy_source, + aws_byte_cursor_from_c_str(endpoint), destination_key, AWS_ERROR_SUCCESS, AWS_HTTP_STATUS_CODE_200_OK, - MB_TO_BYTES(256)); + MB_TO_BYTES(256), + false); } static int s_s3_get_object_mrap_helper(struct aws_allocator *allocator, bool multipart) { diff --git a/tests/s3_s3express_client_test.c b/tests/s3_s3express_client_test.c index 7c2e5a017..299dc9ef9 100644 --- a/tests/s3_s3express_client_test.c +++ b/tests/s3_s3express_client_test.c @@ -648,3 +648,47 @@ TEST_CASE(s3express_client_get_object_create_session_error) { aws_s3_tester_clean_up(&tester); return AWS_OP_SUCCESS; } + +/** + * Test copy object within the same directory bucket. + */ +TEST_CASE(s3express_client_copy_object) { + (void)ctx; + + struct aws_byte_cursor source_key = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("pre-existing-10MB"); + struct aws_byte_cursor destination_key = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("copies/destination_10MB"); + struct aws_byte_cursor source_bucket = + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("aws-c-s3-test-bucket--usw2-az1--x-s3"); + return aws_test_s3_copy_object_helper( + allocator, + source_bucket, + source_key, + g_test_s3express_bucket_usw2_az1_endpoint, + destination_key, + AWS_ERROR_SUCCESS, + AWS_HTTP_STATUS_CODE_200_OK, + MB_TO_BYTES(10), + true); +} + +/** + * Test multipart copy object within the same directory bucket. + */ +TEST_CASE(s3express_client_copy_object_multipart) { + (void)ctx; + + struct aws_byte_cursor source_key = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("pre-existing-2GB"); + struct aws_byte_cursor destination_key = AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("copies/destination_2GB"); + struct aws_byte_cursor source_bucket = + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("aws-c-s3-test-bucket--usw2-az1--x-s3"); + return aws_test_s3_copy_object_helper( + allocator, + source_bucket, + source_key, + g_test_s3express_bucket_usw2_az1_endpoint, + destination_key, + AWS_ERROR_SUCCESS, + AWS_HTTP_STATUS_CODE_200_OK, + GB_TO_BYTES(2), + true); +} diff --git a/tests/s3_tester.c b/tests/s3_tester.c index d5ddf4830..2a0711221 100644 --- a/tests/s3_tester.c +++ b/tests/s3_tester.c @@ -2215,3 +2215,261 @@ struct aws_string *aws_s3_tester_create_file( return filepath_str; } + +static const struct aws_byte_cursor g_x_amz_copy_source_name = + AWS_BYTE_CUR_INIT_FROM_STRING_LITERAL("x-amz-copy-source"); + +static struct aws_http_message *s_copy_object_request_new( + struct aws_allocator *allocator, + struct aws_byte_cursor x_amz_source, + struct aws_byte_cursor endpoint, + struct aws_byte_cursor destination_key) { + + AWS_PRECONDITION(allocator); + + struct aws_http_message *message = aws_http_message_new_request(allocator); + + if (message == NULL) { + return NULL; + } + + /* the URI path is / followed by the key */ + char destination_path[1024]; + snprintf(destination_path, sizeof(destination_path), "/%.*s", (int)destination_key.len, destination_key.ptr); + struct aws_byte_cursor unencoded_destination_path = aws_byte_cursor_from_c_str(destination_path); + struct aws_byte_buf copy_destination_path_encoded; + aws_byte_buf_init(©_destination_path_encoded, allocator, 1024); + aws_byte_buf_append_encoding_uri_path(©_destination_path_encoded, &unencoded_destination_path); + if (aws_http_message_set_request_path(message, aws_byte_cursor_from_buf(©_destination_path_encoded))) { + goto error_clean_up_message; + } + + struct aws_http_header host_header = {.name = g_host_header_name, .value = endpoint}; + if (aws_http_message_add_header(message, host_header)) { + goto error_clean_up_message; + } + + struct aws_byte_buf copy_source_value_encoded; + aws_byte_buf_init(©_source_value_encoded, allocator, 1024); + aws_byte_buf_append_encoding_uri_path(©_source_value_encoded, &x_amz_source); + + struct aws_http_header copy_source_header = { + .name = g_x_amz_copy_source_name, + .value = aws_byte_cursor_from_buf(©_source_value_encoded), + }; + + if (aws_http_message_add_header(message, copy_source_header)) { + goto error_clean_up_message; + } + + if (aws_http_message_set_request_method(message, aws_http_method_put)) { + goto error_clean_up_message; + } + + aws_byte_buf_clean_up(©_source_value_encoded); + aws_byte_buf_clean_up(©_destination_path_encoded); + return message; + +error_clean_up_message: + + aws_byte_buf_clean_up(©_source_value_encoded); + aws_byte_buf_clean_up(©_destination_path_encoded); + if (message != NULL) { + aws_http_message_release(message); + message = NULL; + } + + return NULL; +} + +struct copy_object_test_data { + struct aws_mutex mutex; + struct aws_condition_variable c_var; + bool execution_completed; + bool headers_callback_was_invoked; + int meta_request_error_code; + int response_status_code; + uint64_t progress_callback_content_length; + uint64_t progress_callback_total_bytes_transferred; +}; + +static void s_copy_object_meta_request_finish( + struct aws_s3_meta_request *meta_request, + const struct aws_s3_meta_request_result *meta_request_result, + void *user_data) { + + (void)meta_request; + + struct copy_object_test_data *test_data = user_data; + + /* if error response body is available, dump it to test result to help investigation of failed tests */ + if (meta_request_result->error_response_body != NULL && meta_request_result->error_response_body->len > 0) { + AWS_LOGF_ERROR( + AWS_LS_S3_GENERAL, + "Response error body: %.*s", + (int)meta_request_result->error_response_body->len, + meta_request_result->error_response_body->buffer); + } + + aws_mutex_lock(&test_data->mutex); + test_data->meta_request_error_code = meta_request_result->error_code; + test_data->response_status_code = meta_request_result->response_status; + test_data->execution_completed = true; + aws_mutex_unlock(&test_data->mutex); + aws_condition_variable_notify_one(&test_data->c_var); +} + +static int s_copy_object_meta_request_headers_callback( + struct aws_s3_meta_request *meta_request, + const struct aws_http_headers *headers, + int response_status, + void *user_data) { + + (void)meta_request; + (void)headers; + (void)response_status; + + struct copy_object_test_data *test_data = user_data; + + aws_mutex_lock(&test_data->mutex); + test_data->headers_callback_was_invoked = true; + aws_mutex_unlock(&test_data->mutex); + + return AWS_OP_SUCCESS; +} + +static void s_copy_object_meta_request_progress_callback( + struct aws_s3_meta_request *meta_request, + const struct aws_s3_meta_request_progress *progress, + void *user_data) { + + (void)meta_request; + struct copy_object_test_data *test_data = user_data; + + aws_mutex_lock(&test_data->mutex); + test_data->progress_callback_content_length = progress->content_length; + test_data->progress_callback_total_bytes_transferred += progress->bytes_transferred; + aws_mutex_unlock(&test_data->mutex); +} + +static bool s_copy_test_completion_predicate(void *arg) { + struct copy_object_test_data *test_data = arg; + return test_data->execution_completed; +} + +int aws_test_s3_copy_object_from_x_amz_copy_source( + struct aws_allocator *allocator, + struct aws_byte_cursor x_amz_copy_source, + struct aws_byte_cursor destination_endpoint, + struct aws_byte_cursor destination_key, + int expected_error_code, + int expected_response_status, + uint64_t expected_size, + bool s3express) { + struct aws_s3_tester tester; + AWS_ZERO_STRUCT(tester); + ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); + + struct aws_s3_client_config client_config; + AWS_ZERO_STRUCT(client_config); + client_config.enable_s3express = s3express; + struct aws_byte_cursor region_cursor = g_test_s3_region; + client_config.region = region_cursor; + ASSERT_SUCCESS(aws_s3_tester_bind_client(&tester, &client_config, AWS_S3_TESTER_BIND_CLIENT_SIGNING)); + + struct aws_s3_client *client = aws_s3_client_new(allocator, &client_config); + + /* creates a CopyObject request */ + struct aws_http_message *message = + s_copy_object_request_new(allocator, x_amz_copy_source, destination_endpoint, destination_key); + + struct copy_object_test_data test_data; + AWS_ZERO_STRUCT(test_data); + + struct aws_signing_config_aws s3express_signing_config = { + .algorithm = AWS_SIGNING_ALGORITHM_V4_S3EXPRESS, + .service = g_s3express_service_name, + }; + test_data.c_var = (struct aws_condition_variable)AWS_CONDITION_VARIABLE_INIT; + aws_mutex_init(&test_data.mutex); + + struct aws_s3_meta_request_options meta_request_options = { + .user_data = &test_data, + .body_callback = NULL, + .finish_callback = s_copy_object_meta_request_finish, + .headers_callback = s_copy_object_meta_request_headers_callback, + .progress_callback = s_copy_object_meta_request_progress_callback, + .message = message, + .shutdown_callback = NULL, + .signing_config = client_config.signing_config, + .type = AWS_S3_META_REQUEST_TYPE_COPY_OBJECT, + }; + + if (s3express) { + meta_request_options.signing_config = &s3express_signing_config; + } + + struct aws_s3_meta_request *meta_request = aws_s3_client_make_meta_request(client, &meta_request_options); + ASSERT_NOT_NULL(meta_request); + + /* wait completion of the meta request */ + aws_mutex_lock(&test_data.mutex); + aws_condition_variable_wait_pred(&test_data.c_var, &test_data.mutex, s_copy_test_completion_predicate, &test_data); + aws_mutex_unlock(&test_data.mutex); + + /* assert error_code and response_status_code */ + ASSERT_INT_EQUALS(expected_error_code, test_data.meta_request_error_code); + ASSERT_INT_EQUALS(expected_response_status, test_data.response_status_code); + + /* assert that progress_callback matches the expected size*/ + if (test_data.meta_request_error_code == AWS_ERROR_SUCCESS) { + ASSERT_UINT_EQUALS(expected_size, test_data.progress_callback_total_bytes_transferred); + ASSERT_UINT_EQUALS(expected_size, test_data.progress_callback_content_length); + } + + /* assert headers callback was invoked */ + ASSERT_TRUE(test_data.headers_callback_was_invoked); + + aws_s3_meta_request_release(meta_request); + aws_mutex_clean_up(&test_data.mutex); + aws_http_message_destroy(message); + client = aws_s3_client_release(client); + + aws_s3_tester_clean_up(&tester); + + return 0; +} + +int aws_test_s3_copy_object_helper( + struct aws_allocator *allocator, + struct aws_byte_cursor source_bucket, + struct aws_byte_cursor source_key, + struct aws_byte_cursor destination_endpoint, + struct aws_byte_cursor destination_key, + int expected_error_code, + int expected_response_status, + uint64_t expected_size, + bool s3_express) { + + char copy_source_value[1024]; + snprintf( + copy_source_value, + sizeof(copy_source_value), + "%.*s/%.*s", + (int)source_bucket.len, + source_bucket.ptr, + (int)source_key.len, + source_key.ptr); + + struct aws_byte_cursor x_amz_copy_source = aws_byte_cursor_from_c_str(copy_source_value); + + return aws_test_s3_copy_object_from_x_amz_copy_source( + allocator, + x_amz_copy_source, + destination_endpoint, + destination_key, + expected_error_code, + expected_response_status, + expected_size, + s3_express); +} diff --git a/tests/s3_tester.h b/tests/s3_tester.h index 3c2b1f343..e939b5416 100644 --- a/tests/s3_tester.h +++ b/tests/s3_tester.h @@ -515,4 +515,31 @@ extern struct aws_byte_cursor g_test_s3express_bucket_usw2_az1_endpoint; * aws-c-s3-test-bucket--use1-az4--x-s3.s3express-use1-az4.us-east-1.amazonaws.com */ extern struct aws_byte_cursor g_test_s3express_bucket_use1_az4_endpoint; +/** + * Take the source with source_bucket and source_key, and the destination_endpoint to help testing copy object + */ +int aws_test_s3_copy_object_helper( + struct aws_allocator *allocator, + struct aws_byte_cursor source_bucket, + struct aws_byte_cursor source_key, + struct aws_byte_cursor destination_endpoint, + struct aws_byte_cursor destination_key, + int expected_error_code, + int expected_response_status, + uint64_t expected_size, + bool s3_express); + +/** + * Take the source with x_amz_copy_source, and the destination_endpoint to help testing copy object. + */ +int aws_test_s3_copy_object_from_x_amz_copy_source( + struct aws_allocator *allocator, + struct aws_byte_cursor x_amz_copy_source, + struct aws_byte_cursor destination_endpoint, + struct aws_byte_cursor destination_key, + int expected_error_code, + int expected_response_status, + uint64_t expected_size, + bool s3_express); + #endif /* AWS_S3_TESTER_H */ diff --git a/tests/test_helper/README.md b/tests/test_helper/README.md index 7124f190a..7a07ad947 100644 --- a/tests/test_helper/README.md +++ b/tests/test_helper/README.md @@ -46,10 +46,14 @@ python3 test_helper.py clean * Create directory bucket `--usw2-az1--x-s3` in us-west-2 * Upload files: + `pre-existing-10MB` 10MB file. + + with `--large_objects` enabled + - `pre-existing-2GB` * Create directory bucket `--use1-az4--x-s3` in us-east-1 * Upload files: + `pre-existing-10MB` 10MB file. + + with `--large_objects` enabled + - `pre-existing-2GB` ### `clean` action diff --git a/tests/test_helper/test_helper.py b/tests/test_helper/test_helper.py index c58c0cef7..62cdc54b0 100755 --- a/tests/test_helper/test_helper.py +++ b/tests/test_helper/test_helper.py @@ -16,9 +16,6 @@ s3_client_east1 = boto3.client('s3', region_name=REGION_EAST_1) -s3_control_client = boto3.client('s3control') - - MB = 1024*1024 GB = 1024*1024*1024 @@ -166,6 +163,10 @@ def create_bucket_with_lifecycle(availability_zone=None, client=s3_client): put_pre_existing_objects( 10*MB, 'pre-existing-10MB', bucket=bucket_name, client=client) + if args.large_objects: + put_pre_existing_objects( + 2*GB, 'pre-existing-2GB', bucket=bucket_name, client=client) + if availability_zone is None: put_pre_existing_objects( 10*MB, 'pre-existing-10MB-aes256-c', sse='aes256-c', bucket=bucket_name) @@ -186,8 +187,6 @@ def create_bucket_with_lifecycle(availability_zone=None, client=s3_client): 256*MB, 'pre-existing-256MB', bucket=bucket_name) put_pre_existing_objects( 256*MB, 'pre-existing-256MB-@', bucket=bucket_name) - put_pre_existing_objects( - 2*GB, 'pre-existing-2GB', bucket=bucket_name) put_pre_existing_objects( 2*GB, 'pre-existing-2GB-@', bucket=bucket_name) else: From 45eee8d2687717eac7bd5c3a20fb978e50172e11 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 16 Oct 2024 08:39:29 -0700 Subject: [PATCH 6/7] Validate Invalid Network Interface Names at Client Initialization (#456) --- .gitignore | 3 ++ source/s3_client.c | 88 ++++++++++++++++++++++-------------- tests/CMakeLists.txt | 2 + tests/s3_data_plane_tests.c | 55 ++++++++++++++++++++++ tests/s3_mock_server_tests.c | 16 ++++--- 5 files changed, 125 insertions(+), 39 deletions(-) diff --git a/.gitignore b/.gitignore index 6fbfca54f..6046e192d 100644 --- a/.gitignore +++ b/.gitignore @@ -68,3 +68,6 @@ cmake-build* # js package locks irrelevant to the overall package's security benchmarks/benchmarks-stack/benchmarks-stack/package-lock.json benchmarks/dashboard-stack/package-lock.json + +# virtual environment +.venv/ diff --git a/source/s3_client.c b/source/s3_client.c index 80e5462ee..85066027a 100644 --- a/source/s3_client.c +++ b/source/s3_client.c @@ -346,7 +346,7 @@ struct aws_s3_client *aws_s3_client_new( client->buffer_pool = aws_s3_buffer_pool_new(allocator, part_size, mem_limit); if (client->buffer_pool == NULL) { - goto on_early_fail; + goto on_error; } struct aws_s3_buffer_pool_usage_stats pool_usage = aws_s3_buffer_pool_get_usage(client->buffer_pool); @@ -357,7 +357,7 @@ struct aws_s3_client *aws_s3_client_new( "Cannot create client from client_config; configured max part size should not exceed memory limit." "size."); aws_raise_error(AWS_ERROR_S3_INVALID_MEMORY_LIMIT_CONFIG); - goto on_early_fail; + goto on_error; } client->vtable = &s_s3_client_default_vtable; @@ -365,7 +365,7 @@ struct aws_s3_client *aws_s3_client_new( aws_ref_count_init(&client->ref_count, client, (aws_simple_completion_callback *)s_s3_client_start_destroy); if (aws_mutex_init(&client->synced_data.lock) != AWS_OP_SUCCESS) { - goto on_early_fail; + goto on_error; } aws_linked_list_init(&client->synced_data.pending_meta_request_work); @@ -488,6 +488,44 @@ struct aws_s3_client *aws_s3_client_new( } } + client->num_network_interface_names = client_config->num_network_interface_names; + if (client_config->num_network_interface_names > 0) { + AWS_LOGF_DEBUG( + AWS_LS_S3_CLIENT, + "id=%p Client received network interface names array with length %zu.", + (void *)client, + client->num_network_interface_names); + aws_array_list_init_dynamic( + &client->network_interface_names, + client->allocator, + client_config->num_network_interface_names, + sizeof(struct aws_string *)); + client->network_interface_names_cursor_array = aws_mem_calloc( + client->allocator, client_config->num_network_interface_names, sizeof(struct aws_byte_cursor)); + for (size_t i = 0; i < client_config->num_network_interface_names; i++) { + struct aws_byte_cursor interface_name = client_config->network_interface_names_array[i]; + struct aws_string *interface_name_str = aws_string_new_from_cursor(client->allocator, &interface_name); + aws_array_list_push_back(&client->network_interface_names, &interface_name_str); + if (aws_is_network_interface_name_valid(aws_string_c_str(interface_name_str)) == false) { + AWS_LOGF_ERROR( + AWS_LS_S3_CLIENT, + "id=%p network_interface_names_array[%zu]=" PRInSTR " is not valid.", + (void *)client, + i, + AWS_BYTE_CURSOR_PRI(interface_name)); + aws_raise_error(AWS_ERROR_INVALID_ARGUMENT); + goto on_error; + } + client->network_interface_names_cursor_array[i] = aws_byte_cursor_from_string(interface_name_str); + AWS_LOGF_DEBUG( + AWS_LS_S3_CLIENT, + "id=%p network_interface_names_array[%zu]=" PRInSTR "", + (void *)client, + i, + AWS_BYTE_CURSOR_PRI(client->network_interface_names_cursor_array[i])); + } + } + /* Set up body streaming ELG */ { uint16_t num_event_loops = @@ -579,34 +617,6 @@ struct aws_s3_client *aws_s3_client_new( *((bool *)&client->enable_read_backpressure) = client_config->enable_read_backpressure; *((size_t *)&client->initial_read_window) = client_config->initial_read_window; - client->num_network_interface_names = client_config->num_network_interface_names; - if (client_config->num_network_interface_names > 0) { - AWS_LOGF_DEBUG( - AWS_LS_S3_CLIENT, - "id=%p Client received network interface names array with length %zu.", - (void *)client, - client->num_network_interface_names); - aws_array_list_init_dynamic( - &client->network_interface_names, - client->allocator, - client_config->num_network_interface_names, - sizeof(struct aws_string *)); - client->network_interface_names_cursor_array = aws_mem_calloc( - client->allocator, client_config->num_network_interface_names, sizeof(struct aws_byte_cursor)); - for (size_t i = 0; i < client_config->num_network_interface_names; i++) { - struct aws_byte_cursor interface_name = client_config->network_interface_names_array[i]; - struct aws_string *interface_name_str = aws_string_new_from_cursor(client->allocator, &interface_name); - aws_array_list_push_back(&client->network_interface_names, &interface_name_str); - client->network_interface_names_cursor_array[i] = aws_byte_cursor_from_string(interface_name_str); - AWS_LOGF_DEBUG( - AWS_LS_S3_CLIENT, - "id=%p network_interface_names_array[%zu]=" PRInSTR "", - (void *)client, - i, - AWS_BYTE_CURSOR_PRI(client->network_interface_names_cursor_array[i])); - } - } - return client; on_error: @@ -628,10 +638,22 @@ struct aws_s3_client *aws_s3_client_new( aws_mem_release(client->allocator, client->proxy_ev_settings); aws_mem_release(client->allocator, client->tcp_keep_alive_options); - aws_event_loop_group_release(client->client_bootstrap->event_loop_group); + if (client->client_bootstrap != NULL) { + aws_event_loop_group_release(client->client_bootstrap->event_loop_group); + } aws_client_bootstrap_release(client->client_bootstrap); aws_mutex_clean_up(&client->synced_data.lock); -on_early_fail: + + aws_mem_release(client->allocator, client->network_interface_names_cursor_array); + for (size_t i = 0; i < aws_array_list_length(&client->network_interface_names); i++) { + struct aws_string *interface_name = NULL; + aws_array_list_get_at(&client->network_interface_names, &interface_name, i); + aws_string_destroy(interface_name); + } + + aws_array_list_clean_up(&client->network_interface_names); + aws_s3_buffer_pool_destroy(client->buffer_pool); + aws_mem_release(client->allocator, client); return NULL; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 81462114e..39ce8efb4 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -27,6 +27,8 @@ add_test_case(test_s3_abort_multipart_upload_message_new) add_net_test_case(test_s3_client_create_destroy) add_net_test_case(test_s3_client_create_error) +add_net_test_case(test_s3_client_create_error_with_invalid_memory_limit_config) +add_net_test_case(test_s3_client_create_error_with_invalid_network_interface) add_net_test_case(test_s3_client_monitoring_options_override) add_net_test_case(test_s3_client_proxy_ev_settings_override) add_net_test_case(test_s3_client_tcp_keep_alive_options_override) diff --git a/tests/s3_data_plane_tests.c b/tests/s3_data_plane_tests.c index 1e7beb96d..953fead0c 100644 --- a/tests/s3_data_plane_tests.c +++ b/tests/s3_data_plane_tests.c @@ -77,6 +77,61 @@ static int s_test_s3_client_create_error(struct aws_allocator *allocator, void * return 0; } +AWS_TEST_CASE( + test_s3_client_create_error_with_invalid_memory_limit_config, + s_test_s3_client_create_error_with_invalid_memory_limit_config) +static int s_test_s3_client_create_error_with_invalid_memory_limit_config(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_s3_tester tester; + AWS_ZERO_STRUCT(tester); + ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); + + struct aws_s3_client_config client_config; + AWS_ZERO_STRUCT(client_config); + ASSERT_SUCCESS(aws_s3_tester_bind_client(&tester, &client_config, AWS_S3_TESTER_BIND_CLIENT_REGION)); + + client_config.memory_limit_in_bytes = 100; + struct aws_s3_client *client = aws_s3_client_new(allocator, &client_config); + ASSERT_TRUE(client == NULL); + client_config.memory_limit_in_bytes = GB_TO_BYTES(1); + client_config.max_part_size = GB_TO_BYTES(2); + client = aws_s3_client_new(allocator, &client_config); + ASSERT_TRUE(client == NULL); + + tester.bound_to_client = false; + aws_s3_tester_clean_up(&tester); + return 0; +} + +AWS_TEST_CASE( + test_s3_client_create_error_with_invalid_network_interface, + s_test_s3_client_create_error_with_invalid_network_interface) +static int s_test_s3_client_create_error_with_invalid_network_interface(struct aws_allocator *allocator, void *ctx) { + (void)ctx; + + struct aws_s3_tester tester; + AWS_ZERO_STRUCT(tester); + ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); + + struct aws_s3_client_config client_config; + AWS_ZERO_STRUCT(client_config); + ASSERT_SUCCESS(aws_s3_tester_bind_client(&tester, &client_config, AWS_S3_TESTER_BIND_CLIENT_REGION)); + + struct aws_byte_cursor *interface_names_array = aws_mem_calloc(allocator, 1, sizeof(struct aws_byte_cursor)); + interface_names_array[0] = aws_byte_cursor_from_c_str("invalid-nic"); + client_config.network_interface_names_array = interface_names_array; + client_config.num_network_interface_names = 1; + + struct aws_s3_client *client = aws_s3_client_new(allocator, &client_config); + ASSERT_TRUE(client == NULL); + tester.bound_to_client = false; + + aws_s3_tester_clean_up(&tester); + aws_mem_release(allocator, interface_names_array); + return 0; +} + AWS_TEST_CASE(test_s3_client_monitoring_options_override, s_test_s3_client_monitoring_options_override) static int s_test_s3_client_monitoring_options_override(struct aws_allocator *allocator, void *ctx) { (void)ctx; diff --git a/tests/s3_mock_server_tests.c b/tests/s3_mock_server_tests.c index 29aadda03..1bb4b5b5d 100644 --- a/tests/s3_mock_server_tests.c +++ b/tests/s3_mock_server_tests.c @@ -169,16 +169,19 @@ TEST_CASE(multipart_upload_mock_server) { TEST_CASE(multipart_upload_with_network_interface_names_mock_server) { (void)ctx; - +#if defined(AWS_OS_WINDOWS) + (void)allocator; + return AWS_OP_SKIP; +#else struct aws_s3_tester tester; ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); struct aws_byte_cursor *interface_names_array = aws_mem_calloc(allocator, 2, sizeof(struct aws_byte_cursor)); char *localhost_interface = "\0"; -#if defined(AWS_OS_APPLE) +# if defined(AWS_OS_APPLE) localhost_interface = "lo0"; -#else +# else localhost_interface = "lo"; -#endif +# endif interface_names_array[0] = aws_byte_cursor_from_c_str(localhost_interface); interface_names_array[1] = aws_byte_cursor_from_c_str(localhost_interface); @@ -212,11 +215,11 @@ TEST_CASE(multipart_upload_with_network_interface_names_mock_server) { aws_s3_meta_request_test_results_init(&out_results, allocator); ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &put_options, &out_results)); if (out_results.finished_error_code != 0) { -#if !defined(AWS_OS_APPLE) && !defined(AWS_OS_LINUX) +# if !defined(AWS_OS_APPLE) && !defined(AWS_OS_LINUX) if (out_results.finished_error_code == AWS_ERROR_PLATFORM_NOT_SUPPORTED) { return AWS_OP_SKIP; } -#endif +# endif ASSERT_TRUE(false, "aws_s3_tester_send_meta_request_with_options(() failed"); } aws_s3_meta_request_test_results_clean_up(&out_results); @@ -225,6 +228,7 @@ TEST_CASE(multipart_upload_with_network_interface_names_mock_server) { aws_mem_release(allocator, interface_names_array); return AWS_OP_SUCCESS; +#endif } TEST_CASE(multipart_upload_checksum_with_retry_mock_server) { From 16701501fa9d1684b0ff5406211d058ce2a5b404 Mon Sep 17 00:00:00 2001 From: Waqar Ahmed Khan Date: Wed, 16 Oct 2024 11:44:47 -0700 Subject: [PATCH 7/7] Retry RequestTimeout error by S3. (#457) --- include/aws/s3/s3.h | 1 + source/s3.c | 1 + source/s3_util.c | 5 ++ tests/CMakeLists.txt | 1 + .../request_timeout.json | 15 ++++++ tests/s3_mock_server_tests.c | 47 +++++++++++++++++++ 6 files changed, 70 insertions(+) create mode 100644 tests/mock_s3_server/CreateMultipartUpload/request_timeout.json diff --git a/include/aws/s3/s3.h b/include/aws/s3/s3.h index c5416f10b..fa146f256 100644 --- a/include/aws/s3/s3.h +++ b/include/aws/s3/s3.h @@ -48,6 +48,7 @@ enum aws_s3_errors { AWS_ERROR_S3_REQUEST_HAS_COMPLETED, AWS_ERROR_S3_RECV_FILE_ALREADY_EXISTS, AWS_ERROR_S3_RECV_FILE_NOT_FOUND, + AWS_ERROR_S3_REQUEST_TIMEOUT, AWS_ERROR_S3_END_RANGE = AWS_ERROR_ENUM_END_RANGE(AWS_C_S3_PACKAGE_ID) }; diff --git a/source/s3.c b/source/s3.c index 266582d0c..a5a0d739a 100644 --- a/source/s3.c +++ b/source/s3.c @@ -51,6 +51,7 @@ static struct aws_error_info s_errors[] = { AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_REQUEST_HAS_COMPLETED, "Request has already completed, action cannot be performed."), AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_RECV_FILE_ALREADY_EXISTS, "File already exists, cannot create as new."), AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_RECV_FILE_NOT_FOUND, "The receive file doesn't exist, cannot create as configuration required."), + AWS_DEFINE_ERROR_INFO_S3(AWS_ERROR_S3_REQUEST_TIMEOUT, "RequestTimeout error received from S3."), }; /* clang-format on */ diff --git a/source/s3_util.c b/source/s3_util.c index 106f29be6..93d51e93a 100644 --- a/source/s3_util.c +++ b/source/s3_util.c @@ -687,6 +687,11 @@ int aws_s3_crt_error_code_from_recoverable_server_error_code_string(struct aws_b if (aws_byte_cursor_eq_c_str_ignore_case(&error_code_string, "RequestTimeTooSkewed")) { return AWS_ERROR_S3_REQUEST_TIME_TOO_SKEWED; } + + if (aws_byte_cursor_eq_c_str_ignore_case(&error_code_string, "RequestTimeout")) { + return AWS_ERROR_S3_REQUEST_TIMEOUT; + } + return AWS_ERROR_UNKNOWN; } diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 39ce8efb4..d34032446 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -339,6 +339,7 @@ if(ENABLE_MOCK_SERVER_TESTS) add_net_test_case(s3express_client_sanity_test_mock_server) add_net_test_case(s3express_client_sanity_override_test_mock_server) add_net_test_case(request_time_too_skewed_mock_server) + add_net_test_case(request_timeout_error_mock_server) endif() add_net_test_case(s3express_provider_long_running_session_refresh) diff --git a/tests/mock_s3_server/CreateMultipartUpload/request_timeout.json b/tests/mock_s3_server/CreateMultipartUpload/request_timeout.json new file mode 100644 index 000000000..a655ccd37 --- /dev/null +++ b/tests/mock_s3_server/CreateMultipartUpload/request_timeout.json @@ -0,0 +1,15 @@ + +{ + "status": 400, + "headers": {"x-amz-request-id": "12345"}, + "body": [ + "", + "", + "", + "RequestTimeout", + "Your socket connection to the server was not read from or written to within the timeout period. Idle connections will be closed.", + "1234", + "asdf", + "" + ] + } diff --git a/tests/s3_mock_server_tests.c b/tests/s3_mock_server_tests.c index 1bb4b5b5d..4ac61a315 100644 --- a/tests/s3_mock_server_tests.c +++ b/tests/s3_mock_server_tests.c @@ -1078,3 +1078,50 @@ TEST_CASE(request_time_too_skewed_mock_server) { return AWS_OP_SUCCESS; } + +TEST_CASE(request_timeout_error_mock_server) { + (void)ctx; + + struct aws_s3_tester tester; + ASSERT_SUCCESS(aws_s3_tester_init(allocator, &tester)); + struct aws_s3_tester_client_options client_options = { + .part_size = MB_TO_BYTES(5), + .tls_usage = AWS_S3_TLS_DISABLED, + }; + + struct aws_s3_client *client = NULL; + ASSERT_SUCCESS(aws_s3_tester_client_new(&tester, &client_options, &client)); + + struct aws_byte_cursor object_path = aws_byte_cursor_from_c_str("/request_timeout"); + struct aws_s3_meta_request_test_results out_results; + aws_s3_meta_request_test_results_init(&out_results, allocator); + + struct aws_s3_tester_meta_request_options put_options = { + .allocator = allocator, + .meta_request_type = AWS_S3_META_REQUEST_TYPE_PUT_OBJECT, + .client = client, + .checksum_algorithm = AWS_SCA_CRC32, + .validate_get_response_checksum = false, + .put_options = + { + .object_size_mb = 10, + .object_path_override = object_path, + }, + .mock_server = true, + .validate_type = AWS_S3_TESTER_VALIDATE_TYPE_EXPECT_FAILURE, + }; + + ASSERT_SUCCESS(aws_s3_tester_send_meta_request_with_options(&tester, &put_options, &out_results)); + + ASSERT_UINT_EQUALS(AWS_ERROR_S3_REQUEST_TIMEOUT, out_results.finished_error_code); + + /* The default retry will max out after 5 times. So, in total, it will be 6 requests, first one and 5 retries. */ + size_t result_num = aws_array_list_length(&out_results.synced_data.metrics); + ASSERT_UINT_EQUALS(6, result_num); + + aws_s3_meta_request_test_results_clean_up(&out_results); + aws_s3_client_release(client); + aws_s3_tester_clean_up(&tester); + + return AWS_OP_SUCCESS; +}