in Libraries/src/Amazon.Lambda.AspNetCoreServer/APIGatewayProxyFunction.cs [115:274]
protected override void MarshallRequest(InvokeFeatures features, APIGatewayProxyRequest apiGatewayRequest, ILambdaContext lambdaContext)
{
{
var authFeatures = (IHttpAuthenticationFeature)features;
var authorizer = apiGatewayRequest?.RequestContext?.Authorizer;
if (authorizer != null)
{
// handling claims output from cognito user pool authorizer
if (authorizer.Claims != null && authorizer.Claims.Count != 0)
{
var identity = new ClaimsIdentity(authorizer.Claims.Select(
entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity");
_logger.LogDebug(
$"Configuring HttpContext.User with {authorizer.Claims.Count} claims coming from API Gateway's Request Context");
authFeatures.User = new ClaimsPrincipal(identity);
}
else
{
// handling claims output from custom lambda authorizer
var identity = new ClaimsIdentity(
authorizer.Where(x => x.Value != null && !string.Equals(x.Key, "claims", StringComparison.OrdinalIgnoreCase))
.Select(entry => new Claim(entry.Key, entry.Value.ToString())), "AuthorizerIdentity");
_logger.LogDebug(
$"Configuring HttpContext.User with {authorizer.Count} claims coming from API Gateway's Request Context");
authFeatures.User = new ClaimsPrincipal(identity);
}
}
// Call consumers customize method in case they want to change how API Gateway's request
// was marshalled into ASP.NET Core request.
PostMarshallHttpAuthenticationFeature(authFeatures, apiGatewayRequest, lambdaContext);
}
{
var requestFeatures = (IHttpRequestFeature)features;
requestFeatures.Scheme = "https";
requestFeatures.Method = apiGatewayRequest.HttpMethod;
string path = null;
// Replaces {proxy+} in path, if exists
if (apiGatewayRequest.PathParameters != null && apiGatewayRequest.PathParameters.TryGetValue("proxy", out var proxy) &&
!string.IsNullOrEmpty(apiGatewayRequest.Resource))
{
var proxyPath = proxy;
path = apiGatewayRequest.Resource.Replace("{proxy+}", proxyPath);
// Adds all the rest of non greedy parameters in apiGateway.Resource to the path
foreach (var pathParameter in apiGatewayRequest.PathParameters.Where(pp => pp.Key != "proxy"))
{
path = path.Replace($"{{{pathParameter.Key}}}", pathParameter.Value);
}
}
if (string.IsNullOrEmpty(path))
{
path = apiGatewayRequest.Path;
}
if (!path.StartsWith("/"))
{
path = "/" + path;
}
var rawQueryString = Utilities.CreateQueryStringParameters(
apiGatewayRequest.QueryStringParameters, apiGatewayRequest.MultiValueQueryStringParameters, true);
requestFeatures.RawTarget = apiGatewayRequest.Path + rawQueryString;
requestFeatures.QueryString = rawQueryString;
requestFeatures.Path = path;
requestFeatures.PathBase = string.Empty;
if (!string.IsNullOrEmpty(apiGatewayRequest?.RequestContext?.Path))
{
// This is to cover the case where the request coming in is https://myapigatewayid.execute-api.us-west-2.amazonaws.com/Prod where
// Prod is the stage name and there is no ending '/'. Path will be set to '/' so to make sure we detect the correct base path
// append '/' on the end to make the later EndsWith and substring work correctly.
var requestContextPath = apiGatewayRequest.RequestContext.Path;
if (path.EndsWith("/") && !requestContextPath.EndsWith("/"))
{
requestContextPath += "/";
}
else if (!path.EndsWith("/") && requestContextPath.EndsWith("/"))
{
// Handle a trailing slash in the request path: e.g. https://myapigatewayid.execute-api.us-west-2.amazonaws.com/Prod/foo/
requestFeatures.Path = path += "/";
}
if (requestContextPath.EndsWith(path))
{
requestFeatures.PathBase = requestContextPath.Substring(0,
requestContextPath.Length - requestFeatures.Path.Length);
}
}
requestFeatures.Path = Utilities.DecodeResourcePath(requestFeatures.Path);
Utilities.SetHeadersCollection(requestFeatures.Headers, apiGatewayRequest.Headers, apiGatewayRequest.MultiValueHeaders);
if (!requestFeatures.Headers.ContainsKey("Host"))
{
var apiId = apiGatewayRequest?.RequestContext?.ApiId ?? "";
var stage = apiGatewayRequest?.RequestContext?.Stage ?? "";
requestFeatures.Headers["Host"] = $"apigateway-{apiId}-{stage}";
}
if (!string.IsNullOrEmpty(apiGatewayRequest.Body))
{
requestFeatures.Body = Utilities.ConvertLambdaRequestBodyToAspNetCoreBody(apiGatewayRequest.Body, apiGatewayRequest.IsBase64Encoded);
}
// Make sure the content-length header is set if header was not present.
const string contentLengthHeaderName = "Content-Length";
if (!requestFeatures.Headers.ContainsKey(contentLengthHeaderName))
{
requestFeatures.Headers[contentLengthHeaderName] = requestFeatures.Body == null ? "0" : requestFeatures.Body.Length.ToString(CultureInfo.InvariantCulture);
}
// Call consumers customize method in case they want to change how API Gateway's request
// was marshalled into ASP.NET Core request.
PostMarshallRequestFeature(requestFeatures, apiGatewayRequest, lambdaContext);
}
{
// set up connection features
var connectionFeatures = (IHttpConnectionFeature)features;
if (!string.IsNullOrEmpty(apiGatewayRequest?.RequestContext?.Identity?.SourceIp) &&
IPAddress.TryParse(apiGatewayRequest.RequestContext.Identity.SourceIp, out var remoteIpAddress))
{
connectionFeatures.RemoteIpAddress = remoteIpAddress;
}
if (apiGatewayRequest?.Headers?.TryGetValue("X-Forwarded-Port", out var forwardedPort) == true)
{
connectionFeatures.RemotePort = int.Parse(forwardedPort, CultureInfo.InvariantCulture);
}
// Call consumers customize method in case they want to change how API Gateway's request
// was marshalled into ASP.NET Core request.
PostMarshallConnectionFeature(connectionFeatures, apiGatewayRequest, lambdaContext);
}
{
var tlsConnectionFeature = (ITlsConnectionFeature)features;
var clientCertPem = apiGatewayRequest?.RequestContext?.Identity?.ClientCert?.ClientCertPem;
if (clientCertPem != null)
{
tlsConnectionFeature.ClientCertificate = Utilities.GetX509Certificate2FromPem(clientCertPem);
}
PostMarshallTlsConnectionFeature(tlsConnectionFeature, apiGatewayRequest, lambdaContext);
}
}