Skip to content

Commit 4b75b92

Browse files
committed
optimize HttpRequestMessageSerializer and remove AspNetCoreServers's dependency on RuntimeSupport
1 parent 4d504ca commit 4b75b92

4 files changed

Lines changed: 132 additions & 85 deletions

File tree

Libraries/src/Amazon.Lambda.AspNetCoreServer/AbstractAspNetCoreFunction.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -337,19 +337,17 @@ protected void Start()
337337
{
338338
var invokeTimes = 5;
339339

340-
var json = await HttpRequestMessageSerializer.SerializeToJson<TREQUEST>(httpRequest);
341-
342-
var request = HttpRequestMessageSerializer.Deserialize<TREQUEST>(json);
340+
var request = await HttpRequestMessageSerializer.ConvertToLambdaRequest<TREQUEST>(httpRequest);
343341

344342
InvokeFeatures features = new InvokeFeatures();
345343
MarshallRequest(features, request, new SnapStartEmptyLambdaContext());
346344

347345
var context = CreateContext(features);
348346

349-
var lambdaContext = new SnapStartEmptyLambdaContext();
350-
351347
for (var i = 0; i < invokeTimes; i++)
352348
{
349+
var lambdaContext = new SnapStartEmptyLambdaContext();
350+
353351
await ProcessRequest(lambdaContext, context, features);
354352
}
355353
}

Libraries/src/Amazon.Lambda.AspNetCoreServer/Amazon.Lambda.AspNetCoreServer.csproj

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,6 @@
3131

3232
<ItemGroup>
3333
<FrameworkReference Include="Microsoft.AspNetCore.App" />
34-
<ProjectReference Include="..\Amazon.Lambda.RuntimeSupport\Amazon.Lambda.RuntimeSupport.csproj" />
3534
<ProjectReference Include="..\Amazon.Lambda.Serialization.SystemTextJson\Amazon.Lambda.Serialization.SystemTextJson.csproj" />
3635
</ItemGroup>
3736

Lines changed: 85 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
#if NET8_0_OR_GREATER
12
using System;
23
using System.Collections.Generic;
34
using System.Linq;
@@ -8,9 +9,10 @@
89
using System.Threading.Tasks;
910
using Amazon.Lambda.APIGatewayEvents;
1011
using Amazon.Lambda.ApplicationLoadBalancerEvents;
12+
using Microsoft.AspNetCore.Identity.Data;
1113
using Microsoft.AspNetCore.WebUtilities;
14+
using Microsoft.Extensions.Primitives;
1215

