3
3
4
4
namespace Amazon . Lambda . TestTool . Extensions ;
5
5
6
+ using System . Text ;
6
7
using System . Web ;
7
8
using Amazon . Lambda . APIGatewayEvents ;
8
9
using Amazon . Lambda . TestTool . Models ;
@@ -20,19 +21,20 @@ public static class HttpContextExtensions
20
21
/// <param name="context">The <see cref="HttpContext"/> to be translated.</param>
21
22
/// <param name="apiGatewayRouteConfig">The configuration of the API Gateway route, including the HTTP method, path, and other metadata.</param>
22
23
/// <returns>An <see cref="APIGatewayHttpApiV2ProxyRequest"/> object representing the translated request.</returns>
23
- public static APIGatewayHttpApiV2ProxyRequest ToApiGatewayHttpV2Request (
24
+ public static async Task < APIGatewayHttpApiV2ProxyRequest > ToApiGatewayHttpV2Request (
24
25
this HttpContext context ,
25
26
ApiGatewayRouteConfig apiGatewayRouteConfig )
26
27
{
27
28
var request = context . Request ;
28
29
var currentTime = DateTimeOffset . UtcNow ;
29
- var body = HttpRequestUtility . ReadRequestBody ( request ) ;
30
+ var body = await HttpRequestUtility . ReadRequestBody ( request ) ;
30
31
var contentLength = HttpRequestUtility . CalculateContentLength ( request , body ) ;
31
32
32
33
var pathParameters = RouteTemplateUtility . ExtractPathParameters ( apiGatewayRouteConfig . Path , request . Path ) ;
33
34
34
35
// Format 2.0 doesn't have multiValueHeaders or multiValueQueryStringParameters fields. Duplicate headers are combined with commas and included in the headers field.
35
- var ( _, allHeaders ) = HttpRequestUtility . ExtractHeaders ( request . Headers ) ;
36
+ // 2.0 also lowercases all header keys
37
+ var ( _, allHeaders ) = HttpRequestUtility . ExtractHeaders ( request . Headers , true ) ;
36
38
var headers = allHeaders . ToDictionary (
37
39
kvp => kvp . Key ,
38
40
kvp => string . Join ( ", " , kvp . Value )
@@ -91,6 +93,7 @@ public static APIGatewayHttpApiV2ProxyRequest ToApiGatewayHttpV2Request(
91
93
}
92
94
93
95
httpApiV2ProxyRequest . RawQueryString = string . Empty ; // default is empty string
96
+
94
97
if ( queryStringParameters . Any ( ) )
95
98
{
96
99
// this should be decoded value
@@ -123,35 +126,52 @@ public static APIGatewayHttpApiV2ProxyRequest ToApiGatewayHttpV2Request(
123
126
/// <param name="context">The <see cref="HttpContext"/> to be translated.</param>
124
127
/// <param name="apiGatewayRouteConfig">The configuration of the API Gateway route, including the HTTP method, path, and other metadata.</param>
125
128
/// <returns>An <see cref="APIGatewayProxyRequest"/> object representing the translated request.</returns>
126
- public static APIGatewayProxyRequest ToApiGatewayRequest (
129
+ public static async Task < APIGatewayProxyRequest > ToApiGatewayRequest (
127
130
this HttpContext context ,
128
- ApiGatewayRouteConfig apiGatewayRouteConfig )
131
+ ApiGatewayRouteConfig apiGatewayRouteConfig ,
132
+ ApiGatewayEmulatorMode emulatorMode )
129
133
{
130
134
var request = context . Request ;
131
- var body = HttpRequestUtility . ReadRequestBody ( request ) ;
135
+ var body = await HttpRequestUtility . ReadRequestBody ( request ) ;
132
136
var contentLength = HttpRequestUtility . CalculateContentLength ( request , body ) ;
133
137
134
138
var pathParameters = RouteTemplateUtility . ExtractPathParameters ( apiGatewayRouteConfig . Path , request . Path ) ;
135
139
136
140
var ( headers , multiValueHeaders ) = HttpRequestUtility . ExtractHeaders ( request . Headers ) ;
137
141
var ( queryStringParameters , multiValueQueryStringParameters ) = HttpRequestUtility . ExtractQueryStringParameters ( request . Query ) ;
138
142
139
- if ( ! headers . ContainsKey ( "content-length" ) )
143
+ if ( ! headers . ContainsKey ( "content-length" ) && emulatorMode != ApiGatewayEmulatorMode . Rest ) // rest doesnt set content-length by default
140
144
{
141
145
headers [ "content-length" ] = contentLength . ToString ( ) ;
142
- multiValueHeaders [ "content-length" ] = new List < string > { contentLength . ToString ( ) } ;
146
+ multiValueHeaders [ "content-length" ] = [ contentLength . ToString ( ) ] ;
143
147
}
144
148
145
149
if ( ! headers . ContainsKey ( "content-type" ) )
146
150
{
147
151
headers [ "content-type" ] = "text/plain; charset=utf-8" ;
148
- multiValueHeaders [ "content-type" ] = new List < string > { "text/plain; charset=utf-8" } ;
152
+ multiValueHeaders [ "content-type" ] = [ "text/plain; charset=utf-8" ] ;
153
+ }
154
+
155
+ // This is the decoded value
156
+ var path = request . Path . Value ;
157
+
158
+ if ( emulatorMode == ApiGatewayEmulatorMode . HttpV1 || emulatorMode == ApiGatewayEmulatorMode . Rest ) // rest and httpv1 uses the encoded value for path an
159
+ {
160
+ path = request . Path . ToUriComponent ( ) ;
161
+ }
162
+
163
+ if ( emulatorMode == ApiGatewayEmulatorMode . Rest ) // rest uses encoded value for the path params
164
+ {
165
+ var encodedPathParameters = pathParameters . ToDictionary (
166
+ kvp => kvp . Key ,
167
+ kvp => Uri . EscapeUriString ( kvp . Value ) ) ; // intentionally using EscapeURiString over EscapeDataString since EscapeURiString correctly handles reserved characters :/?#[]@!$&'()*+,;= in this case
168
+ pathParameters = encodedPathParameters ;
149
169
}
150
170
151
171
var proxyRequest = new APIGatewayProxyRequest
152
172
{
153
173
Resource = apiGatewayRouteConfig . Path ,
154
- Path = request . Path . Value ,
174
+ Path = path ,
155
175
HttpMethod = request . Method ,
156
176
Body = body ,
157
177
IsBase64Encoded = false
@@ -181,13 +201,11 @@ public static APIGatewayProxyRequest ToApiGatewayRequest(
181
201
182
202
if ( pathParameters . Any ( ) )
183
203
{
184
- // this should be decoded value
185
204
proxyRequest . PathParameters = pathParameters ;
186
205
}
187
206
188
207
if ( HttpRequestUtility . IsBinaryContent ( request . ContentType ) )
189
208
{
190
- // we already converted it when we read the body so we dont need to re-convert it
191
209
proxyRequest . IsBase64Encoded = true ;
192
210
}
193
211
0 commit comments