13-
#if NET8_0_OR_GREATER
1416
namespace Amazon.Lambda.AspNetCoreServer.Internal
1517
{
1618
/// <summary>
@@ -26,8 +28,18 @@ namespace Amazon.Lambda.AspNetCoreServer.Internal
2628
public class HttpRequestMessageSerializer
2729
{
2830
private static readonly Uri _baseUri = new Uri("http://localhost");
31+
32+
internal class DuckRequest
33+
{
34+
public string Body { get; set; }
35+
public Dictionary<string, string> Headers { get; set; } = new();
36+
public string HttpMethod { get; set; }
37+
public string Path { get; set; }
38+
public string RawQuery { get; set; }
39+
public Dictionary<string, StringValues> Query { get; set; } = new();
40+
}
2941

30-
public static async Task<string> SerializeToJson<TRequest>(HttpRequestMessage request)
42+
public static async Task<TRequest> ConvertToLambdaRequest<TRequest>(HttpRequestMessage request)
3143
{
3244
if (null == request.RequestUri)
3345
{
@@ -42,7 +54,7 @@ public static async Task<string> SerializeToJson<TRequest>(HttpRequestMessage re
4254
// make request absolut (relative to localhost) otherwise parsing the query will not work
4355
request.RequestUri = new Uri(_baseUri, request.RequestUri);
4456

45-
var duckRequest = new
57+
var duckRequest = new DuckRequest
4658
{
4759
Body = await ReadContent(request),
4860
Headers = request.Headers
@@ -56,52 +68,75 @@ public static async Task<string> SerializeToJson<TRequest>(HttpRequestMessage re
5668
Query = QueryHelpers.ParseNullableQuery(request.RequestUri?.Query)
5769
};
5870

71+
if (typeof(TRequest) == typeof(ApplicationLoadBalancerRequest))
72+
{
73+
return (TRequest)(object) new ApplicationLoadBalancerRequest
74+
{
75+
Body = duckRequest.Body,
76+
Headers = duckRequest.Headers,
77+
Path = duckRequest.Path,
78+
HttpMethod = duckRequest.HttpMethod,
79+
QueryStringParameters = duckRequest.Query?.ToDictionary(k => k.Key, v => v.Value.ToString())
80+
};
81+
}
82+
83+
if (typeof(TRequest) == typeof(APIGatewayHttpApiV2ProxyRequest))
84+
{
85+
return (TRequest)(object)new APIGatewayHttpApiV2ProxyRequest
86+
{
87+
Body = duckRequest.Body,
88+
Headers = duckRequest.Headers,
89+
RawPath = duckRequest.Path,
90+
RequestContext = new APIGatewayHttpApiV2ProxyRequest.ProxyRequestContext
91+
{
92+
Http = new APIGatewayHttpApiV2ProxyRequest.HttpDescription
93+
{
94+
Method = duckRequest.HttpMethod,
95+
Path = duckRequest.Path
96+
}
97+
},
98+
QueryStringParameters = duckRequest.Query?.ToDictionary(k => k.Key, v => v.Value.ToString()),
99+
RawQueryString = duckRequest.RawQuery
100+
};
101+
}
102+
103+
if (typeof(TRequest) == typeof(APIGatewayProxyRequest))
104+
{
105+
return (TRequest)(object)new APIGatewayProxyRequest
106+
{
107+
Body = duckRequest.Body,
108+
Headers = duckRequest.Headers,
109+
Path = duckRequest.Path,
110+
HttpMethod = duckRequest.HttpMethod,
111+
RequestContext = new APIGatewayProxyRequest.ProxyRequestContext
112+
{
113+
HttpMethod = duckRequest.HttpMethod
114+
},
115+
QueryStringParameters = duckRequest.Query?.ToDictionary(k => k.Key, v => v.Value.ToString())
116+
};
117+
}
118+
119+
throw new NotImplementedException(
120+
$"Unknown request type: {typeof(TRequest).FullName}");
121+
}
122+
123+
public static async Task<string> SerializeToJson<TRequest>(HttpRequestMessage request)
124+
{
125+
var lambdaRequest = await ConvertToLambdaRequest<TRequest>(request);
126+
59127
string translatedRequestJson = typeof(TRequest) switch
60128
{
61129
var t when t == typeof(ApplicationLoadBalancerRequest) =>
62130
JsonSerializer.Serialize(
63-
new ApplicationLoadBalancerRequest
64-
{
65-
Body = duckRequest.Body,
66-
Headers = duckRequest.Headers,
67-
Path = duckRequest.Path,
68-
HttpMethod = duckRequest.HttpMethod,
69-
QueryStringParameters = duckRequest.Query?.ToDictionary(k => k.Key, v => v.Value.ToString())
70-
},
131+
lambdaRequest,
71132
LambdaRequestTypeClasses.Default.ApplicationLoadBalancerRequest),
72133
var t when t == typeof(APIGatewayHttpApiV2ProxyRequest) =>
73134
JsonSerializer.Serialize(
74-
new APIGatewayHttpApiV2ProxyRequest
75-
{
76-
Body = duckRequest.Body,
77-
Headers = duckRequest.Headers,
78-
RawPath = duckRequest.Path,
79-
RequestContext = new APIGatewayHttpApiV2ProxyRequest.ProxyRequestContext
80-
{
81-
Http = new APIGatewayHttpApiV2ProxyRequest.HttpDescription
82-
{
83-
Method = duckRequest.HttpMethod,
84-
Path = duckRequest.Path
85-
}
86-
},
87-
QueryStringParameters = duckRequest.Query?.ToDictionary(k => k.Key, v => v.Value.ToString()),
88-
RawQueryString = duckRequest.RawQuery
89-
},
135+
lambdaRequest,
90136
LambdaRequestTypeClasses.Default.APIGatewayHttpApiV2ProxyRequest),
91137
var t when t == typeof(APIGatewayProxyRequest) =>
92138
JsonSerializer.Serialize(
93-
new APIGatewayProxyRequest
94-
{
95-
Body = duckRequest.Body,
96-
Headers = duckRequest.Headers,
97-
Path = duckRequest.Path,
98-
HttpMethod = duckRequest.HttpMethod,
99-
RequestContext = new APIGatewayProxyRequest.ProxyRequestContext
100-
{
101-
HttpMethod = duckRequest.HttpMethod
102-
},
103-
QueryStringParameters = duckRequest.Query?.ToDictionary(k => k.Key, v => v.Value.ToString())
104-
},
139+
lambdaRequest,
105140
LambdaRequestTypeClasses.Default.APIGatewayProxyRequest),
106141
_ => throw new NotImplementedException(
107142
$"Unknown request type: {typeof(TRequest).FullName}")
@@ -110,37 +145,18 @@ public static async Task<string> SerializeToJson<TRequest>(HttpRequestMessage re
110145
return translatedRequestJson;
111146
}
112147

113-
/// <summary>
114-
/// Specialized Deserializer that uses the AOT Compatible
115-
/// <see cref="LambdaRequestTypeClasses"/> to deserialize common
116-
/// Request types.
117-
/// </summary>
118-
public static TRequest Deserialize<TRequest>(string json)
119-
{
120-
return typeof(TRequest) switch
121-
{
122-
var t when t == typeof(ApplicationLoadBalancerRequest) =>
123-
JsonSerializer.Deserialize<TRequest>(json, LambdaRequestTypeClasses.Default.ApplicationLoadBalancerRequest),
124-
var t when t == typeof(APIGatewayHttpApiV2ProxyRequest) =>
125-
JsonSerializer.Deserialize<TRequest>(json, LambdaRequestTypeClasses.Default.APIGatewayHttpApiV2ProxyRequest),
126-
var t when t == typeof(APIGatewayProxyRequest) =>
127-
JsonSerializer.Deserialize<TRequest>(json, LambdaRequestTypeClasses.Default.APIGatewayProxyRequest),
128-
_ => throw new NotImplementedException(
129-
$"Unknown request type: {typeof(TRequest).FullName}")
130-
};
131-
}
132-
133148
private static async Task<string> ReadContent(HttpRequestMessage r)
134149
{
135150
if (r.Content == null)
136151
return string.Empty;
137152

138153
return await r.Content.ReadAsStringAsync();
139154
}
155+
}
140156

141-
[JsonSourceGenerationOptions(WriteIndented = true)]
142-
[JsonSerializable(typeof(ApplicationLoadBalancerRequest))]
143-
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest))]
157+
[JsonSourceGenerationOptions]
158+
[JsonSerializable(typeof(ApplicationLoadBalancerRequest))]
159+
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest))]
144160
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest.ProxyRequestContext))]
145161
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest.ProxyRequestAuthentication))]
146162
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest.ProxyRequestClientCert))]
@@ -150,14 +166,13 @@ private static async Task<string> ReadContent(HttpRequestMessage r)
150166
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest.AuthorizerDescription.IAMDescription))]
151167
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest.AuthorizerDescription.CognitoIdentityDescription))]
152168
[JsonSerializable(typeof(APIGatewayHttpApiV2ProxyRequest.AuthorizerDescription.JwtDescription))]
153-
[JsonSerializable(typeof(APIGatewayProxyRequest))]
154-
[JsonSerializable(typeof(APIGatewayProxyRequest.ClientCertValidity))]
155-
[JsonSerializable(typeof(APIGatewayProxyRequest.ProxyRequestClientCert))]
156-
[JsonSerializable(typeof(APIGatewayProxyRequest.ProxyRequestContext))]
157-
[JsonSerializable(typeof(APIGatewayProxyRequest.RequestIdentity))]
158-
internal partial class LambdaRequestTypeClasses : JsonSerializerContext
159-
{
160-
}
169+
[JsonSerializable(typeof(APIGatewayProxyRequest))]
170+
[JsonSerializable(typeof(APIGatewayProxyRequest.ClientCertValidity))]
171+
[JsonSerializable(typeof(APIGatewayProxyRequest.ProxyRequestClientCert))]
172+
[JsonSerializable(typeof(APIGatewayProxyRequest.ProxyRequestContext))]
173+
[JsonSerializable(typeof(APIGatewayProxyRequest.RequestIdentity))]
174+
internal partial class LambdaRequestTypeClasses : JsonSerializerContext
175+
{
161176
}
162177
}
163178
#endif

Libraries/src/Amazon.Lambda.AspNetCoreServer/Internal/SnapStartEmptyLambdaContext.cs

Lines changed: 44 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,64 @@
1-
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
1+
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
22
// SPDX-License-Identifier: Apache-2.0
33

44
using System;
55
using System.Collections.Generic;
66
using Amazon.Lambda.Core;
7-
using Amazon.Lambda.RuntimeSupport;
87

98
namespace Amazon.Lambda.AspNetCoreServer.Internal;
109

1110
internal class SnapStartEmptyLambdaContext : ILambdaContext, ICognitoIdentity, IClientContext
1211
{
13-
private LambdaEnvironment _lambdaEnvironment = new();
12+
private static Dictionary<string, string> _environmentVariables = new();
13+
14+
// Copied from Amazon.Lambda.RuntimeSupport.LambdaEnvironment to avoid adding
15+
// a reference to that project
16+
private const string EnvVarFunctionMemorySize = "AWS_LAMBDA_FUNCTION_MEMORY_SIZE";
17+
private const string EnvVarFunctionName = "AWS_LAMBDA_FUNCTION_NAME";
18+
private const string EnvVarFunctionVersion = "AWS_LAMBDA_FUNCTION_VERSION";
19+
private const string EnvVarHandler = "_HANDLER";
20+
private const string EnvVarLogGroupName = "AWS_LAMBDA_LOG_GROUP_NAME";
21+
private const string EnvVarLogStreamName = "AWS_LAMBDA_LOG_STREAM_NAME";
22+
23+
24+
static SnapStartEmptyLambdaContext()
25+
{
26+
AddEnvValue(EnvVarFunctionMemorySize, "128");
27+
AddEnvValue(EnvVarFunctionName, "fallbackFunctionName");
28+
AddEnvValue(EnvVarFunctionVersion, "0");
29+
AddEnvValue(EnvVarLogGroupName, "fallbackLogGroup");
30+
AddEnvValue(EnvVarLogStreamName, "fallbackLogStream");
31+
}
32+
33+
private static void AddEnvValue(string envName, string fallback)
34+
{
35+
var val = System.Environment.GetEnvironmentVariable(envName);
36+
37+
val = string.IsNullOrEmpty(val) ? fallback : val;
38+
39+
_environmentVariables[envName] = val;
40+
}
41+
42+
public SnapStartEmptyLambdaContext()
43+
{
44+
// clone the static environment variables into the local instance
45+
foreach (var k in _environmentVariables.Keys)
46+
Environment[k] = _environmentVariables[k];
47+
}
48+
1449

1550
public string TraceId => string.Empty;
1651
public string AwsRequestId => string.Empty;
1752
public IClientContext ClientContext => this;
18-
public string FunctionName => _lambdaEnvironment.FunctionName;
19-
public string FunctionVersion => _lambdaEnvironment.FunctionVersion;
53+
public string FunctionName => Environment[EnvVarFunctionName];
54+
public string FunctionVersion => Environment[EnvVarFunctionVersion];
2055
public ICognitoIdentity Identity => this;
2156
public string InvokedFunctionArn => string.Empty;
2257
public ILambdaLogger Logger => null;
23-
public string LogGroupName => _lambdaEnvironment.LogGroupName;
24-
public string LogStreamName => _lambdaEnvironment.LogStreamName;
25-
public int MemoryLimitInMB => 128;
26-
public TimeSpan RemainingTime => TimeSpan.FromMilliseconds(100);
58+
public string LogGroupName => Environment[EnvVarLogGroupName];
59+
public string LogStreamName => Environment[EnvVarLogStreamName];
60+
public int MemoryLimitInMB => int.Parse(Environment[EnvVarFunctionMemorySize]);
61+
public TimeSpan RemainingTime => TimeSpan.FromSeconds(5);
2762
public string IdentityId { get; }
2863
public string IdentityPoolId { get; }
2964
public IDictionary<string, string> Environment { get; } = new Dictionary<string, string>();

0 commit comments

Comments
 (0)